Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue #5899: Add InterfaceImpliedModifierCheck
- Loading branch information
1 parent
ecd1b65
commit 7711035
Showing
25 changed files
with
1,117 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
274 changes: 274 additions & 0 deletions
274
.../com/puppycrawl/tools/checkstyle/checks/modifier/InterfaceMemberImpliedModifierCheck.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,274 @@ | ||
//////////////////////////////////////////////////////////////////////////////// | ||
// checkstyle: Checks Java source code for adherence to a set of rules. | ||
// Copyright (C) 2001-2018 the original author or authors. | ||
// | ||
// This library is free software; you can redistribute it and/or | ||
// modify it under the terms of the GNU Lesser General Public | ||
// License as published by the Free Software Foundation; either | ||
// version 2.1 of the License, or (at your option) any later version. | ||
// | ||
// This library is distributed in the hope that it will be useful, | ||
// but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
// Lesser General Public License for more details. | ||
// | ||
// You should have received a copy of the GNU Lesser General Public | ||
// License along with this library; if not, write to the Free Software | ||
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
//////////////////////////////////////////////////////////////////////////////// | ||
|
||
package com.puppycrawl.tools.checkstyle.checks.modifier; | ||
|
||
import com.puppycrawl.tools.checkstyle.StatelessCheck; | ||
import com.puppycrawl.tools.checkstyle.api.AbstractCheck; | ||
import com.puppycrawl.tools.checkstyle.api.DetailAST; | ||
import com.puppycrawl.tools.checkstyle.api.TokenTypes; | ||
|
||
/** | ||
* Checks modifiers on interface members, ensuring that certain modifiers are | ||
* explicitly specified even though they are actually redundant. | ||
* | ||
* <p>Methods on interfaces are {@code public} by default, however from Java 9 | ||
* they can also be {@code private}. This check provides the ability to enforce | ||
* that {@code public} is not implicitly added by the compiler. | ||
* | ||
* <p>From Java 8, there are three types of methods on interfaces - static methods | ||
* marked with {@code static}, default methods marked with {@code default} and | ||
* abstract methods which do not have to be marked with anything. | ||
* From Java 9, there are also private methods marked with {@code private}. | ||
* This check provides the ability to enforce that {@code abstract} is not | ||
* implicitly added by the compiler. | ||
* | ||
* <p>Fields on interfaces are always {@code public static final} and as such the | ||
* compiler does not require these modifiers. This check provides the ability to | ||
* enforce that these modifiers are not implicitly added by the compiler. | ||
* | ||
* <p>Nested types within an interface are always {@code public static} and as such the | ||
* compiler does not require the {@code public static} modifiers. This check provides | ||
* the ability to enforce that the {@code public} and {@code static} modifiers are | ||
* not implicitly added by the compiler. | ||
* | ||
* <p>Rationale for these checks: | ||
* Methods, fields and nested types are treated differently depending on whether | ||
* they are part of an interface or part of a class. For example, by default methods | ||
* are package-scoped on classes, but public on interfaces. | ||
* However, from Java 8 onwards, interfaces have changed to be much more like abstract classes. | ||
* Interfaces now have static and instance methods with code. | ||
* Developers should not have to remember which modifiers are required and which are implicit. | ||
* This check allows the simpler alternative approach to be adopted where the | ||
* implied modifiers must always be coded explicitly. | ||
*/ | ||
@StatelessCheck | ||
public class InterfaceMemberImpliedModifierCheck | ||
extends AbstractCheck { | ||
|
||
/** | ||
* A key is pointing to the warning message text in "messages.properties" file. | ||
*/ | ||
public static final String MSG_KEY = "interface.implied.modifier"; | ||
|
||
/** Name for 'public' access modifier. */ | ||
private static final String PUBLIC_ACCESS_MODIFIER = "public"; | ||
|
||
/** Name for 'abstract' keyword. */ | ||
private static final String ABSTRACT_KEYWORD = "abstract"; | ||
|
||
/** Name for 'static' keyword. */ | ||
private static final String STATIC_KEYWORD = "static"; | ||
|
||
/** Name for 'final' keyword. */ | ||
private static final String FINAL_KEYWORD = "final"; | ||
|
||
/** Whether to check that interface methods are not implicitly {@code public}. */ | ||
private boolean violateImpliedPublicMethod = true; | ||
|
||
/** Whether to check that interface methods are not implicitly {@code abstract}. */ | ||
private boolean violateImpliedAbstractMethod = true; | ||
|
||
/** Whether to check that interface fields are not implicitly {@code public}. */ | ||
private boolean violateImpliedPublicField = true; | ||
|
||
/** Whether to check that interface fields are not implicitly {@code static}. */ | ||
private boolean violateImpliedStaticField = true; | ||
|
||
/** Whether to check that interface fields are not implicitly {@code final}. */ | ||
private boolean violateImpliedFinalField = true; | ||
|
||
/** Whether to check that interface nested types are not implicitly {@code public}. */ | ||
private boolean violateImpliedPublicNested = true; | ||
|
||
/** Whether to check that interface nested types are not implicitly {@code static}. */ | ||
private boolean violateImpliedStaticNested = true; | ||
|
||
/** | ||
* Setter that checks interface methods to make sure that {@code public} is not implied. | ||
* @param violateImpliedPublicMethod | ||
* True to perform the check, false to turn the check off. | ||
*/ | ||
public void setViolateImpliedPublicMethod(boolean violateImpliedPublicMethod) { | ||
this.violateImpliedPublicMethod = violateImpliedPublicMethod; | ||
} | ||
|
||
/** | ||
* Setter that checks interface method to make sure that {@code abstract} is not implied. | ||
* @param violateImpliedAbstractMethod | ||
* True to perform the check, false to turn the check off. | ||
*/ | ||
public void setViolateImpliedAbstractMethod(boolean violateImpliedAbstractMethod) { | ||
this.violateImpliedAbstractMethod = violateImpliedAbstractMethod; | ||
} | ||
|
||
/** | ||
* Setter that checks interface fields to make sure that {@code public} is not implied. | ||
* @param violateImpliedPublicField | ||
* True to perform the check, false to turn the check off. | ||
*/ | ||
public void setViolateImpliedPublicField(boolean violateImpliedPublicField) { | ||
this.violateImpliedPublicField = violateImpliedPublicField; | ||
} | ||
|
||
/** | ||
* Setter that checks interface fields to make sure that {@code static} is not implied. | ||
* @param violateImpliedStaticField | ||
* True to perform the check, false to turn the check off. | ||
*/ | ||
public void setViolateImpliedStaticField(boolean violateImpliedStaticField) { | ||
this.violateImpliedStaticField = violateImpliedStaticField; | ||
} | ||
|
||
/** | ||
* Setter that checks interface fields to make sure that {@code final} is not implied. | ||
* @param violateImpliedFinalField | ||
* True to perform the check, false to turn the check off. | ||
*/ | ||
public void setViolateImpliedFinalField(boolean violateImpliedFinalField) { | ||
this.violateImpliedFinalField = violateImpliedFinalField; | ||
} | ||
|
||
/** | ||
* Setter that checks interface nested types to make sure that {@code public} is not implied. | ||
* @param violateImpliedPublicNested | ||
* True to perform the check, false to turn the check off. | ||
*/ | ||
public void setViolateImpliedPublicNested(boolean violateImpliedPublicNested) { | ||
this.violateImpliedPublicNested = violateImpliedPublicNested; | ||
} | ||
|
||
/** | ||
* Setter that checks interface nested types to make sure that {@code static} is not implied. | ||
* @param violateImpliedStaticNested | ||
* True to perform the check, false to turn the check off. | ||
*/ | ||
public void setViolateImpliedStaticNested(boolean violateImpliedStaticNested) { | ||
this.violateImpliedStaticNested = violateImpliedStaticNested; | ||
} | ||
|
||
@Override | ||
public int[] getDefaultTokens() { | ||
return getAcceptableTokens(); | ||
} | ||
|
||
@Override | ||
public int[] getRequiredTokens() { | ||
return getAcceptableTokens(); | ||
} | ||
|
||
@Override | ||
public int[] getAcceptableTokens() { | ||
return new int[] { | ||
TokenTypes.METHOD_DEF, | ||
TokenTypes.VARIABLE_DEF, | ||
TokenTypes.INTERFACE_DEF, | ||
TokenTypes.CLASS_DEF, | ||
TokenTypes.ENUM_DEF, | ||
}; | ||
} | ||
|
||
@Override | ||
public void visitToken(DetailAST ast) { | ||
if (isInterfaceMember(ast)) { | ||
if (ast.getType() == TokenTypes.METHOD_DEF) { | ||
processMethod(ast); | ||
} | ||
if (ast.getType() == TokenTypes.VARIABLE_DEF) { | ||
processField(ast); | ||
} | ||
if (ast.getType() == TokenTypes.CLASS_DEF | ||
|| ast.getType() == TokenTypes.INTERFACE_DEF | ||
|| ast.getType() == TokenTypes.ENUM_DEF) { | ||
processNestedType(ast); | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Checks if current AST node is member of Interface, not of their subnodes. | ||
* @param ast AST node | ||
* @return true or false | ||
*/ | ||
private static boolean isInterfaceMember(DetailAST ast) { | ||
DetailAST parentTypeDef = ast.getParent(); | ||
if (parentTypeDef != null) { | ||
parentTypeDef = parentTypeDef.getParent(); | ||
} | ||
return parentTypeDef != null | ||
&& parentTypeDef.getType() == TokenTypes.INTERFACE_DEF; | ||
} | ||
|
||
/** | ||
* Check method on interface. | ||
* @param ast method AST | ||
*/ | ||
private void processMethod(DetailAST ast) { | ||
final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); | ||
if (violateImpliedPublicMethod | ||
&& modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null | ||
&& modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) { | ||
log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER); | ||
} | ||
if (violateImpliedAbstractMethod | ||
&& modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null | ||
&& modifiers.findFirstToken(TokenTypes.LITERAL_DEFAULT) == null | ||
&& modifiers.findFirstToken(TokenTypes.LITERAL_PRIVATE) == null | ||
&& modifiers.findFirstToken(TokenTypes.ABSTRACT) == null) { | ||
log(ast, MSG_KEY, ABSTRACT_KEYWORD); | ||
} | ||
} | ||
|
||
/** | ||
* Check field on interface. | ||
* @param ast method AST | ||
*/ | ||
private void processField(DetailAST ast) { | ||
final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); | ||
if (violateImpliedPublicField | ||
&& modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) { | ||
log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER); | ||
} | ||
if (violateImpliedStaticField | ||
&& modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { | ||
log(ast, MSG_KEY, STATIC_KEYWORD); | ||
} | ||
if (violateImpliedFinalField | ||
&& modifiers.findFirstToken(TokenTypes.FINAL) == null) { | ||
log(ast, MSG_KEY, FINAL_KEYWORD); | ||
} | ||
} | ||
|
||
/** | ||
* Check nested types on interface. | ||
* @param ast method AST | ||
*/ | ||
private void processNestedType(DetailAST ast) { | ||
final DetailAST modifiers = ast.findFirstToken(TokenTypes.MODIFIERS); | ||
if (violateImpliedPublicNested | ||
&& modifiers.findFirstToken(TokenTypes.LITERAL_PUBLIC) == null) { | ||
log(ast, MSG_KEY, PUBLIC_ACCESS_MODIFIER); | ||
} | ||
if (violateImpliedStaticNested | ||
&& modifiers.findFirstToken(TokenTypes.LITERAL_STATIC) == null) { | ||
log(ast, MSG_KEY, STATIC_KEYWORD); | ||
} | ||
} | ||
|
||
} |
1 change: 1 addition & 0 deletions
1
src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
annotation.order=''{0}'' annotation modifier does not precede non-annotation modifiers. | ||
mod.order=''{0}'' modifier out of order with the JLS suggestions. | ||
redundantModifier=Redundant ''{0}'' modifier. | ||
interface.implied.modifier=Implied modifier ''{0}'' should be explicit. |
1 change: 1 addition & 0 deletions
1
src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_de.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
annotation.order=Annotation-Modifier ''{0}'' sollte vor den anderen Modifiern stehen. | ||
mod.order=Modifier ''{0}'' weicht von der empfohlenen Modifier-Reihenfolge der Java Language Specification ab. | ||
redundantModifier=Überflüssiger Modifier ''{0}''. | ||
interface.implied.modifier=Impliziter Modifikator sollte explizit sein: ''{0}'' |
1 change: 1 addition & 0 deletions
1
src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_es.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
annotation.order=El modificador de anotación ''{0}'' no precede a los modificadores normales. | ||
mod.order=Modificador ''{0}'' desordenado según las sugerencias de la JLS. | ||
redundantModifier=Modificador ''{0}'' redundante. | ||
interface.implied.modifier=El modificador impl\u00edcito debe ser expl\u00edcito: ''{0}'' |
1 change: 1 addition & 0 deletions
1
src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_fi.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
annotation.order=''{0}'' huomautus muokkaaja ei synny ennen kuin merkintä määritteet. | ||
mod.order=''{0}'' määrite rikkoo JLS:n suositusten mukaisesen järjestyksen. | ||
redundantModifier=Tarpeeton määrite: ''{0}'' | ||
interface.implied.modifier=Johdettu modifioijan on oltava selke\u00e4: ''{0}'' |
1 change: 1 addition & 0 deletions
1
src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_fr.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
annotation.order=Le modificateur d''annotation ''{0}'' ne précède pas les autres modificateurs. | ||
mod.order=Le mot-clef ''{0}'' n''apparaît pas dans l''ordre préconisé par les JLS. | ||
redundantModifier=Mot-clef ''{0}'' redondant. | ||
interface.implied.modifier=Le modificateur implicite devrait \u00eatre explicite: ''{0}'' |
1 change: 1 addition & 0 deletions
1
src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_ja.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
annotation.order=''{0}'' 注釈修飾子は、非注釈修飾子に先行していません。 | ||
mod.order=''{0}'' 修飾子が JLS 提案の順序に沿いません。 | ||
redundantModifier=冗長な ''{0}'' 修飾子です。 | ||
interface.implied.modifier=\u6697\u793A\u3055\u308C\u305F\u4FEE\u98FE\u5B50\u306F\u660E\u793A\u7684\u306B\u3059\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059: ''{0}'' |
1 change: 1 addition & 0 deletions
1
src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_pt.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
annotation.order=A anotação ''{0}'' não deveria preceder modificadores que não são anotações. | ||
mod.order=O modificador ''{0}'' está fora da ordem sugerida pela JLS. | ||
redundantModifier=O modificador ''{0}'' é redundante. | ||
interface.implied.modifier=O modificador impl\u00edcito deve ser expl\u00edcito: ''{0}'' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
1 change: 1 addition & 0 deletions
1
src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_zh.properties
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
annotation.order=注解 ''{0}'' 前不应有非注解修饰符。 | ||
mod.order=''{0}'' 修饰符顺序违反 JLS 建议. | ||
redundantModifier=多余修饰符: ''{0}''。 | ||
interface.implied.modifier=\u9690\u542B\u7684\u4FEE\u9970\u7B26\u5E94\u8BE5\u662F\u660E\u786E\u7684: ''{0}'' |
Oops, something went wrong.