Skip to content

Commit d282234

Browse files
Nargeshdbmernstmsridharkelloggm
authored
First Phase of RLC inference (#5870)
Co-authored-by: Michael Ernst <mernst@cs.washington.edu> Co-authored-by: Manu Sridharan <msridhar@gmail.com> Co-authored-by: Martin Kellogg <martin.kellogg@njit.edu>
1 parent 8047616 commit d282234

20 files changed

+1210
-116
lines changed

checker/build.gradle

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -910,7 +910,8 @@ task wpiManyTest(group: 'Verification') {
910910
'-i', "${project.projectDir}/tests/wpi-many/testin.txt",
911911
'-o', "${project.projectDir}/build/wpi-many-tests",
912912
'-s',
913-
'--', '--checker', 'nullness,interning,lock,regex,signature,calledmethods,resourceleak'
913+
'--', '--checker', 'nullness,interning,lock,regex,signature,calledmethods,resourceleak',
914+
'--extraJavacArgs=-AenableWpiForRlc'
914915
}
915916
} catch (Exception e) {
916917
println('Failure: Running wpi-many.sh failed with a non-zero exit code.')

checker/src/main/java/org/checkerframework/checker/calledmethods/CalledMethodsTransfer.java

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.checkerframework.checker.calledmethods;
22

3+
import com.sun.source.tree.Tree;
34
import java.util.Arrays;
45
import java.util.Collections;
56
import java.util.LinkedHashMap;
@@ -11,6 +12,7 @@
1112
import javax.lang.model.type.TypeMirror;
1213
import org.checkerframework.checker.calledmethods.qual.EnsuresCalledMethodsVarArgs;
1314
import org.checkerframework.checker.nullness.qual.Nullable;
15+
import org.checkerframework.checker.resourceleak.ResourceLeakChecker;
1416
import org.checkerframework.common.accumulation.AccumulationStore;
1517
import org.checkerframework.common.accumulation.AccumulationTransfer;
1618
import org.checkerframework.common.accumulation.AccumulationValue;
@@ -50,6 +52,12 @@ public class CalledMethodsTransfer extends AccumulationTransfer {
5052
*/
5153
private final ExecutableElement calledMethodsValueElement;
5254

55+
/**
56+
* True if -AenableWpiForRlc was passed on the command line. See {@link
57+
* ResourceLeakChecker#ENABLE_WPI_FOR_RLC}.
58+
*/
59+
private final boolean enableWpiForRlc;
60+
5361
/**
5462
* Create a new CalledMethodsTransfer.
5563
*
@@ -59,6 +67,40 @@ public CalledMethodsTransfer(CalledMethodsAnalysis analysis) {
5967
super(analysis);
6068
calledMethodsValueElement =
6169
((CalledMethodsAnnotatedTypeFactory) atypeFactory).calledMethodsValueElement;
70+
enableWpiForRlc = atypeFactory.getChecker().hasOption(ResourceLeakChecker.ENABLE_WPI_FOR_RLC);
71+
}
72+
73+
/**
74+
* @param tree a tree
75+
* @return false if Resource Leak Checker is running as one of the upstream checkers and the
76+
* -AenableWpiForRlc flag (see {@link ResourceLeakChecker#ENABLE_WPI_FOR_RLC}) is not passed
77+
* as a command line argument, otherwise returns the result of the super call
78+
*/
79+
@Override
80+
protected boolean shouldPerformWholeProgramInference(Tree tree) {
81+
if (!isWpiEnabledForRLC()
82+
&& atypeFactory.getCheckerNames().contains(ResourceLeakChecker.class.getCanonicalName())) {
83+
return false;
84+
}
85+
return super.shouldPerformWholeProgramInference(tree);
86+
}
87+
88+
/**
89+
* See {@link ResourceLeakChecker#ENABLE_WPI_FOR_RLC}.
90+
*
91+
* @param expressionTree a tree
92+
* @param lhsTree its element
93+
* @return false if Resource Leak Checker is running as one of the upstream checkers and the
94+
* -AenableWpiForRlc flag is not passed as a command line argument, otherwise returns the
95+
* result of the super call
96+
*/
97+
@Override
98+
protected boolean shouldPerformWholeProgramInference(Tree expressionTree, Tree lhsTree) {
99+
if (!isWpiEnabledForRLC()
100+
&& atypeFactory.getCheckerNames().contains(ResourceLeakChecker.class.getCanonicalName())) {
101+
return false;
102+
}
103+
return super.shouldPerformWholeProgramInference(expressionTree, lhsTree);
62104
}
63105

64106
@Override
@@ -229,4 +271,14 @@ private void handleEnsuresCalledMethodsVarArgs(
229271

230272
return atypeFactory.createAccumulatorAnnotation(newList);
231273
}
274+
275+
/**
276+
* Checks if WPI is enabled for the Resource Leak Checker inference. See {@link
277+
* ResourceLeakChecker#ENABLE_WPI_FOR_RLC}.
278+
*
279+
* @return returns true if WPI is enabled for the Resource Leak Checker
280+
*/
281+
protected boolean isWpiEnabledForRLC() {
282+
return enableWpiForRlc;
283+
}
232284
}

checker/src/main/java/org/checkerframework/checker/mustcall/MustCallAnnotatedTypeFactory.java

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.checkerframework.checker.mustcall.qual.Owning;
3232
import org.checkerframework.checker.mustcall.qual.PolyMustCall;
3333
import org.checkerframework.checker.nullness.qual.Nullable;
34+
import org.checkerframework.checker.resourceleak.ResourceLeakChecker;
3435
import org.checkerframework.common.basetype.BaseAnnotatedTypeFactory;
3536
import org.checkerframework.common.basetype.BaseTypeChecker;
3637
import org.checkerframework.dataflow.cfg.block.Block;
@@ -116,6 +117,12 @@ public class MustCallAnnotatedTypeFactory extends BaseAnnotatedTypeFactory
116117
/** True if -AnoLightweightOwnership was passed on the command line. */
117118
private final boolean noLightweightOwnership;
118119

120+
/**
121+
* True if -AenableWpiForRlc (see {@link ResourceLeakChecker#ENABLE_WPI_FOR_RLC}) was passed on
122+
* the command line.
123+
*/
124+
private final boolean enableWpiForRlc;
125+
119126
/**
120127
* Creates a MustCallAnnotatedTypeFactory.
121128
*
@@ -132,6 +139,7 @@ public MustCallAnnotatedTypeFactory(BaseTypeChecker checker) {
132139
addAliasedTypeAnnotation(MustCallAlias.class, POLY);
133140
}
134141
noLightweightOwnership = checker.hasOption(MustCallChecker.NO_LIGHTWEIGHT_OWNERSHIP);
142+
enableWpiForRlc = checker.hasOption(ResourceLeakChecker.ENABLE_WPI_FOR_RLC);
135143
this.postInit();
136144
}
137145

@@ -223,6 +231,13 @@ protected void constructorFromUsePreSubstitution(
223231
*/
224232
private void changeNonOwningParameterTypesToTop(
225233
ExecutableElement declaration, AnnotatedExecutableType type) {
234+
// Formal parameters without a declared owning annotation are disregarded by the RLC _analysis_,
235+
// as their @MustCall obligation is set to Top in this method. However, this computation is not
236+
// desirable for RLC _inference_ in unannotated programs, where a goal is to infer and add
237+
// @Owning annotations to formal parameters.
238+
if (getWholeProgramInference() != null && !isWpiEnabledForRLC()) {
239+
return;
240+
}
226241
List<AnnotatedTypeMirror> parameterTypes = type.getParameterTypes();
227242
for (int i = 0; i < parameterTypes.size(); i++) {
228243
Element paramDecl = declaration.getParameters().get(i);
@@ -416,7 +431,11 @@ public MustCallTreeAnnotator(MustCallAnnotatedTypeFactory mustCallAnnotatedTypeF
416431
@Override
417432
public Void visitIdentifier(IdentifierTree tree, AnnotatedTypeMirror type) {
418433
Element elt = TreeUtils.elementFromUse(tree);
419-
if (elt.getKind() == ElementKind.PARAMETER
434+
// The following changes are not desired for RLC _inference_ in unannotated programs, where a
435+
// goal is to infer and add @Owning annotations to formal parameters. Therefore, if WPI is
436+
// enabled, they should not be executed.
437+
if (getWholeProgramInference() == null
438+
&& elt.getKind() == ElementKind.PARAMETER
420439
&& (noLightweightOwnership || getDeclAnnotation(elt, Owning.class) == null)) {
421440
if (!type.hasPrimaryAnnotation(POLY)) {
422441
// Parameters that are not annotated with @Owning should be treated as bottom
@@ -443,6 +462,16 @@ public Void visitIdentifier(IdentifierTree tree, AnnotatedTypeMirror type) {
443462
return tempVars.get(node.getTree());
444463
}
445464

465+
/**
466+
* Checks if WPI is enabled for the Resource Leak Checker inference. See {@link
467+
* ResourceLeakChecker#ENABLE_WPI_FOR_RLC}.
468+
*
469+
* @return returns true if WPI is enabled for the Resource Leak Checker
470+
*/
471+
protected boolean isWpiEnabledForRLC() {
472+
return enableWpiForRlc;
473+
}
474+
446475
/**
447476
* Returns true if the given type should never have a must-call obligation.
448477
*

checker/src/main/java/org/checkerframework/checker/mustcall/MustCallTransfer.java

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.sun.source.tree.ClassTree;
44
import com.sun.source.tree.ExpressionTree;
55
import com.sun.source.tree.IdentifierTree;
6+
import com.sun.source.tree.Tree;
67
import com.sun.source.tree.VariableTree;
78
import com.sun.source.util.TreePath;
89
import java.util.List;
@@ -14,6 +15,7 @@
1415
import org.checkerframework.checker.mustcall.qual.MustCall;
1516
import org.checkerframework.checker.nullness.qual.MonotonicNonNull;
1617
import org.checkerframework.checker.nullness.qual.Nullable;
18+
import org.checkerframework.checker.resourceleak.ResourceLeakChecker;
1719
import org.checkerframework.dataflow.analysis.TransferInput;
1820
import org.checkerframework.dataflow.analysis.TransferResult;
1921
import org.checkerframework.dataflow.cfg.node.AssignmentNode;
@@ -58,6 +60,12 @@ public class MustCallTransfer extends CFTransfer {
5860
/** True if -AnoCreatesMustCallFor was passed on the command line. */
5961
private final boolean noCreatesMustCallFor;
6062

63+
/**
64+
* True if -AenableWpiForRlc was passed on the command line. See {@link
65+
* ResourceLeakChecker#ENABLE_WPI_FOR_RLC}.
66+
*/
67+
private final boolean enableWpiForRlc;
68+
6169
/**
6270
* Create a MustCallTransfer.
6371
*
@@ -68,6 +76,7 @@ public MustCallTransfer(CFAnalysis analysis) {
6876
atypeFactory = (MustCallAnnotatedTypeFactory) analysis.getTypeFactory();
6977
noCreatesMustCallFor =
7078
atypeFactory.getChecker().hasOption(MustCallChecker.NO_CREATES_MUSTCALLFOR);
79+
enableWpiForRlc = atypeFactory.getChecker().hasOption(ResourceLeakChecker.ENABLE_WPI_FOR_RLC);
7180
ProcessingEnvironment env = atypeFactory.getChecker().getProcessingEnvironment();
7281
treeBuilder = new TreeBuilder(env);
7382
}
@@ -175,6 +184,41 @@ private void lubWithStoreValue(CFStore store, JavaExpression expr, AnnotationMir
175184
store.insertValue(expr, newValue);
176185
}
177186

187+
/**
188+
* See {@link ResourceLeakChecker#ENABLE_WPI_FOR_RLC}.
189+
*
190+
* @param tree a tree
191+
* @return false if Resource Leak Checker is running as one of the upstream checkers and the
192+
* -AenableWpiForRlc flag is not passed as a command line argument, otherwise returns the
193+
* result of the super call
194+
*/
195+
@Override
196+
protected boolean shouldPerformWholeProgramInference(Tree tree) {
197+
if (!isWpiEnabledForRLC()
198+
&& atypeFactory.getCheckerNames().contains(ResourceLeakChecker.class.getCanonicalName())) {
199+
return false;
200+
}
201+
return super.shouldPerformWholeProgramInference(tree);
202+
}
203+
204+
/**
205+
* See {@link ResourceLeakChecker#ENABLE_WPI_FOR_RLC}.
206+
*
207+
* @param expressionTree a tree
208+
* @param lhsTree its element
209+
* @return false if Resource Leak Checker is running as one of the upstream checkers and the
210+
* -AenableWpiForRlc flag is not passed as a command line argument, otherwise returns the
211+
* result of the super call
212+
*/
213+
@Override
214+
protected boolean shouldPerformWholeProgramInference(Tree expressionTree, Tree lhsTree) {
215+
if (!isWpiEnabledForRLC()
216+
&& atypeFactory.getCheckerNames().contains(ResourceLeakChecker.class.getCanonicalName())) {
217+
return false;
218+
}
219+
return super.shouldPerformWholeProgramInference(expressionTree, lhsTree);
220+
}
221+
178222
@Override
179223
public TransferResult<CFValue, CFStore> visitObjectCreation(
180224
ObjectCreationNode node, TransferInput<CFValue, CFStore> input) {
@@ -299,4 +343,14 @@ public void updateStoreWithTempVar(TransferResult<CFValue, CFStore> result, Node
299343
protected String uniqueName(String prefix) {
300344
return prefix + "-" + uid.getAndIncrement();
301345
}
346+
347+
/**
348+
* Checks if WPI is enabled for the Resource Leak Checker inference. See {@link
349+
* ResourceLeakChecker#ENABLE_WPI_FOR_RLC}.
350+
*
351+
* @return returns true if WPI is enabled for the Resource Leak Checker
352+
*/
353+
protected boolean isWpiEnabledForRLC() {
354+
return enableWpiForRlc;
355+
}
302356
}

checker/src/main/java/org/checkerframework/checker/mustcall/MustCallVisitor.java

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import org.checkerframework.checker.mustcall.qual.MustCallAlias;
2424
import org.checkerframework.checker.mustcall.qual.NotOwning;
2525
import org.checkerframework.checker.mustcall.qual.Owning;
26+
import org.checkerframework.checker.mustcall.qual.PolyMustCall;
2627
import org.checkerframework.common.basetype.BaseTypeChecker;
2728
import org.checkerframework.common.basetype.BaseTypeVisitor;
2829
import org.checkerframework.framework.type.AnnotatedTypeMirror;
@@ -232,9 +233,24 @@ public boolean isValidUse(
232233
// Note that isValidUse does not need to consider component types, on which it should be
233234
// called separately.
234235
Element elt = TreeUtils.elementFromTree(tree);
235-
if (elt != null
236-
&& AnnotationUtils.containsSameByClass(elt.getAnnotationMirrors(), MustCallAlias.class)) {
237-
return true;
236+
if (elt != null) {
237+
if (AnnotationUtils.containsSameByClass(elt.getAnnotationMirrors(), MustCallAlias.class)) {
238+
return true;
239+
}
240+
// Need to check the type mirror for ajava-derived annotations and the element itself
241+
// for human-written annotations from the source code. Getting to the ajava file directly
242+
// at this point is impossible, so we approximate "the ajava file has an @MustCallAlias
243+
// annotation" with "there is an @PolyMustCall annotation on the use type, but not in the
244+
// source code". This only works because none of our inference techniques infer @PolyMustCall,
245+
// so if @PolyMustCall is present but wasn't in the source, it must have been derived from
246+
// an @MustCallAlias annotation (which we do infer).
247+
boolean ajavaFileHasMustCallAlias =
248+
useType.hasPrimaryAnnotation(PolyMustCall.class)
249+
&& !AnnotationUtils.containsSameByClass(
250+
elt.getAnnotationMirrors(), PolyMustCall.class);
251+
if (ajavaFileHasMustCallAlias) {
252+
return true;
253+
}
238254
}
239255
return super.isValidUse(declarationType, useType, tree);
240256
}

0 commit comments

Comments
 (0)