diff --git a/java/compiler/notNull/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java b/java/compiler/notNull/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java index d6bcc68bbe76..7990ab4d73f1 100644 --- a/java/compiler/notNull/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java +++ b/java/compiler/notNull/src/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenter.java @@ -93,15 +93,13 @@ public class NotNullVerifyingInstrumenter extends ClassAdapter implements Opcode av = mv.visitParameterAnnotation(parameter, anno, visible); - if (isReferenceType(args[parameter])) { - if (anno.equals(NOT_NULL_ANNO)) { - myNotNullParams.add(new Integer(parameter)); - } - else if (anno.equals("Ljava/lang/Synthetic;")) { - // See asm r1278 for what we do this, - // http://forge.objectweb.org/tracker/index.php?func=detail&aid=307392&group_id=23&atid=100023 - mySyntheticCount++; - } + if (isReferenceType(args[parameter]) && anno.equals(NOT_NULL_ANNO)) { + myNotNullParams.add(new Integer(parameter)); + } + else if (anno.equals("Ljava/lang/Synthetic;")) { + // See asm r1278 for what we do this, + // http://forge.objectweb.org/tracker/index.php?func=detail&aid=307392&group_id=23&atid=100023 + mySyntheticCount++; } return av; } @@ -197,9 +195,6 @@ public class NotNullVerifyingInstrumenter extends ClassAdapter implements Opcode private int getStartParameterIndex(final String name) { int result = 0; if (CONSTRUCTOR_NAME.equals(name)) { - if (mySuperName.equals(ENUM_CLASS_NAME)) { - result += 2; - } if (myIsNotStaticInner) { result += 1; } diff --git a/java/java-tests/java-tests.iml b/java/java-tests/java-tests.iml index d18da953d9a7..643d97843f30 100644 --- a/java/java-tests/java-tests.iml +++ b/java/java-tests/java-tests.iml @@ -15,6 +15,9 @@ + + + diff --git a/java/java-tests/testData/compiler/notNullVerification/EnumConstructor.java b/java/java-tests/testData/compiler/notNullVerification/EnumConstructor.java new file mode 100644 index 000000000000..0c78423970c1 --- /dev/null +++ b/java/java-tests/testData/compiler/notNullVerification/EnumConstructor.java @@ -0,0 +1,11 @@ +import org.jetbrains.annotations.NotNull; + +public enum EnumConstructor { + Value("label"); + + private final String label; + + EnumConstructor(@NotNull String label) { + this.label = label; + } +} diff --git a/java/java-tests/testData/compiler/notNullVerification/MultipleReturns.java b/java/java-tests/testData/compiler/notNullVerification/MultipleReturns.java new file mode 100644 index 000000000000..61446a6f654b --- /dev/null +++ b/java/java-tests/testData/compiler/notNullVerification/MultipleReturns.java @@ -0,0 +1,9 @@ +import org.jetbrains.annotations.NotNull; + +public class MultipleReturns { + @NotNull public Object test(int i) { + if (i == 0) return null; + if (i == 1) return null; + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compiler/notNullVerification/SimpleReturn.java b/java/java-tests/testData/compiler/notNullVerification/SimpleReturn.java new file mode 100644 index 000000000000..1a85ad9407de --- /dev/null +++ b/java/java-tests/testData/compiler/notNullVerification/SimpleReturn.java @@ -0,0 +1,7 @@ +import org.jetbrains.annotations.NotNull; + +public class SimpleReturn { + @NotNull public Object test() { + return null; + } +} \ No newline at end of file diff --git a/java/java-tests/testData/compiler/notNullVerification/annotations.jar b/java/java-tests/testData/compiler/notNullVerification/annotations.jar new file mode 100644 index 000000000000..45860d829e91 Binary files /dev/null and b/java/java-tests/testData/compiler/notNullVerification/annotations.jar differ diff --git a/java/java-tests/testSrc/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java b/java/java-tests/testSrc/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java new file mode 100644 index 000000000000..12f5e0aa4f84 --- /dev/null +++ b/java/java-tests/testSrc/com/intellij/compiler/notNullVerification/NotNullVerifyingInstrumenterTest.java @@ -0,0 +1,119 @@ +package com.intellij.compiler.notNullVerification; + +import com.intellij.JavaTestUtil; +import com.intellij.compiler.PsiClassWriter; +import com.intellij.openapi.util.io.FileUtil; +import com.intellij.testFramework.IdeaTestCase; +import com.intellij.testFramework.UsefulTestCase; +import com.intellij.testFramework.fixtures.IdeaProjectTestFixture; +import com.intellij.testFramework.fixtures.JavaTestFixtureFactory; +import com.intellij.testFramework.fixtures.TestFixtureBuilder; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassWriter; + +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; + +/** + * @author yole + */ +public class NotNullVerifyingInstrumenterTest extends UsefulTestCase { + private IdeaProjectTestFixture myFixture; + + @SuppressWarnings({"JUnitTestCaseWithNonTrivialConstructors"}) + public NotNullVerifyingInstrumenterTest() { + IdeaTestCase.initPlatformPrefix(); + } + + @Override + protected void setUp() throws Exception { + super.setUp(); + final JavaTestFixtureFactory fixtureFactory = JavaTestFixtureFactory.getFixtureFactory(); + final TestFixtureBuilder testFixtureBuilder = fixtureFactory.createLightFixtureBuilder(); + myFixture = testFixtureBuilder.getFixture(); + myFixture.setUp(); + } + + @Override + protected void tearDown() throws Exception { + myFixture.tearDown(); + super.tearDown(); + } + + public void testSimpleReturn() throws Exception { + Class testClass = prepareTest(); + Object instance = testClass.newInstance(); + Method method = testClass.getMethod("test"); + verifyCallThrowsException("@NotNull method SimpleReturn.test must not return null", instance, method); + } + + public void testMultipleReturns() throws Exception { + Class testClass = prepareTest(); + Object instance = testClass.newInstance(); + Method method = testClass.getMethod("test", int.class); + verifyCallThrowsException("@NotNull method MultipleReturns.test must not return null", instance, method, 1); + } + + public void testEnumConstructor() throws Exception { + Class testClass = prepareTest(); + Object field = testClass.getField("Value"); + assertNotNull(field); + } + + private static void verifyCallThrowsException(final String expectedError, final Object instance, final Method method, final Object... args) throws IllegalAccessException { + String exceptionText = null; + try { + method.invoke(instance, args); + } + catch(InvocationTargetException ex) { + Throwable cause = ex.getCause(); + if (cause instanceof IllegalStateException) { + exceptionText = cause.getMessage(); + } + } + assertEquals(expectedError, exceptionText); + } + + private Class prepareTest() throws IOException { + String base = JavaTestUtil.getJavaTestDataPath() + "/compiler/notNullVerification/"; + String path = base + getTestName(false); + String javaPath = path + ".java"; + String classPath = path + ".class"; + try { + com.sun.tools.javac.Main.compile(new String[] { "-classpath", base+"annotations.jar", javaPath } ); + FileInputStream stream = new FileInputStream(classPath); + byte[] content = FileUtil.adaptiveLoadBytes(stream); + stream.close(); + + ClassReader reader = new ClassReader(content, 0, content.length); + ClassWriter writer = new PsiClassWriter(myFixture.getProject(), false); + final NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer); + reader.accept(instrumenter, 0); + assertTrue(instrumenter.isModification()); + + MyClassLoader classLoader = new MyClassLoader(getClass().getClassLoader()); + byte[] instrumented = writer.toByteArray(); + return classLoader.doDefineClass(getTestName(false), instrumented); + } + finally { + FileUtil.delete(new File(classPath)); + } + } + + private static class MyClassLoader extends ClassLoader { + public MyClassLoader(ClassLoader parent) { + super(parent); + } + + public Class doDefineClass(String name, byte[] data) { + return defineClass(name, data, 0, data.length); + } + + public Class loadClass(String name) throws ClassNotFoundException { + return super.loadClass(name); + } + } +}