diff --git a/src/checkers/inference/InferenceLauncher.java b/src/checkers/inference/InferenceLauncher.java index 3d1fdce1..c229d259 100644 --- a/src/checkers/inference/InferenceLauncher.java +++ b/src/checkers/inference/InferenceLauncher.java @@ -205,7 +205,7 @@ public void infer() { Mode mode = Mode.valueOf(InferenceOptions.mode); if (InferenceOptions.makeDefaultsExplicit - && (mode == Mode.ROUNDTRIP || mode == Mode.ROUNDTRIP_TYPECHECK)) { + && (mode == Mode.ROUNDTRIP || mode == Mode.ROUNDTRIP_TYPECHECK || mode == Mode.INFER)) { // Two conditions have to be met to make defaults explicit: // 1. the command-line flag `makeDefaultsExplicit` is provided // 2. the inference solution will be written back to the source code (roundtrip `mode`) diff --git a/src/checkers/inference/InferenceValidator.java b/src/checkers/inference/InferenceValidator.java index 6b39b76c..888a205f 100644 --- a/src/checkers/inference/InferenceValidator.java +++ b/src/checkers/inference/InferenceValidator.java @@ -10,6 +10,7 @@ import org.checkerframework.javacutil.AnnotationUtils; import javax.lang.model.element.AnnotationMirror; +import java.util.List; /** * A visitor to validate the types in a tree. diff --git a/src/checkers/inference/InferenceVisitor.java b/src/checkers/inference/InferenceVisitor.java index 7ccd7451..a0b787c3 100644 --- a/src/checkers/inference/InferenceVisitor.java +++ b/src/checkers/inference/InferenceVisitor.java @@ -23,6 +23,7 @@ import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; @@ -50,6 +51,7 @@ import com.sun.source.tree.Tree.Kind; import com.sun.source.tree.VariableTree; +import org.checkerframework.javacutil.TreeUtils; import org.plumelib.util.ArraysPlume; @@ -82,12 +84,11 @@ public class InferenceVisitor> locationToIllegalQuals; + protected final Map locationToIllegalQuals; public InferenceVisitor(Checker checker, InferenceChecker ichecker, Factory factory, boolean infer) { super((infer) ? ichecker : checker, factory); @@ -272,6 +273,44 @@ public void mainIsNoneOf(AnnotatedTypeMirror ty, AnnotationMirror[] mods, String } } + private void addDeepPreferenceImpl(AnnotatedTypeMirror ty, AnnotationMirror goal, int weight, + java.util.List visited, Tree node) { + if (infer) { + if (visited.contains(ty)) { + return; + } + visited.add(ty); + + final SlotManager slotManager = InferenceMain.getInstance().getSlotManager(); + Slot el = slotManager.getSlot(ty); + + if (el == null) { + logger.warning("InferenceVisitor::addDeepPreferenceImpl: no annotation in type: " + ty); + } else { + addPreference(ty, goal, weight); + } + + if (ty.getKind() == TypeKind.DECLARED) { + AnnotatedDeclaredType declaredType = (AnnotatedDeclaredType) ty; + for (AnnotatedTypeMirror typearg : declaredType.getTypeArguments()) { + addDeepPreferenceImpl(typearg, goal, weight, visited, node); + } + } else if (ty.getKind() == TypeKind.ARRAY) { + AnnotatedArrayType arrayType = (AnnotatedArrayType) ty; + addDeepPreferenceImpl(arrayType.getComponentType(), goal, weight, visited, node); + } else if (ty.getKind() == TypeKind.TYPEVAR) { + AnnotatedTypeVariable atv = (AnnotatedTypeVariable) ty; + addDeepPreferenceImpl(atv.getUpperBound(), goal, weight, visited, node); + addDeepPreferenceImpl(atv.getLowerBound(), goal, weight, visited, node); + } + } + // Else, do nothing + } + + public void addDeepPreference(AnnotatedTypeMirror ty, AnnotationMirror goal, int weight, Tree node) { + addDeepPreferenceImpl(ty, goal, weight, new LinkedList<>(), node); + } + public void addPreference(AnnotatedTypeMirror type, AnnotationMirror anno, int weight) { if (infer) { ConstraintManager cManager = InferenceMain.getInstance().getConstraintManager(); @@ -866,16 +905,16 @@ protected void checkConstructorResult( * * @return a mapping from type-use locations to a set of qualifiers which cannot be applied to that location */ - protected Map> createMapForIllegalQuals() { - Map> locationToIllegalQuals = new HashMap<>(); + protected Map createMapForIllegalQuals() { + Map locationToIllegalQuals = new HashMap<>(); // First, init each type-use location to contain all type qualifiers. Set> supportQualifiers = atypeFactory.getSupportedTypeQualifiers(); - Set supportedAnnos = new AnnotationMirrorSet(); + AnnotationMirrorSet supportedAnnos = new AnnotationMirrorSet(); for (Class qual: supportQualifiers) { supportedAnnos.add(new AnnotationBuilder(atypeFactory.getProcessingEnv(), qual).build()); } for (TypeUseLocation location : TypeUseLocation.values()) { - locationToIllegalQuals.put(location, new HashSet<>(supportedAnnos)); + locationToIllegalQuals.put(location, new AnnotationMirrorSet(supportedAnnos)); } // Then, delete some qualifiers which can be applied to that type-use location. // this leaves only qualifiers not allowed on that location. @@ -886,7 +925,7 @@ protected Map> createMapForIllegalQuals() // the qualifier can be written on any type use. if (tls == null) { for (TypeUseLocation location : TypeUseLocation.values()) { - Set amSet = locationToIllegalQuals.get(location); + AnnotationMirrorSet amSet = locationToIllegalQuals.get(location); amSet.remove(AnnotationUtils.getAnnotationByName(supportedAnnos, qual.getCanonicalName())); } continue; @@ -894,12 +933,12 @@ protected Map> createMapForIllegalQuals() for (TypeUseLocation location : tls.value()) { if (location == TypeUseLocation.ALL) { for (TypeUseLocation val : TypeUseLocation.values()) { - Set amSet = locationToIllegalQuals.get(val); + AnnotationMirrorSet amSet = locationToIllegalQuals.get(val); amSet.remove(AnnotationUtils.getAnnotationByName(supportedAnnos, qual.getCanonicalName())); } break; } - Set amSet = locationToIllegalQuals.get(location); + AnnotationMirrorSet amSet = locationToIllegalQuals.get(location); amSet.remove(AnnotationUtils.getAnnotationByName(supportedAnnos, qual.getCanonicalName())); } } @@ -912,7 +951,6 @@ protected void validateVariablesTargetLocation(Tree tree, AnnotatedTypeMirror ty super.validateVariablesTargetLocation(tree, type); return; } - if (ignoreTargetLocations) { return; } @@ -943,6 +981,7 @@ protected void validateVariablesTargetLocation(Tree tree, AnnotatedTypeMirror ty break; case ENUM_CONSTANT: location = TypeUseLocation.CONSTRUCTOR_RESULT; + // TODO: Add ? mainIsNoneOf(type, targetLocationToAnno.get(TypeUseLocation.FIELD).toArray(mirrors), "type.invalid.annotations.on.location", tree); break; default: throw new BugInCF("Location not matched"); diff --git a/src/checkers/inference/VariableAnnotator.java b/src/checkers/inference/VariableAnnotator.java index b4b48df1..c507406a 100644 --- a/src/checkers/inference/VariableAnnotator.java +++ b/src/checkers/inference/VariableAnnotator.java @@ -734,7 +734,7 @@ public Void visitDeclared(final AnnotatedDeclaredType adt, final Tree tree) { return null; } - private boolean handleWasRawDeclaredTypes(AnnotatedDeclaredType adt) { + protected boolean handleWasRawDeclaredTypes(AnnotatedDeclaredType adt) { if (adt.isUnderlyingTypeRaw() && adt.getTypeArguments().size() != 0) { // the type arguments should be wildcards AND if I get the real type of "tree" // it corresponds to the declaration of adt.getUnderlyingType @@ -1677,7 +1677,7 @@ public AnnotationMirror getClassDeclVarAnnot(TypeElement ele) { } - private void addDeclarationConstraints(Slot declSlot, Slot instanceSlot) { + protected void addDeclarationConstraints(Slot declSlot, Slot instanceSlot) { constraintManager.addSubtypeConstraint(instanceSlot, declSlot); } diff --git a/src/checkers/inference/util/SlotDefaultTypeResolver.java b/src/checkers/inference/util/SlotDefaultTypeResolver.java index 2f081931..e2034e46 100644 --- a/src/checkers/inference/util/SlotDefaultTypeResolver.java +++ b/src/checkers/inference/util/SlotDefaultTypeResolver.java @@ -3,6 +3,8 @@ import com.sun.source.tree.AnnotatedTypeTree; import com.sun.source.tree.ArrayTypeTree; import com.sun.source.tree.ClassTree; +import com.sun.source.tree.ExpressionTree; +import com.sun.source.tree.NewClassTree; import com.sun.source.tree.ParameterizedTypeTree; import com.sun.source.tree.PrimitiveTypeTree; import com.sun.source.tree.Tree; @@ -185,5 +187,21 @@ public Void visitAnnotatedType(AnnotatedTypeTree tree, Void unused) { return super.visitAnnotatedType(tree, unused); } + + @Override + public Void visitNewClass(NewClassTree tree, Void unused) { + AnnotatedTypeMirror defaultType = getDefaultTypeFor(tree); + if (InferenceUtil.isAnonymousClass(tree)) { + // don't associate the identifier with the defaultType if it's an anonymousclass + // should associate the identifier with the direct super type of the defaultType. + // choose the last one of the directSupertypes, which is either the direct super class + // or implemented interface + List superTypes = defaultType.directSupertypes(); + defaultTypes.put(tree.getIdentifier(), superTypes.get(superTypes.size()-1)); + } else { + defaultTypes.put(tree.getIdentifier(), defaultType); + } + return super.visitNewClass(tree, unused); + } } } diff --git a/src/dataflow/qual/DataFlowInferenceBottom.java b/src/dataflow/qual/DataFlowInferenceBottom.java index 44a68f37..eed69ce7 100644 --- a/src/dataflow/qual/DataFlowInferenceBottom.java +++ b/src/dataflow/qual/DataFlowInferenceBottom.java @@ -16,7 +16,7 @@ @InvisibleQualifier @SubtypeOf({ DataFlow.class }) @Target({ ElementType.TYPE_USE }) -@TargetLocations({ TypeUseLocation.ALL }) +@TargetLocations({ TypeUseLocation.ALL}) public @interface DataFlowInferenceBottom { } diff --git a/tests/checkers/inference/test/CFInferenceTest.java b/tests/checkers/inference/test/CFInferenceTest.java index 7383b874..c7d4d09f 100644 --- a/tests/checkers/inference/test/CFInferenceTest.java +++ b/tests/checkers/inference/test/CFInferenceTest.java @@ -31,6 +31,10 @@ public boolean useHacks() { return SystemPlume.getBooleanSystemProperty("use.hacks"); } + public boolean makeDefaultsExplicit() { + return false; + } + public abstract IPair> getSolverNameAndOptions(); public List getAdditionalInferenceOptions() { @@ -55,7 +59,8 @@ public void run() { InferenceTestConfiguration config = InferenceTestConfigurationBuilder.buildDefaultConfiguration(testDir, testFile, testDataDir, checker, checkerOptions, getAdditionalInferenceOptions(), solverArgs.first, - solverArgs.second, useHacks(), shouldEmitDebugInfo, getPathToAfuScripts(), getPathToInferenceScript()); + solverArgs.second, useHacks(), makeDefaultsExplicit(), shouldEmitDebugInfo, getPathToAfuScripts(), + getPathToInferenceScript()); InferenceTestResult testResult = new InferenceTestExecutor().runTest(config); InferenceTestUtilities.assertResultsAreValid(testResult); diff --git a/tests/checkers/inference/test/ImmutableInferenceTestConfiguration.java b/tests/checkers/inference/test/ImmutableInferenceTestConfiguration.java index f44fcf09..12a47261 100644 --- a/tests/checkers/inference/test/ImmutableInferenceTestConfiguration.java +++ b/tests/checkers/inference/test/ImmutableInferenceTestConfiguration.java @@ -16,13 +16,15 @@ public class ImmutableInferenceTestConfiguration implements InferenceTestConfigu private final String solver; private final Map solverArgs; private final boolean shouldUseHacks; + private final boolean makeDefaultsExplicit; private final String pathToAfuScripts; private final String pathToInferenceScript; private final TestConfiguration initialConfig; public ImmutableInferenceTestConfiguration(File outputJaif, File testDataDir, File annotatedSourceDir, Map inferenceJavacArgs, String solver, - Map solverArgs, boolean shouldUseHacks, String pathToAfuScripts, + Map solverArgs, boolean shouldUseHacks, + boolean makeDefaultsExplicit, String pathToAfuScripts, String pathToInferenceScript, TestConfiguration initialConfig) { this.outputJaif = outputJaif; this.testDataDir = testDataDir; @@ -31,6 +33,7 @@ public ImmutableInferenceTestConfiguration(File outputJaif, File testDataDir, Fi this.solver = solver; this.solverArgs = solverArgs; this.shouldUseHacks = shouldUseHacks; + this.makeDefaultsExplicit = makeDefaultsExplicit; this.pathToAfuScripts = pathToAfuScripts; this.initialConfig = initialConfig; this.pathToInferenceScript = pathToInferenceScript; @@ -69,6 +72,10 @@ public boolean shouldUseHacks() { return shouldUseHacks; } + public boolean makeDefaultsExplicit() { + return makeDefaultsExplicit; + } + public String getPathToAfuScripts() { return pathToAfuScripts; } diff --git a/tests/checkers/inference/test/InferenceTestConfiguration.java b/tests/checkers/inference/test/InferenceTestConfiguration.java index b084f7bf..d7c4ce90 100644 --- a/tests/checkers/inference/test/InferenceTestConfiguration.java +++ b/tests/checkers/inference/test/InferenceTestConfiguration.java @@ -20,6 +20,7 @@ public interface InferenceTestConfiguration { List getFlatSolverArgs(); boolean shouldUseHacks(); + boolean makeDefaultsExplicit(); String getPathToAfuScripts(); String getPathToInferenceScript(); diff --git a/tests/checkers/inference/test/InferenceTestConfigurationBuilder.java b/tests/checkers/inference/test/InferenceTestConfigurationBuilder.java index 1d296209..668b0f1d 100644 --- a/tests/checkers/inference/test/InferenceTestConfigurationBuilder.java +++ b/tests/checkers/inference/test/InferenceTestConfigurationBuilder.java @@ -17,6 +17,7 @@ public class InferenceTestConfigurationBuilder { private File testDataDir = null; private String solver = null; private boolean shouldUseHacks; + private boolean makeDefaultsExplicit; private String pathToAfuScripts=""; private String pathToInferenceScript=""; @@ -60,6 +61,11 @@ public InferenceTestConfigurationBuilder setShouldUseHacks(boolean shouldUseHack return this; } + public InferenceTestConfigurationBuilder setMakeDefaultsExplicit(boolean makeDefaultsExplicit) { + this.makeDefaultsExplicit = makeDefaultsExplicit; + return this; + } + public InferenceTestConfigurationBuilder setPathToAfuScripts(String pathToAfuScripts) { this.pathToAfuScripts = pathToAfuScripts; return this; @@ -156,8 +162,8 @@ public List validate() { public InferenceTestConfiguration build() { return new ImmutableInferenceTestConfiguration(outputJaif, testDataDir, annotatedSourceDir, new LinkedHashMap<>(inferenceJavacArgs.getOptions()), - solver, new LinkedHashMap<>(solverArgs.getOptions()), shouldUseHacks,pathToAfuScripts, - pathToInferenceScript, initialConfiguration); + solver, new LinkedHashMap<>(solverArgs.getOptions()), shouldUseHacks, makeDefaultsExplicit, + pathToAfuScripts, pathToInferenceScript, initialConfiguration); } public InferenceTestConfiguration validateThenBuild() { @@ -175,7 +181,8 @@ public InferenceTestConfiguration validateThenBuild() { public static InferenceTestConfiguration buildDefaultConfiguration( String testSourcePath, File testFile, File testDataRoot, Class checker, List typecheckOptions, List inferenceOptions, String solverName, List solverOptions, - boolean shouldUseHacks, boolean shouldEmitDebugInfo, String pathToAfuScripts, String pathToInferenceScript) { + boolean shouldUseHacks, boolean makeDefaultsExplicit, boolean shouldEmitDebugInfo, String pathToAfuScripts, + String pathToInferenceScript) { final File defaultInferenceOutDir = new File("testdata/tmp"); final File defaultOutputJaif = new File(defaultInferenceOutDir, "default.jaif"); @@ -192,6 +199,7 @@ public static InferenceTestConfiguration buildDefaultConfiguration( .setAnnotatedSourceDir(defaultAnnotatedSourceDir) .setSolver(solverName) .setShouldUseHacks(shouldUseHacks) + .setMakeDefaultsExplicit(makeDefaultsExplicit) .setPathToAfuScripts(pathToAfuScripts) .setPathToInferenceScript(pathToInferenceScript); diff --git a/tests/checkers/inference/test/InferenceTestExecutor.java b/tests/checkers/inference/test/InferenceTestExecutor.java index 14345bf9..310145d4 100644 --- a/tests/checkers/inference/test/InferenceTestExecutor.java +++ b/tests/checkers/inference/test/InferenceTestExecutor.java @@ -80,6 +80,9 @@ public static InferenceResult infer(InferenceTestConfiguration configuration) { if (configuration.shouldUseHacks()) { options.add("--hacks"); } + if (configuration.makeDefaultsExplicit()) { + options.add("--makeDefaultsExplicit"); + } options.add("--jaifFile=" + configuration.getOutputJaif().getAbsolutePath());