GitHub Access Token became invalid

It seems like the GitHub access token used for retrieving details about this repository from GitHub became invalid. This might prevent certain types of inspections from being run (in particular, everything related to pull requests).
Please ask an admin of your repository to re-new the access token on this website.
Passed
Push — master ( e5e542...80e7c7 )
by Gytis
04:34 queued 13s
created

FqcnMethodSniff::configure()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 12
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 8
CRAP Score 2

Importance

Changes 3
Bugs 0 Features 1
Metric Value
cc 2
eloc 7
c 3
b 0
f 1
nc 2
nop 1
dl 0
loc 12
ccs 8
cts 8
cp 1
crap 2
rs 10
1
<?php
2
3
namespace Gskema\TypeSniff\Sniffs\CodeElement;
4
5
use Gskema\TypeSniff\Core\CodeElement\Element\ClassElement;
6
use Gskema\TypeSniff\Core\DocBlock\Tag\VarTag;
7
use Gskema\TypeSniff\Core\Type\Common\UndefinedType;
8
use Gskema\TypeSniff\Core\Type\DocBlock\NullType;
9
use Gskema\TypeSniff\Core\Type\TypeHelper;
10
use Gskema\TypeSniff\Inspection\FnTypeInspector;
11
use Gskema\TypeSniff\Inspection\DocTypeInspector;
12
use Gskema\TypeSniff\Inspection\Subject\AbstractTypeSubject;
13
use Gskema\TypeSniff\Inspection\Subject\ParamTypeSubject;
14
use Gskema\TypeSniff\Inspection\Subject\ReturnTypeSubject;
15
use PHP_CodeSniffer\Files\File;
16
use Gskema\TypeSniff\Core\CodeElement\Element\AbstractFqcnMethodElement;
17
use Gskema\TypeSniff\Core\CodeElement\Element\ClassMethodElement;
18
use Gskema\TypeSniff\Core\CodeElement\Element\CodeElementInterface;
19
use Gskema\TypeSniff\Core\CodeElement\Element\InterfaceMethodElement;
20
use Gskema\TypeSniff\Core\DocBlock\UndefinedDocBlock;
21
use Gskema\TypeSniff\Core\Type\Declaration\NullableType;
22
23
/**
24
 * @see FqcnMethodSniffTest
25
 */
26
class FqcnMethodSniff implements CodeElementSniffInterface
27
{
28
    protected const CODE = 'FqcnMethodSniff';
29
30
    /** @var string[] */
31
    protected $invalidTags = [];
32
33
    /** @var bool */
34
    protected $reportMissingTags = true;
35
36
    /** @var bool */
37
    protected $reportNullableBasicGetter = true;
38
39
    /**
40
     * @inheritDoc
41
     */
42 10
    public function configure(array $config): void
43
    {
44
        // TagInterface uses lowercase tags names, no @ symbol in front
45 10
        $invalidTags = [];
46 10
        foreach ($config['invalidTags'] ?? [] as $rawTag) {
47 1
            $invalidTags[] = strtolower(ltrim($rawTag, '@'));
48
        }
49 10
        $invalidTags = array_unique($invalidTags);
50
51 10
        $this->invalidTags = $invalidTags;
52 10
        $this->reportMissingTags = $config['reportMissingTags'] ?? true;
53 10
        $this->reportNullableBasicGetter = $config['reportNullableBasicGetter'] ?? true;
54 10
    }
55
56
    /**
57
     * @inheritDoc
58
     */
59 10
    public function register(): array
60
    {
61
        return [
62 10
            ClassMethodElement::class,
63
            // TraitMethodElement::class, // can be used to implement interface, not possible to know if it is extended
64
            InterfaceMethodElement::class,
65
        ];
66
    }
67
68
    /**
69
     * @inheritDoc
70
     * @param AbstractFqcnMethodElement $method
71
     */
72 8
    public function process(File $file, CodeElementInterface $method, CodeElementInterface $parentElement): void
73
    {
74 8
        $warningCountBefore = $file->getWarningCount();
75
76 8
        static::reportInvalidTags($file, $method, $this->invalidTags);
77 8
        $this->processMethod($file, $method, $parentElement);
78
79 8
        $hasNewWarnings = $file->getWarningCount() > $warningCountBefore;
80 8
        if (!$hasNewWarnings && $this->hasUselessDocBlock($method)) {
81 3
            $file->addWarningOnLine('Useless PHPDoc', $method->getLine(), static::CODE);
82
        }
83 8
    }
84
85 8
    protected function processMethod(File $file, AbstractFqcnMethodElement $method, CodeElementInterface $parent): void
86
    {
87 8
        $fnSig = $method->getSignature();
88 8
        $docBlock = $method->getDocBlock();
89 8
        $isMagicMethod = '__' === substr($fnSig->getName(), 0, 2);
90 8
        $isConstructMethod = '__construct' === $fnSig->getName();
91 8
        $hasInheritDocTag = $docBlock->hasTag('inheritdoc');
92
93
        // @inheritDoc
94
        // __construct can be detected as extended and magic, but we want to inspect it anyway
95 8
        if (!$isConstructMethod) {
96 8
            if ($hasInheritDocTag || $isMagicMethod) {
97 1
                return;
98 8
            } elseif ($method->getMetadata()->isExtended()) {
99 1
                $file->addWarningOnLine('Missing @inheritDoc tag. Remove duplicated parent PHPDoc content.', $method->getLine(), static::CODE);
100 1
                return;
101
            }
102
        }
103
104
        // @param
105 8
        foreach ($fnSig->getParams() as $fnParam) {
106 7
            $paramTag = $docBlock->getParamTag($fnParam->getName());
107 7
            $subject = ParamTypeSubject::fromParam($fnParam, $paramTag, $docBlock);
108 7
            $this->processSigType($file, $subject);
109
        }
110
111
        // @return
112 8
        if (!$isConstructMethod) {
113 8
            $returnTag = $docBlock->getReturnTag();
114 8
            $subject = ReturnTypeSubject::fromSignature($fnSig, $returnTag, $docBlock);
115 8
            $this->processSigType($file, $subject);
116
117 8
            if ($method instanceof ClassMethodElement && $parent instanceof ClassElement) {
118 8
                $this->reportNullableBasicGetter && static::reportNullableBasicGetter($file, $subject, $method, $parent);
119
            }
120
        } else {
121 5
            foreach ($docBlock->getDescriptionLines() as $lineNum => $descLine) {
122 1
                if (preg_match('#^\w+\s+constructor\.?$#', $descLine)) {
123 1
                    $file->addWarningOnLine('Useless description.', $lineNum, static::CODE);
124
                }
125
            }
126
        }
127 8
    }
128
129 8
    protected function processSigType(File $file, AbstractTypeSubject $subject): void
130
    {
131 8
        FnTypeInspector::reportMandatoryTypes($subject);
132 8
        FnTypeInspector::reportSuggestedTypes($subject);
133 8
        FnTypeInspector::reportReplaceableTypes($subject);
134
135 8
        if ($this->reportMissingTags || $subject->hasDocTypeTag()) {
136 8
            DocTypeInspector::reportMandatoryTypes($subject);
137 8
            DocTypeInspector::reportSuggestedTypes($subject);
138 8
            DocTypeInspector::reportReplaceableTypes($subject);
139
140 8
            DocTypeInspector::reportRemovableTypes($subject);
141 8
            DocTypeInspector::reportInvalidTypes($subject);
142 8
            DocTypeInspector::reportMissingOrWrongTypes($subject);
143
        } else {
144 1
            DocTypeInspector::reportMandatoryTypes($subject, true);
145
        }
146
147 8
        $subject->writeWarningsTo($file, static::CODE);
148 8
    }
149
150 12
    protected function hasUselessDocBlock(AbstractFqcnMethodElement $method): bool
151
    {
152 12
        $fnSig = $method->getSignature();
153 12
        $docBlock = $method->getDocBlock();
154
155 12
        $docReturnTag = $docBlock->getReturnTag();
156
157 12
        if ($docBlock instanceof UndefinedDocBlock
158 10
            || $docBlock->hasDescription()
159 9
            || ($docReturnTag && $docReturnTag->hasDescription())
160
            // check if other "useful" tags are present
161 12
            || array_diff($docBlock->getTagNames(), ['param', 'return'])
162
        ) {
163 6
            return false;
164
        }
165
166 9
        foreach ($fnSig->getParams() as $fnParam) {
167 9
            $paramTag = $docBlock->getParamTag($fnParam->getName());
168 9
            if (null === $paramTag) {
169 1
                return false; // missing, needs to be fixed
170
            }
171
172 8
            if ($paramTag->hasDescription()) {
173 3
                return false;
174
            }
175
176 5
            $fnType = $fnParam->getType();
177 5
            $rawFnType = $fnType instanceof NullableType
178 1
                ? $fnType->toDocString()
179 5
                : $fnType->toString();
180 5
            if ($paramTag->getType()->toString() !== $rawFnType) {
181 3
                return false;
182
            }
183
        }
184
185 4
        $returnTag  = $docBlock->getReturnTag();
186 4
        $returnType = $fnSig->getReturnType();
187
188 4
        if ($returnTag && $returnType) {
189 3
            $rawReturnType = $returnType instanceof NullableType
190 1
                ? $returnType->toDocString()
191 3
                : $returnType->toString();
192 3
            if ($returnTag->getType()->toString() !== $rawReturnType) {
193 1
                return false;
194
            }
195
        }
196
197 4
        return true;
198
    }
199
200
    /**
201
     * @param File                      $file
202
     * @param AbstractFqcnMethodElement $method
203
     * @param static[]                  $invalidTags
204
     */
205 8
    protected static function reportInvalidTags(File $file, AbstractFqcnMethodElement $method, array $invalidTags): void
206
    {
207 8
        foreach ($method->getDocBlock()->getTags() as $tag) {
208 5
            foreach ($invalidTags as $invalidTagName) {
209 1
                if ($tag->getName() === $invalidTagName) {
210 1
                    $file->addWarningOnLine('Useless tag', $tag->getLine(), static::CODE);
211
                }
212
            }
213
        }
214 8
    }
215
216 7
    protected static function reportNullableBasicGetter(
217
        File $file,
218
        ReturnTypeSubject $subject,
219
        ClassMethodElement $method,
220
        ClassElement $class
221
    ): void {
222 7
        $propName = $method->getMetadata()->getBasicGetterPropName();
223 7
        if (null === $propName) {
224 5
            return;
225
        }
226
227 3
        $prop = $class->getProperty($propName);
228 3
        if (null === $prop) {
229 3
            return;
230
        }
231
232
        /** @var VarTag|null $varTag */
233 1
        $varTag = $prop->getDocBlock()->getTagsByName('var')[0] ?? null;
234 1
        if (null === $varTag) {
235 1
            return;
236
        }
237
238 1
        $propDocType = $varTag->getType();
239 1
        $isPropNullable = TypeHelper::containsType($varTag->getType(), NullType::class);
240 1
        if (!$isPropNullable) {
241 1
            return;
242
        }
243
244 1
        $returnDocType = $subject->getDocType();
245 1
        $isGetterDocTypeNullable = TypeHelper::containsType($returnDocType, NullType::class);
246 1
        if (!$isGetterDocTypeNullable && $subject->hasDefinedDocBlock()) {
247 1
            $subject->addDocTypeWarning(sprintf(
248 1
                'Returned property $%s is nullable, add null return doc type, e.g. %s',
249 1
                $propName,
250 1
                ($returnDocType->toString() ?? $propDocType).'|null'
0 ignored issues
show
Bug introduced by
Are you sure $returnDocType->toString() ?? $propDocType of type Gskema\TypeSniff\Core\Type\TypeInterface|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

250
                (/** @scrutinizer ignore-type */ $returnDocType->toString() ?? $propDocType).'|null'
Loading history...
251
            ));
252
        }
253
254
        // Only report in fn type is defined. Doc type and fn type is synced by other sniffs.
255 1
        $returnFnType = $subject->getFnType();
256 1
        if (!($returnFnType instanceof UndefinedType) && !($returnFnType instanceof NullableType)) {
257 1
            $subject->addFnTypeWarning(sprintf(
258 1
                'Returned property $%s is nullable, use nullable return type declaration, e.g. ?%s',
259 1
                $propName,
260 1
                $returnFnType->toString()
261
            ));
262
        }
263
264 1
        $subject->writeWarningsTo($file, static::CODE);
265 1
    }
266
}
267