1616import java .util .Collections ;
1717import java .util .Deque ;
1818import java .util .EnumSet ;
19+ import java .util .HashMap ;
1920import java .util .HashSet ;
2021import java .util .IdentityHashMap ;
2122import java .util .Iterator ;
7475import org .checkerframework .framework .flow .CFStore ;
7576import org .checkerframework .framework .flow .CFValue ;
7677import org .checkerframework .framework .type .AnnotatedTypeMirror ;
77- import org .checkerframework .framework .type .QualifierHierarchy ;
7878import org .checkerframework .framework .util .JavaExpressionParseUtil .JavaExpressionParseException ;
7979import org .checkerframework .framework .util .StringToJavaExpression ;
8080import org .checkerframework .javacutil .AnnotationUtils ;
@@ -319,35 +319,34 @@ public boolean derivedFromMustCallAlias() {
319319
320320 /**
321321 * Gets the must-call methods (i.e. the list of methods that must be called to satisfy the
322- * must-call obligation) of the resource represented by this Obligation.
322+ * must-call obligation) of each resource alias represented by this Obligation.
323323 *
324324 * @param rlAtf a Resource Leak Annotated Type Factory
325325 * @param mcStore a CFStore produced by the MustCall checker's dataflow analysis. If this is
326326 * null, then the default MustCall type of each variable's class will be used.
327- * @return the list of must-call method names, or null if the resource's must-call obligations
328- * are unsatisfiable (i.e. its value in the Must Call store is MustCallUnknown)
327+ * @return a map from each resource alias of this to a list of its must-call method names, or
328+ * null if the must-call obligations are unsatisfiable (i.e. the value of some tracked
329+ * resource alias of this in the Must Call store is MustCallUnknown)
329330 */
330- public @ Nullable List <String > getMustCallMethods (
331+ public @ Nullable Map < ResourceAlias , List <String > > getMustCallMethods (
331332 ResourceLeakAnnotatedTypeFactory rlAtf , @ Nullable CFStore mcStore ) {
333+ Map <ResourceAlias , List <String >> result = new HashMap <>(this .resourceAliases .size ());
332334 MustCallAnnotatedTypeFactory mustCallAnnotatedTypeFactory =
333335 rlAtf .getTypeFactoryOfSubchecker (MustCallChecker .class );
334336
335- // Need to get the LUB (ie, union) of the MC values, because if a CreatesMustCallFor
336- // method was called on just one of the aliases then they all need to be treated as if
337- // they need to call the relevant methods.
338- QualifierHierarchy qualHierarchy = mustCallAnnotatedTypeFactory .getQualifierHierarchy ();
339- AnnotationMirror mcLub = mustCallAnnotatedTypeFactory .BOTTOM ;
340337 for (ResourceAlias alias : this .resourceAliases ) {
341338 AnnotationMirror mcAnno = getMustCallValue (alias , mcStore , mustCallAnnotatedTypeFactory );
342- TypeMirror aliasTm = alias .reference .getType ();
343- mcLub = qualHierarchy .leastUpperBoundShallow (mcLub , aliasTm , mcAnno , aliasTm );
344- }
345- if (AnnotationUtils .areSameByName (
346- mcLub , "org.checkerframework.checker.mustcall.qual.MustCall" )) {
347- return rlAtf .getMustCallValues (mcLub );
348- } else {
349- return null ;
339+ if (!AnnotationUtils .areSameByName (mcAnno , MustCall .class .getCanonicalName ())) {
340+ // MustCallUnknown; cannot be satisfied
341+ return null ;
342+ }
343+ List <String > annoVals = rlAtf .getMustCallValues (mcAnno );
344+ // Really, annoVals should never be empty here; we should not have created the obligation in
345+ // the first place
346+ // TODO: add an assertion that annoVals is non-empty and address any failures
347+ result .put (alias , annoVals );
350348 }
349+ return result ;
351350 }
352351
353352 /**
@@ -2324,11 +2323,12 @@ private static boolean varTrackedInObligations(
23242323 private void checkMustCall (
23252324 Obligation obligation , AccumulationStore cmStore , CFStore mcStore , String outOfScopeReason ) {
23262325
2327- List <String > mustCallValue = obligation .getMustCallMethods (typeFactory , mcStore );
2326+ Map <ResourceAlias , List <String >> mustCallValues =
2327+ obligation .getMustCallMethods (typeFactory , mcStore );
23282328
2329- // optimization: if mustCallValue is null, always issue a warning (there is no way to satisfy
2329+ // optimization: if mustCallValues is null, always issue a warning (there is no way to satisfy
23302330 // the check). A null mustCallValue occurs when the type is top (@MustCallUnknown).
2331- if (mustCallValue == null ) {
2331+ if (mustCallValues == null ) {
23322332 // Report the error at the first alias' definition. This choice is arbitrary but
23332333 // consistent.
23342334 ResourceAlias firstAlias = obligation .resourceAliases .iterator ().next ();
@@ -2345,14 +2345,20 @@ private void checkMustCall(
23452345 }
23462346 return ;
23472347 }
2348- // optimization: if there are no must-call methods, do not need to perform the check
2349- if (mustCallValue .isEmpty ()) {
2350- return ;
2348+ if (mustCallValues .isEmpty ()) {
2349+ throw new TypeSystemError ("unexpected empty must-call values for obligation " + obligation );
23512350 }
23522351
23532352 boolean mustCallSatisfied = false ;
23542353 for (ResourceAlias alias : obligation .resourceAliases ) {
23552354
2355+ List <String > mustCallValuesForAlias = mustCallValues .get (alias );
2356+ // optimization when there are no methods to call
2357+ if (mustCallValuesForAlias .isEmpty ()) {
2358+ mustCallSatisfied = true ;
2359+ break ;
2360+ }
2361+
23562362 // sometimes the store is null! this looks like a bug in checker dataflow.
23572363 // TODO track down and report the root-cause bug
23582364 AccumulationValue cmValue = cmStore != null ? cmStore .getValue (alias .reference ) : null ;
@@ -2378,7 +2384,7 @@ private void checkMustCall(
23782384 .getEffectiveAnnotationInHierarchy (typeFactory .top );
23792385 }
23802386
2381- if (calledMethodsSatisfyMustCall (mustCallValue , cmAnno )) {
2387+ if (calledMethodsSatisfyMustCall (mustCallValuesForAlias , cmAnno )) {
23822388 mustCallSatisfied = true ;
23832389 break ;
23842390 }
@@ -2394,7 +2400,7 @@ private void checkMustCall(
23942400 checker .reportError (
23952401 firstAlias .tree ,
23962402 "required.method.not.called" ,
2397- formatMissingMustCallMethods (mustCallValue ),
2403+ formatMissingMustCallMethods (mustCallValues . get ( firstAlias ) ),
23982404 firstAlias .stringForErrorMessage (),
23992405 firstAlias .reference .getType ().toString (),
24002406 outOfScopeReason );
0 commit comments