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 77bc53816847..080286716fef 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 @@ -34,6 +34,8 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode private static final String CONSTRUCTOR_NAME = ""; private static final String EXCEPTION_INIT_SIGNATURE = "(L" + STRING_CLASS_NAME + ";)V"; + private static final String ANNOTATION_DEFAULT_METHOD = "value"; + private static final String NULL_ARG_MESSAGE = "Argument %d for @NotNull parameter of %s.%s must not be null"; private static final String NULL_RESULT_MESSAGE = "@NotNull method %s.%s must not return null"; @@ -65,6 +67,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode private final List myNotNullParams = new ArrayList(); private int mySyntheticCount = 0; private boolean myIsNotNull = false; + private String myMessage = null; private Label myStartGeneratedCodeLabel; public AnnotationVisitor visitParameterAnnotation(final int parameter, final String anno, final boolean visible) { @@ -76,7 +79,19 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode // see http://forge.ow2.org/tracker/?aid=307392&group_id=23&atid=100023&func=detail mySyntheticCount++; } - return av; + + return new AnnotationVisitor(Opcodes.ASM4, av) { + @Override + public void visit(String methodName, Object o) { + if(ANNOTATION_DEFAULT_METHOD.equals(methodName)) { + String message = (String) o; + if(!message.isEmpty()) { + myMessage = message; + } + } + super.visit(methodName, o); + } + }; } @Override @@ -86,7 +101,18 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode myIsNotNull = true; } - return av; + return new AnnotationVisitor(Opcodes.ASM4, av) { + @Override + public void visit(String methodName, Object o) { + if(ANNOTATION_DEFAULT_METHOD.equals(methodName)) { + String message = (String) o; + if(!message.isEmpty()) { + myMessage = message; + } + } + super.visit(methodName, o); + } + }; } @Override @@ -105,7 +131,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode Label end = new Label(); mv.visitJumpInsn(IFNONNULL, end); - generateThrow(IAE_CLASS_NAME, String.format(NULL_ARG_MESSAGE, param - mySyntheticCount, myClassName, name), end); + generateThrow(IAE_CLASS_NAME, myMessage == null ? String.format(NULL_ARG_MESSAGE, param - mySyntheticCount, myClassName, name) : myMessage, end); } } @@ -124,7 +150,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode mv.visitInsn(DUP); final Label skipLabel = new Label(); mv.visitJumpInsn(IFNONNULL, skipLabel); - generateThrow(ISE_CLASS_NAME, String.format(NULL_RESULT_MESSAGE, myClassName, name), skipLabel); + generateThrow(ISE_CLASS_NAME, myMessage == null ? String.format(NULL_RESULT_MESSAGE, myClassName, name) : myMessage, skipLabel); } } diff --git a/java/java-tests/testData/compiler/notNullVerification/ConstructorParamWithMessage.java b/java/java-tests/testData/compiler/notNullVerification/ConstructorParamWithMessage.java new file mode 100644 index 000000000000..3da49f30b8da --- /dev/null +++ b/java/java-tests/testData/compiler/notNullVerification/ConstructorParamWithMessage.java @@ -0,0 +1,6 @@ +import org.jetbrains.annotations.NotNull; + +public class ConstructorParamWithMessage { + public ConstructorParamWithMessage(@NotNull("ConstructorParam.ConstructorParam.o cant be null") Object o) { + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compiler/notNullVerification/SimpleParamWithMessage.java b/java/java-tests/testData/compiler/notNullVerification/SimpleParamWithMessage.java new file mode 100644 index 000000000000..cdd8f75ab9b6 --- /dev/null +++ b/java/java-tests/testData/compiler/notNullVerification/SimpleParamWithMessage.java @@ -0,0 +1,6 @@ +import org.jetbrains.annotations.NotNull; + +public class SimpleParamWithMessage { + public void test(@NotNull("SimpleParamWithMessage.test(o) cant be null") Object o) { + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compiler/notNullVerification/SimpleReturnWithMessage.java b/java/java-tests/testData/compiler/notNullVerification/SimpleReturnWithMessage.java new file mode 100644 index 000000000000..8d52dc853e97 --- /dev/null +++ b/java/java-tests/testData/compiler/notNullVerification/SimpleReturnWithMessage.java @@ -0,0 +1,8 @@ +import org.jetbrains.annotations.NotNull; + +public class SimpleReturnWithMessage { + @NotNull("This method cannot return null") + public Object test() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testSrc/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java b/java/java-tests/testSrc/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java index 4ba78cfd9b5b..b1b6a941f861 100644 --- a/java/java-tests/testSrc/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java +++ b/java/java-tests/testSrc/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java @@ -70,6 +70,13 @@ public class NotNullVerifyingInstrumenterTest extends UsefulTestCase { verifyCallThrowsException("@NotNull method SimpleReturn.test must not return null", instance, method); } + public void testSimpleReturnWithMessage() throws Exception { + Class testClass = prepareTest(); + Object instance = testClass.newInstance(); + Method method = testClass.getMethod("test"); + verifyCallThrowsException("This method cannot return null", instance, method); + } + public void testMultipleReturns() throws Exception { Class testClass = prepareTest(); Object instance = testClass.newInstance(); @@ -84,12 +91,25 @@ public class NotNullVerifyingInstrumenterTest extends UsefulTestCase { verifyCallThrowsException("Argument 0 for @NotNull parameter of SimpleParam.test must not be null", instance, method, (Object)null); } + public void testSimpleParamWithMessage() throws Exception { + Class testClass = prepareTest(); + Object instance = testClass.newInstance(); + Method method = testClass.getMethod("test", Object.class); + verifyCallThrowsException("SimpleParamWithMessage.test(o) cant be null", instance, method, (Object)null); + } + public void testConstructorParam() throws Exception { Class testClass = prepareTest(); Constructor method = testClass.getConstructor(Object.class); verifyCallThrowsException("Argument 0 for @NotNull parameter of ConstructorParam. must not be null", null, method, (Object)null); } + public void testConstructorParamWithMessage() throws Exception { + Class testClass = prepareTest(); + Constructor method = testClass.getConstructor(Object.class); + verifyCallThrowsException("ConstructorParam.ConstructorParam.o cant be null", null, method, (Object)null); + } + public void testEnumConstructor() throws Exception { Class testClass = prepareTest(); Object field = testClass.getField("Value");