1   ////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code for adherence to a set of rules.
3   // Copyright (C) 2001-2019 the original author or authors.
4   //
5   // This library is free software; you can redistribute it and/or
6   // modify it under the terms of the GNU Lesser General Public
7   // License as published by the Free Software Foundation; either
8   // version 2.1 of the License, or (at your option) any later version.
9   //
10  // This library is distributed in the hope that it will be useful,
11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13  // Lesser General Public License for more details.
14  //
15  // You should have received a copy of the GNU Lesser General Public
16  // License along with this library; if not, write to the Free Software
17  // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18  ////////////////////////////////////////////////////////////////////////////////
19  
20  package com.puppycrawl.tools.checkstyle.checks.annotation;
21  
22  import java.util.regex.Matcher;
23  import java.util.regex.Pattern;
24  
25  import com.puppycrawl.tools.checkstyle.StatelessCheck;
26  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
27  import com.puppycrawl.tools.checkstyle.api.DetailAST;
28  import com.puppycrawl.tools.checkstyle.api.TextBlock;
29  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
30  import com.puppycrawl.tools.checkstyle.checks.javadoc.JavadocTagInfo;
31  import com.puppycrawl.tools.checkstyle.utils.AnnotationUtil;
32  import com.puppycrawl.tools.checkstyle.utils.CommonUtil;
33  
34  /**
35   * <p>
36   * Verifies that the &#64;Override annotation is present
37   * when the &#64;inheritDoc javadoc tag is present.
38   * </p>
39   * <p>
40   * Rationale: The &#64;Override annotation helps
41   * compiler tools ensure that an override is actually occurring.  It is
42   * quite easy to accidentally overload a method or hide a static method
43   * and using the &#64;Override annotation points out these problems.
44   * </p>
45   * <p>
46   * This check will log a violation if using the &#64;inheritDoc tag on a method that
47   * is not valid (ex: private, or static method).
48   * </p>
49   * <p>
50   * There is a slight difference between the &#64;Override annotation in Java 5 versus
51   * Java 6 and above. In Java 5, any method overridden from an interface cannot
52   * be annotated with &#64;Override. In Java 6 this behavior is allowed.
53   * </p>
54   * <p>
55   * As a result of the aforementioned difference between Java 5 and Java 6, a
56   * property called {@code javaFiveCompatibility} is available. This
57   * property will only check classes, interfaces, etc. that do not contain the
58   * extends or implements keyword or are not anonymous classes. This means it
59   * only checks methods overridden from {@code java.lang.Object}.
60   * <b>Java 5 Compatibility mode severely limits this check. It is recommended to
61   * only use it on Java 5 source.</b>
62   * </p>
63   * <ul>
64   * <li>
65   * Property {@code javaFiveCompatibility} - Enable java 5 compatibility mode.
66   * Default value is {@code false}.
67   * </li>
68   * </ul>
69   * <p>
70   * To configure the check:
71   * </p>
72   * <pre>
73   * &lt;module name=&quot;MissingOverride&quot;/&gt;
74   * </pre>
75   * <p>
76   * To configure the check for the {@code javaFiveCompatibility} mode:
77   * </p>
78   * <pre>
79   * &lt;module name="MissingOverride"&gt;
80   *   &lt;property name="javaFiveCompatibility"
81   *       value="true"/&gt;
82   * &lt;/module&gt;
83   * </pre>
84   *
85   * @since 5.0
86   */
87  @StatelessCheck
88  public final class MissingOverrideCheck extends AbstractCheck {
89  
90      /**
91       * A key is pointing to the warning message text in "messages.properties"
92       * file.
93       */
94      public static final String MSG_KEY_TAG_NOT_VALID_ON = "tag.not.valid.on";
95  
96      /**
97       * A key is pointing to the warning message text in "messages.properties"
98       * file.
99       */
100     public static final String MSG_KEY_ANNOTATION_MISSING_OVERRIDE =
101         "annotation.missing.override";
102 
103     /** {@link Override Override} annotation name. */
104     private static final String OVERRIDE = "Override";
105 
106     /** Fully-qualified {@link Override Override} annotation name. */
107     private static final String FQ_OVERRIDE = "java.lang." + OVERRIDE;
108 
109     /** Compiled regexp to match Javadoc tags with no argument and {}. */
110     private static final Pattern MATCH_INHERIT_DOC =
111             CommonUtil.createPattern("\\{\\s*@(inheritDoc)\\s*\\}");
112 
113     /**
114      * Enable java 5 compatibility mode.
115      */
116     private boolean javaFiveCompatibility;
117 
118     /**
119      * Setter to enable java 5 compatibility mode.
120      * @param compatibility compatibility or not
121      */
122     public void setJavaFiveCompatibility(final boolean compatibility) {
123         javaFiveCompatibility = compatibility;
124     }
125 
126     @Override
127     public int[] getDefaultTokens() {
128         return getRequiredTokens();
129     }
130 
131     @Override
132     public int[] getAcceptableTokens() {
133         return getRequiredTokens();
134     }
135 
136     @Override
137     public int[] getRequiredTokens() {
138         return new int[]
139         {TokenTypes.METHOD_DEF, };
140     }
141 
142     // -@cs[CyclomaticComplexity] Too complex to break apart.
143     @Override
144     public void visitToken(final DetailAST ast) {
145         final TextBlock javadoc =
146             getFileContents().getJavadocBefore(ast.getLineNo());
147 
148         final boolean containsTag = containsJavadocTag(javadoc);
149         if (containsTag && !JavadocTagInfo.INHERIT_DOC.isValidOn(ast)) {
150             log(ast.getLineNo(), MSG_KEY_TAG_NOT_VALID_ON,
151                 JavadocTagInfo.INHERIT_DOC.getText());
152         }
153         else {
154             boolean check = true;
155 
156             if (javaFiveCompatibility) {
157                 final DetailAST defOrNew = ast.getParent().getParent();
158 
159                 if (defOrNew.findFirstToken(TokenTypes.EXTENDS_CLAUSE) != null
160                     || defOrNew.findFirstToken(TokenTypes.IMPLEMENTS_CLAUSE) != null
161                     || defOrNew.getType() == TokenTypes.LITERAL_NEW) {
162                     check = false;
163                 }
164             }
165 
166             if (check
167                 && containsTag
168                 && !AnnotationUtil.containsAnnotation(ast, OVERRIDE)
169                 && !AnnotationUtil.containsAnnotation(ast, FQ_OVERRIDE)) {
170                 log(ast.getLineNo(), MSG_KEY_ANNOTATION_MISSING_OVERRIDE);
171             }
172         }
173     }
174 
175     /**
176      * Checks to see if the text block contains a inheritDoc tag.
177      *
178      * @param javadoc the javadoc of the AST
179      * @return true if contains the tag
180      */
181     private static boolean containsJavadocTag(final TextBlock javadoc) {
182         boolean javadocTag = false;
183 
184         if (javadoc != null) {
185             final String[] lines = javadoc.getText();
186 
187             for (final String line : lines) {
188                 final Matcher matchInheritDoc =
189                     MATCH_INHERIT_DOC.matcher(line);
190 
191                 if (matchInheritDoc.find()) {
192                     javadocTag = true;
193                     break;
194                 }
195             }
196         }
197         return javadocTag;
198     }
199 
200 }