Skip to content

Commit 895242f

Browse files
committed
[analyzer] Provide .def-files and visitors for SVal/SymExpr/MemRegion, v2.
Provide separate visitor templates for the three hierarchies, and also the `FullSValVisitor' class, which is a union of all three visitors. Additionally, add a particular example visitor, `SValExplainer', in order to test the visitor templates. This visitor is capable of explaining the SVal, SymExpr, or MemRegion in a natural language. Compared to the reverted r257605, this fixes the test that used to fail on some triples, and fixes build failure under -fmodules. Differential Revision: http://reviews.llvm.org/D15448 llvm-svn: 257893
1 parent f6cc7e6 commit 895242f

File tree

12 files changed

+809
-100
lines changed

12 files changed

+809
-100
lines changed

clang/docs/analyzer/DebugChecks.rst

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,41 @@ ExprInspection checks
162162
} while(0); // expected-warning{{SYMBOL DEAD}}
163163

164164

165+
- void clang_analyzer_explain(a single argument of any type);
166+
167+
This function explains the value of its argument in a human-readable manner
168+
in the warning message. You can make as many overrides of its prototype
169+
in the test code as necessary to explain various integral, pointer,
170+
or even record-type values.
171+
172+
Example usage::
173+
174+
void clang_analyzer_explain(int);
175+
void clang_analyzer_explain(void *);
176+
177+
void foo(int param, void *ptr) {
178+
clang_analyzer_explain(param); // expected-warning{{argument 'param'}}
179+
if (!ptr)
180+
clang_analyzer_explain(ptr); // expected-warning{{memory address '0'}}
181+
}
182+
183+
- size_t clang_analyzer_getExtent(void *);
184+
185+
This function returns the value that represents the extent of a memory region
186+
pointed to by the argument. This value is often difficult to obtain otherwise,
187+
because no valid code that produces this value. However, it may be useful
188+
for testing purposes, to see how well does the analyzer model region extents.
189+
190+
Example usage::
191+
192+
void foo() {
193+
int x, *y;
194+
size_t xs = clang_analyzer_getExtent(&x);
195+
clang_analyzer_explain(xs); // expected-warning{{'4'}}
196+
size_t ys = clang_analyzer_getExtent(&y);
197+
clang_analyzer_explain(ys); // expected-warning{{'8'}}
198+
}
199+
165200
Statistics
166201
==========
167202

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
//== SValExplainer.h - Symbolic value explainer -----------------*- C++ -*--==//
2+
//
3+
// The LLVM Compiler Infrastructure
4+
//
5+
// This file is distributed under the University of Illinois Open Source
6+
// License. See LICENSE.TXT for details.
7+
//
8+
//===----------------------------------------------------------------------===//
9+
//
10+
// This file defines SValExplainer, a class for pretty-printing a
11+
// human-readable description of a symbolic value. For example,
12+
// "reg_$0<x>" is turned into "initial value of variable 'x'".
13+
//
14+
//===----------------------------------------------------------------------===//
15+
16+
#ifndef LLVM_CLANG_STATICANALYZER_CHECKERS_SVALEXPLAINER_H
17+
#define LLVM_CLANG_STATICANALYZER_CHECKERS_SVALEXPLAINER_H
18+
19+
#include "clang/AST/DeclCXX.h"
20+
#include "clang/StaticAnalyzer/Core/PathSensitive/SValVisitor.h"
21+
22+
namespace clang {
23+
24+
namespace ento {
25+
26+
class SValExplainer : public FullSValVisitor<SValExplainer, std::string> {
27+
private:
28+
ASTContext &ACtx;
29+
30+
std::string printStmt(const Stmt *S) {
31+
std::string Str;
32+
llvm::raw_string_ostream OS(Str);
33+
S->printPretty(OS, nullptr, PrintingPolicy(ACtx.getLangOpts()));
34+
return OS.str();
35+
}
36+
37+
bool isThisObject(const SymbolicRegion *R) {
38+
if (auto S = dyn_cast<SymbolRegionValue>(R->getSymbol()))
39+
if (isa<CXXThisRegion>(S->getRegion()))
40+
return true;
41+
return false;
42+
}
43+
44+
public:
45+
SValExplainer(ASTContext &Ctx) : ACtx(Ctx) {}
46+
47+
std::string VisitUnknownVal(UnknownVal V) {
48+
return "unknown value";
49+
}
50+
51+
std::string VisitUndefinedVal(UndefinedVal V) {
52+
return "undefined value";
53+
}
54+
55+
std::string VisitLocMemRegionVal(loc::MemRegionVal V) {
56+
const MemRegion *R = V.getRegion();
57+
// Avoid the weird "pointer to pointee of ...".
58+
if (auto SR = dyn_cast<SymbolicRegion>(R)) {
59+
// However, "pointer to 'this' object" is fine.
60+
if (!isThisObject(SR))
61+
return Visit(SR->getSymbol());
62+
}
63+
return "pointer to " + Visit(R);
64+
}
65+
66+
std::string VisitLocConcreteInt(loc::ConcreteInt V) {
67+
llvm::APSInt I = V.getValue();
68+
std::string Str;
69+
llvm::raw_string_ostream OS(Str);
70+
OS << "concrete memory address '" << I << "'";
71+
return OS.str();
72+
}
73+
74+
std::string VisitNonLocSymbolVal(nonloc::SymbolVal V) {
75+
return Visit(V.getSymbol());
76+
}
77+
78+
std::string VisitNonLocConcreteInt(nonloc::ConcreteInt V) {
79+
llvm::APSInt I = V.getValue();
80+
std::string Str;
81+
llvm::raw_string_ostream OS(Str);
82+
OS << (I.isSigned() ? "signed " : "unsigned ") << I.getBitWidth()
83+
<< "-bit integer '" << I << "'";
84+
return OS.str();
85+
}
86+
87+
std::string VisitNonLocLazyCompoundVal(nonloc::LazyCompoundVal V) {
88+
return "lazily frozen compound value of " + Visit(V.getRegion());
89+
}
90+
91+
std::string VisitSymbolRegionValue(const SymbolRegionValue *S) {
92+
const MemRegion *R = S->getRegion();
93+
// Special handling for argument values.
94+
if (auto V = dyn_cast<VarRegion>(R))
95+
if (auto D = dyn_cast<ParmVarDecl>(V->getDecl()))
96+
return "argument '" + D->getQualifiedNameAsString() + "'";
97+
return "initial value of " + Visit(R);
98+
}
99+
100+
std::string VisitSymbolConjured(const SymbolConjured *S) {
101+
return "symbol of type '" + S->getType().getAsString() +
102+
"' conjured at statement '" + printStmt(S->getStmt()) + "'";
103+
}
104+
105+
std::string VisitSymbolDerived(const SymbolDerived *S) {
106+
return "value derived from (" + Visit(S->getParentSymbol()) +
107+
") for " + Visit(S->getRegion());
108+
}
109+
110+
std::string VisitSymbolExtent(const SymbolExtent *S) {
111+
return "extent of " + Visit(S->getRegion());
112+
}
113+
114+
std::string VisitSymbolMetadata(const SymbolMetadata *S) {
115+
return "metadata of type '" + S->getType().getAsString() + "' tied to " +
116+
Visit(S->getRegion());
117+
}
118+
119+
std::string VisitSymIntExpr(const SymIntExpr *S) {
120+
std::string Str;
121+
llvm::raw_string_ostream OS(Str);
122+
OS << "(" << Visit(S->getLHS()) << ") "
123+
<< std::string(BinaryOperator::getOpcodeStr(S->getOpcode())) << " "
124+
<< S->getRHS();
125+
return OS.str();
126+
}
127+
128+
// TODO: IntSymExpr doesn't appear in practice.
129+
// Add the relevant code once it does.
130+
131+
std::string VisitSymSymExpr(const SymSymExpr *S) {
132+
return "(" + Visit(S->getLHS()) + ") " +
133+
std::string(BinaryOperator::getOpcodeStr(S->getOpcode())) +
134+
" (" + Visit(S->getRHS()) + ")";
135+
}
136+
137+
// TODO: SymbolCast doesn't appear in practice.
138+
// Add the relevant code once it does.
139+
140+
std::string VisitSymbolicRegion(const SymbolicRegion *R) {
141+
// Explain 'this' object here.
142+
// TODO: Explain CXXThisRegion itself, find a way to test it.
143+
if (isThisObject(R))
144+
return "'this' object";
145+
return "pointee of " + Visit(R->getSymbol());
146+
}
147+
148+
std::string VisitAllocaRegion(const AllocaRegion *R) {
149+
return "region allocated by '" + printStmt(R->getExpr()) + "'";
150+
}
151+
152+
std::string VisitCompoundLiteralRegion(const CompoundLiteralRegion *R) {
153+
return "compound literal " + printStmt(R->getLiteralExpr());
154+
}
155+
156+
std::string VisitStringRegion(const StringRegion *R) {
157+
return "string literal " + R->getString();
158+
}
159+
160+
std::string VisitElementRegion(const ElementRegion *R) {
161+
std::string Str;
162+
llvm::raw_string_ostream OS(Str);
163+
OS << "element of type '" << R->getElementType().getAsString()
164+
<< "' with index ";
165+
// For concrete index: omit type of the index integer.
166+
if (auto I = R->getIndex().getAs<nonloc::ConcreteInt>())
167+
OS << I->getValue();
168+
else
169+
OS << "'" << Visit(R->getIndex()) << "'";
170+
OS << " of " + Visit(R->getSuperRegion());
171+
return OS.str();
172+
}
173+
174+
std::string VisitVarRegion(const VarRegion *R) {
175+
const VarDecl *VD = R->getDecl();
176+
std::string Name = VD->getQualifiedNameAsString();
177+
if (isa<ParmVarDecl>(VD))
178+
return "parameter '" + Name + "'";
179+
else if (VD->hasLocalStorage())
180+
return "local variable '" + Name + "'";
181+
else if (VD->isStaticLocal())
182+
return "static local variable '" + Name + "'";
183+
else if (VD->hasGlobalStorage())
184+
return "global variable '" + Name + "'";
185+
else
186+
llvm_unreachable("A variable is either local or global");
187+
}
188+
189+
std::string VisitFieldRegion(const FieldRegion *R) {
190+
return "field '" + R->getDecl()->getNameAsString() + "' of " +
191+
Visit(R->getSuperRegion());
192+
}
193+
194+
std::string VisitCXXTempObjectRegion(const CXXTempObjectRegion *R) {
195+
return "temporary object constructed at statement '" +
196+
printStmt(R->getExpr()) + "'";
197+
}
198+
199+
std::string VisitCXXBaseObjectRegion(const CXXBaseObjectRegion *R) {
200+
return "base object '" + R->getDecl()->getQualifiedNameAsString() +
201+
"' inside " + Visit(R->getSuperRegion());
202+
}
203+
204+
std::string VisitSVal(SVal V) {
205+
std::string Str;
206+
llvm::raw_string_ostream OS(Str);
207+
OS << V;
208+
return "a value unsupported by the explainer: (" +
209+
std::string(OS.str()) + ")";
210+
}
211+
212+
std::string VisitSymExpr(SymbolRef S) {
213+
std::string Str;
214+
llvm::raw_string_ostream OS(Str);
215+
S->dumpToStream(OS);
216+
return "a symbolic expression unsupported by the explainer: (" +
217+
std::string(OS.str()) + ")";
218+
}
219+
220+
std::string VisitMemRegion(const MemRegion *R) {
221+
std::string Str;
222+
llvm::raw_string_ostream OS(Str);
223+
OS << R;
224+
return "a memory region unsupported by the explainer (" +
225+
std::string(OS.str()) + ")";
226+
}
227+
};
228+
229+
} // end namespace ento
230+
231+
} // end namespace clang
232+
233+
#endif

clang/include/clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h

Lines changed: 6 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -76,51 +76,13 @@ class RegionOffset {
7676

7777
/// MemRegion - The root abstract class for all memory regions.
7878
class MemRegion : public llvm::FoldingSetNode {
79-
friend class MemRegionManager;
8079
public:
8180
enum Kind {
82-
// Memory spaces.
83-
CodeSpaceRegionKind,
84-
StackLocalsSpaceRegionKind,
85-
StackArgumentsSpaceRegionKind,
86-
HeapSpaceRegionKind,
87-
UnknownSpaceRegionKind,
88-
StaticGlobalSpaceRegionKind,
89-
GlobalInternalSpaceRegionKind,
90-
GlobalSystemSpaceRegionKind,
91-
GlobalImmutableSpaceRegionKind,
92-
BEGIN_NON_STATIC_GLOBAL_MEMSPACES = GlobalInternalSpaceRegionKind,
93-
END_NON_STATIC_GLOBAL_MEMSPACES = GlobalImmutableSpaceRegionKind,
94-
BEGIN_GLOBAL_MEMSPACES = StaticGlobalSpaceRegionKind,
95-
END_GLOBAL_MEMSPACES = GlobalImmutableSpaceRegionKind,
96-
BEGIN_MEMSPACES = CodeSpaceRegionKind,
97-
END_MEMSPACES = GlobalImmutableSpaceRegionKind,
98-
// Untyped regions.
99-
SymbolicRegionKind,
100-
AllocaRegionKind,
101-
// Typed regions.
102-
BEGIN_TYPED_REGIONS,
103-
FunctionCodeRegionKind = BEGIN_TYPED_REGIONS,
104-
BlockCodeRegionKind,
105-
BlockDataRegionKind,
106-
BEGIN_TYPED_VALUE_REGIONS,
107-
CompoundLiteralRegionKind = BEGIN_TYPED_VALUE_REGIONS,
108-
CXXThisRegionKind,
109-
StringRegionKind,
110-
ObjCStringRegionKind,
111-
ElementRegionKind,
112-
// Decl Regions.
113-
BEGIN_DECL_REGIONS,
114-
VarRegionKind = BEGIN_DECL_REGIONS,
115-
FieldRegionKind,
116-
ObjCIvarRegionKind,
117-
END_DECL_REGIONS = ObjCIvarRegionKind,
118-
CXXTempObjectRegionKind,
119-
CXXBaseObjectRegionKind,
120-
END_TYPED_VALUE_REGIONS = CXXBaseObjectRegionKind,
121-
END_TYPED_REGIONS = CXXBaseObjectRegionKind
81+
#define REGION(Id, Parent) Id ## Kind,
82+
#define REGION_RANGE(Id, First, Last) BEGIN_##Id = First, END_##Id = Last,
83+
#include "clang/StaticAnalyzer/Core/PathSensitive/Regions.def"
12284
};
123-
85+
12486
private:
12587
const Kind kind;
12688

@@ -386,8 +348,7 @@ class StackSpaceRegion : public MemSpaceRegion {
386348

387349
static bool classof(const MemRegion *R) {
388350
Kind k = R->getKind();
389-
return k >= StackLocalsSpaceRegionKind &&
390-
k <= StackArgumentsSpaceRegionKind;
351+
return k >= BEGIN_STACK_MEMSPACES && k <= END_STACK_MEMSPACES;
391352
}
392353
};
393354

@@ -549,7 +510,7 @@ class CodeTextRegion : public TypedRegion {
549510

550511
static bool classof(const MemRegion* R) {
551512
Kind k = R->getKind();
552-
return k >= FunctionCodeRegionKind && k <= BlockCodeRegionKind;
513+
return k >= BEGIN_CODE_TEXT_REGIONS && k <= END_CODE_TEXT_REGIONS;
553514
}
554515
};
555516

0 commit comments

Comments
 (0)