Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[analyzer] Avoid out-of-order node traversal on void return #117863

Merged
merged 5 commits into from
Nov 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 23 additions & 9 deletions clang/lib/StaticAnalyzer/Core/BugReporter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2416,6 +2416,28 @@ PathSensitiveBugReport::getRanges() const {
return Ranges;
}

static bool exitingDestructor(const ExplodedNode *N) {
// Need to loop here, as some times the Error node is already outside of the
// destructor context, and the previous node is an edge that is also outside.
while (N && !N->getLocation().getAs<StmtPoint>()) {
N = N->getFirstPred();
}
return N && isa<CXXDestructorDecl>(N->getLocationContext()->getDecl());
}

static const Stmt *
findReasonableStmtCloseToFunctionExit(const ExplodedNode *N) {
if (exitingDestructor(N)) {
// If we are exiting a destructor call, it is more useful to point to
// the next stmt which is usually the temporary declaration.
if (const Stmt *S = N->getNextStmtForDiagnostics())
return S;
// If next stmt is not found, it is likely the end of a top-level
// function analysis. find the last execution statement then.
}
return N->getPreviousStmtForDiagnostics();
}

PathDiagnosticLocation
PathSensitiveBugReport::getLocation() const {
assert(ErrorNode && "Cannot create a location with a null node.");
Expand All @@ -2433,15 +2455,7 @@ PathSensitiveBugReport::getLocation() const {
if (const ReturnStmt *RS = FE->getStmt())
return PathDiagnosticLocation::createBegin(RS, SM, LC);

// If we are exiting a destructor call, it is more useful to point to the
// next stmt which is usually the temporary declaration.
// For non-destructor and non-top-level calls, the next stmt will still
// refer to the last executed stmt of the body.
S = ErrorNode->getNextStmtForDiagnostics();
// If next stmt is not found, it is likely the end of a top-level function
// analysis. find the last execution statement then.
if (!S)
S = ErrorNode->getPreviousStmtForDiagnostics();
S = findReasonableStmtCloseToFunctionExit(ErrorNode);
}
if (!S)
S = ErrorNode->getNextStmtForDiagnostics();
Expand Down
2 changes: 2 additions & 0 deletions clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,6 +349,8 @@ const Stmt *ExplodedNode::getStmtForDiagnostics() const {

const Stmt *ExplodedNode::getNextStmtForDiagnostics() const {
for (const ExplodedNode *N = getFirstSucc(); N; N = N->getFirstSucc()) {
if (N->getLocation().isPurgeKind())
continue;
if (const Stmt *S = N->getStmtForDiagnostics()) {
// Check if the statement is '?' or '&&'/'||'. These are "merges",
// not actual statement points.
Expand Down
9 changes: 7 additions & 2 deletions clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -353,14 +353,19 @@ void ExprEngine::processCallExit(ExplodedNode *CEBNode) {
ExplodedNodeSet CleanedNodes;
if (LastSt && Blk && AMgr.options.AnalysisPurgeOpt != PurgeNone) {
static SimpleProgramPointTag retValBind("ExprEngine", "Bind Return Value");
PostStmt Loc(LastSt, calleeCtx, &retValBind);
auto Loc = isa<ReturnStmt>(LastSt)
? ProgramPoint{PostStmt(LastSt, calleeCtx, &retValBind)}
: ProgramPoint{EpsilonPoint(calleeCtx, /*Data1=*/nullptr,
/*Data2=*/nullptr, &retValBind)};
const CFGBlock *PrePurgeBlock =
isa<ReturnStmt>(LastSt) ? Blk : &CEBNode->getCFG().getExit();
bool isNew;
ExplodedNode *BindedRetNode = G.getNode(Loc, state, false, &isNew);
BindedRetNode->addPredecessor(CEBNode, G);
if (!isNew)
return;

NodeBuilderContext Ctx(getCoreEngine(), Blk, BindedRetNode);
NodeBuilderContext Ctx(getCoreEngine(), PrePurgeBlock, BindedRetNode);
currBldrCtx = &Ctx;
// Here, we call the Symbol Reaper with 0 statement and callee location
// context, telling it to clean up everything in the callee's context
Expand Down
10 changes: 5 additions & 5 deletions clang/test/Analysis/copy-elision.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,17 +263,17 @@ void testVariable() {

struct TestCtorInitializer {
ClassWithDestructor c;
TestCtorInitializer(AddressVector<ClassWithDestructor> &v)
: c(ClassWithDestructor(v)) {}
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
to by the caller variable 'v' upon returning to the caller}}
TestCtorInitializer(AddressVector<ClassWithDestructor> &refParam)
: c(ClassWithDestructor(refParam)) {}
};

void testCtorInitializer() {
AddressVector<ClassWithDestructor> v;
{
TestCtorInitializer t(v);
// no-elide-warning@-1 {{Address of stack memory associated with temporary \
object of type 'ClassWithDestructor' is still referred \
to by the caller variable 'v' upon returning to the caller}}
// Check if the last destructor is an automatic destructor.
// A temporary destructor would have fired by now.
#if ELIDE
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -163,8 +163,8 @@ class UnguardedFieldThroughMethodTest {
Volume = 0;
break;
case A:
Area = 0; // expected-warning {{1 uninitialized field}}
break;
Area = 0;
break; // expected-warning {{1 uninitialized field}}
}
}

Expand Down Expand Up @@ -201,8 +201,8 @@ class UnguardedPublicFieldsTest {
Volume = 0;
break;
case A:
Area = 0; // expected-warning {{1 uninitialized field}}
break;
Area = 0;
break; // expected-warning {{1 uninitialized field}}
}
}

Expand Down
26 changes: 26 additions & 0 deletions clang/test/Analysis/void-call-exit-modelling.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// RUN: %clang_analyze_cc1 -analyzer-checker=core,unix.Malloc -analyzer-output text -verify %s

typedef __typeof(sizeof(int)) size_t;
void *malloc(size_t size);

void inf_loop_break_callee() {
void* data = malloc(10); // expected-note{{Memory is allocated}}
while (1) { // expected-note{{Loop condition is true}}
(void)data;
break; // No note that we jump to the line above from this break
} // expected-note@-1{{Execution jumps to the end of the function}}
} // expected-warning{{Potential leak of memory pointed to by 'data'}}
// expected-note@-1 {{Potential leak of memory pointed to by 'data'}}

void inf_loop_break_caller() {
inf_loop_break_callee(); // expected-note{{Calling 'inf_loop_break_callee'}}
}

void inf_loop_break_top() {
void* data = malloc(10); // expected-note{{Memory is allocated}}
while (1) { // expected-note{{Loop condition is true}}
(void)data;
break; // No note that we jump to the line above from this break
} // expected-note@-1{{Execution jumps to the end of the function}}
} // expected-warning{{Potential leak of memory pointed to by 'data'}}
// expected-note@-1 {{Potential leak of memory pointed to by 'data'}}