Author: DonĂ¡t Nagy
Date: 2026-08-27T19:56:17+02:00
New Revision: 11d5d799507f704d76df795b53837755290cc794

URL: 
https://github.com/llvm/llvm-project/commit/11d5d799507f704d76df795b53837755290cc794
DIFF: 
https://github.com/llvm/llvm-project/commit/11d5d799507f704d76df795b53837755290cc794.diff

LOG: [analyzer] Do not attempt transition from sink node (#218970)

The method `CheckerContext::addSink`, which was added in 2020 by commit
1c8f999e0b59731a4214f76528f83e4196e1fcc3 contained the following code:
```
addTransition(State, generateSink(State, getPredecessor()))
```
At first glance this looks as if this generates a sink then transitions
TO that sink node; but in fact `generateSink` already adds the freshly
generated sink node to the graph, and then `addTransition` tries to add
a transition FROM the sink to a freshly generated non-sink node.

Note that `ExplodedNode::addPredecessor` (which is called by `makeNode`
to connect the freshly created node to its parent) asserts that the
parent node is not a sink -- but this assertion is narrowly dodged
because the early return at the beginning of
`CheckerContext::addTransitionImpl` is practically always [1] triggered
by the `addTransition` call in `addSink`. Due to this early return
`makeNode` is never reached and `addTransition` does nothing.

This commit removes the pointless and logically unjustified
`addTransition` call from `addSink`. This probably won't have any
functional impact, but could in theory prevent crashes in very rare
corner cases e.g. related to `PosteriorlyOverconstrained` states.

After this change it would be important to clean up the `CheckerContext`
interface, where currently multiple methods fulfill the same role (e.g.
`generateSink`, `addSink` and `generateErrorNode` are all redundant) and
the names of `addTransition` vs `generateSink` do not reflect their
relationship.

------

[1] The early return in `addTransitionImpl` is triggered if (1) the
state is equal to `CheckerContext::getState()`, (2) `Tag` is null and
(3) `MarkAsSink` is false (which always holds in the `addTransition`
call). Callers of `addSink` practically always pass a state that is
equal to the initial state (with the possible exception of
`PosteriorlyOverconstrained` states and one corner case in
CStringChecker.cpp which is probably never reached) and never specify a
non-null `Tag`, so these happen to be true in practically all calls of
`addSink`.

Added: 
    

Modified: 
    clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h

Removed: 
    


################################################################################
diff  --git 
a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h 
b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
index 6463bad3de6ab..331a163be443f 100644
--- a/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
+++ b/clang/include/clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h
@@ -220,7 +220,7 @@ class CheckerContext {
                const ProgramPointTag *Tag = nullptr) {
     if (!State)
       State = getState();
-    addTransition(State, generateSink(State, getPredecessor()));
+    generateSink(State, getPredecessor());
   }
 
   /// Generate a transition to a node that will be used to report


        
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to