Skip to content

Commit 9c542bc

Browse files
authored
[analyzer] performTrivialCopy triggers checkLocation before binding (#129016)
The triggered callbacks for the default copy constructed instance and the instance used for initialization now behave in the same way. The LHS already calls checkBind. To keep this consistent, checkLocation is now triggered accordingly for the RHS. Further details on the previous discussion: https://discourse.llvm.org/t/checklocation-for-implicitcastexpr-of-kind-ck-noop/84729 --------- Authored-by: tobias.gruber <[email protected]>
1 parent cd3d10c commit 9c542bc

File tree

2 files changed

+74
-2
lines changed

2 files changed

+74
-2
lines changed

clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -69,14 +69,20 @@ void ExprEngine::performTrivialCopy(NodeBuilder &Bldr, ExplodedNode *Pred,
6969

7070
assert(ThisRD);
7171
SVal V = Call.getArgSVal(0);
72+
const Expr *VExpr = Call.getArgExpr(0);
7273

7374
// If the value being copied is not unknown, load from its location to get
7475
// an aggregate rvalue.
7576
if (std::optional<Loc> L = V.getAs<Loc>())
7677
V = Pred->getState()->getSVal(*L);
7778
else
7879
assert(V.isUnknownOrUndef());
79-
evalBind(Dst, CallExpr, Pred, ThisVal, V, true);
80+
81+
ExplodedNodeSet Tmp;
82+
evalLocation(Tmp, CallExpr, VExpr, Pred, Pred->getState(), V,
83+
/*isLoad=*/true);
84+
for (ExplodedNode *N : Tmp)
85+
evalBind(Dst, CallExpr, N, ThisVal, V, true);
8086

8187
PostStmt PS(CallExpr, LCtx);
8288
for (ExplodedNode *N : Dst) {
@@ -1199,4 +1205,4 @@ void ExprEngine::VisitLambdaExpr(const LambdaExpr *LE, ExplodedNode *Pred,
11991205

12001206
// FIXME: Move all post/pre visits to ::Visit().
12011207
getCheckerManager().runCheckersForPostStmt(Dst, Tmp, LE, *this);
1202-
}
1208+
}

clang/unittests/StaticAnalyzer/ExprEngineVisitTest.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@
1212
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
1313
#include "clang/StaticAnalyzer/Core/Checker.h"
1414
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
15+
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
16+
#include "llvm/Support/Casting.h"
1517
#include "gtest/gtest.h"
1618

1719
using namespace clang;
@@ -44,6 +46,33 @@ CREATE_EXPR_ENGINE_CHECKER(ExprEngineVisitPreChecker, PreStmt, GCCAsmStmt,
4446
CREATE_EXPR_ENGINE_CHECKER(ExprEngineVisitPostChecker, PostStmt, GCCAsmStmt,
4547
"GCCAsmStmtBug")
4648

49+
class MemAccessChecker : public Checker<check::Location, check::Bind> {
50+
public:
51+
void checkLocation(const SVal &Loc, bool IsLoad, const Stmt *S,
52+
CheckerContext &C) const {
53+
emitErrorReport(C, Bug,
54+
"checkLocation: Loc = " + dumpToString(Loc) +
55+
", Stmt = " + S->getStmtClassName());
56+
}
57+
58+
void checkBind(SVal Loc, SVal Val, const Stmt *S, CheckerContext &C) const {
59+
emitErrorReport(C, Bug,
60+
"checkBind: Loc = " + dumpToString(Loc) +
61+
", Val = " + dumpToString(Val) +
62+
", Stmt = " + S->getStmtClassName());
63+
}
64+
65+
private:
66+
const BugType Bug{this, "MemAccess"};
67+
68+
std::string dumpToString(SVal V) const {
69+
std::string StrBuf;
70+
llvm::raw_string_ostream StrStream{StrBuf};
71+
V.dumpToStream(StrStream);
72+
return StrBuf;
73+
}
74+
};
75+
4776
void addExprEngineVisitPreChecker(AnalysisASTConsumer &AnalysisConsumer,
4877
AnalyzerOptions &AnOpts) {
4978
AnOpts.CheckersAndPackages = {{"ExprEngineVisitPreChecker", true}};
@@ -62,6 +91,15 @@ void addExprEngineVisitPostChecker(AnalysisASTConsumer &AnalysisConsumer,
6291
});
6392
}
6493

94+
void addMemAccessChecker(AnalysisASTConsumer &AnalysisConsumer,
95+
AnalyzerOptions &AnOpts) {
96+
AnOpts.CheckersAndPackages = {{"MemAccessChecker", true}};
97+
AnalysisConsumer.AddCheckerRegistrationFn([](CheckerRegistry &Registry) {
98+
Registry.addChecker<MemAccessChecker>("MemAccessChecker", "Desc",
99+
"DocsURI");
100+
});
101+
}
102+
65103
TEST(ExprEngineVisitTest, checkPreStmtGCCAsmStmt) {
66104
std::string Diags;
67105
EXPECT_TRUE(runCheckerOnCode<addExprEngineVisitPreChecker>(R"(
@@ -84,4 +122,32 @@ TEST(ExprEngineVisitTest, checkPostStmtGCCAsmStmt) {
84122
EXPECT_EQ(Diags, "ExprEngineVisitPostChecker: checkPostStmt<GCCAsmStmt>\n");
85123
}
86124

125+
TEST(ExprEngineVisitTest, checkLocationAndBind) {
126+
std::string Diags;
127+
EXPECT_TRUE(runCheckerOnCode<addMemAccessChecker>(R"(
128+
class MyClass{
129+
public:
130+
int Value;
131+
};
132+
extern MyClass MyClassWrite, MyClassRead;
133+
void top() {
134+
MyClassWrite = MyClassRead;
135+
}
136+
)",
137+
Diags));
138+
139+
std::string LocMsg = "checkLocation: Loc = lazyCompoundVal{0x0,MyClassRead}, "
140+
"Stmt = ImplicitCastExpr";
141+
std::string BindMsg =
142+
"checkBind: Loc = &MyClassWrite, Val = lazyCompoundVal{0x0,MyClassRead}, "
143+
"Stmt = CXXOperatorCallExpr";
144+
std::size_t LocPos = Diags.find(LocMsg);
145+
std::size_t BindPos = Diags.find(BindMsg);
146+
EXPECT_NE(LocPos, std::string::npos);
147+
EXPECT_NE(BindPos, std::string::npos);
148+
// Check order: first checkLocation is called, then checkBind.
149+
// In the diagnosis, however, the messages appear in reverse order.
150+
EXPECT_TRUE(LocPos > BindPos);
151+
}
152+
87153
} // namespace

0 commit comments

Comments
 (0)