Skip to content

Commit 3891b45

Browse files
committed
Revert "[analyzer][NFCI] Allow clients of NoStateChangeFuncVisitor to check entire function calls, rather than each ExplodedNode in it"
This reverts commit 7d0e62b.
1 parent e962718 commit 3891b45

File tree

9 files changed

+77
-477
lines changed

9 files changed

+77
-477
lines changed

clang/include/clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h

+6-38
Original file line numberDiff line numberDiff line change
@@ -633,9 +633,6 @@ class CXXConstructorCall;
633633
/// Descendants can define what a "state change is", like a change of value
634634
/// to a memory region, liveness, etc. For function calls where the state did
635635
/// not change as defined, a custom note may be constructed.
636-
///
637-
/// For a minimal example, check out
638-
/// clang/unittests/StaticAnalyzer/NoStateChangeFuncVisitorTest.cpp.
639636
class NoStateChangeFuncVisitor : public BugReporterVisitor {
640637
private:
641638
/// Frames modifying the state as defined in \c wasModifiedBeforeCallExit.
@@ -646,8 +643,6 @@ class NoStateChangeFuncVisitor : public BugReporterVisitor {
646643
/// many times (going up the path for each node and checking whether the
647644
/// region was written into) we instead lazily compute the stack frames
648645
/// along the path.
649-
// TODO: Can't we just use a map instead? This is likely not as cheap as it
650-
// makes the code difficult to read.
651646
llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifying;
652647
llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated;
653648

@@ -656,46 +651,18 @@ class NoStateChangeFuncVisitor : public BugReporterVisitor {
656651
/// The calculation is cached in FramesModifying.
657652
bool isModifiedInFrame(const ExplodedNode *CallExitBeginN);
658653

659-
void markFrameAsModifying(const StackFrameContext *SCtx);
660-
661654
/// Write to \c FramesModifying all stack frames along the path in the current
662655
/// stack frame which modifies the state.
663656
void findModifyingFrames(const ExplodedNode *const CallExitBeginN);
664657

665658
protected:
666659
bugreporter::TrackingKind TKind;
667660

668-
/// \return Whether the state was modified from the current node, \p CurrN, to
669-
/// the end of the stack frame, at \p CallExitBeginN. \p CurrN and
670-
/// \p CallExitBeginN are always in the same stack frame.
671-
/// Clients should override this callback when a state change is important
672-
/// not only on the entire function call, but inside of it as well.
673-
/// Example: we may want to leave a note about the lack of locking/unlocking
674-
/// on a particular mutex, but not if inside the function its state was
675-
/// changed, but also restored. wasModifiedInFunction() wouldn't know of this
676-
/// change.
677-
virtual bool wasModifiedBeforeCallExit(const ExplodedNode *CurrN,
678-
const ExplodedNode *CallExitBeginN) {
679-
return false;
680-
}
681-
682-
/// \return Whether the state was modified in the inlined function call in
683-
/// between \p CallEnterN and \p CallExitEndN. Mind that the stack frame
684-
/// retrieved from a CallEnterN and CallExitEndN is the *caller's* stack
685-
/// frame! The inlined function's stack should be retrieved from either the
686-
/// immediate successor to \p CallEnterN or immediate predecessor to
687-
/// \p CallExitEndN.
688-
/// Clients should override this function if a state changes local to the
689-
/// inlined function are not interesting, only the change occuring as a
690-
/// result of it.
691-
/// Example: we want to leave a not about a leaked resource object not being
692-
/// deallocated / its ownership changed inside a function, and we don't care
693-
/// if it was assigned to a local variable (its change in ownership is
694-
/// inconsequential).
695-
virtual bool wasModifiedInFunction(const ExplodedNode *CallEnterN,
696-
const ExplodedNode *CallExitEndN) {
697-
return false;
698-
}
661+
/// \return Whether the state was modified from the current node, \CurrN, to
662+
/// the end of the stack fram, at \p CallExitBeginN.
663+
virtual bool
664+
wasModifiedBeforeCallExit(const ExplodedNode *CurrN,
665+
const ExplodedNode *CallExitBeginN) = 0;
699666

700667
/// Consume the information on the non-modifying stack frame in order to
701668
/// either emit a note or not. May suppress the report entirely.
@@ -735,6 +702,7 @@ class NoStateChangeFuncVisitor : public BugReporterVisitor {
735702
};
736703

737704
} // namespace ento
705+
738706
} // namespace clang
739707

740708
#endif // LLVM_CLANG_STATICANALYZER_CORE_BUGREPORTER_BUGREPORTERVISITORS_H

clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp

+20-27
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@
5252
#include "clang/AST/Expr.h"
5353
#include "clang/AST/ExprCXX.h"
5454
#include "clang/AST/ParentMap.h"
55-
#include "clang/Analysis/ProgramPoint.h"
5655
#include "clang/Basic/LLVM.h"
5756
#include "clang/Basic/SourceManager.h"
5857
#include "clang/Basic/TargetInfo.h"
@@ -77,10 +76,8 @@
7776
#include "llvm/ADT/SetOperations.h"
7877
#include "llvm/ADT/SmallString.h"
7978
#include "llvm/ADT/StringExtras.h"
80-
#include "llvm/Support/Casting.h"
8179
#include "llvm/Support/Compiler.h"
8280
#include "llvm/Support/ErrorHandling.h"
83-
#include "llvm/Support/raw_ostream.h"
8481
#include <climits>
8582
#include <functional>
8683
#include <utility>
@@ -758,17 +755,6 @@ class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
758755
Owners.insert(Region);
759756
return true;
760757
}
761-
762-
LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
763-
LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &out) const {
764-
out << "Owners: {\n";
765-
for (const MemRegion *Owner : Owners) {
766-
out << " ";
767-
Owner->dumpToStream(out);
768-
out << ",\n";
769-
}
770-
out << "}\n";
771-
}
772758
};
773759

774760
protected:
@@ -782,24 +768,31 @@ class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
782768
return Ret;
783769
}
784770

785-
LLVM_DUMP_METHOD static std::string
786-
getFunctionName(const ExplodedNode *CallEnterN) {
787-
if (const CallExpr *CE = llvm::dyn_cast_or_null<CallExpr>(
788-
CallEnterN->getLocationAs<CallEnter>()->getCallExpr()))
789-
if (const FunctionDecl *FD = CE->getDirectCallee())
790-
return FD->getQualifiedNameAsString();
791-
return "";
771+
static const ExplodedNode *getCallExitEnd(const ExplodedNode *N) {
772+
while (N && !N->getLocationAs<CallExitEnd>())
773+
N = N->getFirstSucc();
774+
return N;
792775
}
793776

794777
virtual bool
795-
wasModifiedInFunction(const ExplodedNode *CallEnterN,
796-
const ExplodedNode *CallExitEndN) override {
797-
if (CallEnterN->getState()->get<RegionState>(Sym) !=
798-
CallExitEndN->getState()->get<RegionState>(Sym))
778+
wasModifiedBeforeCallExit(const ExplodedNode *CurrN,
779+
const ExplodedNode *CallExitN) override {
780+
if (CurrN->getLocationAs<CallEnter>())
781+
return true;
782+
783+
// Its the state right *after* the call that is interesting. Any pointers
784+
// inside the call that pointed to the allocated memory are of little
785+
// consequence if their lifetime ends within the function.
786+
CallExitN = getCallExitEnd(CallExitN);
787+
if (!CallExitN)
788+
return true;
789+
790+
if (CurrN->getState()->get<RegionState>(Sym) !=
791+
CallExitN->getState()->get<RegionState>(Sym))
799792
return true;
800793

801-
OwnerSet CurrOwners = getOwnersAtNode(CallEnterN);
802-
OwnerSet ExitOwners = getOwnersAtNode(CallExitEndN);
794+
OwnerSet CurrOwners = getOwnersAtNode(CurrN);
795+
OwnerSet ExitOwners = getOwnersAtNode(CallExitN);
803796

804797
// Owners in the current set may be purged from the analyzer later on.
805798
// If a variable is dead (is not referenced directly or indirectly after

clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp

+25-63
Original file line numberDiff line numberDiff line change
@@ -355,81 +355,43 @@ bool NoStateChangeFuncVisitor::isModifiedInFrame(const ExplodedNode *N) {
355355
return FramesModifying.count(SCtx);
356356
}
357357

358-
void NoStateChangeFuncVisitor::markFrameAsModifying(
359-
const StackFrameContext *SCtx) {
360-
while (SCtx) {
361-
auto p = FramesModifying.insert(SCtx);
362-
if (!p.second)
363-
break; // Frame and all its parents already inserted.
364-
SCtx = SCtx->getParent()->getStackFrame();
365-
}
366-
}
367-
368-
static const ExplodedNode *getMatchingCallExitEnd(const ExplodedNode *N) {
369-
assert(N->getLocationAs<CallEnter>());
370-
// The stackframe of the callee is only found in the nodes succeeding
371-
// the CallEnter node. CallEnter's stack frame refers to the caller.
372-
const StackFrameContext *OrigSCtx = N->getFirstSucc()->getStackFrame();
373-
374-
// Similarly, the nodes preceding CallExitEnd refer to the callee's stack
375-
// frame.
376-
auto IsMatchingCallExitEnd = [OrigSCtx](const ExplodedNode *N) {
377-
return N->getLocationAs<CallExitEnd>() &&
378-
OrigSCtx == N->getFirstPred()->getStackFrame();
379-
};
380-
while (N && !IsMatchingCallExitEnd(N)) {
381-
assert(N->succ_size() <= 1 &&
382-
"This function is to be used on the trimmed ExplodedGraph!");
383-
N = N->getFirstSucc();
384-
}
385-
return N;
386-
}
387-
388358
void NoStateChangeFuncVisitor::findModifyingFrames(
389359
const ExplodedNode *const CallExitBeginN) {
390360

391361
assert(CallExitBeginN->getLocationAs<CallExitBegin>());
392-
362+
const ExplodedNode *LastReturnN = CallExitBeginN;
393363
const StackFrameContext *const OriginalSCtx =
394364
CallExitBeginN->getLocationContext()->getStackFrame();
395365

396-
const ExplodedNode *CurrCallExitBeginN = CallExitBeginN;
397-
const StackFrameContext *CurrentSCtx = OriginalSCtx;
398-
399-
for (const ExplodedNode *CurrN = CallExitBeginN; CurrN;
400-
CurrN = CurrN->getFirstPred()) {
401-
// Found a new inlined call.
402-
if (CurrN->getLocationAs<CallExitBegin>()) {
403-
CurrCallExitBeginN = CurrN;
404-
CurrentSCtx = CurrN->getStackFrame();
405-
FramesModifyingCalculated.insert(CurrentSCtx);
406-
// We won't see a change in between two identical exploded nodes: skip.
407-
continue;
366+
const ExplodedNode *CurrN = CallExitBeginN;
367+
368+
do {
369+
ProgramStateRef State = CurrN->getState();
370+
auto CallExitLoc = CurrN->getLocationAs<CallExitBegin>();
371+
if (CallExitLoc) {
372+
LastReturnN = CurrN;
408373
}
409374

410-
if (auto CE = CurrN->getLocationAs<CallEnter>()) {
411-
if (const ExplodedNode *CallExitEndN = getMatchingCallExitEnd(CurrN))
412-
if (wasModifiedInFunction(CurrN, CallExitEndN))
413-
markFrameAsModifying(CurrentSCtx);
414-
415-
// We exited this inlined call, lets actualize the stack frame.
416-
CurrentSCtx = CurrN->getStackFrame();
417-
418-
// Stop calculating at the current function, but always regard it as
419-
// modifying, so we can avoid notes like this:
420-
// void f(Foo &F) {
421-
// F.field = 0; // note: 0 assigned to 'F.field'
422-
// // note: returning without writing to 'F.field'
423-
// }
424-
if (CE->getCalleeContext() == OriginalSCtx) {
425-
markFrameAsModifying(CurrentSCtx);
426-
break;
375+
FramesModifyingCalculated.insert(
376+
CurrN->getLocationContext()->getStackFrame());
377+
378+
if (wasModifiedBeforeCallExit(CurrN, LastReturnN)) {
379+
const StackFrameContext *SCtx = CurrN->getStackFrame();
380+
while (!SCtx->inTopFrame()) {
381+
auto p = FramesModifying.insert(SCtx);
382+
if (!p.second)
383+
break; // Frame and all its parents already inserted.
384+
SCtx = SCtx->getParent()->getStackFrame();
427385
}
428386
}
429387

430-
if (wasModifiedBeforeCallExit(CurrN, CurrCallExitBeginN))
431-
markFrameAsModifying(CurrentSCtx);
432-
}
388+
// Stop calculation at the call to the current function.
389+
if (auto CE = CurrN->getLocationAs<CallEnter>())
390+
if (CE->getCalleeContext() == OriginalSCtx)
391+
break;
392+
393+
CurrN = CurrN->getFirstPred();
394+
} while (CurrN);
433395
}
434396

435397
PathDiagnosticPieceRef NoStateChangeFuncVisitor::VisitNode(

clang/unittests/StaticAnalyzer/CMakeLists.txt

-1
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ add_clang_unittest(StaticAnalysisTests
99
CallDescriptionTest.cpp
1010
CallEventTest.cpp
1111
FalsePositiveRefutationBRVisitorTest.cpp
12-
NoStateChangeFuncVisitorTest.cpp
1312
ParamRegionTest.cpp
1413
RangeSetTest.cpp
1514
RegisterCustomCheckersTest.cpp

clang/unittests/StaticAnalyzer/CallEventTest.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ TEST(CXXDeallocatorCall, SimpleDestructor) {
8181
}
8282
)",
8383
Diags));
84-
EXPECT_EQ(Diags, "test.CXXDeallocator: NumArgs: 1\n");
84+
EXPECT_EQ(Diags, "test.CXXDeallocator:NumArgs: 1\n");
8585
}
8686

8787
} // namespace

clang/unittests/StaticAnalyzer/CheckerRegistration.h

+2-18
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
//
77
//===----------------------------------------------------------------------===//
88

9-
#include "clang/Analysis/PathDiagnostic.h"
109
#include "clang/Frontend/CompilerInstance.h"
1110
#include "clang/StaticAnalyzer/Core/BugReporter/BugReporter.h"
1211
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
@@ -27,23 +26,8 @@ class DiagConsumer : public PathDiagnosticConsumer {
2726
DiagConsumer(llvm::raw_ostream &Output) : Output(Output) {}
2827
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
2928
FilesMade *filesMade) override {
30-
for (const auto *PD : Diags) {
31-
Output << PD->getCheckerName() << ": ";
32-
33-
for (PathDiagnosticPieceRef Piece :
34-
PD->path.flatten(/*ShouldFlattenMacros*/ true)) {
35-
if (Piece->getKind() != PathDiagnosticPiece::Event)
36-
continue;
37-
if (Piece->getString().empty())
38-
continue;
39-
// The last event is usually the same as the warning message, skip.
40-
if (Piece->getString() == PD->getShortDescription())
41-
continue;
42-
43-
Output << Piece->getString() << " | ";
44-
}
45-
Output << PD->getShortDescription() << '\n';
46-
}
29+
for (const auto *PD : Diags)
30+
Output << PD->getCheckerName() << ":" << PD->getShortDescription() << '\n';
4731
}
4832

4933
StringRef getName() const override { return "Test"; }

clang/unittests/StaticAnalyzer/FalsePositiveRefutationBRVisitorTest.cpp

+9-9
Original file line numberDiff line numberDiff line change
@@ -128,16 +128,16 @@ TEST_F(FalsePositiveRefutationBRVisitorTestBase, UnSatInTheMiddleNoReport) {
128128
EXPECT_TRUE(runCheckerOnCodeWithArgs<addFalsePositiveGenerator>(
129129
Code, LazyAssumeAndCrossCheckArgs, Diags));
130130
EXPECT_EQ(Diags,
131-
"test.FalsePositiveGenerator: REACHED_WITH_NO_CONTRADICTION\n");
131+
"test.FalsePositiveGenerator:REACHED_WITH_NO_CONTRADICTION\n");
132132
// Single warning. The second report was invalidated by the visitor.
133133

134134
// Without enabling the crosscheck-with-z3 both reports are displayed.
135135
std::string Diags2;
136136
EXPECT_TRUE(runCheckerOnCodeWithArgs<addFalsePositiveGenerator>(
137137
Code, LazyAssumeArgs, Diags2));
138138
EXPECT_EQ(Diags2,
139-
"test.FalsePositiveGenerator: REACHED_WITH_NO_CONTRADICTION\n"
140-
"test.FalsePositiveGenerator: REACHED_WITH_CONTRADICTION\n");
139+
"test.FalsePositiveGenerator:REACHED_WITH_NO_CONTRADICTION\n"
140+
"test.FalsePositiveGenerator:REACHED_WITH_CONTRADICTION\n");
141141
}
142142

143143
TEST_F(FalsePositiveRefutationBRVisitorTestBase,
@@ -159,16 +159,16 @@ TEST_F(FalsePositiveRefutationBRVisitorTestBase,
159159
EXPECT_TRUE(runCheckerOnCodeWithArgs<addFalsePositiveGenerator>(
160160
Code, LazyAssumeAndCrossCheckArgs, Diags));
161161
EXPECT_EQ(Diags,
162-
"test.FalsePositiveGenerator: REACHED_WITH_NO_CONTRADICTION\n");
162+
"test.FalsePositiveGenerator:REACHED_WITH_NO_CONTRADICTION\n");
163163
// Single warning. The second report was invalidated by the visitor.
164164

165165
// Without enabling the crosscheck-with-z3 both reports are displayed.
166166
std::string Diags2;
167167
EXPECT_TRUE(runCheckerOnCodeWithArgs<addFalsePositiveGenerator>(
168168
Code, LazyAssumeArgs, Diags2));
169169
EXPECT_EQ(Diags2,
170-
"test.FalsePositiveGenerator: REACHED_WITH_NO_CONTRADICTION\n"
171-
"test.FalsePositiveGenerator: CAN_BE_TRUE\n");
170+
"test.FalsePositiveGenerator:REACHED_WITH_NO_CONTRADICTION\n"
171+
"test.FalsePositiveGenerator:CAN_BE_TRUE\n");
172172
}
173173

174174
TEST_F(FalsePositiveRefutationBRVisitorTestBase,
@@ -206,16 +206,16 @@ TEST_F(FalsePositiveRefutationBRVisitorTestBase,
206206
EXPECT_TRUE(runCheckerOnCodeWithArgs<addFalsePositiveGenerator>(
207207
Code, LazyAssumeAndCrossCheckArgs, Diags));
208208
EXPECT_EQ(Diags,
209-
"test.FalsePositiveGenerator: REACHED_WITH_NO_CONTRADICTION\n");
209+
"test.FalsePositiveGenerator:REACHED_WITH_NO_CONTRADICTION\n");
210210
// Single warning. The second report was invalidated by the visitor.
211211

212212
// Without enabling the crosscheck-with-z3 both reports are displayed.
213213
std::string Diags2;
214214
EXPECT_TRUE(runCheckerOnCodeWithArgs<addFalsePositiveGenerator>(
215215
Code, LazyAssumeArgs, Diags2));
216216
EXPECT_EQ(Diags2,
217-
"test.FalsePositiveGenerator: REACHED_WITH_NO_CONTRADICTION\n"
218-
"test.FalsePositiveGenerator: CAN_BE_TRUE\n");
217+
"test.FalsePositiveGenerator:REACHED_WITH_NO_CONTRADICTION\n"
218+
"test.FalsePositiveGenerator:CAN_BE_TRUE\n");
219219
}
220220

221221
} // namespace

0 commit comments

Comments
 (0)