@@ -28,34 +28,84 @@ class Symfony3Custom_Sniffs_Commenting_FunctionCommentSniff extends PEAR_Sniffs_
2828 */
2929 public function process (PHP_CodeSniffer_File $ phpcsFile , $ stackPtr )
3030 {
31- if (false === $ commentEnd = $ phpcsFile ->findPrevious (
32- array (
33- T_COMMENT ,
34- T_DOC_COMMENT ,
35- T_CLASS ,
36- T_FUNCTION ,
37- T_OPEN_TAG ,
38- ),
39- ($ stackPtr - 1 )
40- )
31+ $ tokens = $ phpcsFile ->getTokens ();
32+ $ find = PHP_CodeSniffer_Tokens::$ methodPrefixes ;
33+ $ find [] = T_WHITESPACE ;
34+
35+ $ commentEnd = $ phpcsFile ->findPrevious ($ find , ($ stackPtr - 1 ), null , true );
36+ if ($ tokens [$ commentEnd ]['code ' ] === T_COMMENT ) {
37+ // Inline comments might just be closing comments for
38+ // control structures or functions instead of function comments
39+ // using the wrong comment type. If there is other code on the line,
40+ // assume they relate to that code.
41+ $ prev = $ phpcsFile ->findPrevious ($ find , ($ commentEnd - 1 ), null , true );
42+ if ($ prev !== false && $ tokens [$ prev ]['line ' ] === $ tokens [$ commentEnd ]['line ' ]) {
43+ $ commentEnd = $ prev ;
44+ }
45+ }
46+
47+ $ name = $ phpcsFile ->getDeclarationName ($ stackPtr );
48+ $ commentRequired = strpos ($ name , 'test ' ) !== 0
49+ && $ name !== 'setUp '
50+ && $ name !== 'tearDown ' ;
51+
52+ if ($ tokens [$ commentEnd ]['code ' ] !== T_DOC_COMMENT_CLOSE_TAG
53+ && $ tokens [$ commentEnd ]['code ' ] !== T_COMMENT
4154 ) {
42- return ;
55+ $ hasComment = false ;
56+ $ phpcsFile ->recordMetric ($ stackPtr , 'Function has doc comment ' , 'no ' );
57+
58+ if ($ commentRequired ) {
59+ $ phpcsFile ->addError ('Missing function doc comment ' , $ stackPtr , 'Missing ' );
60+ return ;
61+ } else {
62+ // The comment may not be required, we'll see in next checks
63+ }
64+ } else {
65+ $ hasComment = true ;
66+ $ phpcsFile ->recordMetric ($ stackPtr , 'Function has doc comment ' , 'yes ' );
4367 }
4468
45- $ tokens = $ phpcsFile ->getTokens ();
46- $ code = $ tokens [$ commentEnd ]['code ' ];
69+ $ commentStart = null ;
70+ if ($ hasComment ) {
71+ if ($ tokens [$ commentEnd ]['code ' ] === T_COMMENT ) {
72+ $ phpcsFile ->addError ('You must use "/**" style comments for a function comment ' , $ stackPtr , 'WrongStyle ' );
4773
48- $ name = $ phpcsFile ->getDeclarationName ($ stackPtr );
74+ return ;
75+ }
4976
50- $ commentRequired = strpos ($ name , 'test ' ) !== 0 && $ name !== 'setUp ' && $ name !== 'tearDown ' ;
77+ if ($ tokens [$ commentEnd ]['line ' ] !== ($ tokens [$ stackPtr ]['line ' ] - 1 )) {
78+ $ error = 'There must be no blank lines after the function comment ' ;
79+ $ phpcsFile ->addError ($ error , $ commentEnd , 'SpacingAfter ' );
80+ }
5181
52- if (($ code === T_COMMENT && !$ commentRequired )
53- || ($ code !== T_DOC_COMMENT && !$ commentRequired )
54- ) {
55- return ;
82+ $ commentStart = $ tokens [$ commentEnd ]['comment_opener ' ];
83+ foreach ($ tokens [$ commentStart ]['comment_tags ' ] as $ tag ) {
84+ if ($ tokens [$ tag ]['content ' ] === '@see ' ) {
85+ // Make sure the tag isn't empty.
86+ $ string = $ phpcsFile ->findNext (T_DOC_COMMENT_STRING , $ tag , $ commentEnd );
87+ if ($ string === false || $ tokens [$ string ]['line ' ] !== $ tokens [$ tag ]['line ' ]) {
88+ $ error = 'Content missing for @see tag in function comment ' ;
89+ $ phpcsFile ->addError ($ error , $ tag , 'EmptySees ' );
90+ }
91+ }
92+ }
5693 }
5794
58- parent ::process ($ phpcsFile , $ stackPtr );
95+ $ this ->processReturn ($ phpcsFile , $ stackPtr , $ commentStart );
96+
97+ $ realParams = $ phpcsFile ->getMethodParameters ($ stackPtr );
98+ if ($ hasComment ) {
99+ // These checks need function comment
100+ $ this ->processParams ($ phpcsFile , $ stackPtr , $ commentStart );
101+ $ this ->processThrows ($ phpcsFile , $ stackPtr , $ commentStart );
102+ } elseif (count ($ realParams ) > 0 ) {
103+ foreach ($ realParams as $ neededParam ) {
104+ $ error = 'Doc comment for parameter "%s" missing ' ;
105+ $ data = array ($ neededParam ['name ' ]);
106+ $ phpcsFile ->addError ($ error , $ stackPtr , 'MissingParamTag ' , $ data );
107+ }
108+ }
59109 }
60110
61111 /**
@@ -64,7 +114,7 @@ public function process(PHP_CodeSniffer_File $phpcsFile, $stackPtr)
64114 * @param PHP_CodeSniffer_File $phpcsFile The file being scanned.
65115 * @param int $stackPtr The position of the current token
66116 * in the stack passed in $tokens.
67- * @param int $commentStart The position in the stack
117+ * @param int|null $commentStart The position in the stack
68118 * where the comment started.
69119 *
70120 * @return void
@@ -74,8 +124,10 @@ protected function processReturn(
74124 $ stackPtr ,
75125 $ commentStart
76126 ) {
77-
78- if ($ this ->isInheritDoc ($ phpcsFile , $ stackPtr )) {
127+ // Check for inheritDoc if there is comment
128+ if ((null !== $ commentStart )
129+ && $ this ->isInheritDoc ($ phpcsFile , $ stackPtr )
130+ ) {
79131 return ;
80132 }
81133
@@ -102,7 +154,15 @@ protected function processReturn(
102154 if ($ tokens [$ i ]['code ' ] === T_RETURN
103155 && $ this ->isMatchingReturn ($ tokens , $ i )
104156 ) {
105- parent ::processReturn ($ phpcsFile , $ stackPtr , $ commentStart );
157+ if (null !== $ commentStart ) {
158+ parent ::processReturn ($ phpcsFile , $ stackPtr , $ commentStart );
159+ } else {
160+ // There is no doc and we need one with @return
161+ $ error = 'Missing @return tag in function comment ' ;
162+ $ phpcsFile ->addError ($ error , $ stackPtr , 'MissingReturn ' );
163+
164+ }
165+
106166 break ;
107167 }
108168 }
0 commit comments