View Javadoc
1   ///////////////////////////////////////////////////////////////////////////////////////////////
2   // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3   // Copyright (C) 2001-2025 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">
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   * <div class="wrapper"><pre class="prettyprint"><code class="language-java">
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   * </code></pre></div>
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  *
103  * @since 8.12
104  */
105 @StatelessCheck
106 public class InterfaceMemberImpliedModifierCheck
107     extends AbstractCheck {
108 
109     /**
110      * A key is pointing to the warning message text in "messages.properties" file.
111      */
112     public static final String MSG_KEY = "interface.implied.modifier";
113 
114     /** Name for 'public' access modifier. */
115     private static final String PUBLIC_ACCESS_MODIFIER = "public";
116 
117     /** Name for 'abstract' keyword. */
118     private static final String ABSTRACT_KEYWORD = "abstract";
119 
120     /** Name for 'static' keyword. */
121     private static final String STATIC_KEYWORD = "static";
122 
123     /** Name for 'final' keyword. */
124     private static final String FINAL_KEYWORD = "final";
125 
126     /**
127      * Control whether to enforce that {@code public} is explicitly coded
128      * on interface fields.
129      */
130     private boolean violateImpliedPublicField = true;
131 
132     /**
133      * Control whether to enforce that {@code static} is explicitly coded
134      * on interface fields.
135      */
136     private boolean violateImpliedStaticField = true;
137 
138     /**
139      * Control whether to enforce that {@code final} is explicitly coded
140      * on interface fields.
141      */
142     private boolean violateImpliedFinalField = true;
143 
144     /**
145      * Control whether to enforce that {@code public} is explicitly coded
146      * on interface methods.
147      */
148     private boolean violateImpliedPublicMethod = true;
149 
150     /**
151      * Control whether to enforce that {@code abstract} is explicitly coded
152      * on interface methods.
153      */
154     private boolean violateImpliedAbstractMethod = true;
155 
156     /**
157      * Control whether to enforce that {@code public} is explicitly coded
158      * on interface nested types.
159      */
160     private boolean violateImpliedPublicNested = true;
161 
162     /**
163      * Control whether to enforce that {@code static} is explicitly coded
164      * on interface nested types.
165      */
166     private boolean violateImpliedStaticNested = true;
167 
168     /**
169      * Setter to control whether to enforce that {@code public} is explicitly coded
170      * on interface fields.
171      *
172      * @param violateImpliedPublicField
173      *        True to perform the check, false to turn the check off.
174      * @since 8.12
175      */
176     public void setViolateImpliedPublicField(boolean violateImpliedPublicField) {
177         this.violateImpliedPublicField = violateImpliedPublicField;
178     }
179 
180     /**
181      * Setter to control whether to enforce that {@code static} is explicitly coded
182      * on interface fields.
183      *
184      * @param violateImpliedStaticField
185      *        True to perform the check, false to turn the check off.
186      * @since 8.12
187      */
188     public void setViolateImpliedStaticField(boolean violateImpliedStaticField) {
189         this.violateImpliedStaticField = violateImpliedStaticField;
190     }
191 
192     /**
193      * Setter to control whether to enforce that {@code final} is explicitly coded
194      * on interface fields.
195      *
196      * @param violateImpliedFinalField
197      *        True to perform the check, false to turn the check off.
198      * @since 8.12
199      */
200     public void setViolateImpliedFinalField(boolean violateImpliedFinalField) {
201         this.violateImpliedFinalField = violateImpliedFinalField;
202     }
203 
204     /**
205      * Setter to control whether to enforce that {@code public} is explicitly coded
206      * on interface methods.
207      *
208      * @param violateImpliedPublicMethod
209      *        True to perform the check, false to turn the check off.
210      * @since 8.12
211      */
212     public void setViolateImpliedPublicMethod(boolean violateImpliedPublicMethod) {
213         this.violateImpliedPublicMethod = violateImpliedPublicMethod;
214     }
215 
216     /**
217      * Setter to control whether to enforce that {@code abstract} is explicitly coded
218      * on interface methods.
219      *
220      * @param violateImpliedAbstractMethod
221      *        True to perform the check, false to turn the check off.
222      * @since 8.12
223      */
224     public void setViolateImpliedAbstractMethod(boolean violateImpliedAbstractMethod) {
225         this.violateImpliedAbstractMethod = violateImpliedAbstractMethod;
226     }
227 
228     /**
229      * Setter to control whether to enforce that {@code public} is explicitly coded
230      * on interface nested types.
231      *
232      * @param violateImpliedPublicNested
233      *        True to perform the check, false to turn the check off.
234      * @since 8.12
235      */
236     public void setViolateImpliedPublicNested(boolean violateImpliedPublicNested) {
237         this.violateImpliedPublicNested = violateImpliedPublicNested;
238     }
239 
240     /**
241      * Setter to control whether to enforce that {@code static} is explicitly coded
242      * on interface nested types.
243      *
244      * @param violateImpliedStaticNested
245      *        True to perform the check, false to turn the check off.
246      * @since 8.12
247      */
248     public void setViolateImpliedStaticNested(boolean violateImpliedStaticNested) {
249         this.violateImpliedStaticNested = violateImpliedStaticNested;
250     }
251 
252     @Override
253     public int[] getDefaultTokens() {
254         return getAcceptableTokens();
255     }
256 
257     @Override
258     public int[] getRequiredTokens() {
259         return getAcceptableTokens();
260     }
261 
262     @Override
263     public int[] getAcceptableTokens() {
264         return new int[] {
265             TokenTypes.METHOD_DEF,
266             TokenTypes.VARIABLE_DEF,
267             TokenTypes.INTERFACE_DEF,
268             TokenTypes.CLASS_DEF,
269             TokenTypes.ENUM_DEF,
270         };
271     }
272 
273     @Override
274     public void visitToken(DetailAST ast) {
275         if (ScopeUtil.isInInterfaceBlock(ast) && !ScopeUtil.isInCodeBlock(ast)) {
276             switch (ast.getType()) {
277                 case TokenTypes.METHOD_DEF -> processMethod(ast);
278 
279                 case TokenTypes.VARIABLE_DEF -> processField(ast);
280 
281                 case TokenTypes.CLASS_DEF,
282                      TokenTypes.INTERFACE_DEF,
283                      TokenTypes.ENUM_DEF -> processNestedType(ast);
284 
285                 default -> throw new IllegalStateException(ast.toString());
286             }
287         }
288     }
289 
290     /**
291      * Check method in interface.
292      *
293      * @param ast the method AST
294      */
295     private void processMethod(DetailAST ast) {
296         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
297         if (violateImpliedPublicMethod
298                 && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null
299                 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
300             log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
301         }
302         if (violateImpliedAbstractMethod
303                 && modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null
304                 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null
305                 && modifiers.findFirstToken(TokenTypes.LITERAL_DEFAULT) == null
306                 && modifiers.findFirstToken(TokenTypes.ABSTRACT) == null) {
307             log(ast, MSG_KEY, ABSTRACT_KEYWORD);
308         }
309     }
310 
311     /**
312      * Check field in interface.
313      *
314      * @param ast the field AST
315      */
316     private void processField(DetailAST ast) {
317         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
318         if (violateImpliedPublicField
319                 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
320             log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
321         }
322         if (violateImpliedStaticField
323                 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
324             log(ast, MSG_KEY, STATIC_KEYWORD);
325         }
326         if (violateImpliedFinalField
327                 && modifiers.findFirstToken(TokenTypes.FINAL) == null) {
328             log(ast, MSG_KEY, FINAL_KEYWORD);
329         }
330     }
331 
332     /**
333      * Check nested types in interface.
334      *
335      * @param ast the nested type AST
336      */
337     private void processNestedType(DetailAST ast) {
338         final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS);
339         if (violateImpliedPublicNested
340                 && modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) {
341             log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER);
342         }
343         if (violateImpliedStaticNested
344                 && modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) {
345             log(ast, MSG_KEY, STATIC_KEYWORD);
346         }
347     }
348 
349 }