-
Notifications
You must be signed in to change notification settings - Fork 331
Incorrect Cache invalidation in Nullaway 0.12.12 #1351
Description
I believe #1328 introduces a bug because it doesn't keep its caches in sync.
In an internal repo, we were getting the following error messages when running NullAway 0.12.12:
BugPattern: NullAway
Stack Trace:
java.lang.NullPointerException: Cannot invoke "org.checkerframework.nullaway.dataflow.cfg.ControlFlowGraph.getTreeLookup()" because "this.cfg" is null
at org.checkerframework.nullaway.dataflow.analysis.AbstractAnalysis.getResult(AbstractAnalysis.java:180)
at com.uber.nullaway.dataflow.DataFlow.resultFor(DataFlow.java:318)
at com.uber.nullaway.dataflow.DataFlow.resultForExpr(DataFlow.java:296)
at com.uber.nullaway.dataflow.DataFlow.expressionDataflow(DataFlow.java:241)
at com.uber.nullaway.dataflow.AccessPathNullnessAnalysis.getNullness(AccessPathNullnessAnalysis.java:115)
at com.uber.nullaway.NullAway.nullnessFromDataflow(NullAway.java:2746)
at com.uber.nullaway.NullAway.mayBeNullExpr(NullAway.java:2712)
at com.uber.nullaway.NullAway.lambda$handleInvocation$1(NullAway.java:2047)
at com.uber.nullaway.InvocationArguments.forEach(InvocationArguments.java:86)
at com.uber.nullaway.NullAway.handleInvocation(NullAway.java:2014)
at com.uber.nullaway.NullAway.matchMethodInvocation(NullAway.java:444)
at com.google.errorprone.scanner.ErrorProneScanner.processMatchers(ErrorProneScanner.java:509)
...
In the following block:
ControlFlowGraph cfg = cfgCache.getUnchecked(CfgParams.create(path, env));
AnalysisParams aparams = AnalysisParams.create(transfer, cfg);
@SuppressWarnings("unchecked")
Analysis<A, S, T> analysis = (Analysis<A, S, T>) analysisCache.getUnchecked(aparams);
if (performAnalysis && !alreadyRunAnalyses.contains(aparams)) {
analysis.performAnalysis(cfg);
alreadyRunAnalyses.add(aparams);
}What I think happens is that:
Dataflow#analysisCachewill evict an analysis, but it won't clear it fromalreadyRunAnalyses- When we see the same path again, we will create new analyses, but because the
aparamscompleted before at some point, the if condition will fail as it already contains the particularaparams. - It will therefore not run
analysis.performAnalysis(cfg)which initialises the fields we care aboutcfg - The analysis you get back will be malformed and you will get the
NullPointerException.
The fix is probably along the lines of adding a removal listener to analysisCache that will also remove the key from aparams, keeping the two caches in sync.
What was a bit confusing to me was that it was non-deterministic in that some builds would pass, others would fail. However, when it failed, it would consistently fail at the same point. So perhaps it's some sort of ordering issue when Errorprone/Nullaway is processing the files. This has made it a bit difficult to get a reliable reproduction, but I believe the above steps is the only way to get a malformed Analysis object back.
I'm new to the NullAway repository, so I'm not sure about all the different lifecycles, but how I think this manifests is when you have a
class with lots of different method invocations, but you return to one previously handled