aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2021-11-19 20:06:13 +0000
committerDimitry Andric <dim@FreeBSD.org>2021-11-19 20:06:13 +0000
commitc0981da47d5696fe36474fcf86b4ce03ae3ff818 (patch)
treef42add1021b9f2ac6a69ac7cf6c4499962739a45 /clang/lib/StaticAnalyzer
parent344a3780b2e33f6ca763666c380202b18aab72a3 (diff)
downloadsrc-c0981da47d5696fe36474fcf86b4ce03ae3ff818.tar.gz
src-c0981da47d5696fe36474fcf86b4ce03ae3ff818.zip
Diffstat (limited to 'clang/lib/StaticAnalyzer')
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp16
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp28
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp6
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp1
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp50
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp12
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp9
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp8
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp19
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp19
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp203
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp39
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp8
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp45
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp1
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp1
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp5
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp14
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp86
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp24
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp105
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp9
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp24
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h2
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp280
-rw-r--r--clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporter.cpp42
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp406
-rw-r--r--clang/lib/StaticAnalyzer/Core/CallDescription.cpp146
-rw-r--r--clang/lib/StaticAnalyzer/Core/CallEvent.cpp91
-rw-r--r--clang/lib/StaticAnalyzer/Core/CheckerContext.cpp9
-rw-r--r--clang/lib/StaticAnalyzer/Core/CheckerManager.cpp27
-rw-r--r--clang/lib/StaticAnalyzer/Core/Environment.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp16
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp10
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp637
-rw-r--r--clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/LoopWidening.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Core/MemRegion.cpp57
-rw-r--r--clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp389
-rw-r--r--clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp15
-rw-r--r--clang/lib/StaticAnalyzer/Core/RegionStore.cpp349
-rw-r--r--clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Core/SValBuilder.cpp27
-rw-r--r--clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp14
-rw-r--r--clang/lib/StaticAnalyzer/Core/Store.cpp46
-rw-r--r--clang/lib/StaticAnalyzer/Core/SymbolManager.cpp14
69 files changed, 2552 insertions, 879 deletions
diff --git a/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
index c06604b6cffe..6154eeb3419c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/AnalyzerStatsChecker.cpp
@@ -93,11 +93,10 @@ void AnalyzerStatsChecker::checkEndAnalysis(ExplodedGraph &G,
if (!Loc.isValid())
return;
- if (isa<FunctionDecl>(D) || isa<ObjCMethodDecl>(D)) {
+ if (isa<FunctionDecl, ObjCMethodDecl>(D)) {
const NamedDecl *ND = cast<NamedDecl>(D);
output << *ND;
- }
- else if (isa<BlockDecl>(D)) {
+ } else if (isa<BlockDecl>(D)) {
output << "block(line:" << Loc.getLine() << ":col:" << Loc.getColumn();
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
index a86a410ebcbc..2c210fb6cdb9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BasicObjCFoundationChecks.cpp
@@ -12,7 +12,6 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/DeclObjC.h"
#include "clang/AST/Expr.h"
@@ -20,9 +19,11 @@
#include "clang/AST/StmtObjC.h"
#include "clang/Analysis/DomainSpecific/CocoaConventions.h"
#include "clang/Analysis/SelectorExtras.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
@@ -533,10 +534,12 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
namespace {
class CFRetainReleaseChecker : public Checker<check::PreCall> {
mutable APIMisuse BT{this, "null passed to CF memory management function"};
- CallDescription CFRetain{"CFRetain", 1},
- CFRelease{"CFRelease", 1},
- CFMakeCollectable{"CFMakeCollectable", 1},
- CFAutorelease{"CFAutorelease", 1};
+ const CallDescriptionSet ModelledCalls = {
+ {"CFRetain", 1},
+ {"CFRelease", 1},
+ {"CFMakeCollectable", 1},
+ {"CFAutorelease", 1},
+ };
public:
void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
@@ -550,8 +553,7 @@ void CFRetainReleaseChecker::checkPreCall(const CallEvent &Call,
return;
// Check if we called CFRetain/CFRelease/CFMakeCollectable/CFAutorelease.
- if (!(Call.isCalled(CFRetain) || Call.isCalled(CFRelease) ||
- Call.isCalled(CFMakeCollectable) || Call.isCalled(CFAutorelease)))
+ if (!ModelledCalls.contains(Call))
return;
// Get the argument's value.
diff --git a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
index 2752b37f9b3f..8416ab39e194 100644
--- a/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/BlockInCriticalSectionChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -96,14 +97,7 @@ void BlockInCriticalSectionChecker::initIdentifierInfo(ASTContext &Ctx) const {
}
bool BlockInCriticalSectionChecker::isBlockingFunction(const CallEvent &Call) const {
- if (Call.isCalled(SleepFn)
- || Call.isCalled(GetcFn)
- || Call.isCalled(FgetsFn)
- || Call.isCalled(ReadFn)
- || Call.isCalled(RecvFn)) {
- return true;
- }
- return false;
+ return matchesAny(Call, SleepFn, GetcFn, FgetsFn, ReadFn, RecvFn);
}
bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const {
@@ -113,15 +107,8 @@ bool BlockInCriticalSectionChecker::isLockFunction(const CallEvent &Call) const
return true;
}
- if (Call.isCalled(LockFn)
- || Call.isCalled(PthreadLockFn)
- || Call.isCalled(PthreadTryLockFn)
- || Call.isCalled(MtxLock)
- || Call.isCalled(MtxTimedLock)
- || Call.isCalled(MtxTryLock)) {
- return true;
- }
- return false;
+ return matchesAny(Call, LockFn, PthreadLockFn, PthreadTryLockFn, MtxLock,
+ MtxTimedLock, MtxTryLock);
}
bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) const {
@@ -132,12 +119,7 @@ bool BlockInCriticalSectionChecker::isUnlockFunction(const CallEvent &Call) cons
return true;
}
- if (Call.isCalled(UnlockFn)
- || Call.isCalled(PthreadUnlockFn)
- || Call.isCalled(MtxUnlock)) {
- return true;
- }
- return false;
+ return matchesAny(Call, UnlockFn, PthreadUnlockFn, MtxUnlock);
}
void BlockInCriticalSectionChecker::checkPostCall(const CallEvent &Call,
diff --git a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
index 69b90be9aa7e..475cee9ce04b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CStringChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
@@ -2271,11 +2272,10 @@ CStringChecker::FnCheck CStringChecker::identifyCall(const CallEvent &Call,
if (!FD)
return nullptr;
- if (Call.isCalled(StdCopy)) {
+ if (StdCopy.matches(Call))
return &CStringChecker::evalStdCopy;
- } else if (Call.isCalled(StdCopyBackward)) {
+ if (StdCopyBackward.matches(Call))
return &CStringChecker::evalStdCopyBackward;
- }
// Pro-actively check that argument types are safe to do arithmetic upon.
// We do not want to crash if someone accidentally passes a structure
diff --git a/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
index 131c1345af99..4235c0c13821 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -20,6 +20,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
index 175dfcef0df4..a13de306eac8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckObjCInstMethSignature.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines a CheckObjCInstMethSignature, a flow-insenstive check
+// This file defines a CheckObjCInstMethSignature, a flow-insensitive check
// that determines if an Objective-C class interface incorrectly redefines
// the method signature in a subclass.
//
diff --git a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
index d06c87631bfb..61ff5e59f06d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CheckSecuritySyntaxOnly.cpp
@@ -785,9 +785,8 @@ void WalkAST::checkDeprecatedOrUnsafeBufferHandling(const CallExpr *CE,
// real flow analysis.
auto FormatString =
dyn_cast<StringLiteral>(CE->getArg(ArgIndex)->IgnoreParenImpCasts());
- if (FormatString &&
- FormatString->getString().find("%s") == StringRef::npos &&
- FormatString->getString().find("%[") == StringRef::npos)
+ if (FormatString && !FormatString->getString().contains("%s") &&
+ !FormatString->getString().contains("%["))
BoundsProvided = true;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
index fd53c04f4bbf..ce8d6c879870 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ChrootChecker.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -63,11 +64,11 @@ private:
} // end anonymous namespace
bool ChrootChecker::evalCall(const CallEvent &Call, CheckerContext &C) const {
- if (Call.isCalled(Chroot)) {
+ if (Chroot.matches(Call)) {
evalChroot(Call, C);
return true;
}
- if (Call.isCalled(Chdir)) {
+ if (Chdir.matches(Call)) {
evalChdir(Call, C);
return true;
}
@@ -115,7 +116,7 @@ void ChrootChecker::evalChdir(const CallEvent &Call, CheckerContext &C) const {
void ChrootChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
// Ignore chroot and chdir.
- if (Call.isCalled(Chroot) || Call.isCalled(Chdir))
+ if (matchesAny(Call, Chroot, Chdir))
return;
// If jail state is ROOT_CHANGED, generate BugReport.
diff --git a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
index 1a7f0d5ab74c..77a3218f55fb 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ContainerModeling.cpp
@@ -10,11 +10,12 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/DeclTemplate.h"
#include "clang/Driver/DriverDiagnostic.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
@@ -71,42 +72,27 @@ public:
SVal) const;
CallDescriptionMap<NoItParamFn> NoIterParamFunctions = {
- {{0, "clear", 0},
- &ContainerModeling::handleClear},
- {{0, "assign", 2},
- &ContainerModeling::handleAssign},
- {{0, "push_back", 1},
- &ContainerModeling::handlePushBack},
- {{0, "emplace_back", 1},
- &ContainerModeling::handlePushBack},
- {{0, "pop_back", 0},
- &ContainerModeling::handlePopBack},
- {{0, "push_front", 1},
- &ContainerModeling::handlePushFront},
- {{0, "emplace_front", 1},
- &ContainerModeling::handlePushFront},
- {{0, "pop_front", 0},
- &ContainerModeling::handlePopFront},
+ {{"clear", 0}, &ContainerModeling::handleClear},
+ {{"assign", 2}, &ContainerModeling::handleAssign},
+ {{"push_back", 1}, &ContainerModeling::handlePushBack},
+ {{"emplace_back", 1}, &ContainerModeling::handlePushBack},
+ {{"pop_back", 0}, &ContainerModeling::handlePopBack},
+ {{"push_front", 1}, &ContainerModeling::handlePushFront},
+ {{"emplace_front", 1}, &ContainerModeling::handlePushFront},
+ {{"pop_front", 0}, &ContainerModeling::handlePopFront},
};
-
+
CallDescriptionMap<OneItParamFn> OneIterParamFunctions = {
- {{0, "insert", 2},
- &ContainerModeling::handleInsert},
- {{0, "emplace", 2},
- &ContainerModeling::handleInsert},
- {{0, "erase", 1},
- &ContainerModeling::handleErase},
- {{0, "erase_after", 1},
- &ContainerModeling::handleEraseAfter},
+ {{"insert", 2}, &ContainerModeling::handleInsert},
+ {{"emplace", 2}, &ContainerModeling::handleInsert},
+ {{"erase", 1}, &ContainerModeling::handleErase},
+ {{"erase_after", 1}, &ContainerModeling::handleEraseAfter},
};
-
+
CallDescriptionMap<TwoItParamFn> TwoIterParamFunctions = {
- {{0, "erase", 2},
- &ContainerModeling::handleErase},
- {{0, "erase_after", 2},
- &ContainerModeling::handleEraseAfter},
+ {{"erase", 2}, &ContainerModeling::handleErase},
+ {{"erase_after", 2}, &ContainerModeling::handleEraseAfter},
};
-
};
bool isBeginCall(const FunctionDecl *Func);
diff --git a/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
index 4216a6883119..8da482a2aec9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ConversionChecker.cpp
@@ -49,7 +49,8 @@ private:
bool isLossOfSign(const ImplicitCastExpr *Cast, CheckerContext &C) const;
- void reportBug(ExplodedNode *N, CheckerContext &C, const char Msg[]) const;
+ void reportBug(ExplodedNode *N, const Expr *E, CheckerContext &C,
+ const char Msg[]) const;
};
}
@@ -108,20 +109,21 @@ void ConversionChecker::checkPreStmt(const ImplicitCastExpr *Cast,
if (!N)
return;
if (LossOfSign)
- reportBug(N, C, "Loss of sign in implicit conversion");
+ reportBug(N, Cast, C, "Loss of sign in implicit conversion");
if (LossOfPrecision)
- reportBug(N, C, "Loss of precision in implicit conversion");
+ reportBug(N, Cast, C, "Loss of precision in implicit conversion");
}
}
-void ConversionChecker::reportBug(ExplodedNode *N, CheckerContext &C,
- const char Msg[]) const {
+void ConversionChecker::reportBug(ExplodedNode *N, const Expr *E,
+ CheckerContext &C, const char Msg[]) const {
if (!BT)
BT.reset(
new BuiltinBug(this, "Conversion", "Possible loss of sign/precision."));
// Generate a report for this bug.
auto R = std::make_unique<PathSensitiveBugReport>(*BT, Msg, N);
+ bugreporter::trackExpressionValue(N, E, *R);
C.emitReport(std::move(R));
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp
index 6fed999ffc80..47fd57c7db9b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DebugContainerModeling.cpp
@@ -13,6 +13,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -40,10 +41,10 @@ class DebugContainerModeling
CheckerContext &) const;
CallDescriptionMap<FnCheck> Callbacks = {
- {{0, "clang_analyzer_container_begin", 1},
- &DebugContainerModeling::analyzerContainerBegin},
- {{0, "clang_analyzer_container_end", 1},
- &DebugContainerModeling::analyzerContainerEnd},
+ {{"clang_analyzer_container_begin", 1},
+ &DebugContainerModeling::analyzerContainerBegin},
+ {{"clang_analyzer_container_end", 1},
+ &DebugContainerModeling::analyzerContainerEnd},
};
public:
diff --git a/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
index 5833eea56da8..6add9a007a87 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DebugIteratorModeling.cpp
@@ -13,6 +13,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -41,12 +42,12 @@ class DebugIteratorModeling
CheckerContext &) const;
CallDescriptionMap<FnCheck> Callbacks = {
- {{0, "clang_analyzer_iterator_position", 1},
- &DebugIteratorModeling::analyzerIteratorPosition},
- {{0, "clang_analyzer_iterator_container", 1},
- &DebugIteratorModeling::analyzerIteratorContainer},
- {{0, "clang_analyzer_iterator_validity", 1},
- &DebugIteratorModeling::analyzerIteratorValidity},
+ {{"clang_analyzer_iterator_position", 1},
+ &DebugIteratorModeling::analyzerIteratorPosition},
+ {{"clang_analyzer_iterator_container", 1},
+ &DebugIteratorModeling::analyzerIteratorContainer},
+ {{"clang_analyzer_iterator_validity", 1},
+ &DebugIteratorModeling::analyzerIteratorValidity},
};
public:
diff --git a/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp b/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
index df88b71ff063..49486ea796c2 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DirectIvarAssignment.cpp
@@ -44,8 +44,8 @@ static bool DefaultMethodFilter(const ObjCMethodDecl *M) {
M->getMethodFamily() == OMF_dealloc ||
M->getMethodFamily() == OMF_copy ||
M->getMethodFamily() == OMF_mutableCopy ||
- M->getSelector().getNameForSlot(0).find("init") != StringRef::npos ||
- M->getSelector().getNameForSlot(0).find("Init") != StringRef::npos;
+ M->getSelector().getNameForSlot(0).contains("init") ||
+ M->getSelector().getNameForSlot(0).contains("Init");
}
class DirectIvarAssignment :
diff --git a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
index 14ba5d769969..b07f59125a82 100644
--- a/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/DynamicTypePropagation.cpp
@@ -384,7 +384,7 @@ void DynamicTypePropagation::checkPostCall(const CallEvent &Call,
// FIXME: Instead of relying on the ParentMap, we should have the
// trigger-statement (InitListExpr in this case) available in this
// callback, ideally as part of CallEvent.
- if (dyn_cast_or_null<InitListExpr>(
+ if (isa_and_nonnull<InitListExpr>(
LCtx->getParentMap().getParent(Ctor->getOriginExpr())))
return;
diff --git a/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
index 0e94b915a468..e5088fb266bc 100644
--- a/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/EnumCastOutOfRangeChecker.cpp
@@ -94,10 +94,10 @@ void EnumCastOutOfRangeChecker::checkPreStmt(const CastExpr *CE,
// Only perform enum range check on casts where such checks are valid. For
// all other cast kinds (where enum range checks are unnecessary or invalid),
- // just return immediately. TODO: The set of casts whitelisted for enum
- // range checking may be incomplete. Better to add a missing cast kind to
- // enable a missing check than to generate false negatives and have to remove
- // those later.
+ // just return immediately. TODO: The set of casts allowed for enum range
+ // checking may be incomplete. Better to add a missing cast kind to enable a
+ // missing check than to generate false negatives and have to remove those
+ // later.
switch (CE->getCastKind()) {
case CK_IntegralCast:
break;
diff --git a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
index 42c777eb2c52..66ef781871ec 100644
--- a/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/GenericTaintChecker.cpp
@@ -260,7 +260,7 @@ private:
}
bool isDestinationArgument(unsigned ArgNum) const {
- return (llvm::find(DstArgs, ArgNum) != DstArgs.end());
+ return llvm::is_contained(DstArgs, ArgNum);
}
static bool isTaintedOrPointsToTainted(const Expr *E,
@@ -435,7 +435,6 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
.Case("getch", {{}, {ReturnValueIndex}})
.Case("getchar", {{}, {ReturnValueIndex}})
.Case("getchar_unlocked", {{}, {ReturnValueIndex}})
- .Case("getenv", {{}, {ReturnValueIndex}})
.Case("gets", {{}, {0, ReturnValueIndex}})
.Case("scanf", {{}, {}, VariadicType::Dst, 1})
.Case("socket", {{},
@@ -468,6 +467,16 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
if (!Rule.isNull())
return Rule;
+
+ // `getenv` returns taint only in untrusted environments.
+ if (FData.FullName == "getenv") {
+ if (C.getAnalysisManager()
+ .getAnalyzerOptions()
+ .ShouldAssumeControlledEnvironment)
+ return {};
+ return {{}, {ReturnValueIndex}};
+ }
+
assert(FData.FDecl);
// Check if it's one of the memory setting/copying functions.
@@ -505,7 +514,7 @@ GenericTaintChecker::TaintPropagationRule::getTaintPropagationRule(
if (OneOf("snprintf"))
return {{1}, {0, ReturnValueIndex}, VariadicType::Src, 3};
if (OneOf("sprintf"))
- return {{}, {0, ReturnValueIndex}, VariadicType::Src, 2};
+ return {{1}, {0, ReturnValueIndex}, VariadicType::Src, 2};
if (OneOf("strcpy", "stpcpy", "strcat"))
return {{1}, {0, ReturnValueIndex}};
if (OneOf("bcopy"))
@@ -780,7 +789,7 @@ bool GenericTaintChecker::isStdin(const Expr *E, CheckerContext &C) {
// variable named stdin with the proper type.
if (const auto *D = dyn_cast_or_null<VarDecl>(DeclReg->getDecl())) {
D = D->getCanonicalDecl();
- if ((D->getName().find("stdin") != StringRef::npos) && D->isExternC()) {
+ if (D->getName().contains("stdin") && D->isExternC()) {
const auto *PtrTy = dyn_cast<PointerType>(D->getType().getTypePtr());
if (PtrTy && PtrTy->getPointeeType().getCanonicalType() ==
C.getASTContext().getFILEType().getCanonicalType())
@@ -807,7 +816,7 @@ static bool getPrintfFormatArgumentNum(const CallEvent &Call,
}
// Or if a function is named setproctitle (this is a heuristic).
- if (C.getCalleeName(FDecl).find("setproctitle") != StringRef::npos) {
+ if (C.getCalleeName(FDecl).contains("setproctitle")) {
ArgNum = 0;
return true;
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
index bcae73378028..6f9867b9607d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/InnerPointerChecker.cpp
@@ -13,11 +13,12 @@
//===----------------------------------------------------------------------===//
#include "AllocationState.h"
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "InterCheckerAPI.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -125,19 +126,15 @@ bool InnerPointerChecker::isInvalidatingMemberFunction(
return true;
return false;
}
- return (isa<CXXDestructorCall>(Call) || Call.isCalled(AppendFn) ||
- Call.isCalled(AssignFn) || Call.isCalled(ClearFn) ||
- Call.isCalled(EraseFn) || Call.isCalled(InsertFn) ||
- Call.isCalled(PopBackFn) || Call.isCalled(PushBackFn) ||
- Call.isCalled(ReplaceFn) || Call.isCalled(ReserveFn) ||
- Call.isCalled(ResizeFn) || Call.isCalled(ShrinkToFitFn) ||
- Call.isCalled(SwapFn));
+ return isa<CXXDestructorCall>(Call) ||
+ matchesAny(Call, AppendFn, AssignFn, ClearFn, EraseFn, InsertFn,
+ PopBackFn, PushBackFn, ReplaceFn, ReserveFn, ResizeFn,
+ ShrinkToFitFn, SwapFn);
}
bool InnerPointerChecker::isInnerPointerAccessFunction(
const CallEvent &Call) const {
- return (Call.isCalled(CStrFn) || Call.isCalled(DataFn) ||
- Call.isCalled(DataMemberFn));
+ return matchesAny(Call, CStrFn, DataFn, DataMemberFn);
}
void InnerPointerChecker::markPtrSymbolsReleased(const CallEvent &Call,
@@ -184,7 +181,7 @@ void InnerPointerChecker::checkFunctionArguments(const CallEvent &Call,
// std::addressof function accepts a non-const reference as an argument,
// but doesn't modify it.
- if (Call.isCalled(AddressofFn))
+ if (AddressofFn.matches(Call))
continue;
markPtrSymbolsReleased(Call, State, ArgRegion, C);
diff --git a/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
index ab5e6a1c9991..235c9010412a 100644
--- a/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/IteratorModeling.cpp
@@ -64,10 +64,11 @@
// making an assumption e.g. `S1 + n == S2 + m` we store `S1 - S2 == m - n` as
// a constraint which we later retrieve when doing an actual comparison.
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/DeclTemplate.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
diff --git a/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
index a47484497771..c682449921ac 100644
--- a/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/IteratorRangeChecker.cpp
@@ -14,10 +14,10 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
-
#include "Iterator.h"
using namespace clang;
diff --git a/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
index 28d3e058fee2..b57c5dc6de56 100644
--- a/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/LocalizationChecker.cpp
@@ -949,7 +949,7 @@ void NonLocalizedStringChecker::checkPostCall(const CallEvent &Call,
const IdentifierInfo *Identifier = Call.getCalleeIdentifier();
SVal sv = Call.getReturnValue();
- if (isAnnotatedAsReturningLocalized(D) || LSF.count(Identifier) != 0) {
+ if (isAnnotatedAsReturningLocalized(D) || LSF.contains(Identifier)) {
setLocalizedState(sv, C);
} else if (isNSStringType(RT, C.getASTContext()) &&
!hasLocalizedState(sv, C)) {
@@ -1339,7 +1339,10 @@ bool PluralMisuseChecker::MethodCrawler::EndVisitIfStmt(IfStmt *I) {
}
bool PluralMisuseChecker::MethodCrawler::VisitIfStmt(const IfStmt *I) {
- const Expr *Condition = I->getCond()->IgnoreParenImpCasts();
+ const Expr *Condition = I->getCond();
+ if (!Condition)
+ return true;
+ Condition = Condition->IgnoreParenImpCasts();
if (isCheckingPlurality(Condition)) {
MatchingStatements.push_back(I);
InMatchingStatement = true;
diff --git a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
index b72d72580c28..5bf96acc0462 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MIGChecker.cpp
@@ -27,6 +27,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -180,7 +181,7 @@ static bool isInMIGCall(CheckerContext &C) {
}
void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const {
- if (Call.isCalled(OsRefRetain)) {
+ if (OsRefRetain.matches(Call)) {
// If the code is doing reference counting over the parameter,
// it opens up an opportunity for safely calling a destructor function.
// TODO: We should still check for over-releases.
@@ -198,7 +199,7 @@ void MIGChecker::checkPostCall(const CallEvent &Call, CheckerContext &C) const {
auto I = llvm::find_if(Deallocators,
[&](const std::pair<CallDescription, unsigned> &Item) {
- return Call.isCalled(Item.first);
+ return Item.first.matches(Call);
});
if (I == Deallocators.end())
return;
diff --git a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
index a157ee2da5df..635eb00e4ca9 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MacOSKeychainAPIChecker.cpp
@@ -160,7 +160,7 @@ static bool isEnclosingFunctionParam(const Expr *E) {
E = E->IgnoreParenCasts();
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(E)) {
const ValueDecl *VD = DRE->getDecl();
- if (isa<ImplicitParamDecl>(VD) || isa<ParmVarDecl>(VD))
+ if (isa<ImplicitParamDecl, ParmVarDecl>(VD))
return true;
}
return false;
@@ -199,8 +199,7 @@ unsigned MacOSKeychainAPIChecker::getTrackedFunctionIndex(StringRef Name,
static bool isBadDeallocationArgument(const MemRegion *Arg) {
if (!Arg)
return false;
- return isa<AllocaRegion>(Arg) || isa<BlockDataRegion>(Arg) ||
- isa<TypedRegion>(Arg);
+ return isa<AllocaRegion, BlockDataRegion, TypedRegion>(Arg);
}
/// Given the address expression, retrieve the value it's pointing to. Assume
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
index a6470da09c45..10ed6149528c 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocChecker.cpp
@@ -48,9 +48,13 @@
#include "InterCheckerAPI.h"
#include "clang/AST/Attr.h"
#include "clang/AST/DeclCXX.h"
+#include "clang/AST/DeclTemplate.h"
#include "clang/AST/Expr.h"
#include "clang/AST/ExprCXX.h"
#include "clang/AST/ParentMap.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Analysis/ProgramPoint.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/TargetInfo.h"
@@ -60,20 +64,26 @@
#include "clang/StaticAnalyzer/Core/BugReporter/CommonBugCategories.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/ExplodedGraph.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState_Fwd.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/StoreRef.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SetOperations.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringExtras.h"
+#include "llvm/Support/Casting.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/raw_ostream.h"
#include <climits>
#include <functional>
#include <utility>
@@ -298,6 +308,8 @@ public:
/// which might free a pointer are annotated.
DefaultBool ShouldIncludeOwnershipAnnotatedFunctions;
+ DefaultBool ShouldRegisterNoOwnershipChangeVisitor;
+
/// Many checkers are essentially built into this one, so enabling them will
/// make MallocChecker perform additional modeling and reporting.
enum CheckKind {
@@ -722,11 +734,169 @@ private:
bool isArgZERO_SIZE_PTR(ProgramStateRef State, CheckerContext &C,
SVal ArgVal) const;
};
+} // end anonymous namespace
+
+//===----------------------------------------------------------------------===//
+// Definition of NoOwnershipChangeVisitor.
+//===----------------------------------------------------------------------===//
+
+namespace {
+class NoOwnershipChangeVisitor final : public NoStateChangeFuncVisitor {
+ SymbolRef Sym;
+ using OwnerSet = llvm::SmallPtrSet<const MemRegion *, 8>;
+
+ // Collect which entities point to the allocated memory, and could be
+ // responsible for deallocating it.
+ class OwnershipBindingsHandler : public StoreManager::BindingsHandler {
+ SymbolRef Sym;
+ OwnerSet &Owners;
+
+ public:
+ OwnershipBindingsHandler(SymbolRef Sym, OwnerSet &Owners)
+ : Sym(Sym), Owners(Owners) {}
+
+ bool HandleBinding(StoreManager &SMgr, Store Store, const MemRegion *Region,
+ SVal Val) override {
+ if (Val.getAsSymbol() == Sym)
+ Owners.insert(Region);
+ return true;
+ }
+
+ LLVM_DUMP_METHOD void dump() const { dumpToStream(llvm::errs()); }
+ LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &out) const {
+ out << "Owners: {\n";
+ for (const MemRegion *Owner : Owners) {
+ out << " ";
+ Owner->dumpToStream(out);
+ out << ",\n";
+ }
+ out << "}\n";
+ }
+ };
+
+protected:
+ OwnerSet getOwnersAtNode(const ExplodedNode *N) {
+ OwnerSet Ret;
+
+ ProgramStateRef State = N->getState();
+ OwnershipBindingsHandler Handler{Sym, Ret};
+ State->getStateManager().getStoreManager().iterBindings(State->getStore(),
+ Handler);
+ return Ret;
+ }
+
+ LLVM_DUMP_METHOD static std::string
+ getFunctionName(const ExplodedNode *CallEnterN) {
+ if (const CallExpr *CE = llvm::dyn_cast_or_null<CallExpr>(
+ CallEnterN->getLocationAs<CallEnter>()->getCallExpr()))
+ if (const FunctionDecl *FD = CE->getDirectCallee())
+ return FD->getQualifiedNameAsString();
+ return "";
+ }
+
+ bool doesFnIntendToHandleOwnership(const Decl *Callee, ASTContext &ACtx) {
+ using namespace clang::ast_matchers;
+ const FunctionDecl *FD = dyn_cast<FunctionDecl>(Callee);
+ if (!FD)
+ return false;
+ // TODO: Operator delete is hardly the only deallocator -- Can we reuse
+ // isFreeingCall() or something thats already here?
+ auto Deallocations = match(
+ stmt(hasDescendant(cxxDeleteExpr().bind("delete"))
+ ), *FD->getBody(), ACtx);
+ // TODO: Ownership my change with an attempt to store the allocated memory.
+ return !Deallocations.empty();
+ }
+
+ virtual bool
+ wasModifiedInFunction(const ExplodedNode *CallEnterN,
+ const ExplodedNode *CallExitEndN) override {
+ if (!doesFnIntendToHandleOwnership(
+ CallExitEndN->getFirstPred()->getLocationContext()->getDecl(),
+ CallExitEndN->getState()->getAnalysisManager().getASTContext()))
+ return true;
+
+ if (CallEnterN->getState()->get<RegionState>(Sym) !=
+ CallExitEndN->getState()->get<RegionState>(Sym))
+ return true;
+
+ OwnerSet CurrOwners = getOwnersAtNode(CallEnterN);
+ OwnerSet ExitOwners = getOwnersAtNode(CallExitEndN);
+
+ // Owners in the current set may be purged from the analyzer later on.
+ // If a variable is dead (is not referenced directly or indirectly after
+ // some point), it will be removed from the Store before the end of its
+ // actual lifetime.
+ // This means that that if the ownership status didn't change, CurrOwners
+ // must be a superset of, but not necessarily equal to ExitOwners.
+ return !llvm::set_is_subset(ExitOwners, CurrOwners);
+ }
+
+ static PathDiagnosticPieceRef emitNote(const ExplodedNode *N) {
+ PathDiagnosticLocation L = PathDiagnosticLocation::create(
+ N->getLocation(),
+ N->getState()->getStateManager().getContext().getSourceManager());
+ return std::make_shared<PathDiagnosticEventPiece>(
+ L, "Returning without deallocating memory or storing the pointer for "
+ "later deallocation");
+ }
+
+ virtual PathDiagnosticPieceRef
+ maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
+ const ObjCMethodCall &Call,
+ const ExplodedNode *N) override {
+ // TODO: Implement.
+ return nullptr;
+ }
+
+ virtual PathDiagnosticPieceRef
+ maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
+ const CXXConstructorCall &Call,
+ const ExplodedNode *N) override {
+ // TODO: Implement.
+ return nullptr;
+ }
+
+ virtual PathDiagnosticPieceRef
+ maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
+ const ExplodedNode *N) override {
+ // TODO: Factor the logic of "what constitutes as an entity being passed
+ // into a function call" out by reusing the code in
+ // NoStoreFuncVisitor::maybeEmitNoteForParameters, maybe by incorporating
+ // the printing technology in UninitializedObject's FieldChainInfo.
+ ArrayRef<ParmVarDecl *> Parameters = Call.parameters();
+ for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) {
+ SVal V = Call.getArgSVal(I);
+ if (V.getAsSymbol() == Sym)
+ return emitNote(N);
+ }
+ return nullptr;
+ }
+
+public:
+ NoOwnershipChangeVisitor(SymbolRef Sym)
+ : NoStateChangeFuncVisitor(bugreporter::TrackingKind::Thorough),
+ Sym(Sym) {}
+
+ void Profile(llvm::FoldingSetNodeID &ID) const override {
+ static int Tag = 0;
+ ID.AddPointer(&Tag);
+ ID.AddPointer(Sym);
+ }
+
+ void *getTag() const {
+ static int Tag = 0;
+ return static_cast<void *>(&Tag);
+ }
+};
+
+} // end anonymous namespace
//===----------------------------------------------------------------------===//
// Definition of MallocBugVisitor.
//===----------------------------------------------------------------------===//
+namespace {
/// The bug visitor which allows us to print extra diagnostics along the
/// BugReport path. For example, showing the allocation site of the leaked
/// region.
@@ -767,7 +937,7 @@ public:
/// Did not track -> allocated. Other state (released) -> allocated.
static inline bool isAllocated(const RefState *RSCurr, const RefState *RSPrev,
const Stmt *Stmt) {
- return (Stmt && (isa<CallExpr>(Stmt) || isa<CXXNewExpr>(Stmt)) &&
+ return (isa_and_nonnull<CallExpr, CXXNewExpr>(Stmt) &&
(RSCurr &&
(RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) &&
(!RSPrev ||
@@ -780,8 +950,7 @@ public:
const Stmt *Stmt) {
bool IsReleased =
(RSCurr && RSCurr->isReleased()) && (!RSPrev || !RSPrev->isReleased());
- assert(!IsReleased ||
- (Stmt && (isa<CallExpr>(Stmt) || isa<CXXDeleteExpr>(Stmt))) ||
+ assert(!IsReleased || (isa_and_nonnull<CallExpr, CXXDeleteExpr>(Stmt)) ||
(!Stmt && RSCurr->getAllocationFamily() == AF_InnerBuffer));
return IsReleased;
}
@@ -789,11 +958,10 @@ public:
/// Did not track -> relinquished. Other state (allocated) -> relinquished.
static inline bool isRelinquished(const RefState *RSCurr,
const RefState *RSPrev, const Stmt *Stmt) {
- return (Stmt &&
- (isa<CallExpr>(Stmt) || isa<ObjCMessageExpr>(Stmt) ||
- isa<ObjCPropertyRefExpr>(Stmt)) &&
- (RSCurr && RSCurr->isRelinquished()) &&
- (!RSPrev || !RSPrev->isRelinquished()));
+ return (
+ isa_and_nonnull<CallExpr, ObjCMessageExpr, ObjCPropertyRefExpr>(Stmt) &&
+ (RSCurr && RSCurr->isRelinquished()) &&
+ (!RSPrev || !RSPrev->isRelinquished()));
}
/// If the expression is not a call, and the state change is
@@ -803,7 +971,7 @@ public:
static inline bool hasReallocFailed(const RefState *RSCurr,
const RefState *RSPrev,
const Stmt *Stmt) {
- return ((!Stmt || !isa<CallExpr>(Stmt)) &&
+ return ((!isa_and_nonnull<CallExpr>(Stmt)) &&
(RSCurr &&
(RSCurr->isAllocated() || RSCurr->isAllocatedOfSizeZero())) &&
(RSPrev &&
@@ -851,7 +1019,6 @@ private:
}
};
};
-
} // end anonymous namespace
// A map from the freed symbol to the symbol representing the return value of
@@ -1753,7 +1920,7 @@ ProgramStateRef MallocChecker::FreeMemAux(
// Parameters, locals, statics, globals, and memory returned by
// __builtin_alloca() shouldn't be freed.
- if (!(isa<UnknownSpaceRegion>(MS) || isa<HeapSpaceRegion>(MS))) {
+ if (!isa<UnknownSpaceRegion, HeapSpaceRegion>(MS)) {
// FIXME: at the time this code was written, malloc() regions were
// represented by conjured symbols, which are all in UnknownSpaceRegion.
// This means that there isn't actually anything from HeapSpaceRegion
@@ -2303,7 +2470,8 @@ void MallocChecker::HandleUseZeroAlloc(CheckerContext &C, SourceRange Range,
categories::MemoryError));
auto R = std::make_unique<PathSensitiveBugReport>(
- *BT_UseZerroAllocated[*CheckKind], "Use of zero-allocated memory", N);
+ *BT_UseZerroAllocated[*CheckKind],
+ "Use of memory allocated with size zero", N);
R->addRange(Range);
if (Sym) {
@@ -2579,6 +2747,8 @@ void MallocChecker::HandleLeak(SymbolRef Sym, ExplodedNode *N,
AllocNode->getLocationContext()->getDecl());
R->markInteresting(Sym);
R->addVisitor<MallocBugVisitor>(Sym, true);
+ if (ShouldRegisterNoOwnershipChangeVisitor)
+ R->addVisitor<NoOwnershipChangeVisitor>(Sym);
C.emitReport(std::move(R));
}
@@ -2733,7 +2903,7 @@ void MallocChecker::checkEscapeOnReturn(const ReturnStmt *S,
// the callee could still free the memory.
// TODO: This logic should be a part of generic symbol escape callback.
if (const MemRegion *MR = RetVal.getAsRegion())
- if (isa<FieldRegion>(MR) || isa<ElementRegion>(MR))
+ if (isa<FieldRegion, ElementRegion>(MR))
if (const SymbolicRegion *BMR =
dyn_cast<SymbolicRegion>(MR->getBaseRegion()))
Sym = BMR->getSymbol();
@@ -2916,7 +3086,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
// TODO: If we want to be more optimistic here, we'll need to make sure that
// regions escape to C++ containers. They seem to do that even now, but for
// mysterious reasons.
- if (!(isa<SimpleFunctionCall>(Call) || isa<ObjCMethodCall>(Call)))
+ if (!isa<SimpleFunctionCall, ObjCMethodCall>(Call))
return true;
// Check Objective-C messages by selector name.
@@ -3024,7 +3194,7 @@ bool MallocChecker::mayFreeAnyEscapedMemoryOrIsModeledExplicitly(
const Expr *ArgE = Call->getArgExpr(0)->IgnoreParenCasts();
if (const DeclRefExpr *ArgDRE = dyn_cast<DeclRefExpr>(ArgE))
if (const VarDecl *D = dyn_cast<VarDecl>(ArgDRE->getDecl()))
- if (D->getCanonicalDecl()->getName().find("std") != StringRef::npos)
+ if (D->getCanonicalDecl()->getName().contains("std"))
return true;
}
}
@@ -3395,6 +3565,9 @@ void ento::registerDynamicMemoryModeling(CheckerManager &mgr) {
auto *checker = mgr.registerChecker<MallocChecker>();
checker->ShouldIncludeOwnershipAnnotatedFunctions =
mgr.getAnalyzerOptions().getCheckerBooleanOption(checker, "Optimistic");
+ checker->ShouldRegisterNoOwnershipChangeVisitor =
+ mgr.getAnalyzerOptions().getCheckerBooleanOption(
+ checker, "AddNoOwnershipChangeNotes");
}
bool ento::shouldRegisterDynamicMemoryModeling(const CheckerManager &mgr) {
diff --git a/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
index e31630f63b5a..a6e8fcd425d5 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MallocOverflowSecurityChecker.cpp
@@ -32,12 +32,14 @@ using llvm::APSInt;
namespace {
struct MallocOverflowCheck {
+ const CallExpr *call;
const BinaryOperator *mulop;
const Expr *variable;
APSInt maxVal;
- MallocOverflowCheck(const BinaryOperator *m, const Expr *v, APSInt val)
- : mulop(m), variable(v), maxVal(std::move(val)) {}
+ MallocOverflowCheck(const CallExpr *call, const BinaryOperator *m,
+ const Expr *v, APSInt val)
+ : call(call), mulop(m), variable(v), maxVal(std::move(val)) {}
};
class MallocOverflowSecurityChecker : public Checker<check::ASTCodeBody> {
@@ -46,8 +48,8 @@ public:
BugReporter &BR) const;
void CheckMallocArgument(
- SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
- const Expr *TheArgument, ASTContext &Context) const;
+ SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const CallExpr *TheCall, ASTContext &Context) const;
void OutputPossibleOverflows(
SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
@@ -62,16 +64,15 @@ static inline bool EvaluatesToZero(APSInt &Val, BinaryOperatorKind op) {
}
void MallocOverflowSecurityChecker::CheckMallocArgument(
- SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
- const Expr *TheArgument,
- ASTContext &Context) const {
+ SmallVectorImpl<MallocOverflowCheck> &PossibleMallocOverflows,
+ const CallExpr *TheCall, ASTContext &Context) const {
/* Look for a linear combination with a single variable, and at least
one multiplication.
Reject anything that applies to the variable: an explicit cast,
conditional expression, an operation that could reduce the range
of the result, or anything too complicated :-). */
- const Expr *e = TheArgument;
+ const Expr *e = TheCall->getArg(0);
const BinaryOperator * mulop = nullptr;
APSInt maxVal;
@@ -101,8 +102,7 @@ void MallocOverflowSecurityChecker::CheckMallocArgument(
e = rhs;
} else
return;
- }
- else if (isa<DeclRefExpr>(e) || isa<MemberExpr>(e))
+ } else if (isa<DeclRefExpr, MemberExpr>(e))
break;
else
return;
@@ -115,9 +115,8 @@ void MallocOverflowSecurityChecker::CheckMallocArgument(
// the data so when the body of the function is completely available
// we can check for comparisons.
- // TODO: Could push this into the innermost scope where 'e' is
- // defined, rather than the whole function.
- PossibleMallocOverflows.push_back(MallocOverflowCheck(mulop, e, maxVal));
+ PossibleMallocOverflows.push_back(
+ MallocOverflowCheck(TheCall, mulop, e, maxVal));
}
namespace {
@@ -153,17 +152,19 @@ private:
return getDecl(CheckDR) == getDecl(DR) && Pred(Check);
return false;
};
- toScanFor.erase(std::remove_if(toScanFor.begin(), toScanFor.end(), P),
- toScanFor.end());
+ llvm::erase_if(toScanFor, P);
}
void CheckExpr(const Expr *E_p) {
- auto PredTrue = [](const MallocOverflowCheck &) { return true; };
const Expr *E = E_p->IgnoreParenImpCasts();
+ const auto PrecedesMalloc = [E, this](const MallocOverflowCheck &c) {
+ return Context.getSourceManager().isBeforeInTranslationUnit(
+ E->getExprLoc(), c.call->getExprLoc());
+ };
if (const DeclRefExpr *DR = dyn_cast<DeclRefExpr>(E))
- Erase<DeclRefExpr>(DR, PredTrue);
+ Erase<DeclRefExpr>(DR, PrecedesMalloc);
else if (const auto *ME = dyn_cast<MemberExpr>(E)) {
- Erase<MemberExpr>(ME, PredTrue);
+ Erase<MemberExpr>(ME, PrecedesMalloc);
}
}
@@ -322,7 +323,7 @@ void MallocOverflowSecurityChecker::checkASTCodeBody(const Decl *D,
if (FnInfo->isStr ("malloc") || FnInfo->isStr ("_MALLOC")) {
if (TheCall->getNumArgs() == 1)
- CheckMallocArgument(PossibleMallocOverflows, TheCall->getArg(0),
+ CheckMallocArgument(PossibleMallocOverflows, TheCall,
mgr.getASTContext());
}
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
index 5d63d6efd234..517a5d78271b 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MmapWriteExecChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -46,7 +47,7 @@ int MmapWriteExecChecker::ProtRead = 0x01;
void MmapWriteExecChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
- if (Call.isCalled(MmapFn) || Call.isCalled(MprotectFn)) {
+ if (matchesAny(Call, MmapFn, MprotectFn)) {
SVal ProtVal = Call.getArgSVal(2);
Optional<nonloc::ConcreteInt> ProtLoc = ProtVal.getAs<nonloc::ConcreteInt>();
int64_t Prot = ProtLoc->getValue().getSExtValue();
diff --git a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
index cbe938982000..4a232c6f4b3f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/MoveChecker.cpp
@@ -553,8 +553,8 @@ MoveChecker::classifyObject(const MemRegion *MR,
// For the purposes of this checker, we classify move-safe STL types
// as not-"STL" types, because that's how the checker treats them.
MR = unwrapRValueReferenceIndirection(MR);
- bool IsLocal =
- MR && isa<VarRegion>(MR) && isa<StackSpaceRegion>(MR->getMemorySpace());
+ bool IsLocal = isa_and_nonnull<VarRegion>(MR) &&
+ isa<StackSpaceRegion>(MR->getMemorySpace());
if (!RD || !RD->getDeclContext()->isStdNamespace())
return { IsLocal, SK_NonStd };
@@ -712,12 +712,9 @@ ProgramStateRef MoveChecker::checkRegionChanges(
// directly, but not all of them end up being invalidated.
// But when they do, they appear in the InvalidatedRegions array as well.
for (const auto *Region : RequestedRegions) {
- if (ThisRegion != Region) {
- if (llvm::find(InvalidatedRegions, Region) !=
- std::end(InvalidatedRegions)) {
- State = removeFromState(State, Region);
- }
- }
+ if (ThisRegion != Region &&
+ llvm::is_contained(InvalidatedRegions, Region))
+ State = removeFromState(State, Region);
}
} else {
// For invalidations that aren't caused by calls, assume nothing. In
diff --git a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
index 90c5583d8969..dcca8be55e33 100644
--- a/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/NSErrorChecker.cpp
@@ -6,7 +6,7 @@
//
//===----------------------------------------------------------------------===//
//
-// This file defines a CheckNSError, a flow-insenstive check
+// This file defines a CheckNSError, a flow-insensitive check
// that determines if an Objective-C class interface correctly returns
// a non-void return type.
//
diff --git a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
index ee71b55a39e6..f4e9a67438e7 100644
--- a/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/PthreadLockChecker.cpp
@@ -21,6 +21,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -681,9 +682,7 @@ ProgramStateRef PthreadLockChecker::checkRegionChanges(
// We assume that system library function wouldn't touch the mutex unless
// it takes the mutex explicitly as an argument.
// FIXME: This is a bit quadratic.
- if (IsLibraryFunction &&
- std::find(ExplicitRegions.begin(), ExplicitRegions.end(), R) ==
- ExplicitRegions.end())
+ if (IsLibraryFunction && !llvm::is_contained(ExplicitRegions, R))
continue;
State = State->remove<LockMap>(R);
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
index 3f3267ff9391..0bde088d0e85 100644
--- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountChecker.cpp
@@ -290,7 +290,7 @@ void RetainCountChecker::checkPostStmt(const ObjCIvarRefExpr *IRE,
ProgramStateRef State = C.getState();
SymbolRef Sym = State->getSVal(*IVarLoc).getAsSymbol();
- if (!Sym || !dyn_cast_or_null<ObjCIvarRegion>(Sym->getOriginRegion()))
+ if (!Sym || !isa_and_nonnull<ObjCIvarRegion>(Sym->getOriginRegion()))
return;
// Accessing an ivar directly is unusual. If we've done that, be more
@@ -1188,14 +1188,14 @@ ProgramStateRef RetainCountChecker::checkRegionChanges(
if (!invalidated)
return state;
- llvm::SmallPtrSet<SymbolRef, 8> WhitelistedSymbols;
+ llvm::SmallPtrSet<SymbolRef, 8> AllowedSymbols;
for (const MemRegion *I : ExplicitRegions)
if (const SymbolicRegion *SR = I->StripCasts()->getAs<SymbolicRegion>())
- WhitelistedSymbols.insert(SR->getSymbol());
+ AllowedSymbols.insert(SR->getSymbol());
for (SymbolRef sym : *invalidated) {
- if (WhitelistedSymbols.count(sym))
+ if (AllowedSymbols.count(sym))
continue;
// Remove any existing reference-count binding.
state = removeRefBinding(state, sym);
diff --git a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
index 64ac6bc4c06b..41ef45d317cd 100644
--- a/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/RetainCountChecker/RetainCountDiagnostics.cpp
@@ -73,11 +73,8 @@ RefCountBug::RefCountBug(CheckerNameRef Checker, RefCountBugKind BT)
static bool isNumericLiteralExpression(const Expr *E) {
// FIXME: This set of cases was copied from SemaExprObjC.
- return isa<IntegerLiteral>(E) ||
- isa<CharacterLiteral>(E) ||
- isa<FloatingLiteral>(E) ||
- isa<ObjCBoolLiteralExpr>(E) ||
- isa<CXXBoolLiteralExpr>(E);
+ return isa<IntegerLiteral, CharacterLiteral, FloatingLiteral,
+ ObjCBoolLiteralExpr, CXXBoolLiteralExpr>(E);
}
/// If type represents a pointer to CXXRecordDecl,
diff --git a/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
index 885750218b9e..c4dc06d4a077 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ReturnPointerRangeChecker.cpp
@@ -12,6 +12,7 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugReporterVisitors.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -79,16 +80,44 @@ void ReturnPointerRangeChecker::checkPreStmt(const ReturnStmt *RS,
"Returned pointer value points outside the original object "
"(potential buffer overflow)"));
- // FIXME: It would be nice to eventually make this diagnostic more clear,
- // e.g., by referencing the original declaration or by saying *why* this
- // reference is outside the range.
-
// Generate a report for this bug.
- auto report =
+ auto Report =
std::make_unique<PathSensitiveBugReport>(*BT, BT->getDescription(), N);
-
- report->addRange(RetE->getSourceRange());
- C.emitReport(std::move(report));
+ Report->addRange(RetE->getSourceRange());
+
+ const auto ConcreteElementCount = ElementCount.getAs<nonloc::ConcreteInt>();
+ const auto ConcreteIdx = Idx.getAs<nonloc::ConcreteInt>();
+
+ const auto *DeclR = ER->getSuperRegion()->getAs<DeclRegion>();
+
+ if (DeclR)
+ Report->addNote("Original object declared here",
+ {DeclR->getDecl(), C.getSourceManager()});
+
+ if (ConcreteElementCount) {
+ SmallString<128> SBuf;
+ llvm::raw_svector_ostream OS(SBuf);
+ OS << "Original object ";
+ if (DeclR) {
+ OS << "'";
+ DeclR->getDecl()->printName(OS);
+ OS << "' ";
+ }
+ OS << "is an array of " << ConcreteElementCount->getValue() << " '";
+ ER->getValueType().print(OS,
+ PrintingPolicy(C.getASTContext().getLangOpts()));
+ OS << "' objects";
+ if (ConcreteIdx) {
+ OS << ", returned pointer points at index " << ConcreteIdx->getValue();
+ }
+
+ Report->addNote(SBuf,
+ {RetE, C.getSourceManager(), C.getLocationContext()});
+ }
+
+ bugreporter::trackExpressionValue(N, RetE, *Report);
+
+ C.emitReport(std::move(Report));
}
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
index 14ecede17083..cd502241ef61 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ReturnValueChecker.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "llvm/ADT/Optional.h"
diff --git a/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
index 933e0146ff59..ea72ebe3ed57 100644
--- a/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/STLAlgorithmModeling.cpp
@@ -12,6 +12,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
diff --git a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
index 8d380ed1b93d..1de5d7285f65 100644
--- a/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/SimpleStreamChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include <utility>
@@ -119,7 +120,7 @@ void SimpleStreamChecker::checkPostCall(const CallEvent &Call,
if (!Call.isGlobalCFunction())
return;
- if (!Call.isCalled(OpenFn))
+ if (!OpenFn.matches(Call))
return;
// Get the symbolic value corresponding to the file handle.
@@ -138,7 +139,7 @@ void SimpleStreamChecker::checkPreCall(const CallEvent &Call,
if (!Call.isGlobalCFunction())
return;
- if (!Call.isCalled(CloseFn))
+ if (!CloseFn.matches(Call))
return;
// Get the symbolic value corresponding to the file handle.
diff --git a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
index 09e885e8133f..c789a8dbcca1 100644
--- a/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/SmartPtrModeling.cpp
@@ -23,6 +23,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerHelpers.h"
@@ -102,12 +103,8 @@ static bool hasStdClassWithName(const CXXRecordDecl *RD,
ArrayRef<llvm::StringLiteral> Names) {
if (!RD || !RD->getDeclContext()->isStdNamespace())
return false;
- if (RD->getDeclName().isIdentifier()) {
- StringRef Name = RD->getName();
- return llvm::any_of(Names, [&Name](StringRef GivenName) -> bool {
- return Name == GivenName;
- });
- }
+ if (RD->getDeclName().isIdentifier())
+ return llvm::is_contained(Names, RD->getName());
return false;
}
@@ -289,7 +286,7 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
if (ModelSmartPtrDereference && isStdOstreamOperatorCall(Call))
return handleOstreamOperator(Call, C);
- if (Call.isCalled(StdSwapCall)) {
+ if (StdSwapCall.matches(Call)) {
// Check the first arg, if it is of std::unique_ptr type.
assert(Call.getNumArgs() == 2 && "std::swap should have two arguments");
const Expr *FirstArg = Call.getArgExpr(0);
@@ -298,8 +295,7 @@ bool SmartPtrModeling::evalCall(const CallEvent &Call,
return handleSwap(State, Call.getArgSVal(0), Call.getArgSVal(1), C);
}
- if (Call.isCalled(StdMakeUniqueCall) ||
- Call.isCalled(StdMakeUniqueForOverwriteCall)) {
+ if (matchesAny(Call, StdMakeUniqueCall, StdMakeUniqueForOverwriteCall)) {
if (!ModelSmartPtrDereference)
return false;
diff --git a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
index b5c9356322fc..d5e86e86424d 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StackAddrEscapeChecker.cpp
@@ -11,9 +11,9 @@
//
//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/AST/ExprCXX.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
@@ -303,21 +303,53 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
class CallBack : public StoreManager::BindingsHandler {
private:
CheckerContext &Ctx;
- const StackFrameContext *CurSFC;
+ const StackFrameContext *PoppedFrame;
+
+ /// Look for stack variables referring to popped stack variables.
+ /// Returns true only if it found some dangling stack variables
+ /// referred by an other stack variable from different stack frame.
+ bool checkForDanglingStackVariable(const MemRegion *Referrer,
+ const MemRegion *Referred) {
+ const auto *ReferrerMemSpace =
+ Referrer->getMemorySpace()->getAs<StackSpaceRegion>();
+ const auto *ReferredMemSpace =
+ Referred->getMemorySpace()->getAs<StackSpaceRegion>();
+
+ if (!ReferrerMemSpace || !ReferredMemSpace)
+ return false;
+
+ const auto *ReferrerFrame = ReferrerMemSpace->getStackFrame();
+ const auto *ReferredFrame = ReferredMemSpace->getStackFrame();
+
+ if (ReferrerMemSpace && ReferredMemSpace) {
+ if (ReferredFrame == PoppedFrame &&
+ ReferrerFrame->isParentOf(PoppedFrame)) {
+ V.emplace_back(Referrer, Referred);
+ return true;
+ }
+ }
+ return false;
+ }
public:
SmallVector<std::pair<const MemRegion *, const MemRegion *>, 10> V;
- CallBack(CheckerContext &CC) : Ctx(CC), CurSFC(CC.getStackFrame()) {}
+ CallBack(CheckerContext &CC) : Ctx(CC), PoppedFrame(CC.getStackFrame()) {}
bool HandleBinding(StoreManager &SMgr, Store S, const MemRegion *Region,
SVal Val) override {
+ const MemRegion *VR = Val.getAsRegion();
+ if (!VR)
+ return true;
+
+ if (checkForDanglingStackVariable(Region, VR))
+ return true;
+ // Check the globals for the same.
if (!isa<GlobalsSpaceRegion>(Region->getMemorySpace()))
return true;
- const MemRegion *VR = Val.getAsRegion();
- if (VR && isa<StackSpaceRegion>(VR->getMemorySpace()) &&
- !isArcManagedBlock(VR, Ctx) && !isNotInCurrentFrame(VR, Ctx))
+ if (VR && VR->hasStackStorage() && !isArcManagedBlock(VR, Ctx) &&
+ !isNotInCurrentFrame(VR, Ctx))
V.emplace_back(Region, VR);
return true;
}
@@ -344,19 +376,41 @@ void StackAddrEscapeChecker::checkEndFunction(const ReturnStmt *RS,
"invalid after returning from the function");
for (const auto &P : Cb.V) {
+ const MemRegion *Referrer = P.first;
+ const MemRegion *Referred = P.second;
+
// Generate a report for this bug.
+ const StringRef CommonSuffix =
+ "upon returning to the caller. This will be a dangling reference";
SmallString<128> Buf;
llvm::raw_svector_ostream Out(Buf);
- SourceRange Range = genName(Out, P.second, Ctx.getASTContext());
- Out << " is still referred to by the ";
- if (isa<StaticGlobalSpaceRegion>(P.first->getMemorySpace()))
- Out << "static";
- else
- Out << "global";
- Out << " variable '";
- const VarRegion *VR = cast<VarRegion>(P.first->getBaseRegion());
- Out << *VR->getDecl()
- << "' upon returning to the caller. This will be a dangling reference";
+ const SourceRange Range = genName(Out, Referred, Ctx.getASTContext());
+
+ if (isa<CXXTempObjectRegion>(Referrer)) {
+ Out << " is still referred to by a temporary object on the stack "
+ << CommonSuffix;
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
+ Ctx.emitReport(std::move(Report));
+ return;
+ }
+
+ const StringRef ReferrerMemorySpace = [](const MemSpaceRegion *Space) {
+ if (isa<StaticGlobalSpaceRegion>(Space))
+ return "static";
+ if (isa<GlobalsSpaceRegion>(Space))
+ return "global";
+ assert(isa<StackSpaceRegion>(Space));
+ return "stack";
+ }(Referrer->getMemorySpace());
+
+ // This cast supposed to succeed.
+ const VarRegion *ReferrerVar = cast<VarRegion>(Referrer->getBaseRegion());
+ const std::string ReferrerVarName =
+ ReferrerVar->getDecl()->getDeclName().getAsString();
+
+ Out << " is still referred to by the " << ReferrerMemorySpace
+ << " variable '" << ReferrerVarName << "' " << CommonSuffix;
auto Report =
std::make_unique<PathSensitiveBugReport>(*BT_stackleak, Out.str(), N);
if (Range.isValid())
diff --git a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
index e758b465af1b..e8b963a535d8 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StdLibraryFunctionsChecker.cpp
@@ -568,6 +568,7 @@ public:
bool DisplayLoadedSummaries = false;
bool ModelPOSIX = false;
+ bool ShouldAssumeControlledEnvironment = false;
private:
Optional<Summary> findFunctionSummary(const FunctionDecl *FD,
@@ -1433,6 +1434,20 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
RetType{Ssize_tTy}),
GetLineSummary);
+ {
+ Summary GetenvSummary = Summary(NoEvalCall)
+ .ArgConstraint(NotNull(ArgNo(0)))
+ .Case({NotNull(Ret)});
+ // In untrusted environments the envvar might not exist.
+ if (!ShouldAssumeControlledEnvironment)
+ GetenvSummary.Case({NotNull(Ret)->negate()});
+
+ // char *getenv(const char *name);
+ addToFunctionSummaryMap(
+ "getenv", Signature(ArgTypes{ConstCharPtrTy}, RetType{CharPtrTy}),
+ std::move(GetenvSummary));
+ }
+
if (ModelPOSIX) {
// long a64l(const char *str64);
@@ -2645,11 +2660,12 @@ void StdLibraryFunctionsChecker::initFunctionSummaries(
void ento::registerStdCLibraryFunctionsChecker(CheckerManager &mgr) {
auto *Checker = mgr.registerChecker<StdLibraryFunctionsChecker>();
+ const AnalyzerOptions &Opts = mgr.getAnalyzerOptions();
Checker->DisplayLoadedSummaries =
- mgr.getAnalyzerOptions().getCheckerBooleanOption(
- Checker, "DisplayLoadedSummaries");
- Checker->ModelPOSIX =
- mgr.getAnalyzerOptions().getCheckerBooleanOption(Checker, "ModelPOSIX");
+ Opts.getCheckerBooleanOption(Checker, "DisplayLoadedSummaries");
+ Checker->ModelPOSIX = Opts.getCheckerBooleanOption(Checker, "ModelPOSIX");
+ Checker->ShouldAssumeControlledEnvironment =
+ Opts.ShouldAssumeControlledEnvironment;
}
bool ento::shouldRegisterStdCLibraryFunctionsChecker(
diff --git a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
index dd65f8c035aa..26218b8e0454 100644
--- a/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/StreamChecker.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -1118,4 +1119,4 @@ void ento::registerStreamTesterChecker(CheckerManager &Mgr) {
bool ento::shouldRegisterStreamTesterChecker(const CheckerManager &Mgr) {
return true;
-} \ No newline at end of file
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp
new file mode 100644
index 000000000000..0d745f5d8d6f
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/StringChecker.cpp
@@ -0,0 +1,105 @@
+//=== StringChecker.cpp -------------------------------------------*- C++ -*--//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements the modeling of the std::basic_string type.
+// This involves checking preconditions of the operations and applying the
+// effects of the operations, e.g. their post-conditions.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+class StringChecker : public Checker<check::PreCall> {
+ BugType BT_Null{this, "Dereference of null pointer", categories::LogicError};
+ mutable const FunctionDecl *StringConstCharPtrCtor = nullptr;
+ mutable CanQualType SizeTypeTy;
+ const CallDescription TwoParamStdStringCtor = {
+ {"std", "basic_string", "basic_string"}, 2, 2};
+
+ bool isCharToStringCtor(const CallEvent &Call, const ASTContext &ACtx) const;
+
+public:
+ void checkPreCall(const CallEvent &Call, CheckerContext &C) const;
+};
+
+bool StringChecker::isCharToStringCtor(const CallEvent &Call,
+ const ASTContext &ACtx) const {
+ if (!TwoParamStdStringCtor.matches(Call))
+ return false;
+ const auto *FD = dyn_cast<FunctionDecl>(Call.getDecl());
+ assert(FD);
+
+ // See if we already cached it.
+ if (StringConstCharPtrCtor && StringConstCharPtrCtor == FD)
+ return true;
+
+ // Verify that the parameters have the expected types:
+ // - arg 1: `const CharT *`
+ // - arg 2: some allocator - which is definately not `size_t`.
+ const QualType Arg1Ty = Call.getArgExpr(0)->getType().getCanonicalType();
+ const QualType Arg2Ty = Call.getArgExpr(1)->getType().getCanonicalType();
+
+ if (!Arg1Ty->isPointerType())
+ return false;
+
+ // It makes sure that we don't select the `string(const char* p, size_t len)`
+ // overload accidentally.
+ if (Arg2Ty.getCanonicalType() == ACtx.getSizeType())
+ return false;
+
+ StringConstCharPtrCtor = FD; // Cache the decl of the right overload.
+ return true;
+}
+
+void StringChecker::checkPreCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ if (!isCharToStringCtor(Call, C.getASTContext()))
+ return;
+ const auto Param = Call.getArgSVal(0).getAs<Loc>();
+ if (!Param.hasValue())
+ return;
+
+ // We managed to constrain the parameter to non-null.
+ ProgramStateRef NotNull, Null;
+ std::tie(NotNull, Null) = C.getState()->assume(*Param);
+
+ if (NotNull) {
+ const auto Callback = [Param](PathSensitiveBugReport &BR) -> std::string {
+ return BR.isInteresting(*Param) ? "Assuming the pointer is not null."
+ : "";
+ };
+
+ // Emit note only if this operation constrained the pointer to be null.
+ C.addTransition(NotNull, Null ? C.getNoteTag(Callback) : nullptr);
+ return;
+ }
+
+ // We found a path on which the parameter is NULL.
+ if (ExplodedNode *N = C.generateErrorNode(C.getState())) {
+ auto R = std::make_unique<PathSensitiveBugReport>(
+ BT_Null, "The parameter must not be null", N);
+ bugreporter::trackExpressionValue(N, Call.getArgExpr(0), *R);
+ C.emitReport(std::move(R));
+ }
+}
+
+} // end anonymous namespace
+
+void ento::registerStringChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<StringChecker>();
+}
+
+bool ento::shouldRegisterStringChecker(const CheckerManager &) { return true; }
diff --git a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
index 381334de068e..2244cdb96d4f 100644
--- a/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/UnixAPIChecker.cpp
@@ -110,7 +110,7 @@ void UnixAPIMisuseChecker::checkPreStmt(const CallExpr *CE,
// Don't treat functions in namespaces with the same name a Unix function
// as a call to the Unix function.
const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
- if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx))
+ if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
return;
StringRef FName = C.getCalleeName(FD);
@@ -466,7 +466,7 @@ void UnixAPIPortabilityChecker::checkPreStmt(const CallExpr *CE,
// Don't treat functions in namespaces with the same name a Unix function
// as a call to the Unix function.
const DeclContext *NamespaceCtx = FD->getEnclosingNamespaceContext();
- if (NamespaceCtx && isa<NamespaceDecl>(NamespaceCtx))
+ if (isa_and_nonnull<NamespaceDecl>(NamespaceCtx))
return;
StringRef FName = C.getCalleeName(FD);
diff --git a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
index dde5912b6d6e..60da4fca12e6 100644
--- a/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/ValistChecker.cpp
@@ -15,6 +15,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
@@ -126,15 +127,15 @@ void ValistChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
if (!Call.isGlobalCFunction())
return;
- if (Call.isCalled(VaStart))
+ if (VaStart.matches(Call))
checkVAListStartCall(Call, C, false);
- else if (Call.isCalled(VaCopy))
+ else if (VaCopy.matches(Call))
checkVAListStartCall(Call, C, true);
- else if (Call.isCalled(VaEnd))
+ else if (VaEnd.matches(Call))
checkVAListEndCall(Call, C);
else {
for (auto FuncInfo : VAListAccepters) {
- if (!Call.isCalled(FuncInfo.Func))
+ if (!FuncInfo.Func.matches(Call))
continue;
bool Symbolic;
const MemRegion *VAList =
diff --git a/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
index 8f147026ae19..04e6603b4cbe 100644
--- a/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/VforkChecker.cpp
@@ -9,7 +9,7 @@
// This file defines vfork checker which checks for dangerous uses of vfork.
// Vforked process shares memory (including stack) with parent so it's
// range of actions is significantly limited: can't write variables,
-// can't call functions not in whitelist, etc. For more details, see
+// can't call functions not in the allowed list, etc. For more details, see
// http://man7.org/linux/man-pages/man2/vfork.2.html
//
// This checker checks for prohibited constructs in vforked process.
@@ -44,13 +44,14 @@ namespace {
class VforkChecker : public Checker<check::PreCall, check::PostCall,
check::Bind, check::PreStmt<ReturnStmt>> {
mutable std::unique_ptr<BuiltinBug> BT;
- mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkWhitelist;
+ mutable llvm::SmallSet<const IdentifierInfo *, 10> VforkAllowlist;
mutable const IdentifierInfo *II_vfork;
static bool isChildProcess(const ProgramStateRef State);
bool isVforkCall(const Decl *D, CheckerContext &C) const;
- bool isCallWhitelisted(const IdentifierInfo *II, CheckerContext &C) const;
+ bool isCallExplicitelyAllowed(const IdentifierInfo *II,
+ CheckerContext &C) const;
void reportBug(const char *What, CheckerContext &C,
const char *Details = nullptr) const;
@@ -93,9 +94,9 @@ bool VforkChecker::isVforkCall(const Decl *D, CheckerContext &C) const {
}
// Returns true iff ok to call function after successful vfork.
-bool VforkChecker::isCallWhitelisted(const IdentifierInfo *II,
- CheckerContext &C) const {
- if (VforkWhitelist.empty()) {
+bool VforkChecker::isCallExplicitelyAllowed(const IdentifierInfo *II,
+ CheckerContext &C) const {
+ if (VforkAllowlist.empty()) {
// According to manpage.
const char *ids[] = {
"_Exit",
@@ -112,10 +113,10 @@ bool VforkChecker::isCallWhitelisted(const IdentifierInfo *II,
ASTContext &AC = C.getASTContext();
for (const char **id = ids; *id; ++id)
- VforkWhitelist.insert(&AC.Idents.get(*id));
+ VforkAllowlist.insert(&AC.Idents.get(*id));
}
- return VforkWhitelist.count(II);
+ return VforkAllowlist.count(II);
}
void VforkChecker::reportBug(const char *What, CheckerContext &C,
@@ -179,12 +180,13 @@ void VforkChecker::checkPostCall(const CallEvent &Call,
C.addTransition(ChildState);
}
-// Prohibit calls to non-whitelist functions in child process.
+// Prohibit calls to functions in child process which are not explicitly
+// allowed.
void VforkChecker::checkPreCall(const CallEvent &Call,
CheckerContext &C) const {
ProgramStateRef State = C.getState();
- if (isChildProcess(State)
- && !isCallWhitelisted(Call.getCalleeIdentifier(), C))
+ if (isChildProcess(State) &&
+ !isCallExplicitelyAllowed(Call.getCalleeIdentifier(), C))
reportBug("This function call", C);
}
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
index ed4577755457..ec6a7144fa45 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/ASTUtils.h
@@ -29,7 +29,7 @@ class Expr;
/// values).
///
/// For more context see Static Analyzer checkers documentation - specifically
-/// webkit.UncountedCallArgsChecker checker. Whitelist of transformations:
+/// webkit.UncountedCallArgsChecker checker. Allowed list of transformations:
/// - constructors of ref-counted types (including factory methods)
/// - getters of ref-counted types
/// - member overloaded operators
diff --git a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
index d70bd9489d2c..e6d0948f71bb 100644
--- a/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/WebKit/UncountedCallArgsChecker.cpp
@@ -68,8 +68,7 @@ public:
if (auto *F = CE->getDirectCallee()) {
// Skip the first argument for overloaded member operators (e. g. lambda
// or std::function call operator).
- unsigned ArgIdx =
- isa<CXXOperatorCallExpr>(CE) && dyn_cast_or_null<CXXMethodDecl>(F);
+ unsigned ArgIdx = isa<CXXOperatorCallExpr>(CE) && isa_and_nonnull<CXXMethodDecl>(F);
for (auto P = F->param_begin();
// FIXME: Also check variadic function parameters.
diff --git a/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
new file mode 100644
index 000000000000..d7d573cd2d3b
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Checkers/cert/InvalidPtrChecker.cpp
@@ -0,0 +1,280 @@
+//== InvalidPtrChecker.cpp ------------------------------------- -*- C++ -*--=//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+// This file defines InvalidPtrChecker which finds usages of possibly
+// invalidated pointer.
+// CERT SEI Rules ENV31-C and ENV34-C
+// For more information see:
+// https://wiki.sei.cmu.edu/confluence/x/8tYxBQ
+// https://wiki.sei.cmu.edu/confluence/x/5NUxBQ
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h"
+#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
+#include "clang/StaticAnalyzer/Core/Checker.h"
+#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+
+using namespace clang;
+using namespace ento;
+
+namespace {
+
+class InvalidPtrChecker
+ : public Checker<check::Location, check::BeginFunction, check::PostCall> {
+private:
+ BugType BT{this, "Use of invalidated pointer", categories::MemoryError};
+
+ void EnvpInvalidatingCall(const CallEvent &Call, CheckerContext &C) const;
+
+ using HandlerFn = void (InvalidPtrChecker::*)(const CallEvent &Call,
+ CheckerContext &C) const;
+
+ // SEI CERT ENV31-C
+ const CallDescriptionMap<HandlerFn> EnvpInvalidatingFunctions = {
+ {{"setenv", 3}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{"unsetenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{"putenv", 1}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{"_putenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ {{"_wputenv_s", 2}, &InvalidPtrChecker::EnvpInvalidatingCall},
+ };
+
+ void postPreviousReturnInvalidatingCall(const CallEvent &Call,
+ CheckerContext &C) const;
+
+ // SEI CERT ENV34-C
+ const CallDescriptionMap<HandlerFn> PreviousCallInvalidatingFunctions = {
+ {{"getenv", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{"setlocale", 2},
+ &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{"strerror", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{"localeconv", 0},
+ &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ {{"asctime", 1}, &InvalidPtrChecker::postPreviousReturnInvalidatingCall},
+ };
+
+public:
+ // Obtain the environment pointer from 'main()' (if present).
+ void checkBeginFunction(CheckerContext &C) const;
+
+ // Handle functions in EnvpInvalidatingFunctions, that invalidate environment
+ // pointer from 'main()'
+ // Handle functions in PreviousCallInvalidatingFunctions.
+ // Also, check if invalidated region is passed to a
+ // conservatively evaluated function call as an argument.
+ void checkPostCall(const CallEvent &Call, CheckerContext &C) const;
+
+ // Check if invalidated region is being dereferenced.
+ void checkLocation(SVal l, bool isLoad, const Stmt *S,
+ CheckerContext &C) const;
+};
+
+} // namespace
+
+// Set of memory regions that were invalidated
+REGISTER_SET_WITH_PROGRAMSTATE(InvalidMemoryRegions, const MemRegion *)
+
+// Stores the region of the environment pointer of 'main' (if present).
+// Note: This pointer has type 'const MemRegion *', however the trait is only
+// specialized to 'const void*' and 'void*'
+REGISTER_TRAIT_WITH_PROGRAMSTATE(EnvPtrRegion, const void *)
+
+// Stores key-value pairs, where key is function declaration and value is
+// pointer to memory region returned by previous call of this function
+REGISTER_MAP_WITH_PROGRAMSTATE(PreviousCallResultMap, const FunctionDecl *,
+ const MemRegion *)
+
+void InvalidPtrChecker::EnvpInvalidatingCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ StringRef FunctionName = Call.getCalleeIdentifier()->getName();
+ ProgramStateRef State = C.getState();
+ const auto *Reg = State->get<EnvPtrRegion>();
+ if (!Reg)
+ return;
+ const auto *SymbolicEnvPtrRegion =
+ reinterpret_cast<const MemRegion *>(const_cast<const void *>(Reg));
+
+ State = State->add<InvalidMemoryRegions>(SymbolicEnvPtrRegion);
+
+ const NoteTag *Note =
+ C.getNoteTag([SymbolicEnvPtrRegion, FunctionName](
+ PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
+ if (!BR.isInteresting(SymbolicEnvPtrRegion))
+ return;
+ Out << '\'' << FunctionName
+ << "' call may invalidate the environment parameter of 'main'";
+ });
+
+ C.addTransition(State, Note);
+}
+
+void InvalidPtrChecker::postPreviousReturnInvalidatingCall(
+ const CallEvent &Call, CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ const NoteTag *Note = nullptr;
+ const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ // Invalidate the region of the previously returned pointer - if there was
+ // one.
+ if (const MemRegion *const *Reg = State->get<PreviousCallResultMap>(FD)) {
+ const MemRegion *PrevReg = *Reg;
+ State = State->add<InvalidMemoryRegions>(PrevReg);
+ Note = C.getNoteTag([PrevReg, FD](PathSensitiveBugReport &BR,
+ llvm::raw_ostream &Out) {
+ if (!BR.isInteresting(PrevReg))
+ return;
+ Out << '\'';
+ FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
+ Out << "' call may invalidate the the result of the previous " << '\'';
+ FD->getNameForDiagnostic(Out, FD->getASTContext().getLangOpts(), true);
+ Out << '\'';
+ });
+ }
+
+ const LocationContext *LCtx = C.getLocationContext();
+ const auto *CE = cast<CallExpr>(Call.getOriginExpr());
+
+ // Function call will return a pointer to the new symbolic region.
+ DefinedOrUnknownSVal RetVal = C.getSValBuilder().conjureSymbolVal(
+ CE, LCtx, CE->getType(), C.blockCount());
+ State = State->BindExpr(CE, LCtx, RetVal);
+
+ // Remember to this region.
+ const auto *SymRegOfRetVal = cast<SymbolicRegion>(RetVal.getAsRegion());
+ const MemRegion *MR =
+ const_cast<MemRegion *>(SymRegOfRetVal->getBaseRegion());
+ State = State->set<PreviousCallResultMap>(FD, MR);
+
+ ExplodedNode *Node = C.addTransition(State, Note);
+ const NoteTag *PreviousCallNote =
+ C.getNoteTag([MR](PathSensitiveBugReport &BR, llvm::raw_ostream &Out) {
+ if (!BR.isInteresting(MR))
+ return;
+ Out << '\'' << "'previous function call was here" << '\'';
+ });
+
+ C.addTransition(State, Node, PreviousCallNote);
+}
+
+// TODO: This seems really ugly. Simplify this.
+static const MemRegion *findInvalidatedSymbolicBase(ProgramStateRef State,
+ const MemRegion *Reg) {
+ while (Reg) {
+ if (State->contains<InvalidMemoryRegions>(Reg))
+ return Reg;
+ const auto *SymBase = Reg->getSymbolicBase();
+ if (!SymBase)
+ break;
+ const auto *SRV = dyn_cast<SymbolRegionValue>(SymBase->getSymbol());
+ if (!SRV)
+ break;
+ Reg = SRV->getRegion();
+ if (const auto *VarReg = dyn_cast<VarRegion>(SRV->getRegion()))
+ Reg = VarReg;
+ }
+ return nullptr;
+}
+
+// Handle functions in EnvpInvalidatingFunctions, that invalidate environment
+// pointer from 'main()' Also, check if invalidated region is passed to a
+// function call as an argument.
+void InvalidPtrChecker::checkPostCall(const CallEvent &Call,
+ CheckerContext &C) const {
+ // Check if function invalidates 'envp' argument of 'main'
+ if (const auto *Handler = EnvpInvalidatingFunctions.lookup(Call))
+ (this->**Handler)(Call, C);
+
+ // Check if function invalidates the result of previous call
+ if (const auto *Handler = PreviousCallInvalidatingFunctions.lookup(Call))
+ (this->**Handler)(Call, C);
+
+ // Check if one of the arguments of the function call is invalidated
+
+ // If call was inlined, don't report invalidated argument
+ if (C.wasInlined)
+ return;
+
+ ProgramStateRef State = C.getState();
+
+ for (unsigned I = 0, NumArgs = Call.getNumArgs(); I < NumArgs; ++I) {
+
+ if (const auto *SR = dyn_cast_or_null<SymbolicRegion>(
+ Call.getArgSVal(I).getAsRegion())) {
+ if (const MemRegion *InvalidatedSymbolicBase =
+ findInvalidatedSymbolicBase(State, SR)) {
+ ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
+ if (!ErrorNode)
+ return;
+
+ SmallString<256> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+ Out << "use of invalidated pointer '";
+ Call.getArgExpr(I)->printPretty(Out, /*Helper=*/nullptr,
+ C.getASTContext().getPrintingPolicy());
+ Out << "' in a function call";
+
+ auto Report =
+ std::make_unique<PathSensitiveBugReport>(BT, Out.str(), ErrorNode);
+ Report->markInteresting(InvalidatedSymbolicBase);
+ Report->addRange(Call.getArgSourceRange(I));
+ C.emitReport(std::move(Report));
+ }
+ }
+ }
+}
+
+// Obtain the environment pointer from 'main()', if present.
+void InvalidPtrChecker::checkBeginFunction(CheckerContext &C) const {
+ if (!C.inTopFrame())
+ return;
+
+ const auto *FD = dyn_cast<FunctionDecl>(C.getLocationContext()->getDecl());
+ if (!FD || FD->param_size() != 3 || !FD->isMain())
+ return;
+
+ ProgramStateRef State = C.getState();
+ const MemRegion *EnvpReg =
+ State->getRegion(FD->parameters()[2], C.getLocationContext());
+
+ // Save the memory region pointed by the environment pointer parameter of
+ // 'main'.
+ State = State->set<EnvPtrRegion>(
+ reinterpret_cast<void *>(const_cast<MemRegion *>(EnvpReg)));
+ C.addTransition(State);
+}
+
+// Check if invalidated region is being dereferenced.
+void InvalidPtrChecker::checkLocation(SVal Loc, bool isLoad, const Stmt *S,
+ CheckerContext &C) const {
+ ProgramStateRef State = C.getState();
+
+ // Ignore memory operations involving 'non-invalidated' locations.
+ const MemRegion *InvalidatedSymbolicBase =
+ findInvalidatedSymbolicBase(State, Loc.getAsRegion());
+ if (!InvalidatedSymbolicBase)
+ return;
+
+ ExplodedNode *ErrorNode = C.generateNonFatalErrorNode();
+ if (!ErrorNode)
+ return;
+
+ auto Report = std::make_unique<PathSensitiveBugReport>(
+ BT, "dereferencing an invalid pointer", ErrorNode);
+ Report->markInteresting(InvalidatedSymbolicBase);
+ C.emitReport(std::move(Report));
+}
+
+void ento::registerInvalidPtrChecker(CheckerManager &Mgr) {
+ Mgr.registerChecker<InvalidPtrChecker>();
+}
+
+bool ento::shouldRegisterInvalidPtrChecker(const CheckerManager &) {
+ return true;
+}
diff --git a/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp
index 1c67bbd77ec8..ed3bdafad084 100644
--- a/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/cert/PutenvWithAutoChecker.cpp
@@ -17,6 +17,7 @@
#include "clang/StaticAnalyzer/Core/BugReporter/BugType.h"
#include "clang/StaticAnalyzer/Core/Checker.h"
#include "clang/StaticAnalyzer/Core/CheckerManager.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/MemRegion.h"
@@ -38,7 +39,7 @@ public:
void PutenvWithAutoChecker::checkPostCall(const CallEvent &Call,
CheckerContext &C) const {
- if (!Call.isCalled(Putenv))
+ if (!Putenv.matches(Call))
return;
SVal ArgV = Call.getArgSVal(0);
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index d6f69ae03afe..771ed2578f6d 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -188,6 +188,9 @@ public:
PathPieces &getMutablePieces() { return PD->getMutablePieces(); }
bool shouldAddPathEdges() const { return Consumer->shouldAddPathEdges(); }
+ bool shouldAddControlNotes() const {
+ return Consumer->shouldAddControlNotes();
+ }
bool shouldGenerateDiagnostics() const {
return Consumer->shouldGenerateDiagnostics();
}
@@ -534,10 +537,10 @@ static void removeEdgesToDefaultInitializers(PathPieces &Pieces) {
if (auto *CF = dyn_cast<PathDiagnosticControlFlowPiece>(I->get())) {
const Stmt *Start = CF->getStartLocation().asStmt();
const Stmt *End = CF->getEndLocation().asStmt();
- if (Start && isa<CXXDefaultInitExpr>(Start)) {
+ if (isa_and_nonnull<CXXDefaultInitExpr>(Start)) {
I = Pieces.erase(I);
continue;
- } else if (End && isa<CXXDefaultInitExpr>(End)) {
+ } else if (isa_and_nonnull<CXXDefaultInitExpr>(End)) {
PathPieces::iterator Next = std::next(I);
if (Next != E) {
if (auto *NextCF =
@@ -1232,8 +1235,11 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
} else if (auto BE = P.getAs<BlockEdge>()) {
- if (!C.shouldAddPathEdges()) {
+ if (C.shouldAddControlNotes()) {
generateMinimalDiagForBlockEdge(C, *BE);
+ }
+
+ if (!C.shouldAddPathEdges()) {
return;
}
@@ -1254,12 +1260,14 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
// do-while statements are explicitly excluded here
auto p = std::make_shared<PathDiagnosticEventPiece>(
- L, "Looping back to the head "
- "of the loop");
+ L, "Looping back to the head of the loop");
p->setPrunable(true);
addEdgeToPath(C.getActivePath(), PrevLoc, p->getLocation());
- C.getActivePath().push_front(std::move(p));
+ // We might've added a very similar control node already
+ if (!C.shouldAddControlNotes()) {
+ C.getActivePath().push_front(std::move(p));
+ }
if (const auto *CS = dyn_cast_or_null<CompoundStmt>(Body)) {
addEdgeToPath(C.getActivePath(), PrevLoc,
@@ -1300,10 +1308,13 @@ void PathDiagnosticBuilder::generatePathDiagnosticsForNode(
auto PE = std::make_shared<PathDiagnosticEventPiece>(L, str);
PE->setPrunable(true);
addEdgeToPath(C.getActivePath(), PrevLoc, PE->getLocation());
- C.getActivePath().push_front(std::move(PE));
+
+ // We might've added a very similar control node already
+ if (!C.shouldAddControlNotes()) {
+ C.getActivePath().push_front(std::move(PE));
+ }
}
- } else if (isa<BreakStmt>(Term) || isa<ContinueStmt>(Term) ||
- isa<GotoStmt>(Term)) {
+ } else if (isa<BreakStmt, ContinueStmt, GotoStmt>(Term)) {
PathDiagnosticLocation L(Term, SM, C.getCurrLocationContext());
addEdgeToPath(C.getActivePath(), PrevLoc, L);
}
@@ -1342,9 +1353,7 @@ static const Stmt *getStmtParent(const Stmt *S, const ParentMap &PM) {
if (!S)
break;
- if (isa<FullExpr>(S) ||
- isa<CXXBindTemporaryExpr>(S) ||
- isa<SubstNonTypeTemplateParmExpr>(S))
+ if (isa<FullExpr, CXXBindTemporaryExpr, SubstNonTypeTemplateParmExpr>(S))
continue;
break;
@@ -1446,7 +1455,7 @@ static void addContextEdges(PathPieces &pieces, const LocationContext *LC) {
break;
// If the source is in the same context, we're already good.
- if (llvm::find(SrcContexts, DstContext) != SrcContexts.end())
+ if (llvm::is_contained(SrcContexts, DstContext))
break;
// Update the subexpression node to point to the context edge.
@@ -1540,9 +1549,8 @@ static void simplifySimpleBranches(PathPieces &pieces) {
// We only perform this transformation for specific branch kinds.
// We don't want to do this for do..while, for example.
- if (!(isa<ForStmt>(s1Start) || isa<WhileStmt>(s1Start) ||
- isa<IfStmt>(s1Start) || isa<ObjCForCollectionStmt>(s1Start) ||
- isa<CXXForRangeStmt>(s1Start)))
+ if (!isa<ForStmt, WhileStmt, IfStmt, ObjCForCollectionStmt,
+ CXXForRangeStmt>(s1Start))
continue;
// Is s1End the branch condition?
@@ -3181,7 +3189,7 @@ findExecutedLines(const SourceManager &SM, const ExplodedNode *N) {
P = N->getParentMap().getParent(RS);
}
- if (P && (isa<SwitchCase>(P) || isa<LabelStmt>(P)))
+ if (isa_and_nonnull<SwitchCase, LabelStmt>(P))
populateExecutedLinesWithStmt(P, SM, *ExecutedLines);
}
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index d06a2d493303..8774dc3323ab 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -344,45 +344,178 @@ BugReporterVisitor::getDefaultEndPath(const BugReporterContext &BRC,
}
//===----------------------------------------------------------------------===//
+// Implementation of NoStateChangeFuncVisitor.
+//===----------------------------------------------------------------------===//
+
+bool NoStateChangeFuncVisitor::isModifiedInFrame(const ExplodedNode *N) {
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *SCtx = Ctx->getStackFrame();
+ if (!FramesModifyingCalculated.count(SCtx))
+ findModifyingFrames(N);
+ return FramesModifying.count(SCtx);
+}
+
+void NoStateChangeFuncVisitor::markFrameAsModifying(
+ const StackFrameContext *SCtx) {
+ while (!SCtx->inTopFrame()) {
+ auto p = FramesModifying.insert(SCtx);
+ if (!p.second)
+ break; // Frame and all its parents already inserted.
+
+ SCtx = SCtx->getParent()->getStackFrame();
+ }
+}
+
+static const ExplodedNode *getMatchingCallExitEnd(const ExplodedNode *N) {
+ assert(N->getLocationAs<CallEnter>());
+ // The stackframe of the callee is only found in the nodes succeeding
+ // the CallEnter node. CallEnter's stack frame refers to the caller.
+ const StackFrameContext *OrigSCtx = N->getFirstSucc()->getStackFrame();
+
+ // Similarly, the nodes preceding CallExitEnd refer to the callee's stack
+ // frame.
+ auto IsMatchingCallExitEnd = [OrigSCtx](const ExplodedNode *N) {
+ return N->getLocationAs<CallExitEnd>() &&
+ OrigSCtx == N->getFirstPred()->getStackFrame();
+ };
+ while (N && !IsMatchingCallExitEnd(N)) {
+ assert(N->succ_size() <= 1 &&
+ "This function is to be used on the trimmed ExplodedGraph!");
+ N = N->getFirstSucc();
+ }
+ return N;
+}
+
+void NoStateChangeFuncVisitor::findModifyingFrames(
+ const ExplodedNode *const CallExitBeginN) {
+
+ assert(CallExitBeginN->getLocationAs<CallExitBegin>());
+
+ const StackFrameContext *const OriginalSCtx =
+ CallExitBeginN->getLocationContext()->getStackFrame();
+
+ const ExplodedNode *CurrCallExitBeginN = CallExitBeginN;
+ const StackFrameContext *CurrentSCtx = OriginalSCtx;
+
+ for (const ExplodedNode *CurrN = CallExitBeginN; CurrN;
+ CurrN = CurrN->getFirstPred()) {
+ // Found a new inlined call.
+ if (CurrN->getLocationAs<CallExitBegin>()) {
+ CurrCallExitBeginN = CurrN;
+ CurrentSCtx = CurrN->getStackFrame();
+ FramesModifyingCalculated.insert(CurrentSCtx);
+ // We won't see a change in between two identical exploded nodes: skip.
+ continue;
+ }
+
+ if (auto CE = CurrN->getLocationAs<CallEnter>()) {
+ if (const ExplodedNode *CallExitEndN = getMatchingCallExitEnd(CurrN))
+ if (wasModifiedInFunction(CurrN, CallExitEndN))
+ markFrameAsModifying(CurrentSCtx);
+
+ // We exited this inlined call, lets actualize the stack frame.
+ CurrentSCtx = CurrN->getStackFrame();
+
+ // Stop calculating at the current function, but always regard it as
+ // modifying, so we can avoid notes like this:
+ // void f(Foo &F) {
+ // F.field = 0; // note: 0 assigned to 'F.field'
+ // // note: returning without writing to 'F.field'
+ // }
+ if (CE->getCalleeContext() == OriginalSCtx) {
+ markFrameAsModifying(CurrentSCtx);
+ break;
+ }
+ }
+
+ if (wasModifiedBeforeCallExit(CurrN, CurrCallExitBeginN))
+ markFrameAsModifying(CurrentSCtx);
+ }
+}
+
+PathDiagnosticPieceRef NoStateChangeFuncVisitor::VisitNode(
+ const ExplodedNode *N, BugReporterContext &BR, PathSensitiveBugReport &R) {
+
+ const LocationContext *Ctx = N->getLocationContext();
+ const StackFrameContext *SCtx = Ctx->getStackFrame();
+ ProgramStateRef State = N->getState();
+ auto CallExitLoc = N->getLocationAs<CallExitBegin>();
+
+ // No diagnostic if region was modified inside the frame.
+ if (!CallExitLoc || isModifiedInFrame(N))
+ return nullptr;
+
+ CallEventRef<> Call =
+ BR.getStateManager().getCallEventManager().getCaller(SCtx, State);
+
+ // Optimistically suppress uninitialized value bugs that result
+ // from system headers having a chance to initialize the value
+ // but failing to do so. It's too unlikely a system header's fault.
+ // It's much more likely a situation in which the function has a failure
+ // mode that the user decided not to check. If we want to hunt such
+ // omitted checks, we should provide an explicit function-specific note
+ // describing the precondition under which the function isn't supposed to
+ // initialize its out-parameter, and additionally check that such
+ // precondition can actually be fulfilled on the current path.
+ if (Call->isInSystemHeader()) {
+ // We make an exception for system header functions that have no branches.
+ // Such functions unconditionally fail to initialize the variable.
+ // If they call other functions that have more paths within them,
+ // this suppression would still apply when we visit these inner functions.
+ // One common example of a standard function that doesn't ever initialize
+ // its out parameter is operator placement new; it's up to the follow-up
+ // constructor (if any) to initialize the memory.
+ if (!N->getStackFrame()->getCFG()->isLinear()) {
+ static int i = 0;
+ R.markInvalid(&i, nullptr);
+ }
+ return nullptr;
+ }
+
+ if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) {
+ // If we failed to construct a piece for self, we still want to check
+ // whether the entity of interest is in a parameter.
+ if (PathDiagnosticPieceRef Piece = maybeEmitNoteForObjCSelf(R, *MC, N))
+ return Piece;
+ }
+
+ if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
+ // Do not generate diagnostics for not modified parameters in
+ // constructors.
+ return maybeEmitNoteForCXXThis(R, *CCall, N);
+ }
+
+ return maybeEmitNoteForParameters(R, *Call, N);
+}
+
+//===----------------------------------------------------------------------===//
// Implementation of NoStoreFuncVisitor.
//===----------------------------------------------------------------------===//
namespace {
-
/// Put a diagnostic on return statement of all inlined functions
/// for which the region of interest \p RegionOfInterest was passed into,
/// but not written inside, and it has caused an undefined read or a null
/// pointer dereference outside.
-class NoStoreFuncVisitor final : public BugReporterVisitor {
+class NoStoreFuncVisitor final : public NoStateChangeFuncVisitor {
const SubRegion *RegionOfInterest;
MemRegionManager &MmrMgr;
const SourceManager &SM;
const PrintingPolicy &PP;
- bugreporter::TrackingKind TKind;
/// Recursion limit for dereferencing fields when looking for the
/// region of interest.
/// The limit of two indicates that we will dereference fields only once.
static const unsigned DEREFERENCE_LIMIT = 2;
- /// Frames writing into \c RegionOfInterest.
- /// This visitor generates a note only if a function does not write into
- /// a region of interest. This information is not immediately available
- /// by looking at the node associated with the exit from the function
- /// (usually the return statement). To avoid recomputing the same information
- /// many times (going up the path for each node and checking whether the
- /// region was written into) we instead lazily compute the
- /// stack frames along the path which write into the region of interest.
- llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingRegion;
- llvm::SmallPtrSet<const StackFrameContext *, 32> FramesModifyingCalculated;
-
using RegionVector = SmallVector<const MemRegion *, 5>;
public:
NoStoreFuncVisitor(const SubRegion *R, bugreporter::TrackingKind TKind)
- : RegionOfInterest(R), MmrMgr(R->getMemRegionManager()),
+ : NoStateChangeFuncVisitor(TKind), RegionOfInterest(R),
+ MmrMgr(R->getMemRegionManager()),
SM(MmrMgr.getContext().getSourceManager()),
- PP(MmrMgr.getContext().getPrintingPolicy()), TKind(TKind) {}
+ PP(MmrMgr.getContext().getPrintingPolicy()) {}
void Profile(llvm::FoldingSetNodeID &ID) const override {
static int Tag = 0;
@@ -395,11 +528,13 @@ public:
return static_cast<void *>(&Tag);
}
- PathDiagnosticPieceRef VisitNode(const ExplodedNode *N,
- BugReporterContext &BR,
- PathSensitiveBugReport &R) override;
-
private:
+ /// \return Whether \c RegionOfInterest was modified at \p CurrN compared to
+ /// the value it holds in \p CallExitBeginN.
+ virtual bool
+ wasModifiedBeforeCallExit(const ExplodedNode *CurrN,
+ const ExplodedNode *CallExitBeginN) override;
+
/// Attempts to find the region of interest in a given record decl,
/// by either following the base classes or fields.
/// Dereferences fields up to a given recursion limit.
@@ -411,20 +546,21 @@ private:
const MemRegion *R, const RegionVector &Vec = {},
int depth = 0);
- /// Check and lazily calculate whether the region of interest is
- /// modified in the stack frame to which \p N belongs.
- /// The calculation is cached in FramesModifyingRegion.
- bool isRegionOfInterestModifiedInFrame(const ExplodedNode *N) {
- const LocationContext *Ctx = N->getLocationContext();
- const StackFrameContext *SCtx = Ctx->getStackFrame();
- if (!FramesModifyingCalculated.count(SCtx))
- findModifyingFrames(N);
- return FramesModifyingRegion.count(SCtx);
- }
+ // Region of interest corresponds to an IVar, exiting a method
+ // which could have written into that IVar, but did not.
+ virtual PathDiagnosticPieceRef
+ maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
+ const ObjCMethodCall &Call,
+ const ExplodedNode *N) override final;
+
+ virtual PathDiagnosticPieceRef
+ maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
+ const CXXConstructorCall &Call,
+ const ExplodedNode *N) override final;
- /// Write to \c FramesModifyingRegion all stack frames along
- /// the path in the current stack frame which modify \c RegionOfInterest.
- void findModifyingFrames(const ExplodedNode *N);
+ virtual PathDiagnosticPieceRef
+ maybeEmitNoteForParameters(PathSensitiveBugReport &R, const CallEvent &Call,
+ const ExplodedNode *N) override final;
/// Consume the information on the no-store stack frame in order to
/// either emit a note or suppress the report enirely.
@@ -436,22 +572,18 @@ private:
const MemRegion *MatchedRegion, StringRef FirstElement,
bool FirstIsReferenceType, unsigned IndirectionLevel);
- /// Pretty-print region \p MatchedRegion to \p os.
- /// \return Whether printing succeeded.
- bool prettyPrintRegionName(StringRef FirstElement, bool FirstIsReferenceType,
+ bool prettyPrintRegionName(const RegionVector &FieldChain,
const MemRegion *MatchedRegion,
- const RegionVector &FieldChain,
- int IndirectionLevel,
+ StringRef FirstElement, bool FirstIsReferenceType,
+ unsigned IndirectionLevel,
llvm::raw_svector_ostream &os);
- /// Print first item in the chain, return new separator.
- static StringRef prettyPrintFirstElement(StringRef FirstElement,
- bool MoreItemsExpected,
- int IndirectionLevel,
- llvm::raw_svector_ostream &os);
+ StringRef prettyPrintFirstElement(StringRef FirstElement,
+ bool MoreItemsExpected,
+ int IndirectionLevel,
+ llvm::raw_svector_ostream &os);
};
-
-} // end of anonymous namespace
+} // namespace
/// \return Whether the method declaration \p Parent
/// syntactically has a binary operation writing into the ivar \p Ivar.
@@ -486,25 +618,6 @@ static bool potentiallyWritesIntoIvar(const Decl *Parent,
return false;
}
-/// Get parameters associated with runtime definition in order
-/// to get the correct parameter name.
-static ArrayRef<ParmVarDecl *> getCallParameters(CallEventRef<> Call) {
- // Use runtime definition, if available.
- RuntimeDefinition RD = Call->getRuntimeDefinition();
- if (const auto *FD = dyn_cast_or_null<FunctionDecl>(RD.getDecl()))
- return FD->parameters();
- if (const auto *MD = dyn_cast_or_null<ObjCMethodDecl>(RD.getDecl()))
- return MD->parameters();
-
- return Call->parameters();
-}
-
-/// \return whether \p Ty points to a const type, or is a const reference.
-static bool isPointerToConst(QualType Ty) {
- return !Ty->getPointeeType().isNull() &&
- Ty->getPointeeType().getCanonicalType().isConstQualified();
-}
-
/// Attempts to find the region of interest in a given CXX decl,
/// by either following the base classes or fields.
/// Dereferences fields up to a given recursion limit.
@@ -564,68 +677,66 @@ NoStoreFuncVisitor::findRegionOfInterestInRecord(
}
PathDiagnosticPieceRef
-NoStoreFuncVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BR,
- PathSensitiveBugReport &R) {
-
- const LocationContext *Ctx = N->getLocationContext();
- const StackFrameContext *SCtx = Ctx->getStackFrame();
- ProgramStateRef State = N->getState();
- auto CallExitLoc = N->getLocationAs<CallExitBegin>();
-
- // No diagnostic if region was modified inside the frame.
- if (!CallExitLoc || isRegionOfInterestModifiedInFrame(N))
- return nullptr;
-
- CallEventRef<> Call =
- BR.getStateManager().getCallEventManager().getCaller(SCtx, State);
-
- // Region of interest corresponds to an IVar, exiting a method
- // which could have written into that IVar, but did not.
- if (const auto *MC = dyn_cast<ObjCMethodCall>(Call)) {
- if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) {
- const MemRegion *SelfRegion = MC->getReceiverSVal().getAsRegion();
- if (RegionOfInterest->isSubRegionOf(SelfRegion) &&
- potentiallyWritesIntoIvar(Call->getRuntimeDefinition().getDecl(),
- IvarR->getDecl()))
- return maybeEmitNote(R, *Call, N, {}, SelfRegion, "self",
- /*FirstIsReferenceType=*/false, 1);
- }
+NoStoreFuncVisitor::maybeEmitNoteForObjCSelf(PathSensitiveBugReport &R,
+ const ObjCMethodCall &Call,
+ const ExplodedNode *N) {
+ if (const auto *IvarR = dyn_cast<ObjCIvarRegion>(RegionOfInterest)) {
+ const MemRegion *SelfRegion = Call.getReceiverSVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(SelfRegion) &&
+ potentiallyWritesIntoIvar(Call.getRuntimeDefinition().getDecl(),
+ IvarR->getDecl()))
+ return maybeEmitNote(R, Call, N, {}, SelfRegion, "self",
+ /*FirstIsReferenceType=*/false, 1);
}
+ return nullptr;
+}
- if (const auto *CCall = dyn_cast<CXXConstructorCall>(Call)) {
- const MemRegion *ThisR = CCall->getCXXThisVal().getAsRegion();
- if (RegionOfInterest->isSubRegionOf(ThisR) &&
- !CCall->getDecl()->isImplicit())
- return maybeEmitNote(R, *Call, N, {}, ThisR, "this",
- /*FirstIsReferenceType=*/false, 1);
+PathDiagnosticPieceRef
+NoStoreFuncVisitor::maybeEmitNoteForCXXThis(PathSensitiveBugReport &R,
+ const CXXConstructorCall &Call,
+ const ExplodedNode *N) {
+ const MemRegion *ThisR = Call.getCXXThisVal().getAsRegion();
+ if (RegionOfInterest->isSubRegionOf(ThisR) && !Call.getDecl()->isImplicit())
+ return maybeEmitNote(R, Call, N, {}, ThisR, "this",
+ /*FirstIsReferenceType=*/false, 1);
+
+ // Do not generate diagnostics for not modified parameters in
+ // constructors.
+ return nullptr;
+}
- // Do not generate diagnostics for not modified parameters in
- // constructors.
- return nullptr;
- }
+/// \return whether \p Ty points to a const type, or is a const reference.
+static bool isPointerToConst(QualType Ty) {
+ return !Ty->getPointeeType().isNull() &&
+ Ty->getPointeeType().getCanonicalType().isConstQualified();
+}
- ArrayRef<ParmVarDecl *> parameters = getCallParameters(Call);
- for (unsigned I = 0; I < Call->getNumArgs() && I < parameters.size(); ++I) {
- const ParmVarDecl *PVD = parameters[I];
- SVal V = Call->getArgSVal(I);
+PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNoteForParameters(
+ PathSensitiveBugReport &R, const CallEvent &Call, const ExplodedNode *N) {
+ ArrayRef<ParmVarDecl *> Parameters = Call.parameters();
+ for (unsigned I = 0; I < Call.getNumArgs() && I < Parameters.size(); ++I) {
+ const ParmVarDecl *PVD = Parameters[I];
+ SVal V = Call.getArgSVal(I);
bool ParamIsReferenceType = PVD->getType()->isReferenceType();
std::string ParamName = PVD->getNameAsString();
- int IndirectionLevel = 1;
+ unsigned IndirectionLevel = 1;
QualType T = PVD->getType();
while (const MemRegion *MR = V.getAsRegion()) {
if (RegionOfInterest->isSubRegionOf(MR) && !isPointerToConst(T))
- return maybeEmitNote(R, *Call, N, {}, MR, ParamName,
+ return maybeEmitNote(R, Call, N, {}, MR, ParamName,
ParamIsReferenceType, IndirectionLevel);
QualType PT = T->getPointeeType();
if (PT.isNull() || PT->isVoidType())
break;
+ ProgramStateRef State = N->getState();
+
if (const RecordDecl *RD = PT->getAsRecordDecl())
if (Optional<RegionVector> P =
findRegionOfInterestInRecord(RD, State, MR))
- return maybeEmitNote(R, *Call, N, *P, RegionOfInterest, ParamName,
+ return maybeEmitNote(R, Call, N, *P, RegionOfInterest, ParamName,
ParamIsReferenceType, IndirectionLevel);
V = State->getSVal(MR, PT);
@@ -637,40 +748,11 @@ NoStoreFuncVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BR,
return nullptr;
}
-void NoStoreFuncVisitor::findModifyingFrames(const ExplodedNode *N) {
- assert(N->getLocationAs<CallExitBegin>());
- ProgramStateRef LastReturnState = N->getState();
- SVal ValueAtReturn = LastReturnState->getSVal(RegionOfInterest);
- const LocationContext *Ctx = N->getLocationContext();
- const StackFrameContext *OriginalSCtx = Ctx->getStackFrame();
-
- do {
- ProgramStateRef State = N->getState();
- auto CallExitLoc = N->getLocationAs<CallExitBegin>();
- if (CallExitLoc) {
- LastReturnState = State;
- ValueAtReturn = LastReturnState->getSVal(RegionOfInterest);
- }
-
- FramesModifyingCalculated.insert(N->getLocationContext()->getStackFrame());
-
- if (wasRegionOfInterestModifiedAt(RegionOfInterest, N, ValueAtReturn)) {
- const StackFrameContext *SCtx = N->getStackFrame();
- while (!SCtx->inTopFrame()) {
- auto p = FramesModifyingRegion.insert(SCtx);
- if (!p.second)
- break; // Frame and all its parents already inserted.
- SCtx = SCtx->getParent()->getStackFrame();
- }
- }
-
- // Stop calculation at the call to the current function.
- if (auto CE = N->getLocationAs<CallEnter>())
- if (CE->getCalleeContext() == OriginalSCtx)
- break;
-
- N = N->getFirstPred();
- } while (N);
+bool NoStoreFuncVisitor::wasModifiedBeforeCallExit(
+ const ExplodedNode *CurrN, const ExplodedNode *CallExitBeginN) {
+ return ::wasRegionOfInterestModifiedAt(
+ RegionOfInterest, CurrN,
+ CallExitBeginN->getState()->getSVal(RegionOfInterest));
}
static llvm::StringLiteral WillBeUsedForACondition =
@@ -681,27 +763,6 @@ PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote(
const RegionVector &FieldChain, const MemRegion *MatchedRegion,
StringRef FirstElement, bool FirstIsReferenceType,
unsigned IndirectionLevel) {
- // Optimistically suppress uninitialized value bugs that result
- // from system headers having a chance to initialize the value
- // but failing to do so. It's too unlikely a system header's fault.
- // It's much more likely a situation in which the function has a failure
- // mode that the user decided not to check. If we want to hunt such
- // omitted checks, we should provide an explicit function-specific note
- // describing the precondition under which the function isn't supposed to
- // initialize its out-parameter, and additionally check that such
- // precondition can actually be fulfilled on the current path.
- if (Call.isInSystemHeader()) {
- // We make an exception for system header functions that have no branches.
- // Such functions unconditionally fail to initialize the variable.
- // If they call other functions that have more paths within them,
- // this suppression would still apply when we visit these inner functions.
- // One common example of a standard function that doesn't ever initialize
- // its out parameter is operator placement new; it's up to the follow-up
- // constructor (if any) to initialize the memory.
- if (!N->getStackFrame()->getCFG()->isLinear())
- R.markInvalid(getTag(), nullptr);
- return nullptr;
- }
PathDiagnosticLocation L =
PathDiagnosticLocation::create(N->getLocation(), SM);
@@ -717,8 +778,8 @@ PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote(
os << "Returning without writing to '";
// Do not generate the note if failed to pretty-print.
- if (!prettyPrintRegionName(FirstElement, FirstIsReferenceType, MatchedRegion,
- FieldChain, IndirectionLevel, os))
+ if (!prettyPrintRegionName(FieldChain, MatchedRegion, FirstElement,
+ FirstIsReferenceType, IndirectionLevel, os))
return nullptr;
os << "'";
@@ -727,11 +788,11 @@ PathDiagnosticPieceRef NoStoreFuncVisitor::maybeEmitNote(
return std::make_shared<PathDiagnosticEventPiece>(L, os.str());
}
-bool NoStoreFuncVisitor::prettyPrintRegionName(StringRef FirstElement,
- bool FirstIsReferenceType,
+bool NoStoreFuncVisitor::prettyPrintRegionName(const RegionVector &FieldChain,
const MemRegion *MatchedRegion,
- const RegionVector &FieldChain,
- int IndirectionLevel,
+ StringRef FirstElement,
+ bool FirstIsReferenceType,
+ unsigned IndirectionLevel,
llvm::raw_svector_ostream &os) {
if (FirstIsReferenceType)
@@ -754,7 +815,7 @@ bool NoStoreFuncVisitor::prettyPrintRegionName(StringRef FirstElement,
// Just keep going up to the base region.
// Element regions may appear due to casts.
- if (isa<CXXBaseObjectRegion>(R) || isa<CXXTempObjectRegion>(R))
+ if (isa<CXXBaseObjectRegion, CXXTempObjectRegion>(R))
continue;
if (Sep.empty())
@@ -1153,7 +1214,7 @@ class StoreSiteFinder final : public TrackingBugReporterVisitor {
public:
/// \param V We're searching for the store where \c R received this value.
/// \param R The region we're tracking.
- /// \param TKind May limit the amount of notes added to the bug report.
+ /// \param Options Tracking behavior options.
/// \param OriginSFC Only adds notes when the last store happened in a
/// different stackframe to this one. Disregarded if the tracking kind
/// is thorough.
@@ -2674,9 +2735,8 @@ bool ConditionBRVisitor::patternMatch(const Expr *Ex,
const Expr *OriginalExpr = Ex;
Ex = Ex->IgnoreParenCasts();
- if (isa<GNUNullExpr>(Ex) || isa<ObjCBoolLiteralExpr>(Ex) ||
- isa<CXXBoolLiteralExpr>(Ex) || isa<IntegerLiteral>(Ex) ||
- isa<FloatingLiteral>(Ex)) {
+ if (isa<GNUNullExpr, ObjCBoolLiteralExpr, CXXBoolLiteralExpr, IntegerLiteral,
+ FloatingLiteral>(Ex)) {
// Use heuristics to determine if the expression is a macro
// expanding to a literal and if so, use the macro's name.
SourceLocation BeginLoc = OriginalExpr->getBeginLoc();
diff --git a/clang/lib/StaticAnalyzer/Core/CallDescription.cpp b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp
new file mode 100644
index 000000000000..810fe365d021
--- /dev/null
+++ b/clang/lib/StaticAnalyzer/Core/CallDescription.cpp
@@ -0,0 +1,146 @@
+//===- CallDescription.cpp - function/method call matching --*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+//
+/// \file This file defines a generic mechanism for matching for function and
+/// method calls of C, C++, and Objective-C languages. Instances of these
+/// classes are frequently used together with the CallEvent classes.
+//
+//===----------------------------------------------------------------------===//
+
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallEvent.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/Optional.h"
+#include <iterator>
+
+using namespace llvm;
+using namespace clang;
+
+using MaybeCount = Optional<unsigned>;
+
+// A constructor helper.
+static MaybeCount readRequiredParams(MaybeCount RequiredArgs,
+ MaybeCount RequiredParams) {
+ if (RequiredParams)
+ return RequiredParams;
+ if (RequiredArgs)
+ return RequiredArgs;
+ return None;
+}
+
+ento::CallDescription::CallDescription(CallDescriptionFlags Flags,
+ ArrayRef<const char *> QualifiedName,
+ MaybeCount RequiredArgs /*= None*/,
+ MaybeCount RequiredParams /*= None*/)
+ : RequiredArgs(RequiredArgs),
+ RequiredParams(readRequiredParams(RequiredArgs, RequiredParams)),
+ Flags(Flags) {
+ assert(!QualifiedName.empty());
+ this->QualifiedName.reserve(QualifiedName.size());
+ llvm::copy(QualifiedName, std::back_inserter(this->QualifiedName));
+}
+
+/// Construct a CallDescription with default flags.
+ento::CallDescription::CallDescription(ArrayRef<const char *> QualifiedName,
+ MaybeCount RequiredArgs /*= None*/,
+ MaybeCount RequiredParams /*= None*/)
+ : CallDescription(CDF_None, QualifiedName, RequiredArgs, RequiredParams) {}
+
+bool ento::CallDescription::matches(const CallEvent &Call) const {
+ // FIXME: Add ObjC Message support.
+ if (Call.getKind() == CE_ObjCMessage)
+ return false;
+
+ const auto *FD = dyn_cast_or_null<FunctionDecl>(Call.getDecl());
+ if (!FD)
+ return false;
+
+ if (Flags & CDF_MaybeBuiltin) {
+ return CheckerContext::isCLibraryFunction(FD, getFunctionName()) &&
+ (!RequiredArgs || *RequiredArgs <= Call.getNumArgs()) &&
+ (!RequiredParams || *RequiredParams <= Call.parameters().size());
+ }
+
+ if (!II.hasValue()) {
+ II = &Call.getState()->getStateManager().getContext().Idents.get(
+ getFunctionName());
+ }
+
+ const auto MatchNameOnly = [](const CallDescription &CD,
+ const NamedDecl *ND) -> bool {
+ DeclarationName Name = ND->getDeclName();
+ if (const auto *II = Name.getAsIdentifierInfo())
+ return II == CD.II.getValue(); // Fast case.
+
+ // Fallback to the slow stringification and comparison for:
+ // C++ overloaded operators, constructors, destructors, etc.
+ // FIXME This comparison is way SLOWER than comparing pointers.
+ // At some point in the future, we should compare FunctionDecl pointers.
+ return Name.getAsString() == CD.getFunctionName();
+ };
+
+ const auto ExactMatchArgAndParamCounts =
+ [](const CallEvent &Call, const CallDescription &CD) -> bool {
+ const bool ArgsMatch =
+ !CD.RequiredArgs || *CD.RequiredArgs == Call.getNumArgs();
+ const bool ParamsMatch =
+ !CD.RequiredParams || *CD.RequiredParams == Call.parameters().size();
+ return ArgsMatch && ParamsMatch;
+ };
+
+ const auto MatchQualifiedNameParts = [](const CallDescription &CD,
+ const Decl *D) -> bool {
+ const auto FindNextNamespaceOrRecord =
+ [](const DeclContext *Ctx) -> const DeclContext * {
+ while (Ctx && !isa<NamespaceDecl, RecordDecl>(Ctx))
+ Ctx = Ctx->getParent();
+ return Ctx;
+ };
+
+ auto QualifierPartsIt = CD.begin_qualified_name_parts();
+ const auto QualifierPartsEndIt = CD.end_qualified_name_parts();
+
+ // Match namespace and record names. Skip unrelated names if they don't
+ // match.
+ const DeclContext *Ctx = FindNextNamespaceOrRecord(D->getDeclContext());
+ for (; Ctx && QualifierPartsIt != QualifierPartsEndIt;
+ Ctx = FindNextNamespaceOrRecord(Ctx->getParent())) {
+ // If not matched just continue and try matching for the next one.
+ if (cast<NamedDecl>(Ctx)->getName() != *QualifierPartsIt)
+ continue;
+ ++QualifierPartsIt;
+ }
+
+ // We matched if we consumed all expected qualifier segments.
+ return QualifierPartsIt == QualifierPartsEndIt;
+ };
+
+ // Let's start matching...
+ if (!ExactMatchArgAndParamCounts(Call, *this))
+ return false;
+
+ if (!MatchNameOnly(*this, FD))
+ return false;
+
+ if (!hasQualifiedNameParts())
+ return true;
+
+ return MatchQualifiedNameParts(*this, FD);
+}
+
+ento::CallDescriptionSet::CallDescriptionSet(
+ std::initializer_list<CallDescription> &&List) {
+ Impl.LinearMap.reserve(List.size());
+ for (const CallDescription &CD : List)
+ Impl.LinearMap.push_back({CD, /*unused*/ true});
+}
+
+bool ento::CallDescriptionSet::contains(const CallEvent &Call) const {
+ return static_cast<bool>(Impl.lookup(Call));
+}
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 3785f498414f..764dad3e7ab4 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -36,6 +36,7 @@
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/Specifiers.h"
#include "clang/CrossTU/CrossTranslationUnit.h"
+#include "clang/StaticAnalyzer/Core/PathSensitive/CallDescription.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/CheckerContext.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
@@ -73,26 +74,7 @@ QualType CallEvent::getResultType() const {
const Expr *E = getOriginExpr();
if (!E)
return Ctx.VoidTy;
- assert(E);
-
- QualType ResultTy = E->getType();
-
- // A function that returns a reference to 'int' will have a result type
- // of simply 'int'. Check the origin expr's value kind to recover the
- // proper type.
- switch (E->getValueKind()) {
- case VK_LValue:
- ResultTy = Ctx.getLValueReferenceType(ResultTy);
- break;
- case VK_XValue:
- ResultTy = Ctx.getRValueReferenceType(ResultTy);
- break;
- case VK_PRValue:
- // No adjustment is necessary.
- break;
- }
-
- return ResultTy;
+ return Ctx.getReferenceQualifiedType(E);
}
static bool isCallback(QualType T) {
@@ -321,64 +303,6 @@ ProgramPoint CallEvent::getProgramPoint(bool IsPreVisit,
return PostImplicitCall(D, Loc, getLocationContext(), Tag);
}
-bool CallEvent::isCalled(const CallDescription &CD) const {
- // FIXME: Add ObjC Message support.
- if (getKind() == CE_ObjCMessage)
- return false;
-
- const IdentifierInfo *II = getCalleeIdentifier();
- if (!II)
- return false;
- const FunctionDecl *FD = dyn_cast_or_null<FunctionDecl>(getDecl());
- if (!FD)
- return false;
-
- if (CD.Flags & CDF_MaybeBuiltin) {
- return CheckerContext::isCLibraryFunction(FD, CD.getFunctionName()) &&
- (!CD.RequiredArgs || CD.RequiredArgs <= getNumArgs()) &&
- (!CD.RequiredParams || CD.RequiredParams <= parameters().size());
- }
-
- if (!CD.IsLookupDone) {
- CD.IsLookupDone = true;
- CD.II = &getState()->getStateManager().getContext().Idents.get(
- CD.getFunctionName());
- }
-
- if (II != CD.II)
- return false;
-
- // If CallDescription provides prefix names, use them to improve matching
- // accuracy.
- if (CD.QualifiedName.size() > 1 && FD) {
- const DeclContext *Ctx = FD->getDeclContext();
- // See if we'll be able to match them all.
- size_t NumUnmatched = CD.QualifiedName.size() - 1;
- for (; Ctx && isa<NamedDecl>(Ctx); Ctx = Ctx->getParent()) {
- if (NumUnmatched == 0)
- break;
-
- if (const auto *ND = dyn_cast<NamespaceDecl>(Ctx)) {
- if (ND->getName() == CD.QualifiedName[NumUnmatched - 1])
- --NumUnmatched;
- continue;
- }
-
- if (const auto *RD = dyn_cast<RecordDecl>(Ctx)) {
- if (RD->getName() == CD.QualifiedName[NumUnmatched - 1])
- --NumUnmatched;
- continue;
- }
- }
-
- if (NumUnmatched > 0)
- return false;
- }
-
- return (!CD.RequiredArgs || CD.RequiredArgs == getNumArgs()) &&
- (!CD.RequiredParams || CD.RequiredParams == parameters().size());
-}
-
SVal CallEvent::getArgSVal(unsigned Index) const {
const Expr *ArgE = getArgExpr(Index);
if (!ArgE)
@@ -406,7 +330,6 @@ void CallEvent::dump(raw_ostream &Out) const {
ASTContext &Ctx = getState()->getStateManager().getContext();
if (const Expr *E = getOriginExpr()) {
E->printPretty(Out, nullptr, Ctx.getPrintingPolicy());
- Out << "\n";
return;
}
@@ -420,9 +343,7 @@ void CallEvent::dump(raw_ostream &Out) const {
}
bool CallEvent::isCallStmt(const Stmt *S) {
- return isa<CallExpr>(S) || isa<ObjCMessageExpr>(S)
- || isa<CXXConstructExpr>(S)
- || isa<CXXNewExpr>(S);
+ return isa<CallExpr, ObjCMessageExpr, CXXConstructExpr, CXXNewExpr>(S);
}
QualType CallEvent::getDeclaredResultType(const Decl *D) {
@@ -676,7 +597,7 @@ bool AnyFunctionCall::argumentsMayEscape() const {
// - NSXXInsertXX, for example NSMapInsertIfAbsent, since they can
// be deallocated by NSMapRemove.
- if (FName.startswith("NS") && (FName.find("Insert") != StringRef::npos))
+ if (FName.startswith("NS") && FName.contains("Insert"))
return true;
// - Many CF containers allow objects to escape through custom
@@ -1058,12 +979,12 @@ const PseudoObjectExpr *ObjCMethodCall::getContainingPseudoObjectExpr() const {
static const Expr *
getSyntacticFromForPseudoObjectExpr(const PseudoObjectExpr *POE) {
- const Expr *Syntactic = POE->getSyntacticForm();
+ const Expr *Syntactic = POE->getSyntacticForm()->IgnoreParens();
// This handles the funny case of assigning to the result of a getter.
// This can happen if the getter returns a non-const reference.
if (const auto *BO = dyn_cast<BinaryOperator>(Syntactic))
- Syntactic = BO->getLHS();
+ Syntactic = BO->getLHS()->IgnoreParens();
return Syntactic;
}
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
index 3d64ce453479..4c684c3ffd9b 100644
--- a/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
@@ -38,7 +38,7 @@ StringRef CheckerContext::getCalleeName(const FunctionDecl *FunDecl) const {
}
StringRef CheckerContext::getDeclDescription(const Decl *D) {
- if (isa<ObjCMethodDecl>(D) || isa<CXXMethodDecl>(D))
+ if (isa<ObjCMethodDecl, CXXMethodDecl>(D))
return "method";
if (isa<BlockDecl>(D))
return "anonymous block";
@@ -55,7 +55,7 @@ bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD,
if (Name.empty())
return true;
StringRef BName = FD->getASTContext().BuiltinInfo.getName(BId);
- if (BName.find(Name) != StringRef::npos)
+ if (BName.contains(Name))
return true;
}
@@ -83,11 +83,10 @@ bool CheckerContext::isCLibraryFunction(const FunctionDecl *FD,
if (FName.equals(Name))
return true;
- if (FName.startswith("__inline") && (FName.find(Name) != StringRef::npos))
+ if (FName.startswith("__inline") && FName.contains(Name))
return true;
- if (FName.startswith("__") && FName.endswith("_chk") &&
- FName.find(Name) != StringRef::npos)
+ if (FName.startswith("__") && FName.endswith("_chk") && FName.contains(Name))
return true;
return false;
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
index e09399a83589..94287b7992dd 100644
--- a/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CheckerManager.cpp
@@ -26,6 +26,7 @@
#include "llvm/ADT/SmallVector.h"
#include "llvm/Support/Casting.h"
#include "llvm/Support/ErrorHandling.h"
+#include "llvm/Support/FormatVariadic.h"
#include <cassert>
#include <vector>
@@ -655,7 +656,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
ExprEngine &Eng,
const EvalCallOptions &CallOpts) {
for (auto *const Pred : Src) {
- bool anyEvaluated = false;
+ Optional<CheckerNameRef> evaluatorChecker;
ExplodedNodeSet checkDst;
NodeBuilder B(Pred, checkDst, Eng.getBuilderContext());
@@ -674,10 +675,26 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
CheckerContext C(B, Eng, Pred, L);
evaluated = EvalCallChecker(Call, C);
}
- assert(!(evaluated && anyEvaluated)
- && "There are more than one checkers evaluating the call");
+#ifndef NDEBUG
+ if (evaluated && evaluatorChecker) {
+ const auto toString = [](const CallEvent &Call) -> std::string {
+ std::string Buf;
+ llvm::raw_string_ostream OS(Buf);
+ Call.dump(OS);
+ OS.flush();
+ return Buf;
+ };
+ std::string AssertionMessage = llvm::formatv(
+ "The '{0}' call has been already evaluated by the {1} checker, "
+ "while the {2} checker also tried to evaluate the same call. At "
+ "most one checker supposed to evaluate a call.",
+ toString(Call), evaluatorChecker->getName(),
+ EvalCallChecker.Checker->getCheckerName());
+ llvm_unreachable(AssertionMessage.c_str());
+ }
+#endif
if (evaluated) {
- anyEvaluated = true;
+ evaluatorChecker = EvalCallChecker.Checker->getCheckerName();
Dst.insert(checkDst);
#ifdef NDEBUG
break; // on release don't check that no other checker also evals.
@@ -686,7 +703,7 @@ void CheckerManager::runCheckersForEvalCall(ExplodedNodeSet &Dst,
}
// If none of the checkers evaluated the call, ask ExprEngine to handle it.
- if (!anyEvaluated) {
+ if (!evaluatorChecker) {
NodeBuilder B(Pred, Dst, Eng.getBuilderContext());
Eng.defaultEvalCall(B, Pred, Call, CallOpts);
}
diff --git a/clang/lib/StaticAnalyzer/Core/Environment.cpp b/clang/lib/StaticAnalyzer/Core/Environment.cpp
index ee7474592528..64e915a09ceb 100644
--- a/clang/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Environment.cpp
@@ -88,7 +88,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
const Stmt *S = Entry.getStmt();
assert(!isa<ObjCForCollectionStmt>(S) &&
"Use ExprEngine::hasMoreIteration()!");
- assert((isa<Expr>(S) || isa<ReturnStmt>(S)) &&
+ assert((isa<Expr, ReturnStmt>(S)) &&
"Environment can only argue about Exprs, since only they express "
"a value! Any non-expression statement stored in Environment is a "
"result of a hack!");
diff --git a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
index 635495e9bf60..294572b7dbe4 100644
--- a/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExplodedGraph.cpp
@@ -50,8 +50,7 @@ ExplodedGraph::~ExplodedGraph() = default;
bool ExplodedGraph::isInterestingLValueExpr(const Expr *Ex) {
if (!Ex->isLValue())
return false;
- return isa<DeclRefExpr>(Ex) || isa<MemberExpr>(Ex) ||
- isa<ObjCIvarRefExpr>(Ex) || isa<ArraySubscriptExpr>(Ex);
+ return isa<DeclRefExpr, MemberExpr, ObjCIvarRefExpr, ArraySubscriptExpr>(Ex);
}
bool ExplodedGraph::shouldCollect(const ExplodedNode *node) {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 66332d3b848c..12b005d43c55 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -393,8 +393,7 @@ ProgramStateRef ExprEngine::createTemporaryRegionIfNeeded(
SVal BaseReg = Reg;
// Make the necessary adjustments to obtain the sub-object.
- for (auto I = Adjustments.rbegin(), E = Adjustments.rend(); I != E; ++I) {
- const SubobjectAdjustment &Adj = *I;
+ for (const SubobjectAdjustment &Adj : llvm::reverse(Adjustments)) {
switch (Adj.Kind) {
case SubobjectAdjustment::DerivedToBaseAdjustment:
Reg = StoreMgr.evalDerivedToBase(Reg, Adj.DerivedToBase.BasePath);
@@ -1297,8 +1296,10 @@ void ExprEngine::Visit(const Stmt *S, ExplodedNode *Pred,
case Stmt::OMPInteropDirectiveClass:
case Stmt::OMPDispatchDirectiveClass:
case Stmt::OMPMaskedDirectiveClass:
+ case Stmt::OMPGenericLoopDirectiveClass:
case Stmt::CapturedStmtClass:
- case Stmt::OMPUnrollDirectiveClass: {
+ case Stmt::OMPUnrollDirectiveClass:
+ case Stmt::OMPMetaDirectiveClass: {
const ExplodedNode *node = Bldr.generateSink(S, Pred, Pred->getState());
Engine.addAbortedBlock(node, currBldrCtx->getBlock());
break;
@@ -1988,8 +1989,7 @@ void ExprEngine::processCFGBlockEntrance(const BlockEdge &L,
if (BlockCount == AMgr.options.maxBlockVisitOnPath - 1 &&
AMgr.options.ShouldWidenLoops) {
const Stmt *Term = nodeBuilder.getContext().getBlock()->getTerminatorStmt();
- if (!(Term &&
- (isa<ForStmt>(Term) || isa<WhileStmt>(Term) || isa<DoStmt>(Term))))
+ if (!isa_and_nonnull<ForStmt, WhileStmt, DoStmt>(Term))
return;
// Widen.
const LocationContext *LCtx = Pred->getLocationContext();
@@ -2265,7 +2265,7 @@ void ExprEngine::processBranch(const Stmt *Condition,
continue;
}
if (StTrue && StFalse)
- assert(!isa<ObjCForCollectionStmt>(Condition));;
+ assert(!isa<ObjCForCollectionStmt>(Condition));
// Process the true branch.
if (builder.isFeasible(true)) {
@@ -2593,7 +2593,7 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
ProgramPoint::PostLValueKind);
return;
}
- if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) {
+ if (isa<FieldDecl, IndirectFieldDecl>(D)) {
// Delegate all work related to pointer to members to the surrounding
// operator&.
return;
@@ -2670,7 +2670,7 @@ void ExprEngine::VisitMemberExpr(const MemberExpr *M, ExplodedNode *Pred,
// Handle static member variables and enum constants accessed via
// member syntax.
- if (isa<VarDecl>(Member) || isa<EnumConstantDecl>(Member)) {
+ if (isa<VarDecl, EnumConstantDecl>(Member)) {
for (const auto I : CheckedSet)
VisitCommonDeclRefExpr(M, Member, I, EvalSet);
} else {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index 7ad3dca831ac..69d67cf9b465 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -550,7 +550,7 @@ void ExprEngine::VisitCompoundLiteralExpr(const CompoundLiteralExpr *CL,
const Expr *Init = CL->getInitializer();
SVal V = State->getSVal(CL->getInitializer(), LCtx);
- if (isa<CXXConstructExpr>(Init) || isa<CXXStdInitializerListExpr>(Init)) {
+ if (isa<CXXConstructExpr, CXXStdInitializerListExpr>(Init)) {
// No work needed. Just pass the value up to this expression.
} else {
assert(isa<InitListExpr>(Init));
@@ -757,9 +757,8 @@ void ExprEngine::VisitInitListExpr(const InitListExpr *IE,
return;
}
- for (InitListExpr::const_reverse_iterator it = IE->rbegin(),
- ei = IE->rend(); it != ei; ++it) {
- SVal V = state->getSVal(cast<Expr>(*it), LCtx);
+ for (const Stmt *S : llvm::reverse(*IE)) {
+ SVal V = state->getSVal(cast<Expr>(S), LCtx);
vals = getBasicVals().prependSVal(V, vals);
}
@@ -984,8 +983,7 @@ void ExprEngine::VisitUnaryOperator(const UnaryOperator* U, ExplodedNode *Pred,
if (const DeclRefExpr *DRE = dyn_cast<DeclRefExpr>(Ex)) {
const ValueDecl *VD = DRE->getDecl();
- if (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD) ||
- isa<IndirectFieldDecl>(VD)) {
+ if (isa<CXXMethodDecl, FieldDecl, IndirectFieldDecl>(VD)) {
ProgramStateRef State = (*I)->getState();
const LocationContext *LCtx = (*I)->getLocationContext();
SVal SV = svalBuilder.getMemberPointer(cast<NamedDecl>(VD));
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index cab65687444b..ba105f34a915 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -531,7 +531,7 @@ void ExprEngine::handleConstructor(const Expr *E,
// FIXME: Instead of relying on the ParentMap, we should have the
// trigger-statement (InitListExpr in this case) passed down from CFG or
// otherwise always available during construction.
- if (dyn_cast_or_null<InitListExpr>(LCtx->getParentMap().getParent(E))) {
+ if (isa_and_nonnull<InitListExpr>(LCtx->getParentMap().getParent(E))) {
MemRegionManager &MRMgr = getSValBuilder().getRegionManager();
Target = loc::MemRegionVal(MRMgr.getCXXTempObjectRegion(E, LCtx));
CallOpts.IsCtorOrDtorWithImproperlyModeledTargetRegion = true;
diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index 64fc32ea7554..3b847d6f0d87 100644
--- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -27,6 +27,8 @@
#include "clang/Rewrite/Core/Rewriter.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/Sequence.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringRef.h"
#include "llvm/ADT/iterator_range.h"
@@ -57,6 +59,8 @@ using namespace ento;
namespace {
+class ArrowMap;
+
class HTMLDiagnostics : public PathDiagnosticConsumer {
PathDiagnosticConsumerOptions DiagOpts;
std::string Directory;
@@ -77,60 +81,93 @@ public:
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) override;
- StringRef getName() const override {
- return "HTMLDiagnostics";
- }
+ StringRef getName() const override { return "HTMLDiagnostics"; }
bool supportsCrossFileDiagnostics() const override {
return SupportsCrossFileDiagnostics;
}
- unsigned ProcessMacroPiece(raw_ostream &os,
- const PathDiagnosticMacroPiece& P,
+ unsigned ProcessMacroPiece(raw_ostream &os, const PathDiagnosticMacroPiece &P,
unsigned num);
+ unsigned ProcessControlFlowPiece(Rewriter &R, FileID BugFileID,
+ const PathDiagnosticControlFlowPiece &P,
+ unsigned Number);
+
void HandlePiece(Rewriter &R, FileID BugFileID, const PathDiagnosticPiece &P,
const std::vector<SourceRange> &PopUpRanges, unsigned num,
unsigned max);
- void HighlightRange(Rewriter& R, FileID BugFileID, SourceRange Range,
+ void HighlightRange(Rewriter &R, FileID BugFileID, SourceRange Range,
const char *HighlightStart = "<span class=\"mrange\">",
const char *HighlightEnd = "</span>");
- void ReportDiag(const PathDiagnostic& D,
- FilesMade *filesMade);
+ void ReportDiag(const PathDiagnostic &D, FilesMade *filesMade);
// Generate the full HTML report
- std::string GenerateHTML(const PathDiagnostic& D, Rewriter &R,
- const SourceManager& SMgr, const PathPieces& path,
+ std::string GenerateHTML(const PathDiagnostic &D, Rewriter &R,
+ const SourceManager &SMgr, const PathPieces &path,
const char *declName);
// Add HTML header/footers to file specified by FID
- void FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
- const SourceManager& SMgr, const PathPieces& path,
+ void FinalizeHTML(const PathDiagnostic &D, Rewriter &R,
+ const SourceManager &SMgr, const PathPieces &path,
FileID FID, const FileEntry *Entry, const char *declName);
// Rewrite the file specified by FID with HTML formatting.
- void RewriteFile(Rewriter &R, const PathPieces& path, FileID FID);
+ void RewriteFile(Rewriter &R, const PathPieces &path, FileID FID);
+ PathGenerationScheme getGenerationScheme() const override {
+ return Everything;
+ }
private:
+ void addArrowSVGs(Rewriter &R, FileID BugFileID,
+ const ArrowMap &ArrowIndices);
+
/// \return Javascript for displaying shortcuts help;
StringRef showHelpJavascript();
/// \return Javascript for navigating the HTML report using j/k keys.
StringRef generateKeyboardNavigationJavascript();
+ /// \return Javascript for drawing control-flow arrows.
+ StringRef generateArrowDrawingJavascript();
+
/// \return JavaScript for an option to only show relevant lines.
- std::string showRelevantLinesJavascript(
- const PathDiagnostic &D, const PathPieces &path);
+ std::string showRelevantLinesJavascript(const PathDiagnostic &D,
+ const PathPieces &path);
/// Write executed lines from \p D in JSON format into \p os.
- void dumpCoverageData(const PathDiagnostic &D,
- const PathPieces &path,
+ void dumpCoverageData(const PathDiagnostic &D, const PathPieces &path,
llvm::raw_string_ostream &os);
};
+bool isArrowPiece(const PathDiagnosticPiece &P) {
+ return isa<PathDiagnosticControlFlowPiece>(P) && P.getString().empty();
+}
+
+unsigned getPathSizeWithoutArrows(const PathPieces &Path) {
+ unsigned TotalPieces = Path.size();
+ unsigned TotalArrowPieces = llvm::count_if(
+ Path, [](const PathDiagnosticPieceRef &P) { return isArrowPiece(*P); });
+ return TotalPieces - TotalArrowPieces;
+}
+
+class ArrowMap : public std::vector<unsigned> {
+ using Base = std::vector<unsigned>;
+
+public:
+ ArrowMap(unsigned Size) : Base(Size, 0) {}
+ unsigned getTotalNumberOfArrows() const { return at(0); }
+};
+
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const ArrowMap &Indices) {
+ OS << "[ ";
+ llvm::interleave(Indices, OS, ",");
+ return OS << " ]";
+}
+
} // namespace
void ento::createHTMLDiagnosticConsumer(
@@ -208,6 +245,18 @@ void HTMLDiagnostics::FlushDiagnosticsImpl(
ReportDiag(*Diag, filesMade);
}
+static llvm::SmallString<32> getIssueHash(const PathDiagnostic &D,
+ const Preprocessor &PP) {
+ SourceManager &SMgr = PP.getSourceManager();
+ PathDiagnosticLocation UPDLoc = D.getUniqueingLoc();
+ FullSourceLoc L(SMgr.getExpansionLoc(UPDLoc.isValid()
+ ? UPDLoc.asLocation()
+ : D.getLocation().asLocation()),
+ SMgr);
+ return getIssueHash(L, D.getCheckerName(), D.getBugType(),
+ D.getDeclWithIssue(), PP.getLangOpts());
+}
+
void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
FilesMade *filesMade) {
// Create the HTML directory if it is missing.
@@ -234,11 +283,6 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
// Create a new rewriter to generate HTML.
Rewriter R(const_cast<SourceManager&>(SMgr), PP.getLangOpts());
- // The file for the first path element is considered the main report file, it
- // will usually be equivalent to SMgr.getMainFileID(); however, it might be a
- // header when -analyzer-opt-analyze-headers is used.
- FileID ReportFile = path.front()->getLocation().asLocation().getExpansionLoc().getFileID();
-
// Get the function/method name
SmallString<128> declName("unknown");
int offsetDecl = 0;
@@ -265,46 +309,52 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
// Create a path for the target HTML file.
int FD;
- SmallString<128> Model, ResultPath;
-
- if (!DiagOpts.ShouldWriteStableReportFilename) {
- llvm::sys::path::append(Model, Directory, "report-%%%%%%.html");
- if (std::error_code EC =
- llvm::sys::fs::make_absolute(Model)) {
- llvm::errs() << "warning: could not make '" << Model
- << "' absolute: " << EC.message() << '\n';
- return;
- }
- if (std::error_code EC = llvm::sys::fs::createUniqueFile(
- Model, FD, ResultPath, llvm::sys::fs::OF_Text)) {
- llvm::errs() << "warning: could not create file in '" << Directory
- << "': " << EC.message() << '\n';
- return;
- }
- } else {
- int i = 1;
- std::error_code EC;
- do {
- // Find a filename which is not already used
- const FileEntry* Entry = SMgr.getFileEntryForID(ReportFile);
- std::stringstream filename;
- Model = "";
- filename << "report-"
- << llvm::sys::path::filename(Entry->getName()).str()
- << "-" << declName.c_str()
- << "-" << offsetDecl
- << "-" << i << ".html";
- llvm::sys::path::append(Model, Directory,
- filename.str());
- EC = llvm::sys::fs::openFileForReadWrite(
- Model, FD, llvm::sys::fs::CD_CreateNew, llvm::sys::fs::OF_None);
- if (EC && EC != llvm::errc::file_exists) {
- llvm::errs() << "warning: could not create file '" << Model
- << "': " << EC.message() << '\n';
- return;
- }
- i++;
- } while (EC);
+
+ SmallString<128> FileNameStr;
+ llvm::raw_svector_ostream FileName(FileNameStr);
+ FileName << "report-";
+
+ // Historically, neither the stable report filename nor the unstable report
+ // filename were actually stable. That said, the stable report filename
+ // was more stable because it was mostly composed of information
+ // about the bug report instead of being completely random.
+ // Now both stable and unstable report filenames are in fact stable
+ // but the stable report filename is still more verbose.
+ if (DiagOpts.ShouldWriteVerboseReportFilename) {
+ // FIXME: This code relies on knowing what constitutes the issue hash.
+ // Otherwise deduplication won't work correctly.
+ FileID ReportFile =
+ path.back()->getLocation().asLocation().getExpansionLoc().getFileID();
+
+ const FileEntry *Entry = SMgr.getFileEntryForID(ReportFile);
+
+ FileName << llvm::sys::path::filename(Entry->getName()).str() << "-"
+ << declName.c_str() << "-" << offsetDecl << "-";
+ }
+
+ FileName << StringRef(getIssueHash(D, PP)).substr(0, 6).str() << ".html";
+
+ SmallString<128> ResultPath;
+ llvm::sys::path::append(ResultPath, Directory, FileName.str());
+ if (std::error_code EC = llvm::sys::fs::make_absolute(ResultPath)) {
+ llvm::errs() << "warning: could not make '" << ResultPath
+ << "' absolute: " << EC.message() << '\n';
+ return;
+ }
+
+ if (std::error_code EC = llvm::sys::fs::openFileForReadWrite(
+ ResultPath, FD, llvm::sys::fs::CD_CreateNew,
+ llvm::sys::fs::OF_Text)) {
+ // Existence of the file corresponds to the situation where a different
+ // Clang instance has emitted a bug report with the same issue hash.
+ // This is an entirely normal situation that does not deserve a warning,
+ // as apart from hash collisions this can happen because the reports
+ // are in fact similar enough to be considered duplicates of each other.
+ if (EC != llvm::errc::file_exists) {
+ llvm::errs() << "warning: could not create file in '" << Directory
+ << "': " << EC.message() << '\n';
+ }
+ return;
}
llvm::raw_fd_ostream os(FD, true);
@@ -452,10 +502,11 @@ window.addEventListener("keydown", function (event) {
if (event.defaultPrevented) {
return;
}
- if (event.key == "S") {
+ // SHIFT + S
+ if (event.shiftKey && event.keyCode == 83) {
var checked = document.getElementsByName("showCounterexample")[0].checked;
filterCounterexample(!checked);
- document.getElementsByName("showCounterexample")[0].checked = !checked;
+ document.getElementsByName("showCounterexample")[0].click();
} else {
return;
}
@@ -475,6 +526,11 @@ document.addEventListener("DOMContentLoaded", function() {
<label for="showCounterexample">
Show only relevant lines
</label>
+ <input type="checkbox" name="showArrows"
+ id="showArrows" style="margin-left: 10px" />
+ <label for="showArrows">
+ Show control flow arrows
+ </label>
</form>
)<<<";
@@ -503,6 +559,9 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
generateKeyboardNavigationJavascript());
+ R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
+ generateArrowDrawingJavascript());
+
// Checkbox and javascript for filtering the output to the counterexample.
R.InsertTextBefore(SMgr.getLocForStartOfFile(FID),
showRelevantLinesJavascript(D, path));
@@ -570,6 +629,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
<a href="#" onclick="toggleHelp(); return false;">Close</a>
</div>
)<<<";
+
R.InsertTextBefore(SMgr.getLocForStartOfFile(FID), os.str());
}
@@ -591,7 +651,6 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
? UPDLoc.asLocation()
: D.getLocation().asLocation()),
SMgr);
- const Decl *DeclWithIssue = D.getDeclWithIssue();
StringRef BugCategory = D.getCategory();
if (!BugCategory.empty())
@@ -603,9 +662,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
os << "\n<!-- FUNCTIONNAME " << declName << " -->\n";
- os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT "
- << getIssueHash(L, D.getCheckerName(), D.getBugType(), DeclWithIssue,
- PP.getLangOpts())
+ os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT " << getIssueHash(D, PP)
<< " -->\n";
os << "\n<!-- BUGLINE "
@@ -616,7 +673,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
<< ColumnNumber
<< " -->\n";
- os << "\n<!-- BUGPATHLENGTH " << path.size() << " -->\n";
+ os << "\n<!-- BUGPATHLENGTH " << getPathSizeWithoutArrows(path) << " -->\n";
// Mark the end of the tags.
os << "\n<!-- BUGMETAEND -->\n";
@@ -695,8 +752,7 @@ static void HandlePopUpPieceEndTag(Rewriter &R,
Out << "</div></td><td>" << Piece.getString() << "</td></tr>";
// If no report made at this range mark the variable and add the end tags.
- if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) ==
- PopUpRanges.end()) {
+ if (!llvm::is_contained(PopUpRanges, Range)) {
// Store that we create a report at this range.
PopUpRanges.push_back(Range);
@@ -711,30 +767,33 @@ static void HandlePopUpPieceEndTag(Rewriter &R,
}
}
-void HTMLDiagnostics::RewriteFile(Rewriter &R,
- const PathPieces& path, FileID FID) {
+void HTMLDiagnostics::RewriteFile(Rewriter &R, const PathPieces &path,
+ FileID FID) {
+
// Process the path.
// Maintain the counts of extra note pieces separately.
- unsigned TotalPieces = path.size();
- unsigned TotalNotePieces = std::count_if(
- path.begin(), path.end(), [](const PathDiagnosticPieceRef &p) {
+ unsigned TotalPieces = getPathSizeWithoutArrows(path);
+ unsigned TotalNotePieces =
+ llvm::count_if(path, [](const PathDiagnosticPieceRef &p) {
return isa<PathDiagnosticNotePiece>(*p);
});
- unsigned PopUpPieceCount = std::count_if(
- path.begin(), path.end(), [](const PathDiagnosticPieceRef &p) {
+ unsigned PopUpPieceCount =
+ llvm::count_if(path, [](const PathDiagnosticPieceRef &p) {
return isa<PathDiagnosticPopUpPiece>(*p);
});
unsigned TotalRegularPieces = TotalPieces - TotalNotePieces - PopUpPieceCount;
unsigned NumRegularPieces = TotalRegularPieces;
unsigned NumNotePieces = TotalNotePieces;
+ unsigned NumberOfArrows = 0;
// Stores the count of the regular piece indices.
std::map<int, int> IndexMap;
+ ArrowMap ArrowIndices(TotalRegularPieces + 1);
// Stores the different ranges where we have reported something.
std::vector<SourceRange> PopUpRanges;
- for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) {
- const auto &Piece = *I->get();
+ for (const PathDiagnosticPieceRef &I : llvm::reverse(path)) {
+ const auto &Piece = *I.get();
if (isa<PathDiagnosticPopUpPiece>(Piece)) {
++IndexMap[NumRegularPieces];
@@ -744,18 +803,40 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R,
// as a separate pass through the piece list.
HandlePiece(R, FID, Piece, PopUpRanges, NumNotePieces, TotalNotePieces);
--NumNotePieces;
+
+ } else if (isArrowPiece(Piece)) {
+ NumberOfArrows = ProcessControlFlowPiece(
+ R, FID, cast<PathDiagnosticControlFlowPiece>(Piece), NumberOfArrows);
+ ArrowIndices[NumRegularPieces] = NumberOfArrows;
+
} else {
HandlePiece(R, FID, Piece, PopUpRanges, NumRegularPieces,
TotalRegularPieces);
--NumRegularPieces;
+ ArrowIndices[NumRegularPieces] = ArrowIndices[NumRegularPieces + 1];
}
}
+ ArrowIndices[0] = NumberOfArrows;
+
+ // At this point ArrowIndices represent the following data structure:
+ // [a_0, a_1, ..., a_N]
+ // where N is the number of events in the path.
+ //
+ // Then for every event with index i \in [0, N - 1], we can say that
+ // arrows with indices \in [a_(i+1), a_i) correspond to that event.
+ // We can say that because arrows with these indices appeared in the
+ // path in between the i-th and the (i+1)-th events.
+ assert(ArrowIndices.back() == 0 &&
+ "No arrows should be after the last event");
+ // This assertion also guarantees that all indices in are <= NumberOfArrows.
+ assert(llvm::is_sorted(ArrowIndices, std::greater<unsigned>()) &&
+ "Incorrect arrow indices map");
// Secondary indexing if we are having multiple pop-ups between two notes.
// (e.g. [(13) 'a' is 'true']; [(13.1) 'b' is 'false']; [(13.2) 'c' is...)
NumRegularPieces = TotalRegularPieces;
- for (auto I = path.rbegin(), E = path.rend(); I != E; ++I) {
- const auto &Piece = *I->get();
+ for (const PathDiagnosticPieceRef &I : llvm::reverse(path)) {
+ const auto &Piece = *I.get();
if (const auto *PopUpP = dyn_cast<PathDiagnosticPopUpPiece>(&Piece)) {
int PopUpPieceIndex = IndexMap[NumRegularPieces];
@@ -771,7 +852,7 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R,
if (PopUpPieceIndex > 0)
--IndexMap[NumRegularPieces];
- } else if (!isa<PathDiagnosticNotePiece>(Piece)) {
+ } else if (!isa<PathDiagnosticNotePiece>(Piece) && !isArrowPiece(Piece)) {
--NumRegularPieces;
}
}
@@ -783,6 +864,8 @@ void HTMLDiagnostics::RewriteFile(Rewriter &R,
html::EscapeText(R, FID);
html::AddLineNumbers(R, FID);
+ addArrowSVGs(R, FID, ArrowIndices);
+
// If we have a preprocessor, relex the file and syntax highlight.
// We might not have a preprocessor if we come from a deserialized AST file,
// for example.
@@ -1007,8 +1090,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
ArrayRef<SourceRange> Ranges = P.getRanges();
for (const auto &Range : Ranges) {
// If we have already highlighted the range as a pop-up there is no work.
- if (std::find(PopUpRanges.begin(), PopUpRanges.end(), Range) !=
- PopUpRanges.end())
+ if (llvm::is_contained(PopUpRanges, Range))
continue;
HighlightRange(R, LPosInfo.first, Range);
@@ -1049,6 +1131,104 @@ unsigned HTMLDiagnostics::ProcessMacroPiece(raw_ostream &os,
return num;
}
+void HTMLDiagnostics::addArrowSVGs(Rewriter &R, FileID BugFileID,
+ const ArrowMap &ArrowIndices) {
+ std::string S;
+ llvm::raw_string_ostream OS(S);
+
+ OS << R"<<<(
+<style type="text/css">
+ svg {
+ position:absolute;
+ top:0;
+ left:0;
+ height:100%;
+ width:100%;
+ pointer-events: none;
+ overflow: visible
+ }
+ .arrow {
+ stroke-opacity: 0.2;
+ stroke-width: 1;
+ marker-end: url(#arrowhead);
+ }
+
+ .arrow.selected {
+ stroke-opacity: 0.6;
+ stroke-width: 2;
+ marker-end: url(#arrowheadSelected);
+ }
+
+ .arrowhead {
+ orient: auto;
+ stroke: none;
+ opacity: 0.6;
+ fill: blue;
+ }
+</style>
+<svg xmlns="http://www.w3.org/2000/svg">
+ <defs>
+ <marker id="arrowheadSelected" class="arrowhead" opacity="0.6"
+ viewBox="0 0 10 10" refX="3" refY="5"
+ markerWidth="4" markerHeight="4">
+ <path d="M 0 0 L 10 5 L 0 10 z" />
+ </marker>
+ <marker id="arrowhead" class="arrowhead" opacity="0.2"
+ viewBox="0 0 10 10" refX="3" refY="5"
+ markerWidth="4" markerHeight="4">
+ <path d="M 0 0 L 10 5 L 0 10 z" />
+ </marker>
+ </defs>
+ <g id="arrows" fill="none" stroke="blue" visibility="hidden">
+)<<<";
+
+ for (unsigned Index : llvm::seq(0u, ArrowIndices.getTotalNumberOfArrows())) {
+ OS << " <path class=\"arrow\" id=\"arrow" << Index << "\"/>\n";
+ }
+
+ OS << R"<<<(
+ </g>
+</svg>
+<script type='text/javascript'>
+const arrowIndices = )<<<";
+
+ OS << ArrowIndices << "\n</script>\n";
+
+ R.InsertTextBefore(R.getSourceMgr().getLocForStartOfFile(BugFileID),
+ OS.str());
+}
+
+std::string getSpanBeginForControl(const char *ClassName, unsigned Index) {
+ std::string Result;
+ llvm::raw_string_ostream OS(Result);
+ OS << "<span id=\"" << ClassName << Index << "\">";
+ return OS.str();
+}
+
+std::string getSpanBeginForControlStart(unsigned Index) {
+ return getSpanBeginForControl("start", Index);
+}
+
+std::string getSpanBeginForControlEnd(unsigned Index) {
+ return getSpanBeginForControl("end", Index);
+}
+
+unsigned HTMLDiagnostics::ProcessControlFlowPiece(
+ Rewriter &R, FileID BugFileID, const PathDiagnosticControlFlowPiece &P,
+ unsigned Number) {
+ for (const PathDiagnosticLocationPair &LPair : P) {
+ std::string Start = getSpanBeginForControlStart(Number),
+ End = getSpanBeginForControlEnd(Number++);
+
+ HighlightRange(R, BugFileID, LPair.getStart().asRange().getBegin(),
+ Start.c_str());
+ HighlightRange(R, BugFileID, LPair.getEnd().asRange().getBegin(),
+ End.c_str());
+ }
+
+ return Number;
+}
+
void HTMLDiagnostics::HighlightRange(Rewriter& R, FileID BugFileID,
SourceRange Range,
const char *HighlightStart,
@@ -1109,7 +1289,7 @@ document.addEventListener("DOMContentLoaded", function() {
});
var findNum = function() {
- var s = document.querySelector(".selected");
+ var s = document.querySelector(".msg.selected");
if (!s || s.id == "EndPath") {
return 0;
}
@@ -1117,14 +1297,32 @@ var findNum = function() {
return out;
};
+var classListAdd = function(el, theClass) {
+ if(!el.className.baseVal)
+ el.className += " " + theClass;
+ else
+ el.className.baseVal += " " + theClass;
+};
+
+var classListRemove = function(el, theClass) {
+ var className = (!el.className.baseVal) ?
+ el.className : el.className.baseVal;
+ className = className.replace(" " + theClass, "");
+ if(!el.className.baseVal)
+ el.className = className;
+ else
+ el.className.baseVal = className;
+};
+
var scrollTo = function(el) {
querySelectorAllArray(".selected").forEach(function(s) {
- s.classList.remove("selected");
+ classListRemove(s, "selected");
});
- el.classList.add("selected");
+ classListAdd(el, "selected");
window.scrollBy(0, el.getBoundingClientRect().top -
(window.innerHeight / 2));
-}
+ highlightArrowsForSelectedEvent();
+};
var move = function(num, up, numItems) {
if (num == 1 && up || num == numItems - 1 && !up) {
@@ -1159,9 +1357,11 @@ window.addEventListener("keydown", function (event) {
if (event.defaultPrevented) {
return;
}
- if (event.key == "j") {
+ // key 'j'
+ if (event.keyCode == 74) {
navigateTo(/*up=*/false);
- } else if (event.key == "k") {
+ // key 'k'
+ } else if (event.keyCode == 75) {
navigateTo(/*up=*/true);
} else {
return;
@@ -1171,3 +1371,258 @@ window.addEventListener("keydown", function (event) {
</script>
)<<<";
}
+
+StringRef HTMLDiagnostics::generateArrowDrawingJavascript() {
+ return R"<<<(
+<script type='text/javascript'>
+// Return range of numbers from a range [lower, upper).
+function range(lower, upper) {
+ var array = [];
+ for (var i = lower; i <= upper; ++i) {
+ array.push(i);
+ }
+ return array;
+}
+
+var getRelatedArrowIndices = function(pathId) {
+ // HTML numeration of events is a bit different than it is in the path.
+ // Everything is rotated one step to the right, so the last element
+ // (error diagnostic) has index 0.
+ if (pathId == 0) {
+ // arrowIndices has at least 2 elements
+ pathId = arrowIndices.length - 1;
+ }
+
+ return range(arrowIndices[pathId], arrowIndices[pathId - 1]);
+}
+
+var highlightArrowsForSelectedEvent = function() {
+ const selectedNum = findNum();
+ const arrowIndicesToHighlight = getRelatedArrowIndices(selectedNum);
+ arrowIndicesToHighlight.forEach((index) => {
+ var arrow = document.querySelector("#arrow" + index);
+ if(arrow) {
+ classListAdd(arrow, "selected")
+ }
+ });
+}
+
+var getAbsoluteBoundingRect = function(element) {
+ const relative = element.getBoundingClientRect();
+ return {
+ left: relative.left + window.pageXOffset,
+ right: relative.right + window.pageXOffset,
+ top: relative.top + window.pageYOffset,
+ bottom: relative.bottom + window.pageYOffset,
+ height: relative.height,
+ width: relative.width
+ };
+}
+
+var drawArrow = function(index) {
+ // This function is based on the great answer from SO:
+ // https://stackoverflow.com/a/39575674/11582326
+ var start = document.querySelector("#start" + index);
+ var end = document.querySelector("#end" + index);
+ var arrow = document.querySelector("#arrow" + index);
+
+ var startRect = getAbsoluteBoundingRect(start);
+ var endRect = getAbsoluteBoundingRect(end);
+
+ // It is an arrow from a token to itself, no need to visualize it.
+ if (startRect.top == endRect.top &&
+ startRect.left == endRect.left)
+ return;
+
+ // Each arrow is a very simple Bézier curve, with two nodes and
+ // two handles. So, we need to calculate four points in the window:
+ // * start node
+ var posStart = { x: 0, y: 0 };
+ // * end node
+ var posEnd = { x: 0, y: 0 };
+ // * handle for the start node
+ var startHandle = { x: 0, y: 0 };
+ // * handle for the end node
+ var endHandle = { x: 0, y: 0 };
+ // One can visualize it as follows:
+ //
+ // start handle
+ // /
+ // X"""_.-""""X
+ // .' \
+ // / start node
+ // |
+ // |
+ // | end node
+ // \ /
+ // `->X
+ // X-'
+ // \
+ // end handle
+ //
+ // NOTE: (0, 0) is the top left corner of the window.
+
+ // We have 3 similar, but still different scenarios to cover:
+ //
+ // 1. Two tokens on different lines.
+ // -xxx
+ // /
+ // \
+ // -> xxx
+ // In this situation, we draw arrow on the left curving to the left.
+ // 2. Two tokens on the same line, and the destination is on the right.
+ // ____
+ // / \
+ // / V
+ // xxx xxx
+ // In this situation, we draw arrow above curving upwards.
+ // 3. Two tokens on the same line, and the destination is on the left.
+ // xxx xxx
+ // ^ /
+ // \____/
+ // In this situation, we draw arrow below curving downwards.
+ const onDifferentLines = startRect.top <= endRect.top - 5 ||
+ startRect.top >= endRect.top + 5;
+ const leftToRight = startRect.left < endRect.left;
+
+ // NOTE: various magic constants are chosen empirically for
+ // better positioning and look
+ if (onDifferentLines) {
+ // Case #1
+ const topToBottom = startRect.top < endRect.top;
+ posStart.x = startRect.left - 1;
+ // We don't want to start it at the top left corner of the token,
+ // it doesn't feel like this is where the arrow comes from.
+ // For this reason, we start it in the middle of the left side
+ // of the token.
+ posStart.y = startRect.top + startRect.height / 2;
+
+ // End node has arrow head and we give it a bit more space.
+ posEnd.x = endRect.left - 4;
+ posEnd.y = endRect.top;
+
+ // Utility object with x and y offsets for handles.
+ var curvature = {
+ // We want bottom-to-top arrow to curve a bit more, so it doesn't
+ // overlap much with top-to-bottom curves (much more frequent).
+ x: topToBottom ? 15 : 25,
+ y: Math.min((posEnd.y - posStart.y) / 3, 10)
+ }
+
+ // When destination is on the different line, we can make a
+ // curvier arrow because we have space for it.
+ // So, instead of using
+ //
+ // startHandle.x = posStart.x - curvature.x
+ // endHandle.x = posEnd.x - curvature.x
+ //
+ // We use the leftmost of these two values for both handles.
+ startHandle.x = Math.min(posStart.x, posEnd.x) - curvature.x;
+ endHandle.x = startHandle.x;
+
+ // Curving downwards from the start node...
+ startHandle.y = posStart.y + curvature.y;
+ // ... and upwards from the end node.
+ endHandle.y = posEnd.y - curvature.y;
+
+ } else if (leftToRight) {
+ // Case #2
+ // Starting from the top right corner...
+ posStart.x = startRect.right - 1;
+ posStart.y = startRect.top;
+
+ // ...and ending at the top left corner of the end token.
+ posEnd.x = endRect.left + 1;
+ posEnd.y = endRect.top - 1;
+
+ // Utility object with x and y offsets for handles.
+ var curvature = {
+ x: Math.min((posEnd.x - posStart.x) / 3, 15),
+ y: 5
+ }
+
+ // Curving to the right...
+ startHandle.x = posStart.x + curvature.x;
+ // ... and upwards from the start node.
+ startHandle.y = posStart.y - curvature.y;
+
+ // And to the left...
+ endHandle.x = posEnd.x - curvature.x;
+ // ... and upwards from the end node.
+ endHandle.y = posEnd.y - curvature.y;
+
+ } else {
+ // Case #3
+ // Starting from the bottom right corner...
+ posStart.x = startRect.right;
+ posStart.y = startRect.bottom;
+
+ // ...and ending also at the bottom right corner, but of the end token.
+ posEnd.x = endRect.right - 1;
+ posEnd.y = endRect.bottom + 1;
+
+ // Utility object with x and y offsets for handles.
+ var curvature = {
+ x: Math.min((posStart.x - posEnd.x) / 3, 15),
+ y: 5
+ }
+
+ // Curving to the left...
+ startHandle.x = posStart.x - curvature.x;
+ // ... and downwards from the start node.
+ startHandle.y = posStart.y + curvature.y;
+
+ // And to the right...
+ endHandle.x = posEnd.x + curvature.x;
+ // ... and downwards from the end node.
+ endHandle.y = posEnd.y + curvature.y;
+ }
+
+ // Put it all together into a path.
+ // More information on the format:
+ // https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Paths
+ var pathStr = "M" + posStart.x + "," + posStart.y + " " +
+ "C" + startHandle.x + "," + startHandle.y + " " +
+ endHandle.x + "," + endHandle.y + " " +
+ posEnd.x + "," + posEnd.y;
+
+ arrow.setAttribute("d", pathStr);
+};
+
+var drawArrows = function() {
+ const numOfArrows = document.querySelectorAll("path[id^=arrow]").length;
+ for (var i = 0; i < numOfArrows; ++i) {
+ drawArrow(i);
+ }
+}
+
+var toggleArrows = function(event) {
+ const arrows = document.querySelector("#arrows");
+ if (event.target.checked) {
+ arrows.setAttribute("visibility", "visible");
+ } else {
+ arrows.setAttribute("visibility", "hidden");
+ }
+}
+
+window.addEventListener("resize", drawArrows);
+document.addEventListener("DOMContentLoaded", function() {
+ // Whenever we show invocation, locations change, i.e. we
+ // need to redraw arrows.
+ document
+ .querySelector('input[id="showinvocation"]')
+ .addEventListener("click", drawArrows);
+ // Hiding irrelevant lines also should cause arrow rerender.
+ document
+ .querySelector('input[name="showCounterexample"]')
+ .addEventListener("change", drawArrows);
+ document
+ .querySelector('input[name="showArrows"]')
+ .addEventListener("change", toggleArrows);
+ drawArrows();
+ // Default highlighting for the last event.
+ highlightArrowsForSelectedEvent();
+});
+</script>
+ )<<<";
+}
diff --git a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
index e5f4e9ea30c9..8bf6fc085c6a 100644
--- a/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
+++ b/clang/lib/StaticAnalyzer/Core/LoopUnrolling.cpp
@@ -69,7 +69,7 @@ namespace clang {
namespace ento {
static bool isLoopStmt(const Stmt *S) {
- return S && (isa<ForStmt>(S) || isa<WhileStmt>(S) || isa<DoStmt>(S));
+ return isa_and_nonnull<ForStmt, WhileStmt, DoStmt>(S);
}
ProgramStateRef processLoopEnd(const Stmt *LoopStmt, ProgramStateRef State) {
diff --git a/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp b/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
index 47e34dd84b9a..748c65f578a8 100644
--- a/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
+++ b/clang/lib/StaticAnalyzer/Core/LoopWidening.cpp
@@ -45,8 +45,7 @@ ProgramStateRef getWidenedLoopState(ProgramStateRef PrevState,
const LocationContext *LCtx,
unsigned BlockCount, const Stmt *LoopStmt) {
- assert(isa<ForStmt>(LoopStmt) || isa<WhileStmt>(LoopStmt) ||
- isa<DoStmt>(LoopStmt));
+ assert((isa<ForStmt, WhileStmt, DoStmt>(LoopStmt)));
// Invalidate values in the current state.
// TODO Make this more conservative by only invalidating values that might
diff --git a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
index bd725ee9eaa3..f77fcb030a15 100644
--- a/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
+++ b/clang/lib/StaticAnalyzer/Core/MemRegion.cpp
@@ -28,6 +28,7 @@
#include "clang/Basic/IdentifierTable.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/SourceManager.h"
+#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicExtent.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/SVals.h"
@@ -768,14 +769,39 @@ DefinedOrUnknownSVal MemRegionManager::getStaticSize(const MemRegion *MR,
return UnknownVal();
QualType Ty = cast<TypedValueRegion>(SR)->getDesugaredValueType(Ctx);
- DefinedOrUnknownSVal Size = getElementExtent(Ty, SVB);
+ const DefinedOrUnknownSVal Size = getElementExtent(Ty, SVB);
+
+ // We currently don't model flexible array members (FAMs), which are:
+ // - int array[]; of IncompleteArrayType
+ // - int array[0]; of ConstantArrayType with size 0
+ // - int array[1]; of ConstantArrayType with size 1 (*)
+ // (*): Consider single element array object members as FAM candidates only
+ // if the consider-single-element-arrays-as-flexible-array-members
+ // analyzer option is true.
+ // https://gcc.gnu.org/onlinedocs/gcc/Zero-Length.html
+ const auto isFlexibleArrayMemberCandidate = [this,
+ &SVB](QualType Ty) -> bool {
+ const ArrayType *AT = Ctx.getAsArrayType(Ty);
+ if (!AT)
+ return false;
+ if (isa<IncompleteArrayType>(AT))
+ return true;
+
+ if (const auto *CAT = dyn_cast<ConstantArrayType>(AT)) {
+ const llvm::APInt &Size = CAT->getSize();
+ if (Size.isZero())
+ return true;
+
+ const AnalyzerOptions &Opts = SVB.getAnalyzerOptions();
+ if (Opts.ShouldConsiderSingleElementArraysAsFlexibleArrayMembers &&
+ Size.isOne())
+ return true;
+ }
+ return false;
+ };
- // A zero-length array at the end of a struct often stands for dynamically
- // allocated extra memory.
- if (Size.isZeroConstant()) {
- if (isa<ConstantArrayType>(Ty))
- return UnknownVal();
- }
+ if (isFlexibleArrayMemberCandidate(Ty))
+ return UnknownVal();
return Size;
}
@@ -948,9 +974,9 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
// First handle the globals defined in system headers.
if (Ctx.getSourceManager().isInSystemHeader(D->getLocation())) {
- // Whitelist the system globals which often DO GET modified, assume the
+ // Allow the system globals which often DO GET modified, assume the
// rest are immutable.
- if (D->getName().find("errno") != StringRef::npos)
+ if (D->getName().contains("errno"))
sReg = getGlobalsRegion(MemRegion::GlobalSystemSpaceRegionKind);
else
sReg = getGlobalsRegion(MemRegion::GlobalImmutableSpaceRegionKind);
@@ -986,14 +1012,15 @@ const VarRegion *MemRegionManager::getVarRegion(const VarDecl *D,
sReg = getUnknownRegion();
} else {
if (D->hasLocalStorage()) {
- sReg = isa<ParmVarDecl>(D) || isa<ImplicitParamDecl>(D)
- ? static_cast<const MemRegion*>(getStackArgumentsRegion(STC))
- : static_cast<const MemRegion*>(getStackLocalsRegion(STC));
+ sReg =
+ isa<ParmVarDecl, ImplicitParamDecl>(D)
+ ? static_cast<const MemRegion *>(getStackArgumentsRegion(STC))
+ : static_cast<const MemRegion *>(getStackLocalsRegion(STC));
}
else {
assert(D->isStaticLocal());
const Decl *STCD = STC->getDecl();
- if (isa<FunctionDecl>(STCD) || isa<ObjCMethodDecl>(STCD))
+ if (isa<FunctionDecl, ObjCMethodDecl>(STCD))
sReg = getGlobalsRegion(MemRegion::StaticGlobalSpaceRegionKind,
getFunctionCodeRegion(cast<NamedDecl>(STCD)));
else if (const auto *BD = dyn_cast<BlockDecl>(STCD)) {
@@ -1257,9 +1284,7 @@ bool MemRegion::hasStackParametersStorage() const {
}
bool MemRegion::hasGlobalsOrParametersStorage() const {
- const MemSpaceRegion *MS = getMemorySpace();
- return isa<StackArgumentsSpaceRegion>(MS) ||
- isa<GlobalsSpaceRegion>(MS);
+ return isa<StackArgumentsSpaceRegion, GlobalsSpaceRegion>(getMemorySpace());
}
// getBaseRegion strips away all elements and fields, and get the base region
diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index 69554576bdb2..74403a160b8e 100644
--- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -494,15 +494,17 @@ RangeSet RangeSet::Factory::deletePoint(RangeSet From,
return intersect(From, Upper, Lower);
}
-void Range::dump(raw_ostream &OS) const {
+LLVM_DUMP_METHOD void Range::dump(raw_ostream &OS) const {
OS << '[' << toString(From(), 10) << ", " << toString(To(), 10) << ']';
}
+LLVM_DUMP_METHOD void Range::dump() const { dump(llvm::errs()); }
-void RangeSet::dump(raw_ostream &OS) const {
+LLVM_DUMP_METHOD void RangeSet::dump(raw_ostream &OS) const {
OS << "{ ";
llvm::interleaveComma(*this, OS, [&OS](const Range &R) { R.dump(OS); });
OS << " }";
}
+LLVM_DUMP_METHOD void RangeSet::dump() const { dump(llvm::errs()); }
REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef)
@@ -599,6 +601,10 @@ public:
LLVM_NODISCARD static inline Optional<bool>
areEqual(ProgramStateRef State, SymbolRef First, SymbolRef Second);
+ /// Remove one member from the class.
+ LLVM_NODISCARD ProgramStateRef removeMember(ProgramStateRef State,
+ const SymbolRef Old);
+
/// Iterate over all symbols and try to simplify them.
LLVM_NODISCARD static inline ProgramStateRef simplify(SValBuilder &SVB,
RangeSet::Factory &F,
@@ -655,6 +661,7 @@ private:
inline ProgramStateRef mergeImpl(RangeSet::Factory &F, ProgramStateRef State,
SymbolSet Members, EquivalenceClass Other,
SymbolSet OtherMembers);
+
static inline bool
addToDisequalityInfo(DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
RangeSet::Factory &F, ProgramStateRef State,
@@ -1112,7 +1119,7 @@ private:
if (!SSE)
return llvm::None;
- BinaryOperatorKind CurrentOP = SSE->getOpcode();
+ const BinaryOperatorKind CurrentOP = SSE->getOpcode();
// We currently do not support <=> (C++20).
if (!BinaryOperator::isComparisonOp(CurrentOP) || (CurrentOP == BO_Cmp))
@@ -1126,7 +1133,12 @@ private:
SymbolManager &SymMgr = State->getSymbolManager();
- int UnknownStates = 0;
+ // We use this variable to store the last queried operator (`QueriedOP`)
+ // for which the `getCmpOpState` returned with `Unknown`. If there are two
+ // different OPs that returned `Unknown` then we have to query the special
+ // `UnknownX2` column. We assume that `getCmpOpState(CurrentOP, CurrentOP)`
+ // never returns `Unknown`, so `CurrentOP` is a good initial value.
+ BinaryOperatorKind LastQueriedOpToUnknown = CurrentOP;
// Loop goes through all of the columns exept the last one ('UnknownX2').
// We treat `UnknownX2` column separately at the end of the loop body.
@@ -1163,15 +1175,18 @@ private:
CmpOpTable.getCmpOpState(CurrentOP, QueriedOP);
if (BranchState == OperatorRelationsTable::Unknown) {
- if (++UnknownStates == 2)
- // If we met both Unknown states.
+ if (LastQueriedOpToUnknown != CurrentOP &&
+ LastQueriedOpToUnknown != QueriedOP) {
+ // If we got the Unknown state for both different operators.
// if (x <= y) // assume true
// if (x != y) // assume true
// if (x < y) // would be also true
// Get a state from `UnknownX2` column.
BranchState = CmpOpTable.getCmpOpStateForUnknownX2(CurrentOP);
- else
+ } else {
+ LastQueriedOpToUnknown = QueriedOP;
continue;
+ }
}
return (BranchState == OperatorRelationsTable::True) ? getTrueRange(T)
@@ -1382,6 +1397,113 @@ RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS,
}
//===----------------------------------------------------------------------===//
+// Constraint manager implementation details
+//===----------------------------------------------------------------------===//
+
+class RangeConstraintManager : public RangedConstraintManager {
+public:
+ RangeConstraintManager(ExprEngine *EE, SValBuilder &SVB)
+ : RangedConstraintManager(EE, SVB), F(getBasicVals()) {}
+
+ //===------------------------------------------------------------------===//
+ // Implementation for interface from ConstraintManager.
+ //===------------------------------------------------------------------===//
+
+ bool haveEqualConstraints(ProgramStateRef S1,
+ ProgramStateRef S2) const override {
+ // NOTE: ClassMembers are as simple as back pointers for ClassMap,
+ // so comparing constraint ranges and class maps should be
+ // sufficient.
+ return S1->get<ConstraintRange>() == S2->get<ConstraintRange>() &&
+ S1->get<ClassMap>() == S2->get<ClassMap>();
+ }
+
+ bool canReasonAbout(SVal X) const override;
+
+ ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override;
+
+ const llvm::APSInt *getSymVal(ProgramStateRef State,
+ SymbolRef Sym) const override;
+
+ ProgramStateRef removeDeadBindings(ProgramStateRef State,
+ SymbolReaper &SymReaper) override;
+
+ void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n",
+ unsigned int Space = 0, bool IsDot = false) const override;
+ void printConstraints(raw_ostream &Out, ProgramStateRef State,
+ const char *NL = "\n", unsigned int Space = 0,
+ bool IsDot = false) const;
+ void printEquivalenceClasses(raw_ostream &Out, ProgramStateRef State,
+ const char *NL = "\n", unsigned int Space = 0,
+ bool IsDot = false) const;
+ void printDisequalities(raw_ostream &Out, ProgramStateRef State,
+ const char *NL = "\n", unsigned int Space = 0,
+ bool IsDot = false) const;
+
+ //===------------------------------------------------------------------===//
+ // Implementation for interface from RangedConstraintManager.
+ //===------------------------------------------------------------------===//
+
+ ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym,
+ const llvm::APSInt &V,
+ const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymWithinInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
+
+ ProgramStateRef assumeSymOutsideInclusiveRange(
+ ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
+ const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
+
+private:
+ RangeSet::Factory F;
+
+ RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
+ RangeSet getRange(ProgramStateRef State, EquivalenceClass Class);
+ ProgramStateRef setRange(ProgramStateRef State, SymbolRef Sym,
+ RangeSet Range);
+ ProgramStateRef setRange(ProgramStateRef State, EquivalenceClass Class,
+ RangeSet Range);
+
+ RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymGTRange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymLERange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymLERange(llvm::function_ref<RangeSet()> RS,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+ RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment);
+};
+
+//===----------------------------------------------------------------------===//
// Constraint assignment logic
//===----------------------------------------------------------------------===//
@@ -1492,7 +1614,28 @@ public:
return Assignor.assign(CoS, NewConstraint);
}
+ /// Handle expressions like: a % b != 0.
+ template <typename SymT>
+ bool handleRemainderOp(const SymT *Sym, RangeSet Constraint) {
+ if (Sym->getOpcode() != BO_Rem)
+ return true;
+ // a % b != 0 implies that a != 0.
+ if (!Constraint.containsZero()) {
+ SVal SymSVal = Builder.makeSymbolVal(Sym->getLHS());
+ if (auto NonLocSymSVal = SymSVal.getAs<nonloc::SymbolVal>()) {
+ State = State->assume(*NonLocSymSVal, true);
+ if (!State)
+ return false;
+ }
+ }
+ return true;
+ }
+
inline bool assignSymExprToConst(const SymExpr *Sym, Const Constraint);
+ inline bool assignSymIntExprToRangeSet(const SymIntExpr *Sym,
+ RangeSet Constraint) {
+ return handleRemainderOp(Sym, Constraint);
+ }
inline bool assignSymSymExprToRangeSet(const SymSymExpr *Sym,
RangeSet Constraint);
@@ -1568,11 +1711,9 @@ private:
assert(!Constraint.isEmpty() && "Empty ranges shouldn't get here");
if (Constraint.getConcreteValue())
- return !Constraint.getConcreteValue()->isNullValue();
+ return !Constraint.getConcreteValue()->isZero();
- APSIntType T{Constraint.getMinValue()};
- Const Zero = T.getZeroValue();
- if (!Constraint.contains(Zero))
+ if (!Constraint.containsZero())
return true;
return llvm::None;
@@ -1583,112 +1724,6 @@ private:
RangeSet::Factory &RangeFactory;
};
-//===----------------------------------------------------------------------===//
-// Constraint manager implementation details
-//===----------------------------------------------------------------------===//
-
-class RangeConstraintManager : public RangedConstraintManager {
-public:
- RangeConstraintManager(ExprEngine *EE, SValBuilder &SVB)
- : RangedConstraintManager(EE, SVB), F(getBasicVals()) {}
-
- //===------------------------------------------------------------------===//
- // Implementation for interface from ConstraintManager.
- //===------------------------------------------------------------------===//
-
- bool haveEqualConstraints(ProgramStateRef S1,
- ProgramStateRef S2) const override {
- // NOTE: ClassMembers are as simple as back pointers for ClassMap,
- // so comparing constraint ranges and class maps should be
- // sufficient.
- return S1->get<ConstraintRange>() == S2->get<ConstraintRange>() &&
- S1->get<ClassMap>() == S2->get<ClassMap>();
- }
-
- bool canReasonAbout(SVal X) const override;
-
- ConditionTruthVal checkNull(ProgramStateRef State, SymbolRef Sym) override;
-
- const llvm::APSInt *getSymVal(ProgramStateRef State,
- SymbolRef Sym) const override;
-
- ProgramStateRef removeDeadBindings(ProgramStateRef State,
- SymbolReaper &SymReaper) override;
-
- void printJson(raw_ostream &Out, ProgramStateRef State, const char *NL = "\n",
- unsigned int Space = 0, bool IsDot = false) const override;
- void printConstraints(raw_ostream &Out, ProgramStateRef State,
- const char *NL = "\n", unsigned int Space = 0,
- bool IsDot = false) const;
- void printEquivalenceClasses(raw_ostream &Out, ProgramStateRef State,
- const char *NL = "\n", unsigned int Space = 0,
- bool IsDot = false) const;
- void printDisequalities(raw_ostream &Out, ProgramStateRef State,
- const char *NL = "\n", unsigned int Space = 0,
- bool IsDot = false) const;
-
- //===------------------------------------------------------------------===//
- // Implementation for interface from RangedConstraintManager.
- //===------------------------------------------------------------------===//
-
- ProgramStateRef assumeSymNE(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymEQ(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymLT(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymGT(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymLE(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymGE(ProgramStateRef State, SymbolRef Sym,
- const llvm::APSInt &V,
- const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymWithinInclusiveRange(
- ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
- const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
-
- ProgramStateRef assumeSymOutsideInclusiveRange(
- ProgramStateRef State, SymbolRef Sym, const llvm::APSInt &From,
- const llvm::APSInt &To, const llvm::APSInt &Adjustment) override;
-
-private:
- RangeSet::Factory F;
-
- RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
- RangeSet getRange(ProgramStateRef State, EquivalenceClass Class);
- ProgramStateRef setRange(ProgramStateRef State, SymbolRef Sym,
- RangeSet Range);
- ProgramStateRef setRange(ProgramStateRef State, EquivalenceClass Class,
- RangeSet Range);
-
- RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment);
- RangeSet getSymGTRange(ProgramStateRef St, SymbolRef Sym,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment);
- RangeSet getSymLERange(ProgramStateRef St, SymbolRef Sym,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment);
- RangeSet getSymLERange(llvm::function_ref<RangeSet()> RS,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment);
- RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment);
-};
bool ConstraintAssignor::assignSymExprToConst(const SymExpr *Sym,
const llvm::APSInt &Constraint) {
@@ -1716,11 +1751,26 @@ bool ConstraintAssignor::assignSymExprToConst(const SymExpr *Sym,
return false;
}
+ // We may have trivial equivalence classes in the disequality info as
+ // well, and we need to simplify them.
+ DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>();
+ for (std::pair<EquivalenceClass, ClassSet> DisequalityEntry :
+ DisequalityInfo) {
+ EquivalenceClass Class = DisequalityEntry.first;
+ ClassSet DisequalClasses = DisequalityEntry.second;
+ State = EquivalenceClass::simplify(Builder, RangeFactory, State, Class);
+ if (!State)
+ return false;
+ }
+
return true;
}
bool ConstraintAssignor::assignSymSymExprToRangeSet(const SymSymExpr *Sym,
RangeSet Constraint) {
+ if (!handleRemainderOp(Sym, Constraint))
+ return false;
+
Optional<bool> ConstraintAsBool = interpreteAsBool(Constraint);
if (!ConstraintAsBool)
@@ -2086,6 +2136,61 @@ inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
return llvm::None;
}
+LLVM_NODISCARD ProgramStateRef
+EquivalenceClass::removeMember(ProgramStateRef State, const SymbolRef Old) {
+
+ SymbolSet ClsMembers = getClassMembers(State);
+ assert(ClsMembers.contains(Old));
+
+ // We don't remove `Old`'s Sym->Class relation for two reasons:
+ // 1) This way constraints for the old symbol can still be found via it's
+ // equivalence class that it used to be the member of.
+ // 2) Performance and resource reasons. We can spare one removal and thus one
+ // additional tree in the forest of `ClassMap`.
+
+ // Remove `Old`'s Class->Sym relation.
+ SymbolSet::Factory &F = getMembersFactory(State);
+ ClassMembersTy::Factory &EMFactory = State->get_context<ClassMembers>();
+ ClsMembers = F.remove(ClsMembers, Old);
+ // Ensure another precondition of the removeMember function (we can check
+ // this only with isEmpty, thus we have to do the remove first).
+ assert(!ClsMembers.isEmpty() &&
+ "Class should have had at least two members before member removal");
+ // Overwrite the existing members assigned to this class.
+ ClassMembersTy ClassMembersMap = State->get<ClassMembers>();
+ ClassMembersMap = EMFactory.add(ClassMembersMap, *this, ClsMembers);
+ State = State->set<ClassMembers>(ClassMembersMap);
+
+ return State;
+}
+
+// Re-evaluate an SVal with top-level `State->assume` logic.
+LLVM_NODISCARD ProgramStateRef reAssume(ProgramStateRef State,
+ const RangeSet *Constraint,
+ SVal TheValue) {
+ if (!Constraint)
+ return State;
+
+ const auto DefinedVal = TheValue.castAs<DefinedSVal>();
+
+ // If the SVal is 0, we can simply interpret that as `false`.
+ if (Constraint->encodesFalseRange())
+ return State->assume(DefinedVal, false);
+
+ // If the constraint does not encode 0 then we can interpret that as `true`
+ // AND as a Range(Set).
+ if (Constraint->encodesTrueRange()) {
+ State = State->assume(DefinedVal, true);
+ if (!State)
+ return nullptr;
+ // Fall through, re-assume based on the range values as well.
+ }
+ // Overestimate the individual Ranges with the RangeSet' lowest and
+ // highest values.
+ return State->assumeInclusiveRange(DefinedVal, Constraint->getMinValue(),
+ Constraint->getMaxValue(), true);
+}
+
// Iterate over all symbols and try to simplify them. Once a symbol is
// simplified then we check if we can merge the simplified symbol's equivalence
// class to this class. This way, we simplify not just the symbols but the
@@ -2096,14 +2201,62 @@ EquivalenceClass::simplify(SValBuilder &SVB, RangeSet::Factory &F,
ProgramStateRef State, EquivalenceClass Class) {
SymbolSet ClassMembers = Class.getClassMembers(State);
for (const SymbolRef &MemberSym : ClassMembers) {
- SymbolRef SimplifiedMemberSym = ento::simplify(State, MemberSym);
+
+ const SVal SimplifiedMemberVal = simplifyToSVal(State, MemberSym);
+ const SymbolRef SimplifiedMemberSym = SimplifiedMemberVal.getAsSymbol();
+
+ // The symbol is collapsed to a constant, check if the current State is
+ // still feasible.
+ if (const auto CI = SimplifiedMemberVal.getAs<nonloc::ConcreteInt>()) {
+ const llvm::APSInt &SV = CI->getValue();
+ const RangeSet *ClassConstraint = getConstraint(State, Class);
+ // We have found a contradiction.
+ if (ClassConstraint && !ClassConstraint->contains(SV))
+ return nullptr;
+ }
+
if (SimplifiedMemberSym && MemberSym != SimplifiedMemberSym) {
// The simplified symbol should be the member of the original Class,
// however, it might be in another existing class at the moment. We
// have to merge these classes.
+ ProgramStateRef OldState = State;
State = merge(F, State, MemberSym, SimplifiedMemberSym);
if (!State)
return nullptr;
+ // No state change, no merge happened actually.
+ if (OldState == State)
+ continue;
+
+ assert(find(State, MemberSym) == find(State, SimplifiedMemberSym));
+ // Remove the old and more complex symbol.
+ State = find(State, MemberSym).removeMember(State, MemberSym);
+
+ // Query the class constraint again b/c that may have changed during the
+ // merge above.
+ const RangeSet *ClassConstraint = getConstraint(State, Class);
+
+ // Re-evaluate an SVal with top-level `State->assume`, this ignites
+ // a RECURSIVE algorithm that will reach a FIXPOINT.
+ //
+ // About performance and complexity: Let us assume that in a State we
+ // have N non-trivial equivalence classes and that all constraints and
+ // disequality info is related to non-trivial classes. In the worst case,
+ // we can simplify only one symbol of one class in each iteration. The
+ // number of symbols in one class cannot grow b/c we replace the old
+ // symbol with the simplified one. Also, the number of the equivalence
+ // classes can decrease only, b/c the algorithm does a merge operation
+ // optionally. We need N iterations in this case to reach the fixpoint.
+ // Thus, the steps needed to be done in the worst case is proportional to
+ // N*N.
+ //
+ // This worst case scenario can be extended to that case when we have
+ // trivial classes in the constraints and in the disequality map. This
+ // case can be reduced to the case with a State where there are only
+ // non-trivial classes. This is because a merge operation on two trivial
+ // classes results in one non-trivial class.
+ State = reAssume(State, ClassConstraint, SimplifiedMemberVal);
+ if (!State)
+ return nullptr;
}
}
return State;
diff --git a/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
index d227c025fb20..892d64ea4e4e 100644
--- a/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
@@ -41,7 +41,12 @@ ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State,
return assumeSymRel(State, SIE->getLHS(), op, SIE->getRHS());
}
- } else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
+ // Handle adjustment with non-comparison ops.
+ const llvm::APSInt &Zero = getBasicVals().getValue(0, SIE->getType());
+ return assumeSymRel(State, SIE, (Assumption ? BO_NE : BO_EQ), Zero);
+ }
+
+ if (const auto *SSE = dyn_cast<SymSymExpr>(Sym)) {
BinaryOperator::Opcode Op = SSE->getOpcode();
assert(BinaryOperator::isComparisonOp(Op));
@@ -226,9 +231,13 @@ void RangedConstraintManager::computeAdjustment(SymbolRef &Sym,
}
}
-SymbolRef simplify(ProgramStateRef State, SymbolRef Sym) {
+SVal simplifyToSVal(ProgramStateRef State, SymbolRef Sym) {
SValBuilder &SVB = State->getStateManager().getSValBuilder();
- SVal SimplifiedVal = SVB.simplifySVal(State, SVB.makeSymbolVal(Sym));
+ return SVB.simplifySVal(State, SVB.makeSymbolVal(Sym));
+}
+
+SymbolRef simplify(ProgramStateRef State, SymbolRef Sym) {
+ SVal SimplifiedVal = simplifyToSVal(State, Sym);
if (SymbolRef SimplifiedSym = SimplifiedVal.getAsSymbol())
return SimplifiedSym;
return Sym;
diff --git a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
index 4ffa1aacb41f..135130b35ba7 100644
--- a/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RegionStore.cpp
@@ -62,8 +62,8 @@ private:
: P(r, k), Data(offset) {
assert(r && "Must have known regions.");
assert(getOffset() == offset && "Failed to store offset");
- assert((r == r->getBaseRegion() || isa<ObjCIvarRegion>(r) ||
- isa <CXXDerivedObjectRegion>(r)) &&
+ assert((r == r->getBaseRegion() ||
+ isa<ObjCIvarRegion, CXXDerivedObjectRegion>(r)) &&
"Not a base");
}
public:
@@ -437,6 +437,15 @@ public:
RegionBindingsRef removeSubRegionBindings(RegionBindingsConstRef B,
const SubRegion *R);
+ Optional<SVal>
+ getConstantValFromConstArrayInitializer(RegionBindingsConstRef B,
+ const ElementRegion *R);
+ Optional<SVal>
+ getSValFromInitListExpr(const InitListExpr *ILE,
+ const SmallVector<uint64_t, 2> &ConcreteOffsets,
+ QualType ElemT);
+ SVal getSValFromStringLiteral(const StringLiteral *SL, uint64_t Offset,
+ QualType ElemT);
public: // Part of public interface to class.
@@ -1135,7 +1144,7 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
if (Regions)
Regions->push_back(baseR);
- if (isa<AllocaRegion>(baseR) || isa<SymbolicRegion>(baseR)) {
+ if (isa<AllocaRegion, SymbolicRegion>(baseR)) {
// Invalidate the region by setting its default value to
// conjured symbol. The type of the symbol is irrelevant.
DefinedOrUnknownSVal V =
@@ -1224,7 +1233,7 @@ void InvalidateRegionsWorker::VisitCluster(const MemRegion *baseR,
// detection.
SVal V = I.getData();
const MemRegion *R = V.getAsRegion();
- if (R && isa<SymbolicRegion>(R))
+ if (isa_and_nonnull<SymbolicRegion>(R))
VisitBinding(V);
}
}
@@ -1625,6 +1634,280 @@ RegionStoreManager::findLazyBinding(RegionBindingsConstRef B,
return Result;
}
+/// This is a helper function for `getConstantValFromConstArrayInitializer`.
+///
+/// Return an array of extents of the declared array type.
+///
+/// E.g. for `int x[1][2][3];` returns { 1, 2, 3 }.
+static SmallVector<uint64_t, 2>
+getConstantArrayExtents(const ConstantArrayType *CAT) {
+ assert(CAT && "ConstantArrayType should not be null");
+ CAT = cast<ConstantArrayType>(CAT->getCanonicalTypeInternal());
+ SmallVector<uint64_t, 2> Extents;
+ do {
+ Extents.push_back(CAT->getSize().getZExtValue());
+ } while ((CAT = dyn_cast<ConstantArrayType>(CAT->getElementType())));
+ return Extents;
+}
+
+/// This is a helper function for `getConstantValFromConstArrayInitializer`.
+///
+/// Return an array of offsets from nested ElementRegions and a root base
+/// region. The array is never empty and a base region is never null.
+///
+/// E.g. for `Element{Element{Element{VarRegion},1},2},3}` returns { 3, 2, 1 }.
+/// This represents an access through indirection: `arr[1][2][3];`
+///
+/// \param ER The given (possibly nested) ElementRegion.
+///
+/// \note The result array is in the reverse order of indirection expression:
+/// arr[1][2][3] -> { 3, 2, 1 }. This helps to provide complexity O(n), where n
+/// is a number of indirections. It may not affect performance in real-life
+/// code, though.
+static std::pair<SmallVector<SVal, 2>, const MemRegion *>
+getElementRegionOffsetsWithBase(const ElementRegion *ER) {
+ assert(ER && "ConstantArrayType should not be null");
+ const MemRegion *Base;
+ SmallVector<SVal, 2> SValOffsets;
+ do {
+ SValOffsets.push_back(ER->getIndex());
+ Base = ER->getSuperRegion();
+ ER = dyn_cast<ElementRegion>(Base);
+ } while (ER);
+ return {SValOffsets, Base};
+}
+
+/// This is a helper function for `getConstantValFromConstArrayInitializer`.
+///
+/// Convert array of offsets from `SVal` to `uint64_t` in consideration of
+/// respective array extents.
+/// \param SrcOffsets [in] The array of offsets of type `SVal` in reversed
+/// order (expectedly received from `getElementRegionOffsetsWithBase`).
+/// \param ArrayExtents [in] The array of extents.
+/// \param DstOffsets [out] The array of offsets of type `uint64_t`.
+/// \returns:
+/// - `None` for successful convertion.
+/// - `UndefinedVal` or `UnknownVal` otherwise. It's expected that this SVal
+/// will be returned as a suitable value of the access operation.
+/// which should be returned as a correct
+///
+/// \example:
+/// const int arr[10][20][30] = {}; // ArrayExtents { 10, 20, 30 }
+/// int x1 = arr[4][5][6]; // SrcOffsets { NonLoc(6), NonLoc(5), NonLoc(4) }
+/// // DstOffsets { 4, 5, 6 }
+/// // returns None
+/// int x2 = arr[42][5][-6]; // returns UndefinedVal
+/// int x3 = arr[4][5][x2]; // returns UnknownVal
+static Optional<SVal>
+convertOffsetsFromSvalToUnsigneds(const SmallVector<SVal, 2> &SrcOffsets,
+ const SmallVector<uint64_t, 2> ArrayExtents,
+ SmallVector<uint64_t, 2> &DstOffsets) {
+ // Check offsets for being out of bounds.
+ // C++20 [expr.add] 7.6.6.4 (excerpt):
+ // If P points to an array element i of an array object x with n
+ // elements, where i < 0 or i > n, the behavior is undefined.
+ // Dereferencing is not allowed on the "one past the last
+ // element", when i == n.
+ // Example:
+ // const int arr[3][2] = {{1, 2}, {3, 4}};
+ // arr[0][0]; // 1
+ // arr[0][1]; // 2
+ // arr[0][2]; // UB
+ // arr[1][0]; // 3
+ // arr[1][1]; // 4
+ // arr[1][-1]; // UB
+ // arr[2][0]; // 0
+ // arr[2][1]; // 0
+ // arr[-2][0]; // UB
+ DstOffsets.resize(SrcOffsets.size());
+ auto ExtentIt = ArrayExtents.begin();
+ auto OffsetIt = DstOffsets.begin();
+ // Reverse `SValOffsets` to make it consistent with `ArrayExtents`.
+ for (SVal V : llvm::reverse(SrcOffsets)) {
+ if (auto CI = V.getAs<nonloc::ConcreteInt>()) {
+ // When offset is out of array's bounds, result is UB.
+ const llvm::APSInt &Offset = CI->getValue();
+ if (Offset.isNegative() || Offset.uge(*(ExtentIt++)))
+ return UndefinedVal();
+ // Store index in a reversive order.
+ *(OffsetIt++) = Offset.getZExtValue();
+ continue;
+ }
+ // Symbolic index presented. Return Unknown value.
+ // FIXME: We also need to take ElementRegions with symbolic indexes into
+ // account.
+ return UnknownVal();
+ }
+ return None;
+}
+
+Optional<SVal> RegionStoreManager::getConstantValFromConstArrayInitializer(
+ RegionBindingsConstRef B, const ElementRegion *R) {
+ assert(R && "ElementRegion should not be null");
+
+ // Treat an n-dimensional array.
+ SmallVector<SVal, 2> SValOffsets;
+ const MemRegion *Base;
+ std::tie(SValOffsets, Base) = getElementRegionOffsetsWithBase(R);
+ const VarRegion *VR = dyn_cast<VarRegion>(Base);
+ if (!VR)
+ return None;
+
+ assert(!SValOffsets.empty() && "getElementRegionOffsets guarantees the "
+ "offsets vector is not empty.");
+
+ // Check if the containing array has an initialized value that we can trust.
+ // We can trust a const value or a value of a global initializer in main().
+ const VarDecl *VD = VR->getDecl();
+ if (!VD->getType().isConstQualified() &&
+ !R->getElementType().isConstQualified() &&
+ (!B.isMainAnalysis() || !VD->hasGlobalStorage()))
+ return None;
+
+ // Array's declaration should have `ConstantArrayType` type, because only this
+ // type contains an array extent. It may happen that array type can be of
+ // `IncompleteArrayType` type. To get the declaration of `ConstantArrayType`
+ // type, we should find the declaration in the redeclarations chain that has
+ // the initialization expression.
+ // NOTE: `getAnyInitializer` has an out-parameter, which returns a new `VD`
+ // from which an initializer is obtained. We replace current `VD` with the new
+ // `VD`. If the return value of the function is null than `VD` won't be
+ // replaced.
+ const Expr *Init = VD->getAnyInitializer(VD);
+ // NOTE: If `Init` is non-null, then a new `VD` is non-null for sure. So check
+ // `Init` for null only and don't worry about the replaced `VD`.
+ if (!Init)
+ return None;
+
+ // Array's declaration should have ConstantArrayType type, because only this
+ // type contains an array extent.
+ const ConstantArrayType *CAT = Ctx.getAsConstantArrayType(VD->getType());
+ if (!CAT)
+ return None;
+
+ // Get array extents.
+ SmallVector<uint64_t, 2> Extents = getConstantArrayExtents(CAT);
+
+ // The number of offsets should equal to the numbers of extents,
+ // otherwise wrong type punning occured. For instance:
+ // int arr[1][2][3];
+ // auto ptr = (int(*)[42])arr;
+ // auto x = ptr[4][2]; // UB
+ // FIXME: Should return UndefinedVal.
+ if (SValOffsets.size() != Extents.size())
+ return None;
+
+ SmallVector<uint64_t, 2> ConcreteOffsets;
+ if (Optional<SVal> V = convertOffsetsFromSvalToUnsigneds(SValOffsets, Extents,
+ ConcreteOffsets))
+ return *V;
+
+ // Handle InitListExpr.
+ // Example:
+ // const char arr[4][2] = { { 1, 2 }, { 3 }, 4, 5 };
+ if (const auto *ILE = dyn_cast<InitListExpr>(Init))
+ return getSValFromInitListExpr(ILE, ConcreteOffsets, R->getElementType());
+
+ // Handle StringLiteral.
+ // Example:
+ // const char arr[] = "abc";
+ if (const auto *SL = dyn_cast<StringLiteral>(Init))
+ return getSValFromStringLiteral(SL, ConcreteOffsets.front(),
+ R->getElementType());
+
+ // FIXME: Handle CompoundLiteralExpr.
+
+ return None;
+}
+
+/// Returns an SVal, if possible, for the specified position of an
+/// initialization list.
+///
+/// \param ILE The given initialization list.
+/// \param Offsets The array of unsigned offsets. E.g. for the expression
+/// `int x = arr[1][2][3];` an array should be { 1, 2, 3 }.
+/// \param ElemT The type of the result SVal expression.
+/// \return Optional SVal for the particular position in the initialization
+/// list. E.g. for the list `{{1, 2},[3, 4],{5, 6}, {}}` offsets:
+/// - {1, 1} returns SVal{4}, because it's the second position in the second
+/// sublist;
+/// - {3, 0} returns SVal{0}, because there's no explicit value at this
+/// position in the sublist.
+///
+/// NOTE: Inorder to get a valid SVal, a caller shall guarantee valid offsets
+/// for the given initialization list. Otherwise SVal can be an equivalent to 0
+/// or lead to assertion.
+Optional<SVal> RegionStoreManager::getSValFromInitListExpr(
+ const InitListExpr *ILE, const SmallVector<uint64_t, 2> &Offsets,
+ QualType ElemT) {
+ assert(ILE && "InitListExpr should not be null");
+
+ for (uint64_t Offset : Offsets) {
+ // C++20 [dcl.init.string] 9.4.2.1:
+ // An array of ordinary character type [...] can be initialized by [...]
+ // an appropriately-typed string-literal enclosed in braces.
+ // Example:
+ // const char arr[] = { "abc" };
+ if (ILE->isStringLiteralInit())
+ if (const auto *SL = dyn_cast<StringLiteral>(ILE->getInit(0)))
+ return getSValFromStringLiteral(SL, Offset, ElemT);
+
+ // C++20 [expr.add] 9.4.17.5 (excerpt):
+ // i-th array element is value-initialized for each k < i ≤ n,
+ // where k is an expression-list size and n is an array extent.
+ if (Offset >= ILE->getNumInits())
+ return svalBuilder.makeZeroVal(ElemT);
+
+ const Expr *E = ILE->getInit(Offset);
+ const auto *IL = dyn_cast<InitListExpr>(E);
+ if (!IL)
+ // Return a constant value, if it is presented.
+ // FIXME: Support other SVals.
+ return svalBuilder.getConstantVal(E);
+
+ // Go to the nested initializer list.
+ ILE = IL;
+ }
+ llvm_unreachable(
+ "Unhandled InitListExpr sub-expressions or invalid offsets.");
+}
+
+/// Returns an SVal, if possible, for the specified position in a string
+/// literal.
+///
+/// \param SL The given string literal.
+/// \param Offset The unsigned offset. E.g. for the expression
+/// `char x = str[42];` an offset should be 42.
+/// E.g. for the string "abc" offset:
+/// - 1 returns SVal{b}, because it's the second position in the string.
+/// - 42 returns SVal{0}, because there's no explicit value at this
+/// position in the string.
+/// \param ElemT The type of the result SVal expression.
+///
+/// NOTE: We return `0` for every offset >= the literal length for array
+/// declarations, like:
+/// const char str[42] = "123"; // Literal length is 4.
+/// char c = str[41]; // Offset is 41.
+/// FIXME: Nevertheless, we can't do the same for pointer declaraions, like:
+/// const char * const str = "123"; // Literal length is 4.
+/// char c = str[41]; // Offset is 41. Returns `0`, but Undef
+/// // expected.
+/// It should be properly handled before reaching this point.
+/// The main problem is that we can't distinguish between these declarations,
+/// because in case of array we can get the Decl from VarRegion, but in case
+/// of pointer the region is a StringRegion, which doesn't contain a Decl.
+/// Possible solution could be passing an array extent along with the offset.
+SVal RegionStoreManager::getSValFromStringLiteral(const StringLiteral *SL,
+ uint64_t Offset,
+ QualType ElemT) {
+ assert(SL && "StringLiteral should not be null");
+ // C++20 [dcl.init.string] 9.4.2.3:
+ // If there are fewer initializers than there are array elements, each
+ // element not explicitly initialized shall be zero-initialized [dcl.init].
+ uint32_t Code = (Offset >= SL->getLength()) ? 0 : SL->getCodeUnit(Offset);
+ return svalBuilder.makeIntVal(Code, ElemT);
+}
+
SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
const ElementRegion* R) {
// Check if the region has a binding.
@@ -1636,59 +1919,21 @@ SVal RegionStoreManager::getBindingForElement(RegionBindingsConstRef B,
// Check if the region is an element region of a string literal.
if (const StringRegion *StrR = dyn_cast<StringRegion>(superR)) {
// FIXME: Handle loads from strings where the literal is treated as
- // an integer, e.g., *((unsigned int*)"hello")
+ // an integer, e.g., *((unsigned int*)"hello"). Such loads are UB according
+ // to C++20 7.2.1.11 [basic.lval].
QualType T = Ctx.getAsArrayType(StrR->getValueType())->getElementType();
if (!Ctx.hasSameUnqualifiedType(T, R->getElementType()))
return UnknownVal();
-
- const StringLiteral *Str = StrR->getStringLiteral();
- SVal Idx = R->getIndex();
- if (Optional<nonloc::ConcreteInt> CI = Idx.getAs<nonloc::ConcreteInt>()) {
- int64_t i = CI->getValue().getSExtValue();
- // Abort on string underrun. This can be possible by arbitrary
- // clients of getBindingForElement().
- if (i < 0)
+ if (const auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
+ const llvm::APSInt &Idx = CI->getValue();
+ if (Idx < 0)
return UndefinedVal();
- int64_t length = Str->getLength();
- // Technically, only i == length is guaranteed to be null.
- // However, such overflows should be caught before reaching this point;
- // the only time such an access would be made is if a string literal was
- // used to initialize a larger array.
- char c = (i >= length) ? '\0' : Str->getCodeUnit(i);
- return svalBuilder.makeIntVal(c, T);
- }
- } else if (const VarRegion *VR = dyn_cast<VarRegion>(superR)) {
- // Check if the containing array has an initialized value that we can trust.
- // We can trust a const value or a value of a global initializer in main().
- const VarDecl *VD = VR->getDecl();
- if (VD->getType().isConstQualified() ||
- R->getElementType().isConstQualified() ||
- (B.isMainAnalysis() && VD->hasGlobalStorage())) {
- if (const Expr *Init = VD->getAnyInitializer()) {
- if (const auto *InitList = dyn_cast<InitListExpr>(Init)) {
- // The array index has to be known.
- if (auto CI = R->getIndex().getAs<nonloc::ConcreteInt>()) {
- int64_t i = CI->getValue().getSExtValue();
- // If it is known that the index is out of bounds, we can return
- // an undefined value.
- if (i < 0)
- return UndefinedVal();
-
- if (auto CAT = Ctx.getAsConstantArrayType(VD->getType()))
- if (CAT->getSize().sle(i))
- return UndefinedVal();
-
- // If there is a list, but no init, it must be zero.
- if (i >= InitList->getNumInits())
- return svalBuilder.makeZeroVal(R->getElementType());
-
- if (const Expr *ElemInit = InitList->getInit(i))
- if (Optional<SVal> V = svalBuilder.getConstantVal(ElemInit))
- return *V;
- }
- }
- }
+ const StringLiteral *SL = StrR->getStringLiteral();
+ return getSValFromStringLiteral(SL, Idx.getZExtValue(), T);
}
+ } else if (isa<ElementRegion, VarRegion>(superR)) {
+ if (Optional<SVal> V = getConstantValFromConstArrayInitializer(B, R))
+ return *V;
}
// Check for loads from a code text region. For such loads, just give up.
diff --git a/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
index 7395622a659c..04165a443fff 100644
--- a/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SMTConstraintManager.cpp
@@ -1,9 +1,8 @@
//== SMTConstraintManager.cpp -----------------------------------*- C++ -*--==//
//
-// The LLVM Compiler Infrastructure
-//
-// This file is distributed under the University of Illinois Open Source
-// License. See LICENSE.TXT for details.
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index b459b5adb511..71bfc86ab8f7 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -49,6 +49,16 @@ using namespace ento;
void SValBuilder::anchor() {}
+SValBuilder::SValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context,
+ ProgramStateManager &stateMgr)
+ : Context(context), BasicVals(context, alloc),
+ SymMgr(context, BasicVals, alloc), MemMgr(context, alloc),
+ StateMgr(stateMgr),
+ AnOpts(
+ stateMgr.getOwningEngine().getAnalysisManager().getAnalyzerOptions()),
+ ArrayIndexTy(context.LongLongTy),
+ ArrayIndexWidth(context.getTypeSize(ArrayIndexTy)) {}
+
DefinedOrUnknownSVal SValBuilder::makeZeroVal(QualType type) {
if (Loc::isLocType(type))
return makeNull();
@@ -244,8 +254,7 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol,
}
DefinedSVal SValBuilder::getMemberPointer(const NamedDecl *ND) {
- assert(!ND || isa<CXXMethodDecl>(ND) || isa<FieldDecl>(ND) ||
- isa<IndirectFieldDecl>(ND));
+ assert(!ND || (isa<CXXMethodDecl, FieldDecl, IndirectFieldDecl>(ND)));
if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(ND)) {
// Sema treats pointers to static member functions as have function pointer
@@ -405,9 +414,7 @@ SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op,
// TODO: When the Max Complexity is reached, we should conjure a symbol
// instead of generating an Unknown value and propagate the taint info to it.
- const unsigned MaxComp = StateMgr.getOwningEngine()
- .getAnalysisManager()
- .options.MaxSymbolComplexity;
+ const unsigned MaxComp = AnOpts.MaxSymbolComplexity;
if (symLHS && symRHS &&
(symLHS->computeComplexity() + symRHS->computeComplexity()) < MaxComp)
@@ -725,16 +732,12 @@ SVal SValBuilder::evalCastSubKind(loc::MemRegionVal V, QualType CastTy,
// This change is needed for architectures with varying
// pointer widths. See the amdgcn opencl reproducer with
// this change as an example: solver-sym-simplification-ptr-bool.cl
- // FIXME: We could encounter a reference here,
- // try returning a concrete 'true' since it might
- // be easier on the solver.
// FIXME: Cleanup remainder of `getZeroWithPtrWidth ()`
// and `getIntWithPtrWidth()` functions to prevent future
// confusion
- const llvm::APSInt &Zero = Ty->isReferenceType()
- ? BasicVals.getZeroWithPtrWidth()
- : BasicVals.getZeroWithTypeSize(Ty);
- return makeNonLoc(Sym, BO_NE, Zero, CastTy);
+ if (!Ty->isReferenceType())
+ return makeNonLoc(Sym, BO_NE, BasicVals.getZeroWithTypeSize(Ty),
+ CastTy);
}
// Non-symbolic memory regions are always true.
return makeTruthVal(true, CastTy);
diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index e57d92fbcebb..681a1f64eadc 100644
--- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -11,7 +11,6 @@
//===----------------------------------------------------------------------===//
#include "clang/StaticAnalyzer/Core/PathSensitive/SValBuilder.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/AnalysisManager.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
@@ -25,7 +24,7 @@ class SimpleSValBuilder : public SValBuilder {
public:
SimpleSValBuilder(llvm::BumpPtrAllocator &alloc, ASTContext &context,
ProgramStateManager &stateMgr)
- : SValBuilder(alloc, context, stateMgr) {}
+ : SValBuilder(alloc, context, stateMgr) {}
~SimpleSValBuilder() override {}
SVal evalMinus(NonLoc val) override;
@@ -129,14 +128,14 @@ SVal SimpleSValBuilder::MakeSymIntVal(const SymExpr *LHS,
// a&0 and a&(~0)
if (RHS == 0)
return makeIntVal(0, resultTy);
- else if (RHS.isAllOnesValue())
+ else if (RHS.isAllOnes())
isIdempotent = true;
break;
case BO_Or:
// a|0 and a|(~0)
if (RHS == 0)
isIdempotent = true;
- else if (RHS.isAllOnesValue()) {
+ else if (RHS.isAllOnes()) {
const llvm::APSInt &Result = BasicVals.Convert(resultTy, RHS);
return nonloc::ConcreteInt(Result);
}
@@ -320,13 +319,10 @@ static Optional<NonLoc> tryRearrange(ProgramStateRef State,
// We expect everything to be of the same type - this type.
QualType SingleTy;
- auto &Opts =
- StateMgr.getOwningEngine().getAnalysisManager().getAnalyzerOptions();
-
// FIXME: After putting complexity threshold to the symbols we can always
// rearrange additive operations but rearrange comparisons only if
// option is set.
- if(!Opts.ShouldAggressivelySimplifyBinaryOperation)
+ if (!SVB.getAnalyzerOptions().ShouldAggressivelySimplifyBinaryOperation)
return None;
SymbolRef LSym = Lhs.getAsSymbol();
@@ -513,7 +509,7 @@ SVal SimpleSValBuilder::evalBinOpNN(ProgramStateRef state,
continue;
case BO_Shr:
// (~0)>>a
- if (LHSValue.isAllOnesValue() && LHSValue.isSigned())
+ if (LHSValue.isAllOnes() && LHSValue.isSigned())
return evalCast(lhs, resultTy, QualType{});
LLVM_FALLTHROUGH;
case BO_Shl:
diff --git a/clang/lib/StaticAnalyzer/Core/Store.cpp b/clang/lib/StaticAnalyzer/Core/Store.cpp
index b867b0746f90..3cc0cd224d7a 100644
--- a/clang/lib/StaticAnalyzer/Core/Store.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Store.cpp
@@ -84,7 +84,7 @@ Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
// involved. Blocks can be casted to/from 'id', as they can be treated
// as Objective-C objects. This could possibly be handled by enhancing
// our reasoning of downcasts of symbolic objects.
- if (isa<CodeTextRegion>(R) || isa<SymbolicRegion>(R))
+ if (isa<CodeTextRegion, SymbolicRegion>(R))
return R;
// We don't know what to make of it. Return a NULL region, which
@@ -96,18 +96,24 @@ Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
// already be handled.
QualType PointeeTy = CastToTy->getPointeeType();
QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
+ CanonPointeeTy = CanonPointeeTy.getLocalUnqualifiedType();
// Handle casts to void*. We just pass the region through.
- if (CanonPointeeTy.getLocalUnqualifiedType() == Ctx.VoidTy)
+ if (CanonPointeeTy == Ctx.VoidTy)
return R;
- // Handle casts from compatible types.
- if (R->isBoundable())
+ const auto IsSameRegionType = [&Ctx](const MemRegion *R, QualType OtherTy) {
if (const auto *TR = dyn_cast<TypedValueRegion>(R)) {
QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
- if (CanonPointeeTy == ObjTy)
- return R;
+ if (OtherTy == ObjTy.getLocalUnqualifiedType())
+ return true;
}
+ return false;
+ };
+
+ // Handle casts from compatible types.
+ if (R->isBoundable() && IsSameRegionType(R, CanonPointeeTy))
+ return R;
// Process region cast according to the kind of the region being cast.
switch (R->getKind()) {
@@ -174,16 +180,11 @@ Optional<const MemRegion *> StoreManager::castRegion(const MemRegion *R,
CharUnits off = rawOff.getOffset();
if (off.isZero()) {
- // Edge case: we are at 0 bytes off the beginning of baseR. We
- // check to see if type we are casting to is the same as the base
- // region. If so, just return the base region.
- if (const auto *TR = dyn_cast<TypedValueRegion>(baseR)) {
- QualType ObjTy = Ctx.getCanonicalType(TR->getValueType());
- QualType CanonPointeeTy = Ctx.getCanonicalType(PointeeTy);
- if (CanonPointeeTy == ObjTy)
- return baseR;
- }
-
+ // Edge case: we are at 0 bytes off the beginning of baseR. We check to
+ // see if the type we are casting to is the same as the type of the base
+ // region. If so, just return the base region.
+ if (IsSameRegionType(baseR, CanonPointeeTy))
+ return baseR;
// Otherwise, create a new ElementRegion at offset 0.
return MakeElementRegion(cast<SubRegion>(baseR), PointeeTy);
}
@@ -442,6 +443,19 @@ SVal StoreManager::getLValueIvar(const ObjCIvarDecl *decl, SVal base) {
SVal StoreManager::getLValueElement(QualType elementType, NonLoc Offset,
SVal Base) {
+
+ // Special case, if index is 0, return the same type as if
+ // this was not an array dereference.
+ if (Offset.isZeroConstant()) {
+ QualType BT = Base.getType(this->Ctx);
+ if (!BT.isNull() && !elementType.isNull()) {
+ QualType PointeeTy = BT->getPointeeType();
+ if (!PointeeTy.isNull() &&
+ PointeeTy.getCanonicalType() == elementType.getCanonicalType())
+ return Base;
+ }
+ }
+
// If the base is an unknown or undefined value, just return it back.
// FIXME: For absolute pointer addresses, we just return that value back as
// well, although in reality we should return the offset added to that
diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index 79a8eef30576..1ae1f97efd2e 100644
--- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -425,19 +425,7 @@ bool SymbolReaper::isLiveRegion(const MemRegion *MR) {
// tell if anything still refers to this region. Unlike SymbolicRegions,
// AllocaRegions don't have associated symbols, though, so we don't actually
// have a way to track their liveness.
- if (isa<AllocaRegion>(MR))
- return true;
-
- if (isa<CXXThisRegion>(MR))
- return true;
-
- if (isa<MemSpaceRegion>(MR))
- return true;
-
- if (isa<CodeTextRegion>(MR))
- return true;
-
- return false;
+ return isa<AllocaRegion, CXXThisRegion, MemSpaceRegion, CodeTextRegion>(MR);
}
bool SymbolReaper::isLive(SymbolRef sym) {