Skip to content

Commit

Permalink
Temporarily support guava's {Required,Incompatible}Modifier annotations
Browse files Browse the repository at this point in the history
-------------
Created by MOE: http://code.google.com/p/moe-java
MOE_MIGRATED_REVID=85735871
  • Loading branch information
cushon committed Feb 13, 2015
1 parent e911f71 commit 53d8d9d
Show file tree
Hide file tree
Showing 4 changed files with 195 additions and 21 deletions.
Expand Up @@ -21,6 +21,10 @@
import static com.google.errorprone.BugPattern.MaturityLevel.MATURE; import static com.google.errorprone.BugPattern.MaturityLevel.MATURE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;


import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.errorprone.BugPattern; import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState; import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.IncompatibleModifiers; import com.google.errorprone.annotations.IncompatibleModifiers;
Expand All @@ -30,11 +34,13 @@


import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ModifiersTree; import com.sun.source.tree.ModifiersTree;
import com.sun.tools.javac.code.Attribute;


import java.util.EnumSet; import java.util.List;
import java.util.Set; import java.util.Set;


import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;


/** /**
* @author sgoldfeder@google.com (Steven Goldfeder) * @author sgoldfeder@google.com (Steven Goldfeder)
Expand All @@ -51,19 +57,44 @@ public class IncompatibleModifiersChecker extends BugChecker implements Annotati
private static final String MESSAGE_TEMPLATE = "%s has specified that it should not be used" private static final String MESSAGE_TEMPLATE = "%s has specified that it should not be used"
+ " together with the following modifiers: %s"; + " together with the following modifiers: %s";


@Override
public Description matchAnnotation(AnnotationTree tree, VisitorState state) { // TODO(user): deprecate and remove
Set<Modifier> incompatible = EnumSet.noneOf(Modifier.class); private static final String GUAVA_ANNOTATION =
IncompatibleModifiers incompatibleModifiersAnnotation = "com.google.common.annotations.IncompatibleModifiers";
ASTHelpers.getAnnotation(tree, IncompatibleModifiers.class);
if (incompatibleModifiersAnnotation != null) { private static final Function<Attribute.Enum, Modifier> TO_MODIFIER =
for (Modifier m : incompatibleModifiersAnnotation.value()) { new Function<Attribute.Enum, Modifier>() {
ModifiersTree modifiers = (ModifiersTree) state.getPath().getParentPath().getLeaf(); public Modifier apply(Attribute.Enum input) {
if (modifiers.getFlags().contains(m)) { return Modifier.valueOf(input.getValue().name.toString());
incompatible.add(m);
} }
};

private static Set<Modifier> getIncompatibleModifiers(AnnotationTree tree, VisitorState state) {
for (Attribute.Compound c : ASTHelpers.getSymbol(tree).getAnnotationMirrors()) {
if (((TypeElement) c.getAnnotationType().asElement()).getQualifiedName()
.contentEquals(GUAVA_ANNOTATION)) {
@SuppressWarnings("unchecked")
List<Attribute.Enum> modifiers =
(List<Attribute.Enum>) c.member(state.getName("value")).getValue();
return ImmutableSet.copyOf(Iterables.transform(modifiers, TO_MODIFIER));
} }
} }

IncompatibleModifiers annotation = ASTHelpers.getAnnotation(tree, IncompatibleModifiers.class);
if (annotation != null) {
return ImmutableSet.copyOf(annotation.value());
}

return ImmutableSet.of();
}


@Override
public Description matchAnnotation(AnnotationTree tree, VisitorState state) {
Set<Modifier> incompatible = Sets.intersection(
getIncompatibleModifiers(tree, state),
((ModifiersTree) state.getPath().getParentPath().getLeaf()).getFlags());

if (incompatible.isEmpty()) { if (incompatible.isEmpty()) {
return Description.NO_MATCH; return Description.NO_MATCH;
} }
Expand Down
Expand Up @@ -21,6 +21,10 @@
import static com.google.errorprone.BugPattern.MaturityLevel.MATURE; import static com.google.errorprone.BugPattern.MaturityLevel.MATURE;
import static com.google.errorprone.BugPattern.SeverityLevel.WARNING; import static com.google.errorprone.BugPattern.SeverityLevel.WARNING;


import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import com.google.errorprone.BugPattern; import com.google.errorprone.BugPattern;
import com.google.errorprone.VisitorState; import com.google.errorprone.VisitorState;
import com.google.errorprone.annotations.RequiredModifiers; import com.google.errorprone.annotations.RequiredModifiers;
Expand All @@ -30,11 +34,14 @@


import com.sun.source.tree.AnnotationTree; import com.sun.source.tree.AnnotationTree;
import com.sun.source.tree.ModifiersTree; import com.sun.source.tree.ModifiersTree;
import com.sun.tools.javac.code.Attribute;
import com.sun.tools.javac.util.Names;


import java.util.EnumSet; import java.util.List;
import java.util.Set; import java.util.Set;


import javax.lang.model.element.Modifier; import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;


/** /**
* @author sgoldfeder@google.com (Steven Goldfeder) * @author sgoldfeder@google.com (Steven Goldfeder)
Expand All @@ -51,18 +58,42 @@ public class RequiredModifiersChecker extends BugChecker implements AnnotationTr
private static final String MESSAGE_TEMPLATE = "%s has specified that it must be used" private static final String MESSAGE_TEMPLATE = "%s has specified that it must be used"
+ " together with the following modifiers: %s"; + " together with the following modifiers: %s";


@Override // TODO(user): deprecate and remove
public Description matchAnnotation(AnnotationTree tree, VisitorState state) { private static final String GUAVA_ANNOTATION =
Set<Modifier> missing = EnumSet.noneOf(Modifier.class); "com.google.common.annotations.RequiredModifiers";
RequiredModifiers requiredModifiersAnnotation =
ASTHelpers.getAnnotation(tree, RequiredModifiers.class); private static final Function<Attribute.Enum, Modifier> TO_MODIFIER =
if (requiredModifiersAnnotation != null) { new Function<Attribute.Enum, Modifier>() {
for (Modifier m : requiredModifiersAnnotation.value()) { public Modifier apply(Attribute.Enum input) {
if (!((ModifiersTree) state.getPath().getParentPath().getLeaf()).getFlags().contains(m)) { return Modifier.valueOf(input.getValue().name.toString());
missing.add(m);
} }
};

private static Set<Modifier> getRequiredModifiers(AnnotationTree tree, VisitorState state) {
for (Attribute.Compound c : ASTHelpers.getSymbol(tree).getAnnotationMirrors()) {
if (((TypeElement) c.getAnnotationType().asElement()).getQualifiedName()
.contentEquals(GUAVA_ANNOTATION)) {
@SuppressWarnings("unchecked")
List<Attribute.Enum> modifiers =
(List<Attribute.Enum>) c.member(Names.instance(state.context).value).getValue();
return ImmutableSet.copyOf(Iterables.transform(modifiers, TO_MODIFIER));
} }
} }

RequiredModifiers annotation = ASTHelpers.getAnnotation(tree, RequiredModifiers.class);
if (annotation != null) {
return ImmutableSet.copyOf(annotation.value());
}

return ImmutableSet.of();
}

@Override
public Description matchAnnotation(AnnotationTree tree, VisitorState state) {
Set<Modifier> missing = Sets.difference(
getRequiredModifiers(tree, state),
((ModifiersTree) state.getPath().getParentPath().getLeaf()).getFlags());

if (missing.isEmpty()) { if (missing.isEmpty()) {
return Description.NO_MATCH; return Description.NO_MATCH;
} }
Expand Down
Expand Up @@ -138,4 +138,60 @@ public void testAnnotationWithCompatibleModifiersSucceeds() throws Exception {
"import test.NotAbstract;", "import test.NotAbstract;",
"public class IncompatibleModifiersTestCase {}"))); "public class IncompatibleModifiersTestCase {}")));
} }

@Test
public void testGuavaAnnotation() throws Exception {
compilationHelper.assertCompileSucceedsWithMessages(Arrays.asList(
compilationHelper.fileManager().forSourceLines(
"com/google/common/annotations/IncompatibleModifiers.java",
"package com.google.common.annotations;",
"import javax.lang.model.element.Modifier;",
"import java.lang.annotation.Target;",
"import java.lang.annotation.ElementType;",
"@Target(ElementType.ANNOTATION_TYPE)",
"public @interface IncompatibleModifiers {",
" Modifier[] value();",
"}"),
compilationHelper.fileManager().forSourceLines("test/NotAbstract.java",
"package test;",
"import static javax.lang.model.element.Modifier.ABSTRACT;",
"import com.google.common.annotations.IncompatibleModifiers;",
"@IncompatibleModifiers(ABSTRACT)",
"public @interface NotAbstract {",
"}"),
compilationHelper.fileManager().forSourceLines("test/RequiredModifiersTestCase.java",
"package test;",
"import test.NotAbstract;",
"// BUG: Diagnostic contains: The annotation '@NotAbstract' has specified that it"
+ " should not be used together with the following modifiers: [abstract]",
"@NotAbstract public abstract class RequiredModifiersTestCase {",
"}")));
}

@Test
public void testGuavaAnnotationOK() throws Exception {
compilationHelper.assertCompileSucceeds(Arrays.asList(
compilationHelper.fileManager().forSourceLines(
"com/google/common/annotations/IncompatibleModifiers.java",
"package com.google.common.annotations;",
"import javax.lang.model.element.Modifier;",
"import java.lang.annotation.Target;",
"import java.lang.annotation.ElementType;",
"@Target(ElementType.ANNOTATION_TYPE)",
"public @interface IncompatibleModifiers {",
" Modifier[] value();",
"}"),
compilationHelper.fileManager().forSourceLines("test/NotAbstract.java",
"package test;",
"import static javax.lang.model.element.Modifier.ABSTRACT;",
"import com.google.common.annotations.IncompatibleModifiers;",
"@IncompatibleModifiers(ABSTRACT)",
"public @interface NotAbstract {",
"}"),
compilationHelper.fileManager().forSourceLines("test/RequiredModifiersTestCase.java",
"package test;",
"import test.NotAbstract;",
"@NotAbstract public class RequiredModifiersTestCase {",
"}")));
}
} }
Expand Up @@ -169,4 +169,60 @@ public void testHasRequiredModifiersSucceeds() throws Exception {
"import test.AbstractRequired;", "import test.AbstractRequired;",
"abstract class RequiredModifiersTestCase {}"))); "abstract class RequiredModifiersTestCase {}")));
} }

@Test
public void testGuavaAnnotation() throws Exception {
compilationHelper.assertCompileSucceedsWithMessages(Arrays.asList(
compilationHelper.fileManager().forSourceLines(
"com/google/common/annotations/RequiredModifiers.java",
"package com.google.common.annotations;",
"import javax.lang.model.element.Modifier;",
"import java.lang.annotation.Target;",
"import java.lang.annotation.ElementType;",
"@Target(ElementType.ANNOTATION_TYPE)",
"public @interface RequiredModifiers {",
" Modifier[] value();",
"}"),
compilationHelper.fileManager().forSourceLines("test/AbstractRequired.java",
"package test;",
"import static javax.lang.model.element.Modifier.ABSTRACT;",
"import com.google.common.annotations.RequiredModifiers;",
"@RequiredModifiers(ABSTRACT)",
"public @interface AbstractRequired {",
"}"),
compilationHelper.fileManager().forSourceLines("test/RequiredModifiersTestCase.java",
"package test;",
"import test.AbstractRequired;",
"// BUG: Diagnostic contains: The annotation '@AbstractRequired' has specified that it"
+ " must be used together with the following modifiers: [abstract]",
"@AbstractRequired public class RequiredModifiersTestCase {",
"}")));
}

@Test
public void testGuavaAnnotationOK() throws Exception {
compilationHelper.assertCompileSucceeds(Arrays.asList(
compilationHelper.fileManager().forSourceLines(
"com/google/common/annotations/RequiredModifiers.java",
"package com.google.common.annotations;",
"import javax.lang.model.element.Modifier;",
"import java.lang.annotation.Target;",
"import java.lang.annotation.ElementType;",
"@Target(ElementType.ANNOTATION_TYPE)",
"public @interface RequiredModifiers {",
" Modifier[] value();",
"}"),
compilationHelper.fileManager().forSourceLines("test/AbstractRequired.java",
"package test;",
"import static javax.lang.model.element.Modifier.ABSTRACT;",
"import com.google.common.annotations.RequiredModifiers;",
"@RequiredModifiers(ABSTRACT)",
"public @interface AbstractRequired {",
"}"),
compilationHelper.fileManager().forSourceLines("test/RequiredModifiersTestCase.java",
"package test;",
"import test.AbstractRequired;",
"@AbstractRequired public abstract class RequiredModifiersTestCase {",
"}")));
}
} }

0 comments on commit 53d8d9d

Please sign in to comment.