From 7711035ddcba92fe7579950ecd60c27d8ce99743 Mon Sep 17 00:00:00 2001 From: Stephen Colebourne Date: Sun, 24 Jun 2018 22:23:55 +0100 Subject: [PATCH] Issue #5899: Add InterfaceImpliedModifierCheck --- config/checkstyle_checks.xml | 4 + .../checkstyle/PackageObjectFactory.java | 2 + .../InterfaceMemberImpliedModifierCheck.java | 274 +++++++++++++ .../checks/modifier/messages.properties | 1 + .../checks/modifier/messages_de.properties | 1 + .../checks/modifier/messages_es.properties | 1 + .../checks/modifier/messages_fi.properties | 1 + .../checks/modifier/messages_fr.properties | 1 + .../checks/modifier/messages_ja.properties | 1 + .../checks/modifier/messages_pt.properties | 1 + .../checks/modifier/messages_tr.properties | 1 + .../checks/modifier/messages_zh.properties | 1 + ...terfaceMemberImpliedModifierCheckTest.java | 372 ++++++++++++++++++ ...aceMemberImpliedModifierFieldsOnClass.java | 21 + ...emberImpliedModifierFieldsOnInterface.java | 21 + ...ceMemberImpliedModifierMethodsOnClass.java | 27 ++ ...erImpliedModifierMethodsOnClassNested.java | 29 ++ ...mberImpliedModifierMethodsOnInterface.java | 26 ++ ...pliedModifierMethodsOnInterfaceNested.java | 29 ++ ...aceMemberImpliedModifierNestedOnClass.java | 16 + ...berImpliedModifierNestedOnClassNested.java | 20 + ...emberImpliedModifierNestedOnInterface.java | 49 +++ ...mpliedModifierNestedOnInterfaceNested.java | 20 + src/xdocs/checks.xml | 6 + src/xdocs/config_modifier.xml | 192 +++++++++ 25 files changed, 1117 insertions(+) create mode 100644 src/main/java/com/puppycrawl/tools/checkstyle/checks/modifier/InterfaceMemberImpliedModifierCheck.java create mode 100644 src/test/java/com/puppycrawl/tools/checkstyle/checks/modifier/InterfaceMemberImpliedModifierCheckTest.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierFieldsOnClass.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierFieldsOnInterface.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnClass.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnClassNested.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnInterface.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnInterfaceNested.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnClass.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnClassNested.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnInterface.java create mode 100644 src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnInterfaceNested.java diff --git a/config/checkstyle_checks.xml b/config/checkstyle_checks.xml index 71a79a1a3ed0..6d4c03c9a6be 100644 --- a/config/checkstyle_checks.xml +++ b/config/checkstyle_checks.xml @@ -488,6 +488,10 @@ + + + + diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/PackageObjectFactory.java b/src/main/java/com/puppycrawl/tools/checkstyle/PackageObjectFactory.java index 739f0f2f1200..0cee31e85ef5 100644 --- a/src/main/java/com/puppycrawl/tools/checkstyle/PackageObjectFactory.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/PackageObjectFactory.java @@ -654,6 +654,8 @@ private static void fillChecksFromModifierPackage() { BASE_PACKAGE + ".checks.modifier.ModifierOrderCheck"); NAME_TO_FULL_MODULE_NAME.put("RedundantModifierCheck", BASE_PACKAGE + ".checks.modifier.RedundantModifierCheck"); + NAME_TO_FULL_MODULE_NAME.put("InterfaceMemberImpliedModifierCheck", + BASE_PACKAGE + ".checks.modifier.InterfaceMemberImpliedModifierCheck"); } /** diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/modifier/InterfaceMemberImpliedModifierCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/modifier/InterfaceMemberImpliedModifierCheck.java new file mode 100644 index 000000000000..eaa0dc98b6a8 --- /dev/null +++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/modifier/InterfaceMemberImpliedModifierCheck.java @@ -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. + * + *

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. + * + *

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. + * + *

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. + * + *

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. + * + *

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); + } + } + +} diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages.properties b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages.properties index 37a4d26c91be..5c2abbdf3edf 100644 --- a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages.properties +++ b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages.properties @@ -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. diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_de.properties b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_de.properties index 475d41ee1409..579ee48e7c1d 100644 --- a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_de.properties +++ b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_de.properties @@ -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}'' diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_es.properties b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_es.properties index dc56b1cc4364..3c94606dabab 100644 --- a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_es.properties +++ b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_es.properties @@ -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}'' diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_fi.properties b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_fi.properties index 3b5a192d010c..816ad7ada8c2 100644 --- a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_fi.properties +++ b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_fi.properties @@ -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}'' diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_fr.properties b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_fr.properties index 6460db61cc7a..362fb791ef15 100644 --- a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_fr.properties +++ b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_fr.properties @@ -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}'' diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_ja.properties b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_ja.properties index 5f0f75656ab1..833a36c94c7e 100644 --- a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_ja.properties +++ b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_ja.properties @@ -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}'' diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_pt.properties b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_pt.properties index d2eb10e1d4fe..2cfd920fb851 100644 --- a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_pt.properties +++ b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_pt.properties @@ -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}'' diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_tr.properties b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_tr.properties index beda6e969125..e5b1bfef3f2f 100644 --- a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_tr.properties +++ b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_tr.properties @@ -5,3 +5,4 @@ annotation.order = ''{0}'' anotasyon niteleyicisi, anotasyon olmayan niteleyicil mod.order = ''{0}'' niteleyicisi, Java tarafından önerilen sırada değil. redundantModifier = Gereksiz ''{0}'' niteleyicisi. +interface.implied.modifier=Z\u0131mni de\u011Fi\u015Ftirici aç\u0131k olmal\u0131d\u0131r: ''{0}'' diff --git a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_zh.properties b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_zh.properties index efd339c97130..d7dddc975976 100644 --- a/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_zh.properties +++ b/src/main/resources/com/puppycrawl/tools/checkstyle/checks/modifier/messages_zh.properties @@ -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}'' diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/modifier/InterfaceMemberImpliedModifierCheckTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/modifier/InterfaceMemberImpliedModifierCheckTest.java new file mode 100644 index 000000000000..68d999ba2c41 --- /dev/null +++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/modifier/InterfaceMemberImpliedModifierCheckTest.java @@ -0,0 +1,372 @@ +//////////////////////////////////////////////////////////////////////////////// +// 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 static com.puppycrawl.tools.checkstyle.checks.modifier.InterfaceMemberImpliedModifierCheck.MSG_KEY; +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.puppycrawl.tools.checkstyle.AbstractModuleTestSupport; +import com.puppycrawl.tools.checkstyle.DefaultConfiguration; +import com.puppycrawl.tools.checkstyle.api.DetailAST; +import com.puppycrawl.tools.checkstyle.api.TokenTypes; + +public class InterfaceMemberImpliedModifierCheckTest + extends AbstractModuleTestSupport { + + @Override + protected String getPackageLocation() { + return "com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier"; + } + + @Test + public void testMethodsOnInterfaceNoImpliedPublicAbstract() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + final String[] expected = { + "8:5: " + getCheckMessage(MSG_KEY, "public"), + "14:5: " + getCheckMessage(MSG_KEY, "public"), + "19:5: " + getCheckMessage(MSG_KEY, "public"), + "21:5: " + getCheckMessage(MSG_KEY, "abstract"), + "23:5: " + getCheckMessage(MSG_KEY, "public"), + "23:5: " + getCheckMessage(MSG_KEY, "abstract"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierMethodsOnInterface.java"), + expected); + } + + @Test + public void testMethodsOnInterfaceNoImpliedAbstractAllowImpliedPublic() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + checkConfig.addAttribute("violateImpliedPublicMethod", "false"); + final String[] expected = { + "21:5: " + getCheckMessage(MSG_KEY, "abstract"), + "23:5: " + getCheckMessage(MSG_KEY, "abstract"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierMethodsOnInterface.java"), + expected); + } + + @Test + public void testMethodsOnInterfaceNoImpliedPublicAllowImpliedAbstract() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + checkConfig.addAttribute("violateImpliedAbstractMethod", "false"); + final String[] expected = { + "8:5: " + getCheckMessage(MSG_KEY, "public"), + "14:5: " + getCheckMessage(MSG_KEY, "public"), + "19:5: " + getCheckMessage(MSG_KEY, "public"), + "23:5: " + getCheckMessage(MSG_KEY, "public"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierMethodsOnInterface.java"), + expected); + } + + @Test + public void testMethodsOnInterfaceAllowImpliedPublicAbstract() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + checkConfig.addAttribute("violateImpliedPublicMethod", "false"); + checkConfig.addAttribute("violateImpliedAbstractMethod", "false"); + final String[] expected = {}; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierMethodsOnInterface.java"), + expected); + } + + @Test + public void testMethodsOnClassIgnored() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + final String[] expected = {}; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierMethodsOnClass.java"), + expected); + } + + @Test + public void testMethodsOnInterfaceNestedNoImpliedPublicAbstract() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + final String[] expected = { + "10:9: " + getCheckMessage(MSG_KEY, "public"), + "16:9: " + getCheckMessage(MSG_KEY, "public"), + "21:9: " + getCheckMessage(MSG_KEY, "public"), + "23:9: " + getCheckMessage(MSG_KEY, "abstract"), + "25:9: " + getCheckMessage(MSG_KEY, "public"), + "25:9: " + getCheckMessage(MSG_KEY, "abstract"), + }; + verify( + checkConfig, + getPath("InputInterfaceMemberImpliedModifierMethodsOnInterfaceNested.java"), + expected); + } + + @Test + public void testMethodsOnClassNestedNoImpliedPublicAbstract() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + final String[] expected = { + "10:9: " + getCheckMessage(MSG_KEY, "public"), + "16:9: " + getCheckMessage(MSG_KEY, "public"), + "21:9: " + getCheckMessage(MSG_KEY, "public"), + "23:9: " + getCheckMessage(MSG_KEY, "abstract"), + "25:9: " + getCheckMessage(MSG_KEY, "public"), + "25:9: " + getCheckMessage(MSG_KEY, "abstract"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierMethodsOnClassNested.java"), + expected); + } + + @Test + public void testFieldsOnInterfaceNoImpliedPublicStaticFinal() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + final String[] expected = { + "7:5: " + getCheckMessage(MSG_KEY, "final"), + "9:5: " + getCheckMessage(MSG_KEY, "static"), + "11:5: " + getCheckMessage(MSG_KEY, "static"), + "11:5: " + getCheckMessage(MSG_KEY, "final"), + "13:5: " + getCheckMessage(MSG_KEY, "public"), + "15:5: " + getCheckMessage(MSG_KEY, "public"), + "15:5: " + getCheckMessage(MSG_KEY, "final"), + "17:5: " + getCheckMessage(MSG_KEY, "public"), + "17:5: " + getCheckMessage(MSG_KEY, "static"), + "19:5: " + getCheckMessage(MSG_KEY, "public"), + "19:5: " + getCheckMessage(MSG_KEY, "static"), + "19:5: " + getCheckMessage(MSG_KEY, "final"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierFieldsOnInterface.java"), + expected); + } + + @Test + public void testFieldsOnInterfaceNoImpliedPublicStaticAllowImpliedFinal() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + checkConfig.addAttribute("violateImpliedFinalField", "false"); + final String[] expected = { + "9:5: " + getCheckMessage(MSG_KEY, "static"), + "11:5: " + getCheckMessage(MSG_KEY, "static"), + "13:5: " + getCheckMessage(MSG_KEY, "public"), + "15:5: " + getCheckMessage(MSG_KEY, "public"), + "17:5: " + getCheckMessage(MSG_KEY, "public"), + "17:5: " + getCheckMessage(MSG_KEY, "static"), + "19:5: " + getCheckMessage(MSG_KEY, "public"), + "19:5: " + getCheckMessage(MSG_KEY, "static"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierFieldsOnInterface.java"), + expected); + } + + @Test + public void testFieldsOnInterfaceNoImpliedPublicFinalAllowImpliedStatic() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + checkConfig.addAttribute("violateImpliedStaticField", "false"); + final String[] expected = { + "7:5: " + getCheckMessage(MSG_KEY, "final"), + "11:5: " + getCheckMessage(MSG_KEY, "final"), + "13:5: " + getCheckMessage(MSG_KEY, "public"), + "15:5: " + getCheckMessage(MSG_KEY, "public"), + "15:5: " + getCheckMessage(MSG_KEY, "final"), + "17:5: " + getCheckMessage(MSG_KEY, "public"), + "19:5: " + getCheckMessage(MSG_KEY, "public"), + "19:5: " + getCheckMessage(MSG_KEY, "final"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierFieldsOnInterface.java"), + expected); + } + + @Test + public void testFieldsOnInterfaceNoImpliedStaticFinalAllowImpliedPublic() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + checkConfig.addAttribute("violateImpliedPublicField", "false"); + final String[] expected = { + "7:5: " + getCheckMessage(MSG_KEY, "final"), + "9:5: " + getCheckMessage(MSG_KEY, "static"), + "11:5: " + getCheckMessage(MSG_KEY, "static"), + "11:5: " + getCheckMessage(MSG_KEY, "final"), + "15:5: " + getCheckMessage(MSG_KEY, "final"), + "17:5: " + getCheckMessage(MSG_KEY, "static"), + "19:5: " + getCheckMessage(MSG_KEY, "static"), + "19:5: " + getCheckMessage(MSG_KEY, "final"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierFieldsOnInterface.java"), + expected); + } + + @Test + public void testFieldsOnInterfaceAllowImpliedPublicStaticFinal() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + checkConfig.addAttribute("violateImpliedPublicField", "false"); + checkConfig.addAttribute("violateImpliedStaticField", "false"); + checkConfig.addAttribute("violateImpliedFinalField", "false"); + final String[] expected = {}; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierFieldsOnInterface.java"), + expected); + } + + @Test + public void testFieldsOnClassIgnored() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + final String[] expected = {}; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierFieldsOnClass.java"), + expected); + } + + @Test + public void testNestedOnInterfaceNoImpliedPublicStatic() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + final String[] expected = { + "8:5: " + getCheckMessage(MSG_KEY, "static"), + "11:5: " + getCheckMessage(MSG_KEY, "public"), + "14:5: " + getCheckMessage(MSG_KEY, "public"), + "14:5: " + getCheckMessage(MSG_KEY, "static"), + "22:5: " + getCheckMessage(MSG_KEY, "static"), + "27:5: " + getCheckMessage(MSG_KEY, "public"), + "32:5: " + getCheckMessage(MSG_KEY, "public"), + "32:5: " + getCheckMessage(MSG_KEY, "static"), + "40:5: " + getCheckMessage(MSG_KEY, "static"), + "43:5: " + getCheckMessage(MSG_KEY, "public"), + "46:5: " + getCheckMessage(MSG_KEY, "public"), + "46:5: " + getCheckMessage(MSG_KEY, "static"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierNestedOnInterface.java"), + expected); + } + + @Test + public void testNestedOnInterfaceNoImpliedStaticAllowImpliedPublic() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + checkConfig.addAttribute("violateImpliedPublicNested", "false"); + final String[] expected = { + "8:5: " + getCheckMessage(MSG_KEY, "static"), + "14:5: " + getCheckMessage(MSG_KEY, "static"), + "22:5: " + getCheckMessage(MSG_KEY, "static"), + "32:5: " + getCheckMessage(MSG_KEY, "static"), + "40:5: " + getCheckMessage(MSG_KEY, "static"), + "46:5: " + getCheckMessage(MSG_KEY, "static"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierNestedOnInterface.java"), + expected); + } + + @Test + public void testNestedOnInterfaceNoImpliedPublicAllowImpliedStatic() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + checkConfig.addAttribute("violateImpliedStaticNested", "false"); + final String[] expected = { + "11:5: " + getCheckMessage(MSG_KEY, "public"), + "14:5: " + getCheckMessage(MSG_KEY, "public"), + "27:5: " + getCheckMessage(MSG_KEY, "public"), + "32:5: " + getCheckMessage(MSG_KEY, "public"), + "43:5: " + getCheckMessage(MSG_KEY, "public"), + "46:5: " + getCheckMessage(MSG_KEY, "public"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierNestedOnInterface.java"), + expected); + } + + @Test + public void testNestedOnInterfaceAllowImpliedPublicStatic() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + checkConfig.addAttribute("violateImpliedPublicNested", "false"); + checkConfig.addAttribute("violateImpliedStaticNested", "false"); + final String[] expected = {}; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierNestedOnInterface.java"), + expected); + } + + @Test + public void testNestedOnClassIgnored() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + final String[] expected = {}; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierNestedOnClass.java"), + expected); + } + + @Test + public void testNestedOnInterfaceNestedNoImpliedPublicStatic() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + final String[] expected = { + "7:9: " + getCheckMessage(MSG_KEY, "public"), + "7:9: " + getCheckMessage(MSG_KEY, "static"), + "10:9: " + getCheckMessage(MSG_KEY, "public"), + "10:9: " + getCheckMessage(MSG_KEY, "static"), + "15:9: " + getCheckMessage(MSG_KEY, "public"), + "15:9: " + getCheckMessage(MSG_KEY, "static"), + }; + verify(checkConfig, + getPath("InputInterfaceMemberImpliedModifierNestedOnInterfaceNested.java"), + expected); + } + + @Test + public void testNestedOnClassNestedNoImpliedPublicStatic() throws Exception { + final DefaultConfiguration checkConfig = + createModuleConfig(InterfaceMemberImpliedModifierCheck.class); + final String[] expected = { + "7:9: " + getCheckMessage(MSG_KEY, "public"), + "7:9: " + getCheckMessage(MSG_KEY, "static"), + "10:9: " + getCheckMessage(MSG_KEY, "public"), + "10:9: " + getCheckMessage(MSG_KEY, "static"), + "15:9: " + getCheckMessage(MSG_KEY, "public"), + "15:9: " + getCheckMessage(MSG_KEY, "static"), + }; + verify(checkConfig, getPath("InputInterfaceMemberImpliedModifierNestedOnClassNested.java"), + expected); + } + + @Test + public void testPrivateStaticMethod() throws Exception { + // private methods on interfaces are Java 9 only, but checkstyle is Java 8 + // this test exists solely to provide coverage of private methods + final DetailAST modPrivate = new DetailAST(); + modPrivate.setType(TokenTypes.LITERAL_PRIVATE); + final DetailAST modifiers = new DetailAST(); + modifiers.setType(TokenTypes.MODIFIERS); + modifiers.addChild(modPrivate); + final DetailAST method = new DetailAST(); + method.setType(TokenTypes.METHOD_DEF); + method.addChild(modifiers); + final DetailAST objBlock = new DetailAST(); + objBlock.setType(TokenTypes.OBJBLOCK); + objBlock.addChild(method); + final DetailAST iface = new DetailAST(); + iface.setType(TokenTypes.INTERFACE_DEF); + iface.addChild(objBlock); + final InterfaceMemberImpliedModifierCheck check = + new InterfaceMemberImpliedModifierCheck(); + check.visitToken(method); + assertEquals("No log message expected from interface with private method", + 0, check.getMessages().size()); + } + +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierFieldsOnClass.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierFieldsOnClass.java new file mode 100644 index 000000000000..f0269c47a1d2 --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierFieldsOnClass.java @@ -0,0 +1,21 @@ +package com.puppycrawl.tools.checkstyle.checks.modifier.interfacememberimpliedmodifier; + +public class InputInterfaceMemberImpliedModifierFieldsOnClass { + + public static final int fieldPublicStaticFinal = 1; + + public static int fieldPublicStatic = 1; + + public final int fieldPublicFinal = 1; + + public int fieldPublic = 1; + + static final int fieldStaticFinal = 1; + + static int fieldStatic = 1; + + final int fieldFinal = 1; + + int field = 1; + +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierFieldsOnInterface.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierFieldsOnInterface.java new file mode 100644 index 000000000000..5dbee78ad5b7 --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierFieldsOnInterface.java @@ -0,0 +1,21 @@ +package com.puppycrawl.tools.checkstyle.checks.modifier.interfacememberimpliedmodifier; + +public interface InputInterfaceMemberImpliedModifierFieldsOnInterface { + + public static final int fieldPublicStaticFinal = 1; + + public static int fieldPublicStatic = 1; + + public final int fieldPublicFinal = 1; + + public int fieldPublic = 1; + + static final int fieldStaticFinal = 1; + + static int fieldStatic = 1; + + final int fieldFinal = 1; + + int field = 1; + +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnClass.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnClass.java new file mode 100644 index 000000000000..8a051c071829 --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnClass.java @@ -0,0 +1,27 @@ +package com.puppycrawl.tools.checkstyle.checks.modifier.interfacememberimpliedmodifier; + +public abstract class InputInterfaceMemberImpliedModifierMethodsOnClass { + + public static void methodPublicStatic() { + } + + private static void methodPrivateStatic() { + } + + static void methodStatic() { + } + + public void methodPublicDefault() { + } + + private void methodPrivateDefault() { + } + + void methodDefault() { + } + + public abstract void methodPublicAbstract(); + + abstract void methodAbstract(); + +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnClassNested.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnClassNested.java new file mode 100644 index 000000000000..da2602c6ab7c --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnClassNested.java @@ -0,0 +1,29 @@ +package com.puppycrawl.tools.checkstyle.checks.modifier.interfacememberimpliedmodifier; + +public class InputInterfaceMemberImpliedModifierMethodsOnClassNested { + + public static interface NestedInterface { + + public static void methodPublicStatic() { + } + + static void methodStatic() { + } + + public default void methodPublicDefault() { + } + + default void methodDefault() { + } + + public abstract void methodPublicAbstract(); + + abstract void methodAbstract(); + + public void methodPublic(); + + void method(); + + } + +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnInterface.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnInterface.java new file mode 100644 index 000000000000..8c48eb774259 --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnInterface.java @@ -0,0 +1,26 @@ +package com.puppycrawl.tools.checkstyle.checks.modifier.interfacememberimpliedmodifier; + +public interface InputInterfaceMemberImpliedModifierMethodsOnInterface { + + public static void methodPublicStatic() { + } + + static void methodStatic() { + } + + public default void methodPublicDefault() { + } + + default void methodDefault() { + } + + public abstract void methodPublicAbstract(); + + abstract void methodAbstract(); + + public void methodPublic(); + + void method(); + + // cannot test private methods due to Java 8 baseline +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnInterfaceNested.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnInterfaceNested.java new file mode 100644 index 000000000000..384b03ac1758 --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierMethodsOnInterfaceNested.java @@ -0,0 +1,29 @@ +package com.puppycrawl.tools.checkstyle.checks.modifier.interfacememberimpliedmodifier; + +public interface InputInterfaceMemberImpliedModifierMethodsOnInterfaceNested { + + public static interface NestedInterface { + + public static void methodPublicStatic() { + } + + static void methodStatic() { + } + + public default void methodPublicDefault() { + } + + default void methodDefault() { + } + + public abstract void methodPublicAbstract(); + + abstract void methodAbstract(); + + public void methodPublic(); + + void method(); + + } + +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnClass.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnClass.java new file mode 100644 index 000000000000..0920d382566d --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnClass.java @@ -0,0 +1,16 @@ +package com.puppycrawl.tools.checkstyle.checks.modifier.interfacememberimpliedmodifier; + +public class InputInterfaceMemberImpliedModifierNestedOnClass { + + interface NestedInterface { + } + + enum NestedEnum { + TRUE, + FALSE + } + + class NestedClass { + } + +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnClassNested.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnClassNested.java new file mode 100644 index 000000000000..1aa512c49019 --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnClassNested.java @@ -0,0 +1,20 @@ +package com.puppycrawl.tools.checkstyle.checks.modifier.interfacememberimpliedmodifier; + +public class InputInterfaceMemberImpliedModifierNestedOnClassNested { + + interface NestedInterface { + + interface NestedNestedInterface { + } + + enum NestedNestedEnum { + TRUE, + FALSE + } + + class NestedNestedClass { + } + + } + +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnInterface.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnInterface.java new file mode 100644 index 000000000000..b53f216a3654 --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnInterface.java @@ -0,0 +1,49 @@ +package com.puppycrawl.tools.checkstyle.checks.modifier.interfacememberimpliedmodifier; + +public interface InputInterfaceMemberImpliedModifierNestedOnInterface { + + public static interface NestedInterfacePublicStatic { + } + + public interface NestedInterfacePublic { + } + + static interface NestedInterfaceStatic { + } + + interface NestedInterface { + } + + public static enum NestedEnumPublicStatic { + TRUE, + FALSE + } + + public enum NestedEnumPublic { + TRUE, + FALSE + } + + static enum NestedEnumStatic { + TRUE, + FALSE + } + + enum NestedEnum { + TRUE, + FALSE + } + + public static class NestedClassPublicStatic { + } + + public class NestedClassPublic { + } + + static class NestedClassStatic { + } + + class NestedClass { + } + +} diff --git a/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnInterfaceNested.java b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnInterfaceNested.java new file mode 100644 index 000000000000..5559a0ddade3 --- /dev/null +++ b/src/test/resources/com/puppycrawl/tools/checkstyle/checks/modifier/interfacememberimpliedmodifier/InputInterfaceMemberImpliedModifierNestedOnInterfaceNested.java @@ -0,0 +1,20 @@ +package com.puppycrawl.tools.checkstyle.checks.modifier.interfacememberimpliedmodifier; + +public interface InputInterfaceMemberImpliedModifierNestedOnInterfaceNested { + + public static interface NestedInterface { + + interface NestedNestedInterface { + } + + enum NestedNestedEnum { + TRUE, + FALSE + } + + class NestedNestedClass { + } + + } + +} diff --git a/src/xdocs/checks.xml b/src/xdocs/checks.xml index 7219a7f70302..273b684b0c58 100644 --- a/src/xdocs/checks.xml +++ b/src/xdocs/checks.xml @@ -346,6 +346,12 @@ Implements Bloch, Effective Java, Item 17 - Use Interfaces only to define types. + + + InterfaceMemberImpliedModifier + + Checks that modifiers on interface members are not implicit. + InterfaceTypeParameterName diff --git a/src/xdocs/config_modifier.xml b/src/xdocs/config_modifier.xml index d32f2fed7087..ca42c36e06f6 100644 --- a/src/xdocs/config_modifier.xml +++ b/src/xdocs/config_modifier.xml @@ -22,6 +22,198 @@ +

+ + +

Since Checkstyle 8.11

+

+ Checks for implicit modifiers in interfaces. +

+

+ This check is effectively the opposite of + RedundantModifier. + It checks the modifiers on interface members, ensuring that certain + modifiers are explicitly specified even though they are actually redundant. +

+

+ Methods on interfaces are public by default, however from Java 9 + they can also be private. This check provides the ability to enforce + that public is not implicitly added by the compiler. +

+

+ From Java 8, there are three types of methods on interfaces - static methods + marked with static, default methods marked with default and + abstract methods which do not have to be marked with anything. + From Java 9, there are also private methods marked with private. + This check provides the ability to enforce that abstract is not + implicitly added by the compiler. +

+

+ Fields on interfaces are always 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. +

+

+ Nested types within an interface are always public static and as such the + compiler does not require the public static modifiers. This check provides + the ability to enforce that the public and static modifiers + are not implicitly added by the compiler. +

+ +public interface AddressFactory { + // check enforces code contains "public static final" + public static final String UNKNOWN = "Unknown"; + + // check enforces code contains "public" or "private" + public static AddressFactory instance(); + + // check enforces code contains "public abstract" + public abstract Address createAddress(String addressLine, String city); + + // check enforces code contains "public default" or "private" + public default Address createAddress(String city) { + return createAddress(UNKNOWN, city); + } +} + +

+ 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. +

+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
namedescriptiontypedefault valuesince
violateImpliedPublicField + Controls whether to enforce that public is not being implied by the + compiler on interface fields (true)Booleantrue8.11
violateImpliedStaticField + Controls whether to enforce that static is not being implied by the + compiler on interface fields (true)Booleantrue8.11
violateImpliedFinalField + Controls whether to enforce that final is not being implied by the + compiler on interface fields (true)Booleantrue8.11
violateImpliedPublicMethod + Controls whether to enforce that public is not being implied by the + compiler on interface methods (true)Booleantrue8.11
violateImpliedAbstractMethod + Controls whether to enforce that abstract is not being implied by the + compiler on interface methods (true)Booleantrue8.11
violateImpliedPublicNested + Controls whether to enforce that public is not being implied by the + compiler on interface nested types (true)Booleantrue8.11
violateImpliedStaticNested + Controls whether to enforce that static is not being implied by the + compiler on interface nested types (true)Booleantrue8.11
+
+ + +

To configure the check:

+ +<module name="InterfaceMemberImpliedModifier"/> + + +

+ To configure the check to check methods and fields but not nested types: +

+ +<module name="InterfaceMemberImpliedModifier"> + <property name="violateImpliedPublicNested" value="false"/> + <property name="violateImpliedStaticNested" value="false"/> +</module> + +
+ + + + + + + +

+ All messages can be customized if the default message doesn't suit you. + Please see the documentation to learn how to. +

+
+ + +

com.puppycrawl.tools.checkstyle.checks.modifier

+
+ + +

+ TreeWalker +

+
+
+

Since Checkstyle 3.0