Skip to content

Commit 46929df

Browse files
committed
[analyzer] Prune calls to functions with linear CFGs that return a non-zero constrained value
During the evaluation of D62883, I noticed a bunch of totally meaningless notes with the pattern of "Calling 'A'" -> "Returning value" -> "Returning from 'A'", which added no value to the report at all. This patch (not only affecting tracked conditions mind you) prunes diagnostic messages to functions that return a value not constrained to be 0, and are also linear. Differential Revision: https://reviews.llvm.org/D64232 llvm-svn: 368771
1 parent b5eb3e1 commit 46929df

File tree

4 files changed

+160
-49
lines changed

4 files changed

+160
-49
lines changed

clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp

Lines changed: 34 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ namespace {
841841
/// This visitor is intended to be used when another visitor discovers that an
842842
/// interesting value comes from an inlined function call.
843843
class ReturnVisitor : public BugReporterVisitor {
844-
const StackFrameContext *StackFrame;
844+
const StackFrameContext *CalleeSFC;
845845
enum {
846846
Initial,
847847
MaybeUnsuppress,
@@ -853,10 +853,9 @@ class ReturnVisitor : public BugReporterVisitor {
853853
AnalyzerOptions& Options;
854854

855855
public:
856-
ReturnVisitor(const StackFrameContext *Frame,
857-
bool Suppressed,
856+
ReturnVisitor(const StackFrameContext *Frame, bool Suppressed,
858857
AnalyzerOptions &Options)
859-
: StackFrame(Frame), EnableNullFPSuppression(Suppressed),
858+
: CalleeSFC(Frame), EnableNullFPSuppression(Suppressed),
860859
Options(Options) {}
861860

862861
static void *getTag() {
@@ -866,7 +865,7 @@ class ReturnVisitor : public BugReporterVisitor {
866865

867866
void Profile(llvm::FoldingSetNodeID &ID) const override {
868867
ID.AddPointer(ReturnVisitor::getTag());
869-
ID.AddPointer(StackFrame);
868+
ID.AddPointer(CalleeSFC);
870869
ID.AddBoolean(EnableNullFPSuppression);
871870
}
872871

@@ -950,7 +949,6 @@ class ReturnVisitor : public BugReporterVisitor {
950949
if (Optional<Loc> RetLoc = RetVal.getAs<Loc>())
951950
EnableNullFPSuppression = State->isNull(*RetLoc).isConstrainedTrue();
952951

953-
BR.markInteresting(CalleeContext);
954952
BR.addVisitor(llvm::make_unique<ReturnVisitor>(CalleeContext,
955953
EnableNullFPSuppression,
956954
Options));
@@ -960,7 +958,7 @@ class ReturnVisitor : public BugReporterVisitor {
960958
BugReporterContext &BRC,
961959
BugReport &BR) {
962960
// Only print a message at the interesting return statement.
963-
if (N->getLocationContext() != StackFrame)
961+
if (N->getLocationContext() != CalleeSFC)
964962
return nullptr;
965963

966964
Optional<StmtPoint> SP = N->getLocationAs<StmtPoint>();
@@ -974,7 +972,7 @@ class ReturnVisitor : public BugReporterVisitor {
974972
// Okay, we're at the right return statement, but do we have the return
975973
// value available?
976974
ProgramStateRef State = N->getState();
977-
SVal V = State->getSVal(Ret, StackFrame);
975+
SVal V = State->getSVal(Ret, CalleeSFC);
978976
if (V.isUnknownOrUndef())
979977
return nullptr;
980978

@@ -1008,6 +1006,8 @@ class ReturnVisitor : public BugReporterVisitor {
10081006
SmallString<64> Msg;
10091007
llvm::raw_svector_ostream Out(Msg);
10101008

1009+
bool WouldEventBeMeaningless = false;
1010+
10111011
if (State->isNull(V).isConstrainedTrue()) {
10121012
if (V.getAs<Loc>()) {
10131013

@@ -1030,10 +1030,19 @@ class ReturnVisitor : public BugReporterVisitor {
10301030
} else {
10311031
if (auto CI = V.getAs<nonloc::ConcreteInt>()) {
10321032
Out << "Returning the value " << CI->getValue();
1033-
} else if (V.getAs<Loc>()) {
1034-
Out << "Returning pointer";
10351033
} else {
1036-
Out << "Returning value";
1034+
// There is nothing interesting about returning a value, when it is
1035+
// plain value without any constraints, and the function is guaranteed
1036+
// to return that every time. We could use CFG::isLinear() here, but
1037+
// constexpr branches are obvious to the compiler, not necesserily to
1038+
// the programmer.
1039+
if (N->getCFG().size() == 3)
1040+
WouldEventBeMeaningless = true;
1041+
1042+
if (V.getAs<Loc>())
1043+
Out << "Returning pointer";
1044+
else
1045+
Out << "Returning value";
10371046
}
10381047
}
10391048

@@ -1052,11 +1061,20 @@ class ReturnVisitor : public BugReporterVisitor {
10521061
Out << " (loaded from '" << *DD << "')";
10531062
}
10541063

1055-
PathDiagnosticLocation L(Ret, BRC.getSourceManager(), StackFrame);
1064+
PathDiagnosticLocation L(Ret, BRC.getSourceManager(), CalleeSFC);
10561065
if (!L.isValid() || !L.asLocation().isValid())
10571066
return nullptr;
10581067

1059-
return std::make_shared<PathDiagnosticEventPiece>(L, Out.str());
1068+
auto EventPiece = std::make_shared<PathDiagnosticEventPiece>(L, Out.str());
1069+
1070+
// If we determined that the note is meaningless, make it prunable, and
1071+
// don't mark the stackframe interesting.
1072+
if (WouldEventBeMeaningless)
1073+
EventPiece->setPrunable(true);
1074+
else
1075+
BR.markInteresting(CalleeSFC);
1076+
1077+
return EventPiece;
10601078
}
10611079

10621080
PathDiagnosticPieceRef visitNodeMaybeUnsuppress(const ExplodedNode *N,
@@ -1071,7 +1089,7 @@ class ReturnVisitor : public BugReporterVisitor {
10711089
if (!CE)
10721090
return nullptr;
10731091

1074-
if (CE->getCalleeContext() != StackFrame)
1092+
if (CE->getCalleeContext() != CalleeSFC)
10751093
return nullptr;
10761094

10771095
Mode = Satisfied;
@@ -1083,7 +1101,7 @@ class ReturnVisitor : public BugReporterVisitor {
10831101
CallEventManager &CallMgr = StateMgr.getCallEventManager();
10841102

10851103
ProgramStateRef State = N->getState();
1086-
CallEventRef<> Call = CallMgr.getCaller(StackFrame, State);
1104+
CallEventRef<> Call = CallMgr.getCaller(CalleeSFC, State);
10871105
for (unsigned I = 0, E = Call->getNumArgs(); I != E; ++I) {
10881106
Optional<Loc> ArgV = Call->getArgSVal(I).getAs<Loc>();
10891107
if (!ArgV)
@@ -1126,7 +1144,7 @@ class ReturnVisitor : public BugReporterVisitor {
11261144
void finalizeVisitor(BugReporterContext &, const ExplodedNode *,
11271145
BugReport &BR) override {
11281146
if (EnableNullFPSuppression && ShouldInvalidate)
1129-
BR.markInvalid(ReturnVisitor::getTag(), StackFrame);
1147+
BR.markInvalid(ReturnVisitor::getTag(), CalleeSFC);
11301148
}
11311149
};
11321150

clang/test/Analysis/diagnostics/find_last_store.c

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@
22
typedef struct { float b; } c;
33
void *a();
44
void *d() {
5-
return a(); // expected-note{{Returning pointer}}
5+
return a();
66
}
77

88
void no_find_last_store() {
9-
c *e = d(); // expected-note{{Calling 'd'}}
10-
// expected-note@-1{{Returning from 'd'}}
11-
// expected-note@-2{{'e' initialized here}}
9+
c *e = d(); // expected-note{{'e' initialized here}}
1210

1311
(void)(e || e->b); // expected-note{{Assuming 'e' is null}}
1412
// expected-note@-1{{Left side of '||' is false}}

clang/test/Analysis/track-control-dependency-conditions.cpp

Lines changed: 122 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -119,20 +119,18 @@ namespace variable_declaration_in_condition {
119119
bool coin();
120120

121121
bool foo() {
122-
return coin(); // tracking-note{{Returning value}}
122+
return coin();
123123
}
124124

125125
int bar;
126126

127127
void test() {
128128
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
129129

130-
if (int flag = foo()) // tracking-note{{Calling 'foo'}}
131-
// tracking-note@-1{{Returning from 'foo'}}
132-
// tracking-note@-2{{'flag' initialized here}}
133-
// debug-note@-3{{Tracking condition 'flag'}}
134-
// expected-note@-4{{Assuming 'flag' is not equal to 0}}
135-
// expected-note@-5{{Taking true branch}}
130+
if (int flag = foo()) // tracking-note{{'flag' initialized here}}
131+
// debug-note@-1{{Tracking condition 'flag'}}
132+
// expected-note@-2{{Assuming 'flag' is not equal to 0}}
133+
// expected-note@-3{{Taking true branch}}
136134

137135
*x = 5; // expected-warning{{Dereference of null pointer}}
138136
// expected-note@-1{{Dereference of null pointer}}
@@ -143,39 +141,114 @@ namespace conversion_to_bool {
143141
bool coin();
144142

145143
struct ConvertsToBool {
146-
operator bool() const { return coin(); } // tracking-note{{Returning value}}
144+
operator bool() const { return coin(); }
147145
};
148146

149147
void test() {
150148
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
151149

152150
if (ConvertsToBool())
153-
// tracking-note@-1 {{Calling 'ConvertsToBool::operator bool'}}
154-
// tracking-note@-2{{Returning from 'ConvertsToBool::operator bool'}}
155-
// debug-note@-3{{Tracking condition 'ConvertsToBool()'}}
156-
// expected-note@-4{{Assuming the condition is true}}
157-
// expected-note@-5{{Taking true branch}}
151+
// debug-note@-1{{Tracking condition 'ConvertsToBool()'}}
152+
// expected-note@-2{{Assuming the condition is true}}
153+
// expected-note@-3{{Taking true branch}}
158154
*x = 5; // expected-warning{{Dereference of null pointer}}
159155
// expected-note@-1{{Dereference of null pointer}}
160156
}
161157

162158
} // end of namespace variable_declaration_in_condition
163159

160+
namespace important_returning_pointer_loaded_from {
161+
bool coin();
162+
163+
int *getIntPtr();
164+
165+
void storeValue(int **i) {
166+
*i = getIntPtr(); // tracking-note{{Value assigned to 'i'}}
167+
}
168+
169+
int *conjurePointer() {
170+
int *i;
171+
storeValue(&i); // tracking-note{{Calling 'storeValue'}}
172+
// tracking-note@-1{{Returning from 'storeValue'}}
173+
return i; // tracking-note{{Returning pointer (loaded from 'i')}}
174+
}
175+
176+
void f(int *ptr) {
177+
if (ptr) // expected-note{{Assuming 'ptr' is null}}
178+
// expected-note@-1{{Taking false branch}}
179+
;
180+
if (!conjurePointer())
181+
// tracking-note@-1{{Calling 'conjurePointer'}}
182+
// tracking-note@-2{{Returning from 'conjurePointer'}}
183+
// debug-note@-3{{Tracking condition '!conjurePointer()'}}
184+
// expected-note@-4{{Assuming the condition is true}}
185+
// expected-note@-5{{Taking true branch}}
186+
*ptr = 5; // expected-warning{{Dereference of null pointer}}
187+
// expected-note@-1{{Dereference of null pointer}}
188+
}
189+
} // end of namespace important_returning_pointer_loaded_from
190+
191+
namespace unimportant_returning_pointer_loaded_from {
192+
bool coin();
193+
194+
int *getIntPtr();
195+
196+
int *conjurePointer() {
197+
int *i = getIntPtr(); // tracking-note{{'i' initialized here}}
198+
return i; // tracking-note{{Returning pointer (loaded from 'i')}}
199+
}
200+
201+
void f(int *ptr) {
202+
if (ptr) // expected-note{{Assuming 'ptr' is null}}
203+
// expected-note@-1{{Taking false branch}}
204+
;
205+
if (!conjurePointer())
206+
// tracking-note@-1{{Calling 'conjurePointer'}}
207+
// tracking-note@-2{{Returning from 'conjurePointer'}}
208+
// debug-note@-3{{Tracking condition '!conjurePointer()'}}
209+
// expected-note@-4{{Assuming the condition is true}}
210+
// expected-note@-5{{Taking true branch}}
211+
*ptr = 5; // expected-warning{{Dereference of null pointer}}
212+
// expected-note@-1{{Dereference of null pointer}}
213+
}
214+
} // end of namespace unimportant_returning_pointer_loaded_from
215+
216+
namespace unimportant_returning_pointer_loaded_from_through_cast {
217+
218+
void *conjure();
219+
220+
int *cast(void *P) {
221+
return static_cast<int *>(P);
222+
}
223+
224+
void f() {
225+
int *x = 0; // expected-note{{'x' initialized to a null pointer value}}
226+
227+
if (cast(conjure()))
228+
// tracking-note@-1{{Passing value via 1st parameter 'P'}}
229+
// debug-note@-2{{Tracking condition 'cast(conjure())'}}
230+
// expected-note@-3{{Assuming the condition is false}}
231+
// expected-note@-4{{Taking false branch}}
232+
return;
233+
*x = 5; // expected-warning{{Dereference of null pointer}}
234+
// expected-note@-1{{Dereference of null pointer}}
235+
}
236+
237+
} // end of namespace unimportant_returning_pointer_loaded_from_through_cast
238+
164239
namespace unimportant_returning_value_note {
165240
bool coin();
166241

167-
bool flipCoin() { return coin(); } // tracking-note{{Returning value}}
242+
bool flipCoin() { return coin(); }
168243

169244
void i(int *ptr) {
170245
if (ptr) // expected-note{{Assuming 'ptr' is null}}
171246
// expected-note@-1{{Taking false branch}}
172247
;
173248
if (!flipCoin())
174-
// tracking-note@-1{{Calling 'flipCoin'}}
175-
// tracking-note@-2{{Returning from 'flipCoin'}}
176-
// debug-note@-3{{Tracking condition '!flipCoin()'}}
177-
// expected-note@-4{{Assuming the condition is true}}
178-
// expected-note@-5{{Taking true branch}}
249+
// debug-note@-1{{Tracking condition '!flipCoin()'}}
250+
// expected-note@-2{{Assuming the condition is true}}
251+
// expected-note@-3{{Taking true branch}}
179252
*ptr = 5; // expected-warning{{Dereference of null pointer}}
180253
// expected-note@-1{{Dereference of null pointer}}
181254
}
@@ -207,6 +280,36 @@ void i(int *ptr) {
207280
}
208281
} // end of namespace important_returning_value_note
209282

283+
namespace important_returning_value_note_in_linear_function {
284+
bool coin();
285+
286+
struct super_complicated_template_hackery {
287+
static constexpr bool value = false;
288+
};
289+
290+
bool flipCoin() {
291+
if (super_complicated_template_hackery::value)
292+
// tracking-note@-1{{'value' is false}}
293+
// tracking-note@-2{{Taking false branch}}
294+
return true;
295+
return coin(); // tracking-note{{Returning value}}
296+
}
297+
298+
void i(int *ptr) {
299+
if (ptr) // expected-note{{Assuming 'ptr' is null}}
300+
// expected-note@-1{{Taking false branch}}
301+
;
302+
if (!flipCoin())
303+
// tracking-note@-1{{Calling 'flipCoin'}}
304+
// tracking-note@-2{{Returning from 'flipCoin'}}
305+
// debug-note@-3{{Tracking condition '!flipCoin()'}}
306+
// expected-note@-4{{Assuming the condition is true}}
307+
// expected-note@-5{{Taking true branch}}
308+
*ptr = 5; // expected-warning{{Dereference of null pointer}}
309+
// expected-note@-1{{Dereference of null pointer}}
310+
}
311+
} // end of namespace important_returning_value_note_in_linear_function
312+
210313
namespace tracked_condition_is_only_initialized {
211314
int getInt();
212315

clang/test/Analysis/uninit-vals.c

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -149,8 +149,6 @@ int test_radar12278788_FP() {
149149
RetVoidFuncType f = foo_radar12278788_fp;
150150
return ((RetIntFuncType)f)(); //expected-warning {{Undefined or garbage value returned to caller}}
151151
//expected-note@-1 {{Undefined or garbage value returned to caller}}
152-
//expected-note@-2 {{Calling 'foo_radar12278788_fp'}}
153-
//expected-note@-3 {{Returning from 'foo_radar12278788_fp'}}
154152
}
155153

156154
void rdar13665798() {
@@ -164,8 +162,6 @@ void rdar13665798() {
164162
RetVoidFuncType f = foo_radar12278788_fp;
165163
return ((RetIntFuncType)f)(); //expected-warning {{Undefined or garbage value returned to caller}}
166164
//expected-note@-1 {{Undefined or garbage value returned to caller}}
167-
//expected-note@-2 {{Calling 'foo_radar12278788_fp'}}
168-
//expected-note@-3 {{Returning from 'foo_radar12278788_fp'}}
169165
}();
170166
}
171167

@@ -182,18 +178,14 @@ struct Point getHalfPoint() {
182178
void use(struct Point p);
183179

184180
void testUseHalfPoint() {
185-
struct Point p = getHalfPoint(); // expected-note{{Calling 'getHalfPoint'}}
186-
// expected-note@-1{{Returning from 'getHalfPoint'}}
187-
// expected-note@-2{{'p' initialized here}}
181+
struct Point p = getHalfPoint(); // expected-note{{'p' initialized here}}
188182
use(p); // expected-warning{{uninitialized}}
189183
// expected-note@-1{{uninitialized}}
190184
}
191185

192186
void testUseHalfPoint2() {
193187
struct Point p;
194-
p = getHalfPoint(); // expected-note{{Calling 'getHalfPoint'}}
195-
// expected-note@-1{{Returning from 'getHalfPoint'}}
196-
// expected-note@-2{{Value assigned to 'p'}}
188+
p = getHalfPoint(); // expected-note{{Value assigned to 'p'}}
197189
use(p); // expected-warning{{uninitialized}}
198190
// expected-note@-1{{uninitialized}}
199191
}

0 commit comments

Comments
 (0)