[!!!][TASK] Drop core 6->7 auto TCA migrations
[Packages/TYPO3.CMS.git] / Build / Scripts / annotationChecker.php
1 #!/usr/bin/env php
2 <?php
3 declare(strict_types = 1);
4
5 /*
6 * This file is part of the TYPO3 CMS project.
7 *
8 * It is free software; you can redistribute it and/or modify it under
9 * the terms of the GNU General Public License, either version 2
10 * of the License, or any later version.
11 *
12 * For the full copyright and license information, please read the
13 * LICENSE.txt file that was distributed with this source code.
14 *
15 * The TYPO3 project - inspiring people to share!
16 */
17
18 use PhpParser\Comment\Doc;
19 use PhpParser\Error;
20 use PhpParser\Node;
21 use PhpParser\NodeTraverser;
22 use PhpParser\NodeVisitorAbstract;
23 use PhpParser\ParserFactory;
24 use Symfony\Component\Console\Output\ConsoleOutput;
25
26 require_once __DIR__ . '/../../vendor/autoload.php';
27
28 /**
29 * Class NodeVisitor
30 */
31 class NodeVisitor extends NodeVisitorAbstract
32 {
33 /**
34 * @var array
35 */
36 public $matches = [];
37
38 public function enterNode(Node $node)
39 {
40 switch (get_class($node)) {
41 case Node\Stmt\Class_::class:
42 case Node\Stmt\Property::class:
43 case Node\Stmt\ClassMethod::class:
44 /** Node\Stmt\ClassMethod $node */
45 if (!($docComment = $node->getDocComment()) instanceof Doc) {
46 return;
47 }
48
49 // These annotations are OK to have, everything else is denied
50 $negativeLookaheadMatches = [
51 // Annotation tags
52 'Annotation', 'Attribute', 'Attributes', 'Required', 'Target',
53 // Widely used tags (but not existent in phpdoc)
54 'fix', 'fixme', 'override',
55 // PHPDocumentor 1 tags
56 'abstract', 'code', 'deprec', 'endcode', 'exception', 'final', 'ingroup', 'inheritdoc', 'inheritDoc', 'magic', 'name', 'toc', 'tutorial', 'private', 'static', 'staticvar', 'staticVar', 'throw',
57 // PHPDocumentor 2 tags
58 'api', 'author', 'category', 'copyright', 'deprecated', 'example', 'filesource', 'global', 'ignore', 'internal', 'license', 'link', 'method', 'package', 'param', 'property', 'property-read', 'property-write', 'return', 'see', 'since', 'source', 'subpackage', 'throws', 'todo', 'TODO', 'usedby', 'uses', 'var', 'version',
59 // PHPUnit tags
60 'codeCoverageIgnore', 'codeCoverageIgnoreStart', 'codeCoverageIgnoreEnd', 'test', 'covers', 'dataProvider', 'group', 'skip', 'depends', 'expectedException', 'before', 'requires',
61 // codeception tags
62 'env',
63 // PHPCheckStyle
64 'SuppressWarnings', 'noinspection',
65 // Extbase related
66 'TYPO3\\\\CMS\\\\Extbase\\\\Annotation\\\\IgnoreValidation', 'Extbase\\\\IgnoreValidation', 'IgnoreValidation',
67 'TYPO3\\\\CMS\\\\Extbase\\\\Annotation\\\\Inject', 'Extbase\\\\Inject', 'Inject',
68 'TYPO3\\\\CMS\\\\Extbase\\\\Annotation\\\\Validate', 'Extbase\\\\Validate', 'Validate',
69 'TYPO3\\\\CMS\\\\Extbase\\\\Annotation\\\\ORM\\\\Cascade', 'Extbase\\\\ORM\\\\Cascade', 'Cascade',
70 'TYPO3\\\\CMS\\\\Extbase\\\\Annotation\\\\ORM\\\\Lazy', 'Extbase\\\\ORM\\\\Lazy', 'Lazy',
71 'TYPO3\\\\CMS\\\\Extbase\\\\Annotation\\\\ORM\\\\Transient', 'Extbase\\\\ORM\\\\Transient', 'Transient',
72 // annotations shipped with doctrine/annotations
73 'Doctrine\\\\Common\\\\Annotations\\\\Annotation\\\\Enum', 'Enum',
74 // Extension scanner
75 'extensionScannerIgnoreFile', 'extensionScannerIgnoreLine'
76 ];
77
78 $matches = [];
79 preg_match_all(
80 '/\*\s@(?!' . implode('|', $negativeLookaheadMatches) . ')(?<annotations>[a-zA-Z0-9\\\\]+)/',
81 $docComment->getText(),
82 $matches
83 );
84
85 if (!empty($matches['annotations'])) {
86 $this->matches[$node->getLine()] = array_map(function ($value) {
87 return '@' . $value;
88 }, $matches['annotations']);
89 }
90
91 break;
92 default:
93 break;
94 }
95 }
96 }
97
98 $parser = (new ParserFactory)->create(ParserFactory::ONLY_PHP7);
99
100 $finder = new Symfony\Component\Finder\Finder();
101 $finder->files()
102 ->in(__DIR__ . '/../../typo3/')
103 ->name('/\.php$/')
104 // black list some unit test fixture files from extension scanner that test matchers of old annotations
105 ->notName('MethodAnnotationMatcherFixture.php')
106 ->notName('PropertyAnnotationMatcherFixture.php')
107 ;
108
109 $output = new ConsoleOutput();
110
111 $errors = [];
112 foreach ($finder as $file) {
113 try {
114 $ast = $parser->parse($file->getContents());
115 } catch (Error $error) {
116 $output->writeln('<error>Parse error: ' . $error->getMessage() . '</error>');
117 exit(1);
118 }
119
120 $visitor = new NodeVisitor();
121
122 $traverser = new NodeTraverser();
123 $traverser->addVisitor($visitor);
124
125 $ast = $traverser->traverse($ast);
126
127 if (!empty($visitor->matches)) {
128 $errors[$file->getRealPath()] = $visitor->matches;
129 $output->write('<error>F</error>');
130 } else {
131 $output->write('<fg=green>.</>');
132 }
133 }
134
135 $output->writeln('');
136
137 if (!empty($errors)) {
138 $output->writeln('');
139
140 foreach ($errors as $file => $matchesPerLine) {
141 $output->writeln('');
142 $output->writeln('<error>' . $file . '</error>');
143
144 /**
145 * @var array $matchesPerLine
146 * @var int $line
147 * @var array $matches
148 */
149 foreach ($matchesPerLine as $line => $matches) {
150 $output->writeln($line . ': ' . implode(', ', $matches));
151 }
152 }
153 exit(1);
154 }
155
156 exit(0);