diff --git a/java/compiler/instrumentation-util/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java b/java/compiler/instrumentation-util/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java index 43ffcfe59c68..d3535024d9e6 100644 --- a/java/compiler/instrumentation-util/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java +++ b/java/compiler/instrumentation-util/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java @@ -145,7 +145,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode } @Override - public MethodVisitor visitMethod(final int access, final String name, String desc, String signature, String[] exceptions) { + public MethodVisitor visitMethod(final int access, final String name, final String desc, String signature, String[] exceptions) { if ((access & Opcodes.ACC_BRIDGE) != 0) { return new FailSafeMethodVisitor(Opcodes.API_VERSION, super.visitMethod(access, name, desc, signature, exceptions)); } @@ -175,16 +175,33 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode }; } - public AnnotationVisitor visitParameterAnnotation(final int parameter, final String anno, final boolean visible) { - AnnotationVisitor av = mv.visitParameterAnnotation(parameter, anno, visible); - if (isReferenceType(args[parameter]) && myNotNullAnnos.contains(anno)) { - NotNullState state = new NotNullState(anno, IAE_CLASS_NAME); - myNotNullParams.put(new Integer(parameter), state); - av = collectNotNullArgs(av, state); + @Override + public AnnotationVisitor visitTypeAnnotation(int typeRef, TypePath typePath, String desc, boolean visible) { + TypeReference ref = new TypeReference(typeRef); + if (ref.getSort() == TypeReference.METHOD_RETURN) { + return checkNotNullMethod(desc, mv.visitTypeAnnotation(typeRef, typePath, desc, visible)); } - else if (anno.equals(SYNTHETIC_TYPE)) { + if (ref.getSort() == TypeReference.METHOD_FORMAL_PARAMETER) { + return checkNotNullParameter(ref.getFormalParameterIndex(), desc, mv.visitTypeAnnotation(typeRef, typePath, desc, visible)); + } + return null; + } + + public AnnotationVisitor visitParameterAnnotation(final int parameter, final String anno, final boolean visible) { + if (anno.equals(SYNTHETIC_TYPE)) { // see http://forge.ow2.org/tracker/?aid=307392&group_id=23&atid=100023&func=detail mySyntheticCount++; + return null; + } + + return checkNotNullParameter(parameter, anno, mv.visitParameterAnnotation(parameter, anno, visible)); + } + + private AnnotationVisitor checkNotNullParameter(int parameter, String anno, AnnotationVisitor av) { + if (isReferenceType(args[parameter]) && myNotNullAnnos.contains(anno)) { + NotNullState state = new NotNullState(anno, IAE_CLASS_NAME); + myNotNullParams.put(parameter, state); + return collectNotNullArgs(av, state); } return av; @@ -192,12 +209,14 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode @Override public AnnotationVisitor visitAnnotation(String anno, boolean isRuntime) { - AnnotationVisitor av = mv.visitAnnotation(anno, isRuntime); + return checkNotNullMethod(anno, mv.visitAnnotation(anno, isRuntime)); + } + + private AnnotationVisitor checkNotNullMethod(String anno, AnnotationVisitor av) { if (isReferenceType(returnType) && myNotNullAnnos.contains(anno)) { myMethodNotNull = new NotNullState(anno, ISE_CLASS_NAME); - av = collectNotNullArgs(av, myMethodNotNull); + return collectNotNullArgs(av, myMethodNotNull); } - return av; } diff --git a/java/java-tests/testData/compiler/notNullVerification/TypeUseAndMemberAnnotations.java b/java/java-tests/testData/compiler/notNullVerification/TypeUseAndMemberAnnotations.java new file mode 100644 index 000000000000..2aa1ba5f66cd --- /dev/null +++ b/java/java-tests/testData/compiler/notNullVerification/TypeUseAndMemberAnnotations.java @@ -0,0 +1,16 @@ +import java.lang.annotation.*; + +@Target({ElementType.TYPE_USE, ElementType.METHOD, ElementType.PARAMETER}) +@interface FooAnno {} + +public class TypeUseAndMemberAnnotations { + @FooAnno + public Object foo1() { + return null; + } + + public Object foo2(@FooAnno String arg) { + return null; + } + +} \ No newline at end of file diff --git a/java/java-tests/testData/compiler/notNullVerification/TypeUseOnlyAnnotations.java b/java/java-tests/testData/compiler/notNullVerification/TypeUseOnlyAnnotations.java new file mode 100644 index 000000000000..65c3ee6d576b --- /dev/null +++ b/java/java-tests/testData/compiler/notNullVerification/TypeUseOnlyAnnotations.java @@ -0,0 +1,16 @@ +import java.lang.annotation.*; + +@Target(ElementType.TYPE_USE) +@interface FooAnno {} + +public class TypeUseOnlyAnnotations { + @FooAnno + public Object foo1() { + return null; + } + + public Object foo2(@FooAnno String arg) { + return null; + } + +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/java/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java b/java/java-tests/testSrc/com/intellij/java/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java index 163f8921a98c..14932c0ee383 100644 --- a/java/java-tests/testSrc/com/intellij/java/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java +++ b/java/java-tests/testSrc/com/intellij/java/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java @@ -182,6 +182,20 @@ public class NotNullVerifyingInstrumenterTest extends UsefulTestCase { verifyCallThrowsException("@BarAnno method MultipleAnnotations.foo2 must not return null", instance, test.getMethod("foo2")); } + public void testTypeUseOnlyAnnotations() throws Exception { + Class test = prepareTest(false, "FooAnno"); + Object instance = test.newInstance(); + verifyCallThrowsException("@FooAnno method TypeUseOnlyAnnotations.foo1 must not return null", instance, test.getMethod("foo1")); + verifyCallThrowsException("Argument 0 for @FooAnno parameter of TypeUseOnlyAnnotations.foo2 must not be null", instance, test.getMethod("foo2", String.class), (String)null); + } + + public void testTypeUseAndMemberAnnotations() throws Exception { + Class test = prepareTest(false, "FooAnno"); + Object instance = test.newInstance(); + verifyCallThrowsException("@FooAnno method TypeUseAndMemberAnnotations.foo1 must not return null", instance, test.getMethod("foo1")); + verifyCallThrowsException("Argument 0 for @FooAnno parameter of TypeUseAndMemberAnnotations.foo2 must not be null", instance, test.getMethod("foo2", String.class), (String)null); + } + public void testMalformedBytecode() throws Exception { Class testClass = prepareTest(false, AnnotationUtil.NOT_NULL); verifyCallThrowsException("Argument 0 for @NotNull parameter of MalformedBytecode$NullTest2.handle must not be null", null, testClass.getMethod("main")); @@ -246,7 +260,7 @@ public class NotNullVerifyingInstrumenterTest extends UsefulTestCase { mainClass = aClass; } } - assertTrue(modified); + assertTrue("Class file not instrumented!", modified); assertNotNull("Class " + baseClassName + " not found!", mainClass); return mainClass; }