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.
Completed
Branch 0.14.0 (b23fa7)
by Gytis
12:27 queued 07:51
created

FqcnMethodSniff::hasUselessDocBlock()   C

Complexity

Conditions 15
Paths 17

Size

Total Lines 48
Code Lines 29

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 15

Importance

Changes 3
Bugs 0 Features 0
Metric Value
cc 15
eloc 29
c 3
b 0
f 0
nc 17
nop 1
dl 0
loc 48
ccs 30
cts 30
cp 1
crap 15
rs 5.9166

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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 $reportNullableBasicGetterDocType = true;
38
39
    /** @var bool */
40
    protected $reportNullableBasicGetterFnType = true;
41
42
    /**
43
     * @inheritDoc
44
     */
45 6
    public function configure(array $config): void
46
    {
47
        // TagInterface uses lowercase tags names, no @ symbol in front
48 6
        $invalidTags = [];
49 6
        foreach ($config['invalidTags'] ?? [] as $rawTag) {
50 1
            $invalidTags[] = strtolower(ltrim($rawTag, '@'));
51
        }
52 6
        $invalidTags = array_unique($invalidTags);
53
54 6
        $this->invalidTags = $invalidTags;
55 6
        $this->reportMissingTags = $config['reportMissingTags'] ?? true;
56 6
        $this->reportNullableBasicGetterDocType = $config['reportNullableBasicGetterDocType'] ?? true;
57 6
        $this->reportNullableBasicGetterFnType = $config['reportNullableBasicGetterFnType'] ?? true;
58 6
    }
59
60
    /**
61
     * @inheritDoc
62
     */
63 6
    public function register(): array
64
    {
65
        return [
66 6
            ClassMethodElement::class,
67
            // TraitMethodElement::class, // can be used to implement interface, not possible to know if it is extended
68
            InterfaceMethodElement::class,
69
        ];
70
    }
71
72
    /**
73
     * @inheritDoc
74
     * @param AbstractFqcnMethodElement $method
75
     */
76 5
    public function process(File $file, CodeElementInterface $method, CodeElementInterface $parentElement): void
77
    {
78 5
        $warningCountBefore = $file->getWarningCount();
79
80 5
        static::reportInvalidTags($file, $method, $this->invalidTags);
81 5
        $this->processMethod($file, $method, $parentElement);
82
83 5
        $hasNewWarnings = $file->getWarningCount() > $warningCountBefore;
84 5
        if (!$hasNewWarnings && $this->hasUselessDocBlock($method)) {
85 3
            $file->addWarningOnLine('Useless PHPDoc', $method->getLine(), static::CODE);
86
        }
87 5
    }
88
89 5
    protected function processMethod(File $file, AbstractFqcnMethodElement $method, CodeElementInterface $parent): void
90
    {
91 5
        $fnSig = $method->getSignature();
92 5
        $docBlock = $method->getDocBlock();
93 5
        $isMagicMethod = '__' === substr($fnSig->getName(), 0, 2);
94 5
        $isConstructMethod = '__construct' === $fnSig->getName();
95 5
        $hasInheritDocTag = $docBlock->hasTag('inheritdoc');
96
97
        // @inheritDoc
98
        // __construct can be detected as extended and magic, but we want to inspect it anyway
99 5
        if (!$isConstructMethod) {
100 5
            if ($hasInheritDocTag || $isMagicMethod) {
101 1
                return;
102 5
            } elseif ($method->getMetadata()->isExtended()) {
103 1
                $file->addWarningOnLine('Missing @inheritDoc tag. Remove duplicated parent PHPDoc content.', $method->getLine(), static::CODE);
104 1
                return;
105
            }
106
        }
107
108
        // @param
109 5
        foreach ($fnSig->getParams() as $fnParam) {
110 5
            $paramTag = $docBlock->getParamTag($fnParam->getName());
111 5
            $subject = ParamTypeSubject::fromParam($fnParam, $paramTag, $docBlock);
112 5
            $this->processSigType($file, $subject);
113
        }
114
115
        // @return
116 5
        if (!$isConstructMethod) {
117 5
            $returnTag = $docBlock->getReturnTag();
118 5
            $subject = ReturnTypeSubject::fromSignature($fnSig, $returnTag, $docBlock);
119 5
            $this->processSigType($file, $subject);
120
121 5
            if ($method instanceof ClassMethodElement && $parent instanceof ClassElement) {
122 5
                ($this->reportNullableBasicGetterDocType || $this->reportNullableBasicGetterFnType) // skip if both disabled
123 5
                && static::reportNullableBasicGetter($file, $subject, $method, $parent);
0 ignored issues
show
Bug Best Practice introduced by
The method Gskema\TypeSniff\Sniffs\...rtNullableBasicGetter() is not static, but was called statically. ( Ignorable by Annotation )

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

123
                && static::/** @scrutinizer ignore-call */ reportNullableBasicGetter($file, $subject, $method, $parent);
Loading history...
124
            }
125
        } else {
126 2
            foreach ($docBlock->getDescriptionLines() as $lineNum => $descLine) {
127 1
                if (preg_match('#^\w+\s+constructor\.?$#', $descLine)) {
128 1
                    $file->addWarningOnLine('Useless description.', $lineNum, static::CODE);
129
                }
130
            }
131
        }
132 5
    }
133
134 5
    protected function processSigType(File $file, AbstractTypeSubject $subject): void
135
    {
136 5
        FnTypeInspector::reportMandatoryTypes($subject);
137 5
        FnTypeInspector::reportSuggestedTypes($subject);
138 5
        FnTypeInspector::reportReplaceableTypes($subject);
139
140 5
        if ($this->reportMissingTags || $subject->hasDocTypeTag()) {
141 5
            DocTypeInspector::reportMandatoryTypes($subject);
142 5
            DocTypeInspector::reportSuggestedTypes($subject);
143 5
            DocTypeInspector::reportReplaceableTypes($subject);
144
145 5
            DocTypeInspector::reportRemovableTypes($subject);
146 5
            DocTypeInspector::reportInvalidTypes($subject);
147 5
            DocTypeInspector::reportMissingOrWrongTypes($subject);
148
        } else {
149 1
            DocTypeInspector::reportMandatoryTypes($subject, true);
150
        }
151
152 5
        $subject->writeWarningsTo($file, static::CODE);
153 5
    }
154
155 10
    protected function hasUselessDocBlock(AbstractFqcnMethodElement $method): bool
156
    {
157 10
        $fnSig = $method->getSignature();
158 10
        $docBlock = $method->getDocBlock();
159
160 10
        $docReturnTag = $docBlock->getReturnTag();
161
162 10
        if ($docBlock instanceof UndefinedDocBlock
163 10
            || $docBlock->hasDescription()
164 9
            || ($docReturnTag && $docReturnTag->hasDescription())
165
            // check if other "useful" tags are present
166 10
            || array_diff($docBlock->getTagNames(), ['param', 'return'])
167
        ) {
168 4
            return false;
169
        }
170
171 9
        foreach ($fnSig->getParams() as $fnParam) {
172 9
            $paramTag = $docBlock->getParamTag($fnParam->getName());
173 9
            if (null === $paramTag) {
174 1
                return false; // missing, needs to be fixed
175
            }
176
177 8
            if ($paramTag->hasDescription()) {
178 3
                return false;
179
            }
180
181 5
            $fnType = $fnParam->getType();
182 5
            $rawFnType = $fnType instanceof NullableType
183 1
                ? $fnType->toDocString()
184 5
                : $fnType->toString();
185 5
            if ($paramTag->getType()->toString() !== $rawFnType) {
186 3
                return false;
187
            }
188
        }
189
190 4
        $returnTag  = $docBlock->getReturnTag();
191 4
        $returnType = $fnSig->getReturnType();
192
193 4
        if ($returnTag && $returnType) {
194 3
            $rawReturnType = $returnType instanceof NullableType
195 1
                ? $returnType->toDocString()
196 3
                : $returnType->toString();
197 3
            if ($returnTag->getType()->toString() !== $rawReturnType) {
198 1
                return false;
199
            }
200
        }
201
202 4
        return true;
203
    }
204
205
    /**
206
     * @param File                      $file
207
     * @param AbstractFqcnMethodElement $method
208
     * @param static[]                  $invalidTags
209
     */
210 5
    protected static function reportInvalidTags(File $file, AbstractFqcnMethodElement $method, array $invalidTags): void
211
    {
212 5
        foreach ($method->getDocBlock()->getTags() as $tag) {
213 5
            foreach ($invalidTags as $invalidTagName) {
214 1
                if ($tag->getName() === $invalidTagName) {
215 1
                    $file->addWarningOnLine('Useless tag', $tag->getLine(), static::CODE);
216
                }
217
            }
218
        }
219 5
    }
220
221 5
    protected function reportNullableBasicGetter(
222
        File $file,
223
        ReturnTypeSubject $subject,
224
        ClassMethodElement $method,
225
        ClassElement $class
226
    ): void {
227 5
        $propName = $method->getMetadata()->getBasicGetterPropName();
228 5
        if (null === $propName) {
229 5
            return;
230
        }
231
232 1
        $prop = $class->getProperty($propName);
233 1
        if (null === $prop) {
234
            return;
235
        }
236
237
        /** @var VarTag|null $varTag */
238 1
        $varTag = $prop->getDocBlock()->getTagsByName('var')[0] ?? null;
239 1
        if (null === $varTag) {
240
            return;
241
        }
242
243 1
        $propDocType = $varTag->getType();
244 1
        $isPropNullable = TypeHelper::containsType($varTag->getType(), NullType::class);
245 1
        if (!$isPropNullable) {
246
            return;
247
        }
248
249 1
        $returnDocType = $subject->getDocType();
250 1
        $isGetterDocTypeNullable = TypeHelper::containsType($returnDocType, NullType::class);
251 1
        if (!$isGetterDocTypeNullable && $subject->hasDefinedDocBlock()) {
252 1
            $this->reportNullableBasicGetterDocType && $subject->addDocTypeWarning(sprintf(
253 1
                'Returned property $%s is nullable, add null return doc type, e.g. %s',
254
                $propName,
255 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

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