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.

FqcnMethodSniff::hasUselessDocBlock()   C
last analyzed

Complexity

Conditions 15
Paths 17

Size

Total Lines 49
Code Lines 30

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 30
CRAP Score 15

Importance

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

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\SniffHelper;
8
use Gskema\TypeSniff\Core\Type\Common\UndefinedType;
9
use Gskema\TypeSniff\Core\Type\DocBlock\NullType;
10
use Gskema\TypeSniff\Core\Type\TypeHelper;
11
use Gskema\TypeSniff\Inspection\FnTypeInspector;
12
use Gskema\TypeSniff\Inspection\DocTypeInspector;
13
use Gskema\TypeSniff\Inspection\Subject\AbstractTypeSubject;
14
use Gskema\TypeSniff\Inspection\Subject\ParamTypeSubject;
15
use Gskema\TypeSniff\Inspection\Subject\ReturnTypeSubject;
16
use PHP_CodeSniffer\Files\File;
17
use Gskema\TypeSniff\Core\CodeElement\Element\AbstractFqcnMethodElement;
18
use Gskema\TypeSniff\Core\CodeElement\Element\ClassMethodElement;
19
use Gskema\TypeSniff\Core\CodeElement\Element\CodeElementInterface;
20
use Gskema\TypeSniff\Core\CodeElement\Element\InterfaceMethodElement;
21
use Gskema\TypeSniff\Core\DocBlock\UndefinedDocBlock;
22
use Gskema\TypeSniff\Core\Type\Declaration\NullableType;
23
24
/**
25
 * @see FqcnMethodSniffTest
26
 */
27
class FqcnMethodSniff implements CodeElementSniffInterface
28
{
29
    protected const CODE = 'FqcnMethodSniff';
30
31
    /** @var string[] */
32
    protected array $invalidTags = [];
33
34
    protected bool $reportMissingTags = true;
35
36
    protected bool $reportNullableBasicGetter = true;
37
38
    protected string $reportType = 'warning';
39
40
    protected bool $addViolationId = false;
41
42
    /**
43
     * @inheritDoc
44
     */
45 15
    public function configure(array $config): void
46
    {
47
        // TagInterface uses lowercase tags names, no @ symbol in front
48 15
        $invalidTags = [];
49 15
        foreach ($config['invalidTags'] ?? [] as $rawTag) {
50 1
            $invalidTags[] = strtolower(ltrim($rawTag, '@'));
51
        }
52 15
        $invalidTags = array_unique($invalidTags);
53
54 15
        $this->invalidTags = $invalidTags;
55 15
        $this->reportMissingTags = (bool)($config['reportMissingTags'] ?? true);
56 15
        $this->reportNullableBasicGetter = (bool)($config['reportNullableBasicGetter'] ?? true);
57 15
        $this->reportType = (string)($config['reportType'] ?? 'warning');
58 15
        $this->addViolationId = (bool)($config['addViolationId'] ?? false);
59 15
    }
60
61
    /**
62
     * @inheritDoc
63
     */
64 15
    public function register(): array
65
    {
66
        return [
67 15
            ClassMethodElement::class,
68
            // TraitMethodElement::class, // can be used to implement interface, not possible to know if it is extended
69
            InterfaceMethodElement::class,
70
        ];
71
    }
72
73
    /**
74
     * @inheritDoc
75
     * @param AbstractFqcnMethodElement $method
76
     */
77 12
    public function process(File $file, CodeElementInterface $method, CodeElementInterface $parentElement): void
78
    {
79 12
        $warningCountBefore = $file->getWarningCount();
80
81 12
        $this->reportInvalidTags($file, $method, $this->invalidTags);
82 12
        $this->processMethod($file, $method, $parentElement);
83
84 12
        $hasNewWarnings = $file->getWarningCount() > $warningCountBefore;
85 12
        if (!$hasNewWarnings && $this->hasUselessDocBlock($method)) {
86 4
            $originId = $this->addViolationId ? $method->getId() : null;
0 ignored issues
show
Bug introduced by
The method getId() does not exist on Gskema\TypeSniff\Core\Co...nt\CodeElementInterface. It seems like you code against a sub-type of Gskema\TypeSniff\Core\Co...nt\CodeElementInterface such as Gskema\TypeSniff\Core\Co...stractFqcnMethodElement. ( Ignorable by Annotation )

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

86
            $originId = $this->addViolationId ? $method->/** @scrutinizer ignore-call */ getId() : null;
Loading history...
87 4
            SniffHelper::addViolation($file, 'Useless PHPDoc', $method->getLine(), static::CODE, $this->reportType, $originId);
88
        }
89 12
    }
90
91 12
    protected function processMethod(File $file, AbstractFqcnMethodElement $method, CodeElementInterface $parent): void
92
    {
93 12
        $fnSig = $method->getSignature();
94 12
        $docBlock = $method->getDocBlock();
95 12
        $isMagicMethod = '__' === substr($fnSig->getName(), 0, 2);
96 12
        $isConstructMethod = '__construct' === $fnSig->getName();
97 12
        $hasInheritDocTag = $docBlock->hasTag('inheritdoc');
98
99
        // @inheritDoc
100
        // __construct can be detected as extended and magic, but we want to inspect it anyway
101 12
        if (!$isConstructMethod) {
102 12
            if ($hasInheritDocTag || $isMagicMethod) {
103 1
                return;
104 12
            } elseif ($method->getMetadata()->isExtended()) {
105 1
                $originId = $this->addViolationId ? $method->getId() : null;
106 1
                SniffHelper::addViolation($file, 'Missing @inheritDoc tag. Remove duplicated parent PHPDoc content.', $method->getLine(), static::CODE, $this->reportType, $originId);
107 1
                return;
108
            }
109
        }
110
111
        // @param
112 12
        foreach ($fnSig->getParams() as $fnParam) {
113 10
            $paramTag = $docBlock->getParamTag($fnParam->getName());
114 10
            $id = $method->getId() . $fnParam->getName();
115 10
            $subject = ParamTypeSubject::fromParam($fnParam, $paramTag, $docBlock, $id);
116 10
            $this->processSigType($subject);
117 10
            $subject->writeViolationsTo($file, static::CODE, $this->reportType, $this->addViolationId);
118
        }
119
120
        // @return
121 12
        if (!$isConstructMethod) {
122 12
            $returnTag = $docBlock->getReturnTag();
123 12
            $subject = ReturnTypeSubject::fromSignature($fnSig, $returnTag, $docBlock, $method->getAttributeNames(), $method->getId());
124 12
            $this->processSigType($subject);
125 12
            if ($method instanceof ClassMethodElement && $parent instanceof ClassElement) {
126 10
                $this->reportNullableBasicGetter && $this->reportNullableBasicGetter($subject, $method, $parent);
127
            }
128 12
            $subject->writeViolationsTo($file, static::CODE, $this->reportType, $this->addViolationId);
129
        } else {
130 7
            foreach ($docBlock->getDescriptionLines() as $lineNum => $descLine) {
131 2
                if (preg_match('#^\w+\s+constructor\.?$#', $descLine)) {
132 2
                    $originId = $this->addViolationId ? $method->getId() : null;
133 2
                    SniffHelper::addViolation($file, 'Useless description.', $lineNum, static::CODE, $this->reportType, $originId);
134
                }
135
            }
136
        }
137 12
    }
138
139 12
    protected function processSigType(AbstractTypeSubject $subject): void
140
    {
141 12
        FnTypeInspector::reportSuggestedTypes($subject);
142 12
        FnTypeInspector::reportReplaceableTypes($subject);
143
144 12
        if ($this->reportMissingTags || $subject->hasDocTypeTag()) {
145 12
            DocTypeInspector::reportMandatoryTypes($subject);
146 12
            DocTypeInspector::reportSuggestedTypes($subject);
147 12
            DocTypeInspector::reportReplaceableTypes($subject);
148
149 12
            DocTypeInspector::reportRemovableTypes($subject);
150 12
            DocTypeInspector::reportInvalidTypes($subject);
151 12
            DocTypeInspector::reportMissingOrWrongTypes($subject);
152
        } else {
153 2
            DocTypeInspector::reportMandatoryTypes($subject, true);
154
        }
155 12
    }
156
157 16
    protected function hasUselessDocBlock(AbstractFqcnMethodElement $method): bool
158
    {
159 16
        $fnSig = $method->getSignature();
160 16
        $docBlock = $method->getDocBlock();
161
162 16
        $docReturnTag = $docBlock->getReturnTag();
163
164
        if (
165 16
            $docBlock instanceof UndefinedDocBlock
166 15
            || $docBlock->hasDescription()
167 14
            || ($docReturnTag && $docReturnTag->hasDescription())
168
            // check if other "useful" tags are present
169 16
            || array_diff($docBlock->getTagNames(), ['param', 'return'])
170
        ) {
171 9
            return false;
172
        }
173
174 14
        foreach ($fnSig->getParams() as $fnParam) {
175 11
            $paramTag = $docBlock->getParamTag($fnParam->getName());
176 11
            if (null === $paramTag) {
177 1
                return false; // missing, needs to be fixed
178
            }
179
180 10
            if ($paramTag->hasDescription()) {
181 3
                return false;
182
            }
183
184 7
            $fnType = $fnParam->getType();
185 7
            $rawFnType = $fnType instanceof NullableType
186 2
                ? $fnType->toDocString()
187 7
                : $fnType->toString();
188 7
            if ($paramTag->getType()->toString() !== $rawFnType) {
189 5
                return false;
190
            }
191
        }
192
193 8
        $returnTag  = $docBlock->getReturnTag();
194 8
        $returnType = $fnSig->getReturnType();
195
196 8
        if ($returnTag && $returnType) {
197 7
            $rawReturnType = $returnType instanceof NullableType
198 4
                ? $returnType->toDocString()
199 7
                : $returnType->toString();
200 7
            if ($returnTag->getType()->toString() !== $rawReturnType) {
201 4
                return false;
202
            }
203
        }
204
205 5
        return true;
206
    }
207
208
    /**
209
     * @param File                      $file
210
     * @param AbstractFqcnMethodElement $method
211
     * @param string[]                  $invalidTags
212
     */
213 12
    protected function reportInvalidTags(File $file, AbstractFqcnMethodElement $method, array $invalidTags): void
214
    {
215 12
        foreach ($method->getDocBlock()->getTags() as $tag) {
216 10
            foreach ($invalidTags as $invalidTagName) {
217 1
                if ($tag->getName() === $invalidTagName) {
218 1
                    $originId = $this->addViolationId ? $method->getId() . $invalidTagName : null;
219 1
                    SniffHelper::addViolation($file, 'Useless tag', $tag->getLine(), static::CODE, $this->reportType, $originId);
220
                }
221
            }
222
        }
223 12
    }
224
225 10
    protected function reportNullableBasicGetter(
226
        ReturnTypeSubject $subject,
227
        ClassMethodElement $method,
228
        ClassElement $class
229
    ): void {
230 10
        $propName = $method->getMetadata()->getBasicGetterPropName();
231 10
        if (null === $propName) {
232 7
            return;
233
        }
234
235 5
        $prop = $class->getProperty($propName);
236 5
        if (null === $prop) {
237 5
            return;
238
        }
239
240
        /** @var VarTag|null $varTag */
241 3
        $varTag = $prop->getDocBlock()->getTagsByName('var')[0] ?? null;
242 3
        if (null === $varTag) {
243 1
            return;
244
        }
245
246 3
        $propDocType = $varTag->getType();
247 3
        $isPropNullable = TypeHelper::containsType($varTag->getType(), NullType::class);
248 3
        if (!$isPropNullable) {
249 1
            return;
250
        }
251
252 3
        $returnDocType = $subject->getDocType();
253 3
        $isGetterDocTypeNullable = TypeHelper::containsType($returnDocType, NullType::class);
254 3
        if ($returnDocType || $this->reportMissingTags) {
255 2
            if (!$isGetterDocTypeNullable && $subject->hasDefinedDocBlock()) {
256 2
                $subject->addDocTypeWarning(sprintf(
257 2
                    'Returned property $%s is nullable, add null return doc type, e.g. %s',
258
                    $propName,
259 2
                    $propDocType->toString()
260
                ));
261
            }
262
        }
263
264
        // Only report in fn type is defined. Doc type and fn type is synced by other sniffs.
265 3
        $returnFnType = $subject->getFnType();
266 3
        if (!($returnFnType instanceof UndefinedType) && !($returnFnType instanceof NullableType)) {
267 1
            $subject->addFnTypeWarning(sprintf(
268 1
                'Returned property $%s is nullable, use nullable return type declaration, e.g. ?%s',
269
                $propName,
270 1
                $returnFnType->toString()
271
            ));
272
        }
273 3
    }
274
}
275