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