Skip to content

Commit

Permalink
Workaround Java 8 bug (Fixes #2173) (#2672)
Browse files Browse the repository at this point in the history
  • Loading branch information
smillst authored Aug 1, 2019
1 parent d965f10 commit be0f862
Show file tree
Hide file tree
Showing 8 changed files with 115 additions and 42 deletions.
Binary file added checker/jtreg/nullness/issue2173/Importer.class
Binary file not shown.
Binary file not shown.
27 changes: 27 additions & 0 deletions checker/jtreg/nullness/issue2173/ImporterManager.java.tmp
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import org.checkerframework.checker.i18n.qual.Localized;
import org.checkerframework.checker.nullness.qual.NonNull;
import org.checkerframework.common.subtyping.qual.Bottom;

interface Importer {
List<Pair<@Localized String, List<String>>> getSupportedFileTypes();
}

interface Pair<A, B> {
B getSecond();
}

public class ImporterManager {
private static final List<Importer> registeredImporters = new ArrayList<>();

public static void chooseAndImportFile(Object parent) {
Function<@NonNull ?, @NonNull ?> x =
(@NonNull Importer imp) ->
imp.getSupportedFileTypes().stream()
.flatMap(
(@Bottom Pair<@Localized String, List<String>> p) ->
p.getSecond().stream());
}
}
Binary file added checker/jtreg/nullness/issue2173/Pair.class
Binary file not shown.
7 changes: 7 additions & 0 deletions checker/jtreg/nullness/issue2173/README
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
Test case for issue #2173: https://github.com/typetools/checker-framework/issues/2173

ImporterManager.class was compiled use a Java 8 compiler. The compiler copied
the annotation from a a lambda to a method parameter type argument, but the
parameter does not have a type argument. This bug has been fixed in Java 9,
but byte code generated by Java 8 can still be read by a checker, so this test
that it won't crash a checker.
13 changes: 13 additions & 0 deletions checker/jtreg/nullness/issue2173/View.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/*
* @test
* @summary Test case for issue #2173: https://github.com/typetools/checker-framework/issues/2173
* See README in this directory.
*
* @compile -processor org.checkerframework.checker.nullness.NullnessChecker View.java
*/

public class View {
private static void createTable() {
ImporterManager.chooseAndImportFile("");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,18 @@ static void annotateViaTypeAnnoPosition(
final Map<AnnotatedWildcardType, WildcardBoundAnnos> wildcardToAnnos =
new IdentityHashMap<>();
for (final TypeCompound anno : annos) {
AnnotatedTypeMirror target = getTypeAtLocation(type, anno.position.location);
AnnotatedTypeMirror target;
try {
target = getTypeAtLocation(type, anno.position.location);
} catch (UnexpectedAnnotationLocationException ex) {
// There's a bug in Java 8 compiler that creates bad bytecode such that an
// annotation on a lambda parameter is applied to a method parameter. (This bug has
// been fixed in Java 9.) If this happens, then the location could refer to a
// location, such as a type argument, that doesn't exist. Since Java 8 bytecode
// might be on the classpath, catch this exception and ignore the type.
// TODO: Issue an error if this annotation is from Java 9+ bytecode.
continue;
}
if (target.getKind() == TypeKind.WILDCARD) {
addWildcardToBoundMap((AnnotatedWildcardType) target, anno, wildcardToAnnos);
} else {
Expand Down Expand Up @@ -372,7 +383,8 @@ static int getBoundIndexOffset(final List<? extends AnnotatedTypeMirror> upperBo
* @return the type specified by location
*/
static AnnotatedTypeMirror getTypeAtLocation(
AnnotatedTypeMirror type, List<TypeAnnotationPosition.TypePathEntry> location) {
AnnotatedTypeMirror type, List<TypeAnnotationPosition.TypePathEntry> location)
throws UnexpectedAnnotationLocationException {

if (location.isEmpty() && type.getKind() != TypeKind.DECLARED) {
// An annotation with an empty type path on a declared type applies to the outermost
Expand Down Expand Up @@ -406,13 +418,10 @@ static AnnotatedTypeMirror getTypeAtLocation(
default:
// Raise an error for all other types below.
}
throw new BugInCF(
"ElementAnnotationUtil.getTypeAtLocation: unexpected annotation with location found for type: "
+ type
+ " (kind: "
+ type.getKind()
+ ") location: "
+ location);
throw new UnexpectedAnnotationLocationException(
"ElementAnnotationUtil.getTypeAtLocation: "
+ "unexpected annotation with location found for type: %s (kind: %s ) location: ",
type, type.getKind(), location);
}

/**
Expand All @@ -424,7 +433,8 @@ static AnnotatedTypeMirror getTypeAtLocation(
* @return the type specified by location
*/
private static AnnotatedTypeMirror getLocationTypeADT(
AnnotatedDeclaredType type, List<TypeAnnotationPosition.TypePathEntry> location) {
AnnotatedDeclaredType type, List<TypeAnnotationPosition.TypePathEntry> location)
throws UnexpectedAnnotationLocationException {

// List order by outer most type to inner most type.
ArrayDeque<AnnotatedDeclaredType> outerToInner = new ArrayDeque<>();
Expand Down Expand Up @@ -464,30 +474,30 @@ private static AnnotatedTypeMirror getLocationTypeADT(
}

if (outerToInner.isEmpty() || error) {
throw new BugInCF(
throw new UnexpectedAnnotationLocationException(
"ElementAnnotationUtil.getLocationTypeADT: invalid location %s for type: %s",
location, type);
}

return outerToInner.getFirst();
}

private static AnnotatedTypeMirror getLocationTypeANT(
AnnotatedNullType type, List<TypeAnnotationPosition.TypePathEntry> location) {
AnnotatedNullType type, List<TypeAnnotationPosition.TypePathEntry> location)
throws UnexpectedAnnotationLocationException {
if (location.size() == 1 && location.get(0).tag == TypePathEntryKind.TYPE_ARGUMENT) {
return type;
}

throw new BugInCF(
"ElementAnnotationUtil.getLocationTypeANT: "
+ "invalid location "
+ location
+ " for type: "
+ type);
throw new UnexpectedAnnotationLocationException(
"ElementAnnotationUtil.getLocationTypeANT: " + "invalid location %s for type: %s ",
location, type);
}

private static AnnotatedTypeMirror getLocationTypeAWT(
final AnnotatedWildcardType type,
final List<TypeAnnotationPosition.TypePathEntry> location) {
final List<TypeAnnotationPosition.TypePathEntry> location)
throws UnexpectedAnnotationLocationException {

// the last step into the Wildcard type is handled in WildcardToBoundAnnos.addAnnotation
if (location.size() == 1) {
Expand All @@ -505,12 +515,10 @@ private static AnnotatedTypeMirror getLocationTypeAWT(
}

} else {
throw new BugInCF(
throw new UnexpectedAnnotationLocationException(
"ElementAnnotationUtil.getLocationTypeAWT: "
+ "invalid location "
+ location
+ " for type: "
+ type);
+ "invalid location %s for type: %s ",
location, type);
}
}

Expand All @@ -522,18 +530,16 @@ private static AnnotatedTypeMirror getLocationTypeAWT(
* its position.
*/
private static AnnotatedTypeMirror getLocationTypeAAT(
AnnotatedArrayType type, List<TypeAnnotationPosition.TypePathEntry> location) {
AnnotatedArrayType type, List<TypeAnnotationPosition.TypePathEntry> location)
throws UnexpectedAnnotationLocationException {
if (location.size() >= 1
&& location.get(0).tag.equals(TypeAnnotationPosition.TypePathEntryKind.ARRAY)) {
AnnotatedTypeMirror comptype = type.getComponentType();
return getTypeAtLocation(comptype, tail(location));
} else {
throw new BugInCF(
"ElementAnnotationUtil.annotateAAT: "
+ "invalid location "
+ location
+ " for type: "
+ type);
throw new UnexpectedAnnotationLocationException(
"ElementAnnotationUtil.annotateAAT: " + "invalid location %s for type: %s ",
location, type);
}
}

Expand All @@ -546,31 +552,45 @@ private static AnnotatedTypeMirror getLocationTypeAAT(
* As a hack, always annotate the first alternative.
*/
private static AnnotatedTypeMirror getLocationTypeAUT(
AnnotatedUnionType type, List<TypeAnnotationPosition.TypePathEntry> location) {
AnnotatedUnionType type, List<TypeAnnotationPosition.TypePathEntry> location)
throws UnexpectedAnnotationLocationException {
AnnotatedTypeMirror comptype = type.getAlternatives().get(0);
return getTypeAtLocation(comptype, location);
}

/** Intersection types use the TYPE_ARGUMENT index to separate the individual types. */
private static AnnotatedTypeMirror getLocationTypeAIT(
AnnotatedIntersectionType type, List<TypeAnnotationPosition.TypePathEntry> location) {
AnnotatedIntersectionType type, List<TypeAnnotationPosition.TypePathEntry> location)
throws UnexpectedAnnotationLocationException {
if (location.size() >= 1
&& location.get(0)
.tag
.equals(TypeAnnotationPosition.TypePathEntryKind.TYPE_ARGUMENT)) {
AnnotatedTypeMirror supertype = type.directSuperTypes().get(location.get(0).arg);
return getTypeAtLocation(supertype, tail(location));
} else {
throw new BugInCF(
"ElementAnnotationUtil.getLocatonTypeAIT: "
+ "invalid location "
+ location
+ " for type: "
+ type);
throw new UnexpectedAnnotationLocationException(
"ElementAnnotationUtil.getLocatonTypeAIT: invalid location %s for type: %s ",
location, type);
}
}

private static <T> List<T> tail(List<T> list) {
return list.subList(1, list.size());
}

/** Exception indicating an invalid location for an annotation was found. */
@SuppressWarnings("serial")
static class UnexpectedAnnotationLocationException extends Exception {

/**
* Creates an UnexpectedAnnotationLocationException.
*
* @param format format string
* @param args arguments to the format string
*/
private UnexpectedAnnotationLocationException(String format, Object... args) {
super(String.format(format, args));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@
import javax.lang.model.element.ElementKind;
import javax.lang.model.element.ExecutableElement;
import javax.lang.model.element.TypeParameterElement;
import javax.lang.model.type.TypeKind;
import org.checkerframework.framework.type.AnnotatedTypeFactory;
import org.checkerframework.framework.type.AnnotatedTypeMirror;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedArrayType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedIntersectionType;
import org.checkerframework.framework.type.AnnotatedTypeMirror.AnnotatedTypeVariable;
import org.checkerframework.framework.type.ElementAnnotationApplier;
import org.checkerframework.framework.util.element.ElementAnnotationUtil.UnexpectedAnnotationLocationException;
import org.checkerframework.javacutil.BugInCF;

/** Apply annotations to the use of a type parameter declaration. */
Expand Down Expand Up @@ -170,9 +172,13 @@ private List<Attribute.TypeCompound> removeComponentAnnotations(

private boolean isBaseComponent(
final AnnotatedArrayType arrayType, final Attribute.TypeCompound anno) {
return ElementAnnotationUtil.getTypeAtLocation(arrayType, anno.getPosition().location)
.getClass()
.equals(AnnotatedTypeVariable.class);
try {
return ElementAnnotationUtil.getTypeAtLocation(arrayType, anno.getPosition().location)
.getKind()
== TypeKind.TYPEVAR;
} catch (UnexpectedAnnotationLocationException ex) {
return false;
}
}

/**
Expand Down

0 comments on commit be0f862

Please sign in to comment.