Rework invalid abi version reporting

KotlinClassFileHeader is aware of older annotations
Psi is not used to report invalid Abi version in CLI
This commit is contained in:
Pavel V. Talanov
2013-08-23 17:44:30 +04:00
parent 223c6007d2
commit 4e29aad1c1
6 changed files with 96 additions and 53 deletions
@@ -17,7 +17,6 @@
package org.jetbrains.jet.cli.common.messages; package org.jetbrains.jet.cli.common.messages;
import com.intellij.openapi.util.text.StringUtil; import com.intellij.openapi.util.text.StringUtil;
import com.intellij.psi.PsiClass;
import com.intellij.psi.PsiElement; import com.intellij.psi.PsiElement;
import com.intellij.psi.PsiErrorElement; import com.intellij.psi.PsiErrorElement;
import com.intellij.psi.PsiModifierListOwner; import com.intellij.psi.PsiModifierListOwner;
@@ -43,6 +42,7 @@ import org.jetbrains.jet.lang.resolve.java.JvmAbi;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import static com.intellij.openapi.util.io.FileUtil.toSystemDependentName;
import static org.jetbrains.jet.lang.diagnostics.DiagnosticUtils.sortedDiagnostics; import static org.jetbrains.jet.lang.diagnostics.DiagnosticUtils.sortedDiagnostics;
public final class AnalyzerWithCompilerReport { public final class AnalyzerWithCompilerReport {
@@ -143,13 +143,14 @@ public final class AnalyzerWithCompilerReport {
assert analyzeExhaust != null; assert analyzeExhaust != null;
BindingContext bindingContext = analyzeExhaust.getBindingContext(); BindingContext bindingContext = analyzeExhaust.getBindingContext();
Collection<PsiClass> psiClasses = bindingContext.getKeys(AbiVersionUtil.ABI_VERSION_ERRORS); Collection<AbiVersionUtil.AbiVersionErrorLocation> errorLocations = bindingContext.getKeys(AbiVersionUtil.ABI_VERSION_ERRORS);
for (PsiClass psiClass : psiClasses) { for (AbiVersionUtil.AbiVersionErrorLocation abiVersionErrorLocation : errorLocations) {
Integer abiVersion = bindingContext.get(AbiVersionUtil.ABI_VERSION_ERRORS, psiClass); Integer abiVersion = bindingContext.get(AbiVersionUtil.ABI_VERSION_ERRORS, abiVersionErrorLocation);
messageCollectorWrapper.report(CompilerMessageSeverity.ERROR, messageCollectorWrapper.report(CompilerMessageSeverity.ERROR,
"Class '" + psiClass.getQualifiedName() + "' was compiled with an incompatible version of Kotlin. " + "Class '" + abiVersionErrorLocation.getClassFqName().asString() +
"' was compiled with an incompatible version of Kotlin. " +
"Its ABI version is " + abiVersion + ", expected ABI version is " + JvmAbi.VERSION, "Its ABI version is " + abiVersion + ", expected ABI version is " + JvmAbi.VERSION,
MessageUtil.psiElementToMessageLocation(psiClass)); CompilerMessageLocation.create(toSystemDependentName(abiVersionErrorLocation.getPath()), 0, 0));
} }
} }
@@ -16,26 +16,60 @@
package org.jetbrains.jet.lang.resolve.java; package org.jetbrains.jet.lang.resolve.java;
import com.intellij.psi.PsiClass; import com.intellij.openapi.vfs.VirtualFile;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.jet.lang.resolve.BindingTrace; import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.java.resolver.ErrorReporter;
import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.util.slicedmap.BasicWritableSlice; import org.jetbrains.jet.util.slicedmap.BasicWritableSlice;
import org.jetbrains.jet.util.slicedmap.Slices; import org.jetbrains.jet.util.slicedmap.Slices;
import org.jetbrains.jet.util.slicedmap.WritableSlice; import org.jetbrains.jet.util.slicedmap.WritableSlice;
public class AbiVersionUtil { public final class AbiVersionUtil {
public static final WritableSlice<PsiClass, Integer> ABI_VERSION_ERRORS = public static final WritableSlice<AbiVersionErrorLocation, Integer> ABI_VERSION_ERRORS =
new BasicWritableSlice<PsiClass, Integer>(Slices.ONLY_REWRITE_TO_EQUAL, true); new BasicWritableSlice<AbiVersionErrorLocation, Integer>(Slices.ONLY_REWRITE_TO_EQUAL, true);
public static final int INVALID_VERSION = -1; public static final int INVALID_VERSION = -1;
private AbiVersionUtil() {
}
public static boolean isAbiVersionCompatible(int abiVersion) { public static boolean isAbiVersionCompatible(int abiVersion) {
return abiVersion == JvmAbi.VERSION; return abiVersion == JvmAbi.VERSION;
} }
public static void reportIncompatibleAbiVersion(@NotNull PsiClass psiClass, int version, @NotNull BindingTrace trace) { @NotNull
trace.record(ABI_VERSION_ERRORS, psiClass, version); public static ErrorReporter abiVersionErrorReporter(
@NotNull final VirtualFile file,
@NotNull final FqName classFqName,
@NotNull final BindingTrace trace
) {
return new ErrorReporter() {
@Override
public void reportIncompatibleAbiVersion(int actualVersion) {
trace.record(ABI_VERSION_ERRORS, new AbiVersionErrorLocation(classFqName, file), actualVersion);
}
};
}
public static final class AbiVersionErrorLocation {
@NotNull
private final FqName classFqName;
@NotNull
private final VirtualFile file;
public AbiVersionErrorLocation(@NotNull FqName name, @NotNull VirtualFile file) {
this.classFqName = name;
this.file = file;
}
@NotNull
public FqName getClassFqName() {
return classFqName;
}
@NotNull
public String getPath() {
return file.getPath();
}
}
private AbiVersionUtil() {
} }
} }
@@ -23,8 +23,6 @@ import com.intellij.psi.util.PsiFormatUtil;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import org.jetbrains.jet.lang.descriptors.*; import org.jetbrains.jet.lang.descriptors.*;
import org.jetbrains.jet.lang.resolve.BindingTrace;
import org.jetbrains.jet.lang.resolve.java.resolver.ErrorReporter;
import org.jetbrains.jet.lang.resolve.name.FqName; import org.jetbrains.jet.lang.resolve.name.FqName;
import org.jetbrains.jet.lang.resolve.name.Name; import org.jetbrains.jet.lang.resolve.name.Name;
import org.jetbrains.jet.lang.types.JetType; import org.jetbrains.jet.lang.types.JetType;
@@ -138,16 +136,6 @@ public final class DescriptorResolverUtils {
return isEnumClassObject(ownerDescriptor) == shouldBeInEnumClassObject(member); return isEnumClassObject(ownerDescriptor) == shouldBeInEnumClassObject(member);
} }
@NotNull
public static ErrorReporter createPsiBasedErrorReporter(@NotNull final PsiClass psiClass, @NotNull final BindingTrace trace) {
return new ErrorReporter() {
@Override
public void reportIncompatibleAbiVersion(int actualVersion) {
AbiVersionUtil.reportIncompatibleAbiVersion(psiClass, actualVersion, trace);
}
};
}
public static boolean isObjectMethodInInterface(@NotNull PsiMember member) { public static boolean isObjectMethodInInterface(@NotNull PsiMember member) {
if (!(member instanceof PsiMethod)) { if (!(member instanceof PsiMethod)) {
return false; return false;
@@ -131,15 +131,12 @@ public final class DeserializedDescriptorResolver {
@Nullable @Nullable
private static String[] readData(@NotNull VirtualFile virtualFile, @NotNull ErrorReporter reporter) { private static String[] readData(@NotNull VirtualFile virtualFile, @NotNull ErrorReporter reporter) {
KotlinClassFileHeader headerData = KotlinClassFileHeader.readKotlinHeaderFromClassFile(virtualFile); KotlinClassFileHeader header = KotlinClassFileHeader.readKotlinHeaderFromClassFile(virtualFile);
int version = headerData.getVersion(); int version = header.getVersion();
String[] annotationData = headerData.getAnnotationData(); if (!isAbiVersionCompatible(version) && header.getType() != KotlinClassFileHeader.HeaderType.NONE) {
if (isAbiVersionCompatible(version)) {
return annotationData;
}
if (annotationData != null) {
reporter.reportIncompatibleAbiVersion(version); reporter.reportIncompatibleAbiVersion(version);
return null;
} }
return null; return header.getAnnotationData();
} }
} }
@@ -21,6 +21,7 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import static org.jetbrains.asm4.ClassReader.*; import static org.jetbrains.asm4.ClassReader.*;
import static org.jetbrains.jet.lang.resolve.java.AbiVersionUtil.isAbiVersionCompatible;
public final class KotlinClassFileHeader { public final class KotlinClassFileHeader {
@NotNull @NotNull
@@ -42,9 +43,12 @@ public final class KotlinClassFileHeader {
} }
} }
@SuppressWarnings("deprecation")
public enum HeaderType { public enum HeaderType {
CLASS(JvmAnnotationNames.KOTLIN_CLASS), CLASS(JvmAnnotationNames.KOTLIN_CLASS),
PACKAGE(JvmAnnotationNames.KOTLIN_PACKAGE), PACKAGE(JvmAnnotationNames.KOTLIN_PACKAGE),
OLD_CLASS(JvmAnnotationNames.OLD_JET_CLASS_ANNOTATION),
OLD_PACKAGE(JvmAnnotationNames.OLD_JET_PACKAGE_CLASS_ANNOTATION),
NONE(null); NONE(null);
@Nullable @Nullable
@@ -53,6 +57,24 @@ public final class KotlinClassFileHeader {
HeaderType(@Nullable JvmClassName annotation) { HeaderType(@Nullable JvmClassName annotation) {
correspondingAnnotation = annotation; correspondingAnnotation = annotation;
} }
boolean isValidAnnotation() {
return this == CLASS || this == PACKAGE;
}
@NotNull
public static HeaderType byDescriptor(@NotNull String desc) {
for (HeaderType headerType : HeaderType.values()) {
JvmClassName annotation = headerType.correspondingAnnotation;
if (annotation == null) {
continue;
}
if (desc.equals(annotation.getDescriptor())) {
return headerType;
}
}
return NONE;
}
} }
private int version = AbiVersionUtil.INVALID_VERSION; private int version = AbiVersionUtil.INVALID_VERSION;
@@ -73,8 +95,11 @@ public final class KotlinClassFileHeader {
return type; return type;
} }
/*
Checks that this is a header for compiled Kotlin file with correct abi version which can be processed by compiler or the IDE.
*/
public boolean isKotlinCompiledFile() { public boolean isKotlinCompiledFile() {
return type != HeaderType.NONE; return type.isValidAnnotation() && isAbiVersionCompatible(version);
} }
@NotNull @NotNull
@@ -119,20 +144,18 @@ public final class KotlinClassFileHeader {
@Override @Override
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) { public AnnotationVisitor visitAnnotation(final String desc, boolean visible) {
for (HeaderType headerType : HeaderType.values()) { HeaderType headerTypeByAnnotation = HeaderType.byDescriptor(desc);
JvmClassName annotation = headerType.correspondingAnnotation; if (headerTypeByAnnotation == HeaderType.NONE) {
if (annotation == null) { return null;
continue;
}
if (desc.equals(annotation.getDescriptor())) {
if (type != HeaderType.NONE) {
throw new IllegalStateException(
"Both " + type.correspondingAnnotation + " and " + headerType.correspondingAnnotation + " present!");
}
type = headerType;
}
} }
if (type == HeaderType.NONE) { if (headerTypeByAnnotation.isValidAnnotation() && type.isValidAnnotation()) {
throw new IllegalStateException("Both " + type.correspondingAnnotation + " and "
+ headerTypeByAnnotation.correspondingAnnotation + " present!");
}
if (!type.isValidAnnotation()) {
type = headerTypeByAnnotation;
}
if (!headerTypeByAnnotation.isValidAnnotation()) {
return null; return null;
} }
return new AnnotationVisitor(Opcodes.ASM4) { return new AnnotationVisitor(Opcodes.ASM4) {
@@ -141,7 +164,7 @@ public final class KotlinClassFileHeader {
if (name.equals(JvmAnnotationNames.ABI_VERSION_FIELD_NAME)) { if (name.equals(JvmAnnotationNames.ABI_VERSION_FIELD_NAME)) {
version = (Integer) value; version = (Integer) value;
} }
else if (AbiVersionUtil.isAbiVersionCompatible(version)) { else if (isAbiVersionCompatible(version)) {
throw new IllegalStateException("Unexpected argument " + name + " for annotation " + desc); throw new IllegalStateException("Unexpected argument " + name + " for annotation " + desc);
} }
} }
@@ -151,7 +174,7 @@ public final class KotlinClassFileHeader {
if (name.equals(JvmAnnotationNames.DATA_FIELD_NAME)) { if (name.equals(JvmAnnotationNames.DATA_FIELD_NAME)) {
return stringArrayVisitor(); return stringArrayVisitor();
} }
else if (AbiVersionUtil.isAbiVersionCompatible(version)) { else if (isAbiVersionCompatible(version)) {
throw new IllegalStateException("Unexpected array argument " + name + " for annotation " + desc); throw new IllegalStateException("Unexpected array argument " + name + " for annotation " + desc);
} }
+2 -2
View File
@@ -1,5 +1,5 @@
WARNING: $TESTDATA_DIR$/wrongAbiVersion.kt: (3, 9) Parameter 'x' is never used WARNING: $TESTDATA_DIR$/wrongAbiVersion.kt: (3, 9) Parameter 'x' is never used
ERROR: $TESTDATA_DIR$/wrongAbiVersion.kt: (4, 3) Unresolved reference: bar ERROR: $TESTDATA_DIR$/wrongAbiVersion.kt: (4, 3) Unresolved reference: bar
ERROR: $TESTDATA_DIR$/wrongAbiVersionLib/wrong/WrongPackage.class: (-1, 135) Class 'wrong.WrongPackage' was compiled with an incompatible version of Kotlin. Its ABI version is -1, expected ABI version is 7 ERROR: $TESTDATA_DIR$/wrongAbiVersionLib/wrong/WrongPackage.class: (0, 0) Class 'wrong.WrongPackage' was compiled with an incompatible version of Kotlin. Its ABI version is -1, expected ABI version is 7
ERROR: $TESTDATA_DIR$/wrongAbiVersionLib/ClassWithWrongAbiVersion.class: (-1, 119) Class 'ClassWithWrongAbiVersion' was compiled with an incompatible version of Kotlin. Its ABI version is -1, expected ABI version is 7 ERROR: $TESTDATA_DIR$/wrongAbiVersionLib/ClassWithWrongAbiVersion.class: (0, 0) Class 'ClassWithWrongAbiVersion' was compiled with an incompatible version of Kotlin. Its ABI version is -1, expected ABI version is 7
COMPILATION_ERROR COMPILATION_ERROR