View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2024 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.modifier;
21  
22  import com.puppycrawl.tools.checkstyle.StatelessCheck;
23  import com.puppycrawl.tools.checkstyle.api.AbstractCheck;
24  import com.puppycrawl.tools.checkstyle.api.DetailAST;
25  import com.puppycrawl.tools.checkstyle.api.TokenTypes;
26  import com.puppycrawl.tools.checkstyle.utils.ScopeUtil;
27  
28  /**
29   * <p>
30   * Checks for implicit modifiers on interface members and nested types.
31   * </p>
32   * <p>
33   * This check is effectively the opposite of
34   * <a href="https://checkstyle.org/checks/modifier/redundantmodifier.html#RedundantModifier">
35   * RedundantModifier</a>.
36   * It checks the modifiers on interface members, ensuring that certain modifiers are explicitly
37   * specified even though they are actually redundant.
38   * </p>
39   * <p>
40   * Methods in interfaces are {@code public} by default, however from Java 9 they can also be
41   * {@code private}. This check provides the ability to enforce that {@code public} is explicitly
42   * coded and not implicitly added by the compiler.
43   * </p>
44   * <p>
45   * From Java 8, there are three types of methods in interfaces - static methods marked with
46   * {@code static}, default methods marked with {@code default} and abstract methods which do not
47   * have to be marked with anything. From Java 9, there are also private methods marked with
48   * {@code private}. This check provides the ability to enforce that {@code abstract} is
49   * explicitly coded and not implicitly added by the compiler.
50   * </p>
51   * <p>
52   * Fields in interfaces are always {@code public static final} and as such the compiler does not
53   * require these modifiers. This check provides the ability to enforce that these modifiers are
54   * explicitly coded and not implicitly added by the compiler.
55   * </p>
56   * <p>
57   * Nested types within an interface are always {@code public static} and as such the compiler
58   * does not require the {@code public static} modifiers. This check provides the ability to
59   * enforce that the {@code public} and {@code static} modifiers are explicitly coded and not
60   * implicitly added by the compiler.
61   * </p>
62   * <pre>
63   * public interface AddressFactory {
64   *   // check enforces code contains "public static final"
65   *   public static final String UNKNOWN = "Unknown";
66   *
67   *   String OTHER = "Other";  // violation
68   *
69   *   // check enforces code contains "public" or "private"
70   *   public static AddressFactory instance();
71   *
72   *   // check enforces code contains "public abstract"
73   *   public abstract Address createAddress(String addressLine, String city);
74   *
75   *   List&lt;Address&gt; findAddresses(String city);  // violation
76   *
77   *   // check enforces default methods are explicitly declared "public"
78   *   public default Address createAddress(String city) {
79   *     return createAddress(UNKNOWN, city);
80   *   }
81   *
82   *   default Address createOtherAddress() {  // violation
83   *     return createAddress(OTHER, OTHER);
84   *   }
85   * }
86   * </pre>
87   * <p>
88   * Rationale for this check: Methods, fields and nested types are treated differently
89   * depending on whether they are part of an interface or part of a class. For example, by
90   * default methods are package-scoped on classes, but public in interfaces. However, from
91   * Java 8 onwards, interfaces have changed to be much more like abstract classes.
92   * Interfaces now have static and instance methods with code. Developers should not have to
93   * remember which modifiers are required and which are implied. This check allows the simpler
94   * alternative approach to be adopted where the implied modifiers must always be coded explicitly.
95   * </p>
96   * <ul>
97   * <li>
98   * Property {@code violateImpliedAbstractMethod} - Control whether to enforce that {@code abstract}
99   * is explicitly coded on interface methods.
100  * Type is {@code boolean}.
101  * Default value is {@code true}.
102  * </li>
103  * <li>
104  * Property {@code violateImpliedFinalField} - Control whether to enforce that {@code final}
105  * is explicitly coded on interface fields.
106  * Type is {@code boolean}.
107  * Default value is {@code true}.
108  * </li>
109  * <li>
110  * Property {@code violateImpliedPublicField} - Control whether to enforce that {@code public}
111  * is explicitly coded on interface fields.
112  * Type is {@code boolean}.
113  * Default value is {@code true}.
114  * </li>
115  * <li>
116  * Property {@code violateImpliedPublicMethod} - Control whether to enforce that {@code public}
117  * is explicitly coded on interface methods.
118  * Type is {@code boolean}.
119  * Default value is {@code true}.
120  * </li>
121  * <li>
122  * Property {@code violateImpliedPublicNested} - Control whether to enforce that {@code public}
123  * is explicitly coded on interface nested types.
124  * Type is {@code boolean}.
125  * Default value is {@code true}.
126  * </li>
127  * <li>
128  * Property {@code violateImpliedStaticField} - Control whether to enforce that {@code static}
129  * is explicitly coded on interface fields.
130  * Type is {@code boolean}.
131  * Default value is {@code true}.
132  * </li>
133  * <li>
134  * Property {@code violateImpliedStaticNested} - Control whether to enforce that {@code static}
135  * is explicitly coded on interface nested types.
136  * Type is {@code boolean}.
137  * Default value is {@code true}.
138  * </li>
139  * </ul>
140  * <p>
141  * Parent is {@code com.puppycrawl.tools.checkstyle.TreeWalker}
142  * </p>
143  * <p>
144  * Violation Message Keys:
145  * </p>
146  * <ul>
147  * <li>
148  * {@code interface.implied.modifier}
149  * </li>
150  * </ul>
151  *
152  * @since 8.12
153  */
154 @StatelessCheck
155 public class InterfaceMemberImpliedModifierCheck
156     extends AbstractCheck {
157 
158     /**
159      * A key is pointing to the warning message text in "messages.properties" file.
160      */
161     public static final String MSG_KEY = "interface.implied.modifier";
162 
163     /** Name for 'public' access modifier. */
164     private static final String PUBLIC_ACCESS_MODIFIER = "public";
165 
166     /** Name for 'abstract' keyword. */
167     private static final String ABSTRACT_KEYWORD = "abstract";
168 
169     /** Name for 'static' keyword. */
170     private static final String STATIC_KEYWORD = "static";
171 
172     /** Name for 'final' keyword. */
173     private static final String FINAL_KEYWORD = "final";
174 
175     /**
176      * Control whether to enforce that {@code public} is explicitly coded
177      * on interface fields.
178      */
179     private boolean violateImpliedPublicField = true;
180 
181     /**
182      * Control whether to enforce that {@code static} is explicitly coded
183      * on interface fields.
184      */
185     private boolean violateImpliedStaticField = true;
186 
187     /**
188      * Control whether to enforce that {@code final} is explicitly coded
189      * on interface fields.
190      */
191     private boolean violateImpliedFinalField = true;
192 
193     /**
194      * Control whether to enforce that {@code public} is explicitly coded
195      * on interface methods.
196      */
197     private boolean violateImpliedPublicMethod = true;
198 
199     /**
200      * Control whether to enforce that {@code abstract} is explicitly coded
201      * on interface methods.
202      */
203     private boolean violateImpliedAbstractMethod = true;
204 
205     /**
206      * Control whether to enforce that {@code public} is explicitly coded
207      * on interface nested types.
208      */
209     private boolean violateImpliedPublicNested = true;
210 
211     /**
212      * Control whether to enforce that {@code static} is explicitly coded
213      * on interface nested types.
214      */
215     private boolean violateImpliedStaticNested = true;
216 
217     /**
218      * Setter to control whether to enforce that {@code public} is explicitly coded
219      * on interface fields.
220      *
221      * @param violateImpliedPublicField
222      *        True to perform the check, false to turn the check off.
223      * @since 8.12
224      */
225     public void setViolateImpliedPublicField(boolean violateImpliedPublicField) {
226         this.violateImpliedPublicField = violateImpliedPublicField;
227     }
228 
229     /**
230      * Setter to control whether to enforce that {@code static} is explicitly coded
231      * on interface fields.
232      *
233      * @param violateImpliedStaticField
234      *        True to perform the check, false to turn the check off.
235      * @since 8.12
236      */
237     public void setViolateImpliedStaticField(boolean violateImpliedStaticField) {
238         this.violateImpliedStaticField = violateImpliedStaticField;
239     }
240 
241     /**
242      * Setter to control whether to enforce that {@code final} is explicitly coded
243      * on interface fields.
244      *
245      * @param violateImpliedFinalField
246      *        True to perform the check, false to turn the check off.
247      * @since 8.12
248      */
249     public void setViolateImpliedFinalField(boolean violateImpliedFinalField) {
250         this.violateImpliedFinalField = violateImpliedFinalField;
251     }
252 
253     /**
254      * Setter to control whether to enforce that {@code public} is explicitly coded
255      * on interface methods.
256      *
257      * @param violateImpliedPublicMethod
258      *        True to perform the check, false to turn the check off.
259      * @since 8.12
260      */
261     public void setViolateImpliedPublicMethod(boolean violateImpliedPublicMethod) {
262         this.violateImpliedPublicMethod = violateImpliedPublicMethod;
263     }
264 
265     /**
266      * Setter to control whether to enforce that {@code abstract} is explicitly coded
267      * on interface methods.
268      *
269      * @param violateImpliedAbstractMethod
270      *        True to perform the check, false to turn the check off.
271      * @since 8.12
272      */
273     public void setViolateImpliedAbstractMethod(boolean violateImpliedAbstractMethod) {
274         this.violateImpliedAbstractMethod = violateImpliedAbstractMethod;
275     }
276 
277     /**
278      * Setter to control whether to enforce that {@code public} is explicitly coded
279      * on interface nested types.
280      *
281      * @param violateImpliedPublicNested
282      *        True to perform the check, false to turn the check off.
283      * @since 8.12
284      */
285     public void setViolateImpliedPublicNested(boolean violateImpliedPublicNested) {
286         this.violateImpliedPublicNested = violateImpliedPublicNested;
287     }
288 
289     /**
290      * Setter to control whether to enforce that {@code static} is explicitly coded
291      * on interface nested types.
292      *
293      * @param violateImpliedStaticNested
294      *        True to perform the check, false to turn the check off.
295      * @since 8.12
296      */
297     public void setViolateImpliedStaticNested(boolean violateImpliedStaticNested) {
298         this.violateImpliedStaticNested = violateImpliedStaticNested;
299     }
300 
301     @Override
302     public int[] getDefaultTokens() {
303         return getAcceptableTokens();
304     }
305 
306     @Override
307     public int[] getRequiredTokens() {
308         return getAcceptableTokens();
309     }
310 
311     @Override
312     public int[] getAcceptableTokens() {
313         return new int[] {
314             TokenTypes.METHOD_DEF,
315             TokenTypes.VARIABLE_DEF,
316             TokenTypes.INTERFACE_DEF,
317             TokenTypes.CLASS_DEF,
318             TokenTypes.ENUM_DEF,
319         };
320     }
321 
322     @Override
323     public void visitToken(DetailAST ast) {
324         if (ScopeUtil.isInInterfaceBlock(ast) && !ScopeUtil.isInCodeBlock(ast)) {
325             switch (ast.getType()) {
326                 case TokenTypes.METHOD_DEF:
327                     processMethod(ast);
328                     break;
329                 case TokenTypes.VARIABLE_DEF:
330                     processField(ast);
331                     break;
332                 case TokenTypes.CLASS_DEF:
333                 case TokenTypes.INTERFACE_DEF:
334                 case TokenTypes.ENUM_DEF:
335                     processNestedType(ast);
336                     break;
337                 default:
338                     throw new IllegalStateException(ast.toString());
339             }
340         }
341     }
342 
343     /**
344      * Check method in interface.
345      *
346      * @param ast the method AST
347      */
348     private void processMethod(DetailAST ast) {
349         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
350         if (violateImpliedPublicMethod
351                 && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null
352                 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
353             log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
354         }
355         if (violateImpliedAbstractMethod
356                 && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null
357                 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null
358                 && modifiers.findFirstToken(TokenTypes.LITERAL_DEFAULT) == null
359                 && modifiers.findFirstToken(TokenTypes.ABSTRACT) == null) {
360             log(ast, MSG_KEY, ABSTRACT_KEYWORD);
361         }
362     }
363 
364     /**
365      * Check field in interface.
366      *
367      * @param ast the field AST
368      */
369     private void processField(DetailAST ast) {
370         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
371         if (violateImpliedPublicField
372                 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
373             log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
374         }
375         if (violateImpliedStaticField
376                 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
377             log(ast, MSG_KEY, STATIC_KEYWORD);
378         }
379         if (violateImpliedFinalField
380                 && modifiers.findFirstToken(TokenTypes.FINAL) == null) {
381             log(ast, MSG_KEY, FINAL_KEYWORD);
382         }
383     }
384 
385     /**
386      * Check nested types in interface.
387      *
388      * @param ast the nested type AST
389      */
390     private void processNestedType(DetailAST ast) {
391         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
392         if (violateImpliedPublicNested
393                 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
394             log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
395         }
396         if (violateImpliedStaticNested
397                 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
398             log(ast, MSG_KEY, STATIC_KEYWORD);
399         }
400     }
401 
402 }