diff --git a/src/main/java/com/puppycrawl/tools/checkstyle/checks/modifier/ModifierOrderCheck.java b/src/main/java/com/puppycrawl/tools/checkstyle/checks/modifier/ModifierOrderCheck.java index 76e03754f..837ad3a29 100644 --- a/src/main/java/com/puppycrawl/tools/checkstyle/checks/modifier/ModifierOrderCheck.java +++ b/src/main/java/com/puppycrawl/tools/checkstyle/checks/modifier/ModifierOrderCheck.java @@ -154,8 +154,10 @@ public class ModifierOrderCheck && offendingModifier == null) { if (modifier.getType() == TokenTypes.ANNOTATION) { - //Annotation not at start of modifiers, bad - offendingModifier = modifier; + if (!isAnnotationOnType(modifier)) { + //Annotation not at start of modifiers, bad + offendingModifier = modifier; + } break; } @@ -193,4 +195,20 @@ public class ModifierOrderCheck while (modifierIterator.hasNext() && modifier.getType() == TokenTypes.ANNOTATION); return modifier; } + + /** + * Checks whether annotation on type takes place. + * @param modifier modifier token. + * @return true if annotation on type takes place. + */ + private static boolean isAnnotationOnType(DetailAST modifier) { + boolean annotationOnType = false; + final DetailAST modifiers = modifier.getParent(); + final int definitionTokenType = modifiers.getParent().getType(); + if (definitionTokenType == TokenTypes.VARIABLE_DEF + || definitionTokenType == TokenTypes.PARAMETER_DEF) { + annotationOnType = true; + } + return annotationOnType; + } } diff --git a/src/test/java/com/puppycrawl/tools/checkstyle/checks/modifier/ModifierOrderCheckTest.java b/src/test/java/com/puppycrawl/tools/checkstyle/checks/modifier/ModifierOrderCheckTest.java index 817d82aa8..185ca1443 100644 --- a/src/test/java/com/puppycrawl/tools/checkstyle/checks/modifier/ModifierOrderCheckTest.java +++ b/src/test/java/com/puppycrawl/tools/checkstyle/checks/modifier/ModifierOrderCheckTest.java @@ -110,4 +110,15 @@ public class ModifierOrderCheckTest Assert.assertNotSame(unexpectedArray, actual); Assert.assertNotNull(actual); } + + @Test + public void testSkipTypeAnnotations() throws Exception { + // Type Annotations are avaliable only in Java 8 + // We skip type annotations from validation + // See https://github.com/checkstyle/checkstyle/issues/903#issuecomment-172228013 + final DefaultConfiguration checkConfig = + createCheckConfig(ModifierOrderCheck.class); + final String[] expected = ArrayUtils.EMPTY_STRING_ARRAY; + verify(checkConfig, getNonCompilablePath("InputTypeAnnotations.java"), expected); + } } diff --git a/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/modifier/InputTypeAnnotations.java b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/modifier/InputTypeAnnotations.java new file mode 100644 index 000000000..a92f940f0 --- /dev/null +++ b/src/test/resources-noncompilable/com/puppycrawl/tools/checkstyle/checks/modifier/InputTypeAnnotations.java @@ -0,0 +1,86 @@ +//Compilable with Java8 +package com.puppycrawl.tools.checkstyle.checks.modifier; + +import java.io.File; +import java.io.IOException; +import java.io.Serializable; +import java.lang.annotation.ElementType; +import java.lang.annotation.Target; +import java.util.Arrays; +import java.util.Collection; +import java.util.Comparator; +import java.util.List; +import java.util.Map; + +public class InputTypeAnnotations { + + // Simple type definitions with type annotations + private @TypeAnnotation String hello = "Hello, World!"; + private @TypeAnnotation final String jdk = "JDK8"; + @TypeAnnotation private String projectName = "Checkstyle"; + + // We can use type Annotations with generic type arguments + private Map.@TypeAnnotation Entry entry; + // Type annotations can also be applied to nested types + private List<@TypeAnnotation String> strings; + + // Constructors with type annotations + { + new @TypeAnnotation Object(); + } + + static { + new @TypeAnnotation Object(); + } + + public void foo1() { + new @TypeAnnotation Object(); + } + + // Type annotations work with nested (non static) class constructors too + public void foo2() { + InputTypeAnnotations myObject = new InputTypeAnnotations(); + myObject.new @TypeAnnotation Nested(); + } + + // Type casts + public void foo3() { + String myString = (@TypeAnnotation String) new Object(); + + } + + // Type annotations with method arguments + private void foo4(final @TypeAnnotation String parameterName) { } + + // Inheritance + class MySerializableClass implements @TypeAnnotation Serializable { } + + // Nested type annotations + Map<@TypeAnnotation String, @TypeAnnotation List<@TypeAnnotation String>> documents; + + // Apply type annotations to intersection types + public & @TypeAnnotation Comparable> void foo5() { } + + // Including parameter bounds and wildcard bounds + class Folder { } + Collection c; + List<@TypeAnnotation ? extends Comparable> unchangeable; + + // Throwing exceptions + void foo6() throws @TypeAnnotation IOException { } + + // Type annotations in instanceof statements + public void foo7() { + boolean isNonNull = "string" instanceof @TypeAnnotation String; + + } + + class Nested { } + + class T { } +} + +@Target({ElementType.FIELD, ElementType.LOCAL_VARIABLE, ElementType.PARAMETER, + ElementType.TYPE_PARAMETER, ElementType.TYPE_USE}) +@interface TypeAnnotation { +} \ No newline at end of file diff --git a/src/xdocs/config_modifier.xml b/src/xdocs/config_modifier.xml index 374efcd9d..7911772be 100644 --- a/src/xdocs/config_modifier.xml +++ b/src/xdocs/config_modifier.xml @@ -66,6 +66,12 @@ strictfp + +

+ ATTENTION: We skip + type annotations from validation. +

+