mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
IDEA-76782 Generate runtime assertions for all configured not-null annotations
This commit is contained in:
+38
-23
@@ -21,14 +21,15 @@ import org.jetbrains.org.objectweb.asm.*;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.PrintStream;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* @author ven
|
||||
*/
|
||||
public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcodes {
|
||||
private static final String NOT_NULL_CLASS_NAME = "org/jetbrains/annotations/NotNull";
|
||||
private static final String SYNTHETIC_CLASS_NAME = "java/lang/Synthetic";
|
||||
private static final String SYNTHETIC_TYPE = "L" + SYNTHETIC_CLASS_NAME + ";";
|
||||
private static final String IAE_CLASS_NAME = "java/lang/IllegalArgumentException";
|
||||
@@ -36,9 +37,6 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
|
||||
private static final String ANNOTATION_DEFAULT_METHOD = "value";
|
||||
|
||||
private final String myNullArgMessageIndexed;
|
||||
private final String myNullArgMessageNamed;
|
||||
private final String myNullResultMessage;
|
||||
@SuppressWarnings("SSBasedInspection") private static final String[] EMPTY_STRING_ARRAY = new String[0];
|
||||
private final Map<String, Map<Integer, String>> myMethodParamNames;
|
||||
|
||||
@@ -46,22 +44,19 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
private boolean myIsModification = false;
|
||||
private RuntimeException myPostponedError;
|
||||
private final AuxiliaryMethodGenerator myAuxGenerator;
|
||||
private final String myNotNullAnno;
|
||||
private final Set<String> myNotNullAnnos = new HashSet<String>();
|
||||
|
||||
private NotNullVerifyingInstrumenter(final ClassVisitor classVisitor, ClassReader reader, String notNullAnnotation) {
|
||||
private NotNullVerifyingInstrumenter(final ClassVisitor classVisitor, ClassReader reader, String[] notNullAnnotations) {
|
||||
super(Opcodes.API_VERSION, classVisitor);
|
||||
final String fullName = notNullAnnotation != null ? notNullAnnotation.replace('.', '/') : NOT_NULL_CLASS_NAME;
|
||||
final String shortName = fullName.substring(fullName.lastIndexOf('/') + 1);
|
||||
myNotNullAnno = "L" + fullName + ";";
|
||||
myNullArgMessageIndexed = "Argument %s for @" + shortName + " parameter of %s.%s must not be null";
|
||||
myNullArgMessageNamed = "Argument for @" + shortName + " parameter '%s' of %s.%s must not be null";
|
||||
myNullResultMessage = "@" + shortName + " method %s.%s must not return null";
|
||||
for (String annotation : notNullAnnotations) {
|
||||
myNotNullAnnos.add("L" + annotation.replace('.', '/') + ";");
|
||||
}
|
||||
myMethodParamNames = getAllParameterNames(reader);
|
||||
myAuxGenerator = new AuxiliaryMethodGenerator(reader);
|
||||
}
|
||||
|
||||
public static boolean processClassFile(final FailSafeClassReader reader, final ClassVisitor writer, String notNullAnnotation) {
|
||||
NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer, reader, notNullAnnotation);
|
||||
public static boolean processClassFile(final FailSafeClassReader reader, final ClassVisitor writer, String[] notNullAnnotations) {
|
||||
NotNullVerifyingInstrumenter instrumenter = new NotNullVerifyingInstrumenter(writer, reader, notNullAnnotations);
|
||||
reader.accept(instrumenter, 0);
|
||||
instrumenter.myAuxGenerator.generateReportingMethod(writer);
|
||||
return instrumenter.isModification();
|
||||
@@ -121,10 +116,32 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
private static class NotNullState {
|
||||
String message;
|
||||
String exceptionType;
|
||||
final String notNullAnno;
|
||||
|
||||
NotNullState(String exceptionType) {
|
||||
NotNullState(String notNullAnno, String exceptionType) {
|
||||
this.notNullAnno = notNullAnno;
|
||||
this.exceptionType = exceptionType;
|
||||
}
|
||||
|
||||
String getNullParamMessage(String paramName) {
|
||||
if (message != null) return message;
|
||||
|
||||
final String shortName = getAnnoShortName();
|
||||
if (paramName != null) return "Argument for @" + shortName + " parameter '%s' of %s.%s must not be null";
|
||||
return "Argument %s for @" + shortName + " parameter of %s.%s must not be null";
|
||||
}
|
||||
|
||||
String getNullResultMessage() {
|
||||
if (message != null) return message;
|
||||
|
||||
final String shortName = getAnnoShortName();
|
||||
return "@" + shortName + " method %s.%s must not return null";
|
||||
}
|
||||
|
||||
private String getAnnoShortName() {
|
||||
String fullName = notNullAnno.substring(1, notNullAnno.length() - 1); // "Lpk/name;" -> "pk/name"
|
||||
return fullName.substring(fullName.lastIndexOf('/') + 1);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -160,8 +177,8 @@ 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]) && anno.equals(myNotNullAnno)) {
|
||||
NotNullState state = new NotNullState(IAE_CLASS_NAME);
|
||||
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);
|
||||
}
|
||||
@@ -176,8 +193,8 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
@Override
|
||||
public AnnotationVisitor visitAnnotation(String anno, boolean isRuntime) {
|
||||
AnnotationVisitor av = mv.visitAnnotation(anno, isRuntime);
|
||||
if (isReferenceType(returnType) && anno.equals(myNotNullAnno)) {
|
||||
myMethodNotNull = new NotNullState(ISE_CLASS_NAME);
|
||||
if (isReferenceType(returnType) && myNotNullAnnos.contains(anno)) {
|
||||
myMethodNotNull = new NotNullState(anno, ISE_CLASS_NAME);
|
||||
av = collectNotNullArgs(av, myMethodNotNull);
|
||||
}
|
||||
|
||||
@@ -203,9 +220,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
|
||||
NotNullState state = entry.getValue();
|
||||
String paramName = paramNames == null ? null : paramNames.get(param);
|
||||
String descrPattern = state.message != null
|
||||
? state.message
|
||||
: paramName != null ? myNullArgMessageNamed : myNullArgMessageIndexed;
|
||||
String descrPattern = state.getNullParamMessage(paramName);
|
||||
String[] args = state.message != null
|
||||
? EMPTY_STRING_ARRAY
|
||||
: new String[]{paramName != null ? paramName : String.valueOf(param - mySyntheticCount), myClassName, name};
|
||||
@@ -228,7 +243,7 @@ public class NotNullVerifyingInstrumenter extends ClassVisitor implements Opcode
|
||||
mv.visitInsn(DUP);
|
||||
final Label skipLabel = new Label();
|
||||
mv.visitJumpInsn(IFNONNULL, skipLabel);
|
||||
String descrPattern = myMethodNotNull.message != null ? myMethodNotNull.message : myNullResultMessage;
|
||||
String descrPattern = myMethodNotNull.getNullResultMessage();
|
||||
String[] args = myMethodNotNull.message != null ? EMPTY_STRING_ARRAY : new String[]{myClassName, name};
|
||||
reportError(myMethodNotNull.exceptionType, skipLabel, descrPattern, args);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public class Javac2 extends Javac {
|
||||
private ArrayList myFormFiles;
|
||||
private List myNestedFormPathList;
|
||||
private boolean instrumentNotNull = true;
|
||||
private String myNotNull;
|
||||
private String myNotNullAnnotations;
|
||||
private List<Regexp> myClassFilterAnnotationRegexpList = new ArrayList<Regexp>(0);
|
||||
|
||||
public Javac2() {
|
||||
@@ -76,12 +76,18 @@ public class Javac2 extends Javac {
|
||||
this.instrumentNotNull = instrumentNotNull;
|
||||
}
|
||||
|
||||
public String getNotNull() {
|
||||
return myNotNull;
|
||||
/**
|
||||
* @return semicolon-separated names of not-null annotations to be instrumented. Example: <code>"org.jetbrains.annotations.NotNull;javax.annotation.Nonnull"</code>
|
||||
*/
|
||||
public String getNotNullAnnotations() {
|
||||
return myNotNullAnnotations;
|
||||
}
|
||||
|
||||
public void setNotNull(String notNull) {
|
||||
myNotNull = notNull;
|
||||
/**
|
||||
* @param notNullAnnotations semicolon-separated names of not-null annotations to be instrumented. Example: <code>"org.jetbrains.annotations.NotNull;javax.annotation.Nonnull"</code>
|
||||
*/
|
||||
public void setNotNullAnnotations(String notNullAnnotations) {
|
||||
myNotNullAnnotations = notNullAnnotations;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -447,7 +453,7 @@ public class Javac2 extends Javac {
|
||||
if (version >= Opcodes.V1_5 && !shouldBeSkippedByAnnotationPattern(reader)) {
|
||||
ClassWriter writer = new InstrumenterClassWriter(reader, getAsmClassWriterFlags(version), finder);
|
||||
|
||||
if (NotNullVerifyingInstrumenter.processClassFile(reader, writer, myNotNull)) {
|
||||
if (NotNullVerifyingInstrumenter.processClassFile(reader, writer, myNotNullAnnotations.split(";"))) {
|
||||
final FileOutputStream fileOutputStream = new FileOutputStream(path);
|
||||
try {
|
||||
fileOutputStream.write(writer.toByteArray());
|
||||
|
||||
@@ -18,9 +18,22 @@ package com.intellij.codeInsight;
|
||||
import com.intellij.codeInspection.dataFlow.HardcodedContracts;
|
||||
import com.intellij.openapi.components.State;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import org.jetbrains.jps.model.serialization.java.compiler.JpsJavaCompilerNotNullableSerializer;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@State(name = "NullableNotNullManager")
|
||||
public class NullableNotNullManagerImpl extends NullableNotNullManager {
|
||||
|
||||
public NullableNotNullManagerImpl() {
|
||||
myNotNulls.addAll(getPredefinedNotNulls());
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getPredefinedNotNulls() {
|
||||
return JpsJavaCompilerNotNullableSerializer.DEFAULT_NOT_NULLS;
|
||||
}
|
||||
|
||||
protected boolean hasHardcodedContracts(PsiElement element) {
|
||||
return HardcodedContracts.hasHardcodedContracts(element);
|
||||
}
|
||||
|
||||
@@ -47,12 +47,8 @@ public abstract class NullableNotNullManager implements PersistentStateComponent
|
||||
public static final String[] DEFAULT_NULLABLES = {AnnotationUtil.NULLABLE, JAVAX_ANNOTATION_NULLABLE,
|
||||
"edu.umd.cs.findbugs.annotations.Nullable", "android.support.annotation.Nullable"
|
||||
};
|
||||
public static final String[] DEFAULT_NOT_NULLS = {AnnotationUtil.NOT_NULL, JAVAX_ANNOTATION_NONNULL,
|
||||
"edu.umd.cs.findbugs.annotations.NonNull", "android.support.annotation.NonNull"
|
||||
};
|
||||
|
||||
public NullableNotNullManager() {
|
||||
Collections.addAll(myNotNulls, DEFAULT_NOT_NULLS);
|
||||
Collections.addAll(myNullables, DEFAULT_NULLABLES);
|
||||
}
|
||||
|
||||
@@ -78,7 +74,12 @@ public abstract class NullableNotNullManager implements PersistentStateComponent
|
||||
|
||||
public void setNotNulls(@NotNull String... annotations) {
|
||||
myNotNulls.clear();
|
||||
addAllIfNotPresent(myNotNulls, DEFAULT_NOT_NULLS);
|
||||
for (String annotation : getPredefinedNotNulls()) {
|
||||
LOG.assertTrue(annotation != null);
|
||||
if (!myNotNulls.contains(annotation)) {
|
||||
myNotNulls.add(annotation);
|
||||
}
|
||||
}
|
||||
addAllIfNotPresent(myNotNulls, annotations);
|
||||
}
|
||||
|
||||
@@ -210,7 +211,7 @@ public abstract class NullableNotNullManager implements PersistentStateComponent
|
||||
if (type == null || TypeConversionUtil.isPrimitiveAndNotNull(type)) return null;
|
||||
|
||||
// even if javax.annotation.Nullable is not configured, it should still take precedence over ByDefault annotations
|
||||
if (AnnotationUtil.isAnnotated(owner, Arrays.asList(nullable ? DEFAULT_NOT_NULLS : DEFAULT_NULLABLES), checkBases, false)) {
|
||||
if (AnnotationUtil.isAnnotated(owner, nullable ? getPredefinedNotNulls() : Arrays.asList(DEFAULT_NULLABLES), checkBases, false)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -329,7 +330,8 @@ public abstract class NullableNotNullManager implements PersistentStateComponent
|
||||
}
|
||||
|
||||
public boolean hasDefaultValues() {
|
||||
if (DEFAULT_NULLABLES.length != getNullables().size() || DEFAULT_NOT_NULLS.length != getNotNulls().size()) {
|
||||
List<String> predefinedNotNulls = getPredefinedNotNulls();
|
||||
if (DEFAULT_NULLABLES.length != getNullables().size() || predefinedNotNulls.size() != getNotNulls().size()) {
|
||||
return false;
|
||||
}
|
||||
if (!myDefaultNotNull.equals(AnnotationUtil.NOT_NULL) || !myDefaultNullable.equals(AnnotationUtil.NULLABLE)) {
|
||||
@@ -340,8 +342,8 @@ public abstract class NullableNotNullManager implements PersistentStateComponent
|
||||
return false;
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < DEFAULT_NOT_NULLS.length; i++) {
|
||||
if (!getNotNulls().get(i).equals(DEFAULT_NOT_NULLS[i])) {
|
||||
for (int i = 0; i < predefinedNotNulls.size(); i++) {
|
||||
if (!getNotNulls().get(i).equals(predefinedNotNulls.get(i))) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -376,7 +378,7 @@ public abstract class NullableNotNullManager implements PersistentStateComponent
|
||||
Collections.addAll(myNullables, DEFAULT_NULLABLES);
|
||||
}
|
||||
if (myNotNulls.isEmpty()) {
|
||||
Collections.addAll(myNotNulls, DEFAULT_NOT_NULLS);
|
||||
myNotNulls.addAll(getPredefinedNotNulls());
|
||||
}
|
||||
}
|
||||
catch (InvalidDataException e) {
|
||||
@@ -391,4 +393,6 @@ public abstract class NullableNotNullManager implements PersistentStateComponent
|
||||
public static boolean isNotNull(@NotNull PsiModifierListOwner owner) {
|
||||
return getInstance(owner.getProject()).isNotNull(owner, true);
|
||||
}
|
||||
|
||||
public abstract List<String> getPredefinedNotNulls();
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
@interface FooAnno {}
|
||||
@interface BarAnno {}
|
||||
|
||||
public class MultipleAnnotations {
|
||||
@FooAnno
|
||||
public Object foo1() {
|
||||
return null;
|
||||
}
|
||||
|
||||
@BarAnno
|
||||
public Object foo2() {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -73,7 +73,7 @@ class Foo {
|
||||
|
||||
@Override
|
||||
protected void tearDown() throws Exception {
|
||||
NullableNotNullManager.getInstance(project).notNulls = NullableNotNullManager.DEFAULT_NOT_NULLS
|
||||
NullableNotNullManager.getInstance(project).notNulls = NullableNotNullManager.getInstance(project).predefinedNotNulls as String[]
|
||||
NullableNotNullManager.getInstance(project).defaultNotNull = AnnotationUtil.NOT_NULL
|
||||
|
||||
super.tearDown()
|
||||
|
||||
+15
-7
@@ -16,6 +16,7 @@
|
||||
package com.intellij.compiler.notNullVerification;
|
||||
|
||||
import com.intellij.JavaTestUtil;
|
||||
import com.intellij.codeInsight.AnnotationUtil;
|
||||
import com.intellij.compiler.instrumentation.FailSafeClassReader;
|
||||
import com.intellij.openapi.util.io.FileUtil;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
@@ -88,7 +89,7 @@ public class NotNullVerifyingInstrumenterTest extends UsefulTestCase {
|
||||
}
|
||||
|
||||
public void testUseParameterNames() throws Exception {
|
||||
Class<?> testClass = prepareTest(true);
|
||||
Class<?> testClass = prepareTest(true, AnnotationUtil.NOT_NULL);
|
||||
Constructor constructor = testClass.getConstructor(Object.class, Object.class);
|
||||
verifyCallThrowsException("Argument for @NotNull parameter 'obj2' of UseParameterNames.<init> must not be null", null, constructor, null, null);
|
||||
|
||||
@@ -101,13 +102,13 @@ public class NotNullVerifyingInstrumenterTest extends UsefulTestCase {
|
||||
}
|
||||
|
||||
public void testLongParameter() throws Exception {
|
||||
Class<?> testClass = prepareTest(true);
|
||||
Class<?> testClass = prepareTest(true, AnnotationUtil.NOT_NULL);
|
||||
Method staticMethod = testClass.getMethod("foo", long.class, String.class, String.class);
|
||||
verifyCallThrowsException("Argument for @NotNull parameter 'c' of LongParameter.foo must not be null", null, staticMethod, new Long(2), "z", null);
|
||||
}
|
||||
|
||||
public void testDoubleParameter() throws Exception {
|
||||
Class<?> testClass = prepareTest(true);
|
||||
Class<?> testClass = prepareTest(true, AnnotationUtil.NOT_NULL);
|
||||
Method staticMethod = testClass.getMethod("foo", double.class, String.class, String.class);
|
||||
verifyCallThrowsException("Argument for @NotNull parameter 'c' of DoubleParameter.foo must not be null", null, staticMethod, new Long(2), "z", null);
|
||||
}
|
||||
@@ -173,8 +174,15 @@ public class NotNullVerifyingInstrumenterTest extends UsefulTestCase {
|
||||
verifyCallThrowsException("@NotNull method MultipleMessages.foo2 must not return null", instance, test.getMethod("foo2"));
|
||||
}
|
||||
|
||||
public void testMultipleAnnotations() throws Exception {
|
||||
Class<?> test = prepareTest(false, "FooAnno", "BarAnno");
|
||||
Object instance = test.newInstance();
|
||||
verifyCallThrowsException("@FooAnno method MultipleAnnotations.foo1 must not return null", instance, test.getMethod("foo1"));
|
||||
verifyCallThrowsException("@BarAnno method MultipleAnnotations.foo2 must not return null", instance, test.getMethod("foo2"));
|
||||
}
|
||||
|
||||
public void testMalformedBytecode() throws Exception {
|
||||
Class<?> testClass = prepareTest(false);
|
||||
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"));
|
||||
}
|
||||
|
||||
@@ -198,10 +206,10 @@ public class NotNullVerifyingInstrumenterTest extends UsefulTestCase {
|
||||
}
|
||||
|
||||
private Class prepareTest() throws IOException {
|
||||
return prepareTest(false);
|
||||
return prepareTest(false, AnnotationUtil.NOT_NULL);
|
||||
}
|
||||
|
||||
private Class prepareTest(boolean withDebugInfo) throws IOException {
|
||||
private Class prepareTest(boolean withDebugInfo, String... notNullAnnos) throws IOException {
|
||||
String base = JavaTestUtil.getJavaTestDataPath() + "/compiler/notNullVerification/";
|
||||
final String baseClassName = getTestName(false);
|
||||
String path = base + baseClassName;
|
||||
@@ -228,7 +236,7 @@ public class NotNullVerifyingInstrumenterTest extends UsefulTestCase {
|
||||
|
||||
FailSafeClassReader reader = new FailSafeClassReader(content, 0, content.length);
|
||||
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
|
||||
modified |= NotNullVerifyingInstrumenter.processClassFile(reader, writer, null);
|
||||
modified |= NotNullVerifyingInstrumenter.processClassFile(reader, writer, notNullAnnos);
|
||||
|
||||
byte[] instrumented = writer.toByteArray();
|
||||
final String className = FileUtil.getNameWithoutExtension(fileName);
|
||||
|
||||
@@ -27,6 +27,7 @@ import com.intellij.psi.PsiClass;
|
||||
import com.intellij.psi.search.GlobalSearchScope;
|
||||
import com.intellij.ui.*;
|
||||
import com.intellij.ui.components.JBList;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.ui.EmptyIcon;
|
||||
import com.intellij.util.ui.JBUI;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -64,7 +65,7 @@ public class NullableNotNullDialog extends DialogWrapper {
|
||||
new AnnotationsPanel("Nullable", manager.getDefaultNullable(), manager.getNullables(), NullableNotNullManager.DEFAULT_NULLABLES);
|
||||
splitter.setFirstComponent(myNullablePanel.getComponent());
|
||||
myNotNullPanel =
|
||||
new AnnotationsPanel("NotNull", manager.getDefaultNotNull(), manager.getNotNulls(), NullableNotNullManager.DEFAULT_NOT_NULLS);
|
||||
new AnnotationsPanel("NotNull", manager.getDefaultNotNull(), manager.getNotNulls(), ArrayUtil.toStringArray(manager.getPredefinedNotNulls()));
|
||||
splitter.setSecondComponent(myNotNullPanel.getComponent());
|
||||
splitter.setHonorComponentsMinimumSize(true);
|
||||
splitter.setPreferredSize(JBUI.size(300, 400));
|
||||
|
||||
+4
-2
@@ -19,6 +19,7 @@ import com.intellij.compiler.instrumentation.FailSafeClassReader;
|
||||
import com.intellij.compiler.instrumentation.InstrumentationClassFinder;
|
||||
import com.intellij.compiler.notNullVerification.NotNullVerifyingInstrumenter;
|
||||
import com.intellij.openapi.diagnostic.Logger;
|
||||
import com.intellij.util.ArrayUtil;
|
||||
import com.intellij.util.Function;
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@@ -37,6 +38,7 @@ import org.jetbrains.org.objectweb.asm.Opcodes;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author Eugene Zhuravlev
|
||||
@@ -80,8 +82,8 @@ public class NotNullInstrumentingBuilder extends BaseInstrumentingBuilder{
|
||||
InstrumentationClassFinder finder) {
|
||||
try {
|
||||
final ProjectDescriptor pd = context.getProjectDescriptor();
|
||||
final String notNullAnnotation = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(pd.getProject()).getNotNullAnnotation();
|
||||
if (NotNullVerifyingInstrumenter.processClassFile((FailSafeClassReader)reader, writer, notNullAnnotation)) {
|
||||
final List<String> notNulls = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(pd.getProject()).getNotNullAnnotations();
|
||||
if (NotNullVerifyingInstrumenter.processClassFile((FailSafeClassReader)reader, writer, ArrayUtil.toStringArray(notNulls))) {
|
||||
return new BinaryContent(writer.toByteArray());
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -31,8 +31,8 @@ public interface JpsJavaCompilerConfiguration extends JpsElement {
|
||||
boolean isAddNotNullAssertions();
|
||||
void setAddNotNullAssertions(boolean addNotNullAssertions);
|
||||
|
||||
String getNotNullAnnotation();
|
||||
void setNotNullAnnotation(String notNullAnnotation);
|
||||
List<String> getNotNullAnnotations();
|
||||
void setNotNullAnnotations(List<String> notNullAnnotations);
|
||||
|
||||
boolean isClearOutputDirectoryOnRebuild();
|
||||
void setClearOutputDirectoryOnRebuild(boolean clearOutputDirectoryOnRebuild);
|
||||
|
||||
+5
-5
@@ -35,7 +35,7 @@ import java.util.*;
|
||||
public class JpsJavaCompilerConfigurationImpl extends JpsCompositeElementBase<JpsJavaCompilerConfigurationImpl> implements JpsJavaCompilerConfiguration {
|
||||
public static final JpsElementChildRole<JpsJavaCompilerConfiguration> ROLE = JpsElementChildRoleBase.create("compiler configuration");
|
||||
private boolean myAddNotNullAssertions = true;
|
||||
private String myNotNullAnnotation = NotNull.class.getName();
|
||||
private List<String> myNotNullAnnotations = Collections.singletonList(NotNull.class.getName());
|
||||
private boolean myClearOutputDirectoryOnRebuild = true;
|
||||
private final JpsCompilerExcludes myCompilerExcludes = new JpsCompilerExcludesImpl();
|
||||
private final List<String> myResourcePatterns = new ArrayList<String>();
|
||||
@@ -67,8 +67,8 @@ public class JpsJavaCompilerConfigurationImpl extends JpsCompositeElementBase<Jp
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getNotNullAnnotation() {
|
||||
return myNotNullAnnotation;
|
||||
public List<String> getNotNullAnnotations() {
|
||||
return myNotNullAnnotations;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -82,8 +82,8 @@ public class JpsJavaCompilerConfigurationImpl extends JpsCompositeElementBase<Jp
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setNotNullAnnotation(String notNullAnnotation) {
|
||||
myNotNullAnnotation = notNullAnnotation;
|
||||
public void setNotNullAnnotations(List<String> notNullAnnotations) {
|
||||
myNotNullAnnotations = Collections.unmodifiableList(notNullAnnotations);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+23
-11
@@ -15,6 +15,7 @@
|
||||
*/
|
||||
package org.jetbrains.jps.model.serialization.java.compiler;
|
||||
|
||||
import com.intellij.util.containers.ContainerUtil;
|
||||
import org.jdom.Element;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
import org.jetbrains.jps.model.JpsProject;
|
||||
@@ -22,13 +23,17 @@ import org.jetbrains.jps.model.java.JpsJavaExtensionService;
|
||||
import org.jetbrains.jps.model.java.compiler.JpsJavaCompilerConfiguration;
|
||||
import org.jetbrains.jps.model.serialization.JpsProjectExtensionSerializer;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* @author vladimir.dolzhenko
|
||||
*/
|
||||
public class JpsJavaCompilerNotNullableSerializer extends JpsProjectExtensionSerializer {
|
||||
private static final String DEFAULT_VALUE = NotNull.class.getName();
|
||||
private static final String NOTNULL_ANNOTATION = "myDefaultNotNull";
|
||||
private static final String VALUE = "value";
|
||||
public static final List<String> DEFAULT_NOT_NULLS = Arrays.asList(
|
||||
NotNull.class.getName(), "javax.annotation.Nonnull",
|
||||
"edu.umd.cs.findbugs.annotations.NonNull", "android.support.annotation.NonNull"
|
||||
);
|
||||
|
||||
public JpsJavaCompilerNotNullableSerializer() {
|
||||
super("misc.xml", "NullableNotNullManager");
|
||||
@@ -37,20 +42,27 @@ public class JpsJavaCompilerNotNullableSerializer extends JpsProjectExtensionSer
|
||||
@Override
|
||||
public void loadExtension(@NotNull JpsProject project, @NotNull Element componentTag) {
|
||||
JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project);
|
||||
String value = DEFAULT_VALUE;
|
||||
for (Element element : componentTag.getChildren("option")) {
|
||||
if (NOTNULL_ANNOTATION.equals(element.getAttributeValue("name"))){
|
||||
value = element.getAttributeValue(VALUE, DEFAULT_VALUE);
|
||||
break;
|
||||
List<String> annoNames = ContainerUtil.newArrayList();
|
||||
for (Element option : componentTag.getChildren("option")) {
|
||||
if ("myNotNulls".equals(option.getAttributeValue("name"))){
|
||||
for (Element value : option.getChildren("value")) {
|
||||
for (Element list : value.getChildren("list")) {
|
||||
for (Element item : list.getChildren("item")) {
|
||||
ContainerUtil.addIfNotNull(annoNames, item.getAttributeValue("itemvalue"));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
configuration.setNotNullAnnotation(value);
|
||||
if (annoNames.isEmpty()) {
|
||||
annoNames.addAll(DEFAULT_NOT_NULLS);
|
||||
}
|
||||
configuration.setNotNullAnnotations(annoNames);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void loadExtensionWithDefaultSettings(@NotNull JpsProject project) {
|
||||
JpsJavaCompilerConfiguration configuration = JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project);
|
||||
configuration.setNotNullAnnotation(DEFAULT_VALUE);
|
||||
JpsJavaExtensionService.getInstance().getOrCreateCompilerConfiguration(project).setNotNullAnnotations(DEFAULT_NOT_NULLS);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in New Issue
Block a user