Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
CRAP | |
0.00% |
0 / 1 |
NoBaseExceptionVisitor | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
90 | |
0.00% |
0 / 1 |
visitNew | |
0.00% |
0 / 32 |
|
0.00% |
0 / 1 |
90 |
1 | <?php |
2 | |
3 | declare( strict_types=1 ); |
4 | |
5 | namespace MediaWikiPhanConfig\Plugin; |
6 | |
7 | use ast\Node; |
8 | use Phan\AST\UnionTypeVisitor; |
9 | use Phan\Exception\FQSENException; |
10 | use Phan\Exception\IssueException; |
11 | use Phan\Language\FQSEN\FullyQualifiedClassName; |
12 | use Phan\PluginV3\PluginAwarePostAnalysisVisitor; |
13 | use const ast\AST_NAME; |
14 | |
15 | class NoBaseExceptionVisitor extends PluginAwarePostAnalysisVisitor { |
16 | /** |
17 | * @inheritDoc |
18 | */ |
19 | public function visitNew( Node $node ): void { |
20 | $classNode = $node->children['class']; |
21 | if ( !$classNode instanceof Node ) { |
22 | // Syntax error, bail out |
23 | return; |
24 | } |
25 | if ( $classNode->kind !== AST_NAME ) { |
26 | // A variable or another dynamic expression, skip for now. |
27 | // TODO We could try and handle this, at least in the easy cases (e.g., if the value of the variable |
28 | // can be inferred statically). |
29 | // Note that we can't just check the union type, because it could be `Exception` even if the class is not |
30 | // `Exception` itself (e.g., if the union type is inferred from a doc comment or typehint that only uses |
31 | // `Exception` as the ancestor type of all the potential values). |
32 | return; |
33 | } |
34 | if ( !isset( $classNode->children['name'] ) ) { |
35 | // Syntax error, bail out |
36 | return; |
37 | } |
38 | |
39 | try { |
40 | $classNameType = UnionTypeVisitor::unionTypeFromClassNode( |
41 | $this->code_base, |
42 | $this->context, |
43 | $classNode |
44 | ); |
45 | } catch ( IssueException | FQSENException $_ ) { |
46 | return; |
47 | } |
48 | |
49 | if ( $classNameType->typeCount() !== 1 ) { |
50 | // Shouldn't happen |
51 | return; |
52 | } |
53 | $classType = $classNameType->getTypeSet()[0]; |
54 | if ( !$classType->isObjectWithKnownFQSEN() ) { |
55 | // Syntax error or something. |
56 | return; |
57 | } |
58 | $classFQSEN = $classType->asFQSEN(); |
59 | if ( !$classFQSEN instanceof FullyQualifiedClassName ) { |
60 | // Should not happen. |
61 | return; |
62 | } |
63 | |
64 | $classFQSENStr = (string)$classFQSEN; |
65 | if ( $classFQSENStr === '\Exception' ) { |
66 | self::emitPluginIssue( |
67 | $this->code_base, |
68 | $this->context, |
69 | NoBaseExceptionPlugin::ISSUE_TYPE, |
70 | // Links to https://www.mediawiki.org/wiki/Manual:Coding_conventions/PHP#Exception_handling |
71 | 'Instantiating {CLASS} directly is not allowed. Use SPL exceptions if the exception is ' . |
72 | 'unchecked, or define a custom exception class otherwise. See https://w.wiki/6nur', |
73 | [ $classFQSENStr ] |
74 | ); |
75 | } |
76 | } |
77 | } |