aboutsummaryrefslogtreecommitdiff
path: root/clang/lib/StaticAnalyzer/Core
diff options
context:
space:
mode:
authorDimitry Andric <dim@FreeBSD.org>2021-02-16 20:13:02 +0000
committerDimitry Andric <dim@FreeBSD.org>2021-02-16 20:13:02 +0000
commitb60736ec1405bb0a8dd40989f67ef4c93da068ab (patch)
tree5c43fbb7c9fc45f0f87e0e6795a86267dbd12f9d /clang/lib/StaticAnalyzer/Core
parentcfca06d7963fa0909f90483b42a6d7d194d01e08 (diff)
Diffstat (limited to 'clang/lib/StaticAnalyzer/Core')
-rw-r--r--clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp25
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporter.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Core/CallEvent.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/CheckerContext.cpp2
-rw-r--r--clang/lib/StaticAnalyzer/Core/DynamicType.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Core/Environment.cpp21
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngine.cpp153
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp7
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp26
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp14
-rw-r--r--clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp13
-rw-r--r--clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp47
-rw-r--r--clang/lib/StaticAnalyzer/Core/IssueHash.cpp204
-rw-r--r--clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp400
-rw-r--r--clang/lib/StaticAnalyzer/Core/ProgramState.cpp3
-rw-r--r--clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp1112
-rw-r--r--clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp41
-rw-r--r--clang/lib/StaticAnalyzer/Core/SValBuilder.cpp23
-rw-r--r--clang/lib/StaticAnalyzer/Core/SVals.cpp48
-rw-r--r--clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp16
-rw-r--r--clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp4
-rw-r--r--clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp19
-rw-r--r--clang/lib/StaticAnalyzer/Core/SymbolManager.cpp28
-rw-r--r--clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp49
26 files changed, 1605 insertions, 687 deletions
diff --git a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
index 01ac2bc83bb6..8cd7f75e4e38 100644
--- a/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
+++ b/clang/lib/StaticAnalyzer/Core/AnalyzerOptions.cpp
@@ -40,7 +40,7 @@ void AnalyzerOptions::printFormattedEntry(
const size_t PadForDesc = InitialPad + EntryWidth;
FOut.PadToColumn(InitialPad) << EntryDescPair.first;
- // If the buffer's length is greater then PadForDesc, print a newline.
+ // If the buffer's length is greater than PadForDesc, print a newline.
if (FOut.getColumn() > PadForDesc)
FOut << '\n';
diff --git a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
index 73f057f09550..d1f5ac02278f 100644
--- a/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BasicValueFactory.cpp
@@ -42,7 +42,7 @@ void LazyCompoundValData::Profile(llvm::FoldingSetNodeID& ID,
}
void PointerToMemberData::Profile(
- llvm::FoldingSetNodeID& ID, const DeclaratorDecl *D,
+ llvm::FoldingSetNodeID &ID, const NamedDecl *D,
llvm::ImmutableList<const CXXBaseSpecifier *> L) {
ID.AddPointer(D);
ID.AddPointer(L.getInternalPointer());
@@ -159,17 +159,17 @@ BasicValueFactory::getLazyCompoundValData(const StoreRef &store,
}
const PointerToMemberData *BasicValueFactory::getPointerToMemberData(
- const DeclaratorDecl *DD, llvm::ImmutableList<const CXXBaseSpecifier *> L) {
+ const NamedDecl *ND, llvm::ImmutableList<const CXXBaseSpecifier *> L) {
llvm::FoldingSetNodeID ID;
- PointerToMemberData::Profile(ID, DD, L);
+ PointerToMemberData::Profile(ID, ND, L);
void *InsertPos;
PointerToMemberData *D =
PointerToMemberDataSet.FindNodeOrInsertPos(ID, InsertPos);
if (!D) {
- D = (PointerToMemberData*) BPAlloc.Allocate<PointerToMemberData>();
- new (D) PointerToMemberData(DD, L);
+ D = (PointerToMemberData *)BPAlloc.Allocate<PointerToMemberData>();
+ new (D) PointerToMemberData(ND, L);
PointerToMemberDataSet.InsertNode(D, InsertPos);
}
@@ -180,25 +180,24 @@ const PointerToMemberData *BasicValueFactory::accumCXXBase(
llvm::iterator_range<CastExpr::path_const_iterator> PathRange,
const nonloc::PointerToMember &PTM) {
nonloc::PointerToMember::PTMDataType PTMDT = PTM.getPTMData();
- const DeclaratorDecl *DD = nullptr;
+ const NamedDecl *ND = nullptr;
llvm::ImmutableList<const CXXBaseSpecifier *> PathList;
- if (PTMDT.isNull() || PTMDT.is<const DeclaratorDecl *>()) {
- if (PTMDT.is<const DeclaratorDecl *>())
- DD = PTMDT.get<const DeclaratorDecl *>();
+ if (PTMDT.isNull() || PTMDT.is<const NamedDecl *>()) {
+ if (PTMDT.is<const NamedDecl *>())
+ ND = PTMDT.get<const NamedDecl *>();
PathList = CXXBaseListFactory.getEmptyList();
} else { // const PointerToMemberData *
- const PointerToMemberData *PTMD =
- PTMDT.get<const PointerToMemberData *>();
- DD = PTMD->getDeclaratorDecl();
+ const PointerToMemberData *PTMD = PTMDT.get<const PointerToMemberData *>();
+ ND = PTMD->getDeclaratorDecl();
PathList = PTMD->getCXXBaseList();
}
for (const auto &I : llvm::reverse(PathRange))
PathList = prependCXXBase(I, PathList);
- return getPointerToMemberData(DD, PathList);
+ return getPointerToMemberData(ND, PathList);
}
const llvm::APSInt*
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
index 72be4e81c83d..bf38891b370a 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporter.cpp
@@ -1570,9 +1570,8 @@ static Optional<size_t> getLengthOnSingleLine(const SourceManager &SM,
if (FID != SM.getFileID(ExpansionRange.getEnd()))
return None;
- bool Invalid;
- const llvm::MemoryBuffer *Buffer = SM.getBuffer(FID, &Invalid);
- if (Invalid)
+ Optional<MemoryBufferRef> Buffer = SM.getBufferOrNone(FID);
+ if (!Buffer)
return None;
unsigned BeginOffset = SM.getFileOffset(ExpansionRange.getBegin());
@@ -2194,8 +2193,8 @@ void BasicBugReport::Profile(llvm::FoldingSetNodeID& hash) const {
for (SourceRange range : Ranges) {
if (!range.isValid())
continue;
- hash.AddInteger(range.getBegin().getRawEncoding());
- hash.AddInteger(range.getEnd().getRawEncoding());
+ hash.Add(range.getBegin());
+ hash.Add(range.getEnd());
}
}
@@ -2217,8 +2216,8 @@ void PathSensitiveBugReport::Profile(llvm::FoldingSetNodeID &hash) const {
for (SourceRange range : Ranges) {
if (!range.isValid())
continue;
- hash.AddInteger(range.getBegin().getRawEncoding());
- hash.AddInteger(range.getEnd().getRawEncoding());
+ hash.Add(range.getBegin());
+ hash.Add(range.getEnd());
}
}
diff --git a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
index ef4d38ff498f..bc72f4f8c1e3 100644
--- a/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
+++ b/clang/lib/StaticAnalyzer/Core/BugReporterVisitors.cpp
@@ -2813,7 +2813,7 @@ UndefOrNullArgVisitor::VisitNode(const ExplodedNode *N, BugReporterContext &BRC,
//===----------------------------------------------------------------------===//
FalsePositiveRefutationBRVisitor::FalsePositiveRefutationBRVisitor()
- : Constraints(ConstraintRangeTy::Factory().getEmptyMap()) {}
+ : Constraints(ConstraintMap::Factory().getEmptyMap()) {}
void FalsePositiveRefutationBRVisitor::finalizeVisitor(
BugReporterContext &BRC, const ExplodedNode *EndPathNode,
@@ -2855,9 +2855,8 @@ void FalsePositiveRefutationBRVisitor::finalizeVisitor(
void FalsePositiveRefutationBRVisitor::addConstraints(
const ExplodedNode *N, bool OverwriteConstraintsOnExistingSyms) {
// Collect new constraints
- const ConstraintRangeTy &NewCs = N->getState()->get<ConstraintRange>();
- ConstraintRangeTy::Factory &CF =
- N->getState()->get_context<ConstraintRange>();
+ ConstraintMap NewCs = getConstraintMap(N->getState());
+ ConstraintMap::Factory &CF = N->getState()->get_context<ConstraintMap>();
// Add constraints if we don't have them yet
for (auto const &C : NewCs) {
diff --git a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
index 78d13ddfb773..a55d9302ca58 100644
--- a/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CallEvent.cpp
@@ -687,7 +687,7 @@ void CXXInstanceCall::getExtraInvalidatedValues(
// base class decl, rather than the class of the instance which needs to be
// checked for mutable fields.
// TODO: We might as well look at the dynamic type of the object.
- const Expr *Ex = getCXXThisExpr()->ignoreParenBaseCasts();
+ const Expr *Ex = getCXXThisExpr()->IgnoreParenBaseCasts();
QualType T = Ex->getType();
if (T->isPointerType()) // Arrow or implicit-this syntax?
T = T->getPointeeType();
diff --git a/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp b/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
index 725ff1002e29..3d44d2cbc069 100644
--- a/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
+++ b/clang/lib/StaticAnalyzer/Core/CheckerContext.cpp
@@ -93,7 +93,7 @@ StringRef CheckerContext::getMacroNameOrSpelling(SourceLocation &Loc) {
if (Loc.isMacroID())
return Lexer::getImmediateMacroName(Loc, getSourceManager(),
getLangOpts());
- SmallVector<char, 16> buf;
+ SmallString<16> buf;
return Lexer::getSpelling(Loc, buf, getSourceManager(), getLangOpts());
}
diff --git a/clang/lib/StaticAnalyzer/Core/DynamicType.cpp b/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
index e9b64fd79614..9ed915aafcab 100644
--- a/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
+++ b/clang/lib/StaticAnalyzer/Core/DynamicType.cpp
@@ -65,6 +65,13 @@ const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State,
return State->get<DynamicTypeMap>(MR);
}
+static void unbox(QualType &Ty) {
+ // FIXME: Why are we being fed references to pointers in the first place?
+ while (Ty->isReferenceType() || Ty->isPointerType())
+ Ty = Ty->getPointeeType();
+ Ty = Ty.getCanonicalType().getUnqualifiedType();
+}
+
const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
const MemRegion *MR,
QualType CastFromTy,
@@ -73,6 +80,9 @@ const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
if (!Lookup)
return nullptr;
+ unbox(CastFromTy);
+ unbox(CastToTy);
+
for (const DynamicCastInfo &Cast : *Lookup)
if (Cast.equals(CastFromTy, CastToTy))
return &Cast;
@@ -112,6 +122,9 @@ ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
State = State->set<DynamicTypeMap>(MR, CastToTy);
}
+ unbox(CastFromTy);
+ unbox(CastToTy);
+
DynamicCastInfo::CastResult ResultKind =
CastSucceeds ? DynamicCastInfo::CastResult::Success
: DynamicCastInfo::CastResult::Failure;
diff --git a/clang/lib/StaticAnalyzer/Core/Environment.cpp b/clang/lib/StaticAnalyzer/Core/Environment.cpp
index 9e6d79bb7dcc..ee7474592528 100644
--- a/clang/lib/StaticAnalyzer/Core/Environment.cpp
+++ b/clang/lib/StaticAnalyzer/Core/Environment.cpp
@@ -15,6 +15,7 @@
#include "clang/AST/ExprCXX.h"
#include "clang/AST/PrettyPrinter.h"
#include "clang/AST/Stmt.h"
+#include "clang/AST/StmtObjC.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/LangOptions.h"
@@ -85,6 +86,12 @@ SVal Environment::lookupExpr(const EnvironmentEntry &E) const {
SVal Environment::getSVal(const EnvironmentEntry &Entry,
SValBuilder& svalBuilder) const {
const Stmt *S = Entry.getStmt();
+ assert(!isa<ObjCForCollectionStmt>(S) &&
+ "Use ExprEngine::hasMoreIteration()!");
+ assert((isa<Expr>(S) || isa<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!");
const LocationContext *LCtx = Entry.getLocationContext();
switch (S->getStmtClass()) {
@@ -109,6 +116,7 @@ SVal Environment::getSVal(const EnvironmentEntry &Entry,
case Stmt::StringLiteralClass:
case Stmt::TypeTraitExprClass:
case Stmt::SizeOfPackExprClass:
+ case Stmt::PredefinedExprClass:
// Known constants; defer to SValBuilder.
return svalBuilder.getConstantVal(cast<Expr>(S)).getValue();
@@ -183,18 +191,15 @@ EnvironmentManager::removeDeadBindings(Environment Env,
F.getTreeFactory());
// Iterate over the block-expr bindings.
- for (Environment::iterator I = Env.begin(), E = Env.end(); I != E; ++I) {
+ for (Environment::iterator I = Env.begin(), End = Env.end(); I != End; ++I) {
const EnvironmentEntry &BlkExpr = I.getKey();
const SVal &X = I.getData();
- const bool IsBlkExprLive =
- SymReaper.isLive(BlkExpr.getStmt(), BlkExpr.getLocationContext());
+ const Expr *E = dyn_cast<Expr>(BlkExpr.getStmt());
+ if (!E)
+ continue;
- assert((isa<Expr>(BlkExpr.getStmt()) || !IsBlkExprLive) &&
- "Only Exprs can be live, LivenessAnalysis argues about the liveness "
- "of *values*!");
-
- if (IsBlkExprLive) {
+ if (SymReaper.isLive(E, BlkExpr.getLocationContext())) {
// Copy the binding to the new map.
EBMapRef = EBMapRef.add(BlkExpr, X);
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
index 265dcd134213..f285b652c175 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngine.cpp
@@ -169,7 +169,7 @@ public:
if (S) {
S->printJson(Out, Helper, PP, /*AddQuotes=*/true);
} else {
- Out << '\"' << I->getAnyMember()->getNameAsString() << '\"';
+ Out << '\"' << I->getAnyMember()->getDeclName() << '\"';
}
}
@@ -2129,6 +2129,83 @@ static const Stmt *ResolveCondition(const Stmt *Condition,
llvm_unreachable("could not resolve condition");
}
+using ObjCForLctxPair =
+ std::pair<const ObjCForCollectionStmt *, const LocationContext *>;
+
+REGISTER_MAP_WITH_PROGRAMSTATE(ObjCForHasMoreIterations, ObjCForLctxPair, bool)
+
+ProgramStateRef ExprEngine::setWhetherHasMoreIteration(
+ ProgramStateRef State, const ObjCForCollectionStmt *O,
+ const LocationContext *LC, bool HasMoreIteraton) {
+ assert(!State->contains<ObjCForHasMoreIterations>({O, LC}));
+ return State->set<ObjCForHasMoreIterations>({O, LC}, HasMoreIteraton);
+}
+
+ProgramStateRef
+ExprEngine::removeIterationState(ProgramStateRef State,
+ const ObjCForCollectionStmt *O,
+ const LocationContext *LC) {
+ assert(State->contains<ObjCForHasMoreIterations>({O, LC}));
+ return State->remove<ObjCForHasMoreIterations>({O, LC});
+}
+
+bool ExprEngine::hasMoreIteration(ProgramStateRef State,
+ const ObjCForCollectionStmt *O,
+ const LocationContext *LC) {
+ assert(State->contains<ObjCForHasMoreIterations>({O, LC}));
+ return *State->get<ObjCForHasMoreIterations>({O, LC});
+}
+
+/// Split the state on whether there are any more iterations left for this loop.
+/// Returns a (HasMoreIteration, HasNoMoreIteration) pair, or None when the
+/// acquisition of the loop condition value failed.
+static Optional<std::pair<ProgramStateRef, ProgramStateRef>>
+assumeCondition(const Stmt *Condition, ExplodedNode *N) {
+ ProgramStateRef State = N->getState();
+ if (const auto *ObjCFor = dyn_cast<ObjCForCollectionStmt>(Condition)) {
+ bool HasMoreIteraton =
+ ExprEngine::hasMoreIteration(State, ObjCFor, N->getLocationContext());
+ // Checkers have already ran on branch conditions, so the current
+ // information as to whether the loop has more iteration becomes outdated
+ // after this point.
+ State = ExprEngine::removeIterationState(State, ObjCFor,
+ N->getLocationContext());
+ if (HasMoreIteraton)
+ return std::pair<ProgramStateRef, ProgramStateRef>{State, nullptr};
+ else
+ return std::pair<ProgramStateRef, ProgramStateRef>{nullptr, State};
+ }
+ SVal X = State->getSVal(Condition, N->getLocationContext());
+
+ if (X.isUnknownOrUndef()) {
+ // Give it a chance to recover from unknown.
+ if (const auto *Ex = dyn_cast<Expr>(Condition)) {
+ if (Ex->getType()->isIntegralOrEnumerationType()) {
+ // Try to recover some path-sensitivity. Right now casts of symbolic
+ // integers that promote their values are currently not tracked well.
+ // If 'Condition' is such an expression, try and recover the
+ // underlying value and use that instead.
+ SVal recovered =
+ RecoverCastedSymbol(State, Condition, N->getLocationContext(),
+ N->getState()->getStateManager().getContext());
+
+ if (!recovered.isUnknown()) {
+ X = recovered;
+ }
+ }
+ }
+ }
+
+ // If the condition is still unknown, give up.
+ if (X.isUnknownOrUndef())
+ return None;
+
+ DefinedSVal V = X.castAs<DefinedSVal>();
+
+ ProgramStateRef StTrue, StFalse;
+ return State->assume(V);
+}
+
void ExprEngine::processBranch(const Stmt *Condition,
NodeBuilderContext& BldCtx,
ExplodedNode *Pred,
@@ -2165,48 +2242,28 @@ void ExprEngine::processBranch(const Stmt *Condition,
return;
BranchNodeBuilder builder(CheckersOutSet, Dst, BldCtx, DstT, DstF);
- for (const auto PredI : CheckersOutSet) {
- if (PredI->isSink())
+ for (ExplodedNode *PredN : CheckersOutSet) {
+ if (PredN->isSink())
continue;
- ProgramStateRef PrevState = PredI->getState();
- SVal X = PrevState->getSVal(Condition, PredI->getLocationContext());
+ ProgramStateRef PrevState = PredN->getState();
- if (X.isUnknownOrUndef()) {
- // Give it a chance to recover from unknown.
- if (const auto *Ex = dyn_cast<Expr>(Condition)) {
- if (Ex->getType()->isIntegralOrEnumerationType()) {
- // Try to recover some path-sensitivity. Right now casts of symbolic
- // integers that promote their values are currently not tracked well.
- // If 'Condition' is such an expression, try and recover the
- // underlying value and use that instead.
- SVal recovered = RecoverCastedSymbol(PrevState, Condition,
- PredI->getLocationContext(),
- getContext());
-
- if (!recovered.isUnknown()) {
- X = recovered;
- }
- }
- }
- }
-
- // If the condition is still unknown, give up.
- if (X.isUnknownOrUndef()) {
- builder.generateNode(PrevState, true, PredI);
- builder.generateNode(PrevState, false, PredI);
+ ProgramStateRef StTrue, StFalse;
+ if (const auto KnownCondValueAssumption = assumeCondition(Condition, PredN))
+ std::tie(StTrue, StFalse) = *KnownCondValueAssumption;
+ else {
+ assert(!isa<ObjCForCollectionStmt>(Condition));
+ builder.generateNode(PrevState, true, PredN);
+ builder.generateNode(PrevState, false, PredN);
continue;
}
-
- DefinedSVal V = X.castAs<DefinedSVal>();
-
- ProgramStateRef StTrue, StFalse;
- std::tie(StTrue, StFalse) = PrevState->assume(V);
+ if (StTrue && StFalse)
+ assert(!isa<ObjCForCollectionStmt>(Condition));;
// Process the true branch.
if (builder.isFeasible(true)) {
if (StTrue)
- builder.generateNode(StTrue, true, PredI);
+ builder.generateNode(StTrue, true, PredN);
else
builder.markInfeasible(true);
}
@@ -2214,7 +2271,7 @@ void ExprEngine::processBranch(const Stmt *Condition,
// Process the false branch.
if (builder.isFeasible(false)) {
if (StFalse)
- builder.generateNode(StFalse, false, PredI);
+ builder.generateNode(StFalse, false, PredN);
else
builder.markInfeasible(false);
}
@@ -2530,16 +2587,8 @@ void ExprEngine::VisitCommonDeclRefExpr(const Expr *Ex, const NamedDecl *D,
return;
}
if (isa<FieldDecl>(D) || isa<IndirectFieldDecl>(D)) {
- // FIXME: Compute lvalue of field pointers-to-member.
- // Right now we just use a non-null void pointer, so that it gives proper
- // results in boolean contexts.
- // FIXME: Maybe delegate this to the surrounding operator&.
- // Note how this expression is lvalue, however pointer-to-member is NonLoc.
- SVal V = svalBuilder.conjureSymbolVal(Ex, LCtx, getContext().VoidPtrTy,
- currBldrCtx->blockCount());
- state = state->assume(V.castAs<DefinedOrUnknownSVal>(), true);
- Bldr.generateNode(Ex, Pred, state->BindExpr(Ex, LCtx, V), nullptr,
- ProgramPoint::PostLValueKind);
+ // Delegate all work related to pointer to members to the surrounding
+ // operator&.
return;
}
if (isa<BindingDecl>(D)) {
@@ -3100,7 +3149,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
if (Stop(N))
return true;
- if (N->succ_size() != 1 || !isNodeHidden(N->getFirstSucc()))
+ if (N->succ_size() != 1 || !isNodeHidden(N->getFirstSucc(), nullptr))
break;
PostCallback(N);
@@ -3109,7 +3158,7 @@ struct DOTGraphTraits<ExplodedGraph*> : public DefaultDOTGraphTraits {
return false;
}
- static bool isNodeHidden(const ExplodedNode *N) {
+ static bool isNodeHidden(const ExplodedNode *N, const ExplodedGraph *G) {
return N->isTrivial();
}
@@ -3162,8 +3211,9 @@ void ExprEngine::ViewGraph(bool trim) {
#ifndef NDEBUG
std::string Filename = DumpGraph(trim);
llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT);
-#endif
+#else
llvm::errs() << "Warning: viewing graph requires assertions" << "\n";
+#endif
}
@@ -3171,8 +3221,9 @@ void ExprEngine::ViewGraph(ArrayRef<const ExplodedNode*> Nodes) {
#ifndef NDEBUG
std::string Filename = DumpGraph(Nodes);
llvm::DisplayGraph(Filename, false, llvm::GraphProgram::DOT);
-#endif
+#else
llvm::errs() << "Warning: viewing graph requires assertions" << "\n";
+#endif
}
std::string ExprEngine::DumpGraph(bool trim, StringRef Filename) {
@@ -3209,15 +3260,17 @@ std::string ExprEngine::DumpGraph(ArrayRef<const ExplodedNode*> Nodes,
if (!TrimmedG.get()) {
llvm::errs() << "warning: Trimmed ExplodedGraph is empty.\n";
+ return "";
} else {
return llvm::WriteGraph(TrimmedG.get(), "TrimmedExprEngine",
/*ShortNames=*/false,
/*Title=*/"Trimmed Exploded Graph",
/*Filename=*/std::string(Filename));
}
-#endif
+#else
llvm::errs() << "Warning: dumping graph requires assertions" << "\n";
return "";
+#endif
}
void *ProgramStateTrait<ReplayWithoutInlining>::GDMIndex() {
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
index c5e38cc7423d..18d1b2169eed 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineC.cpp
@@ -418,6 +418,8 @@ void ExprEngine::VisitCast(const CastExpr *CastE, const Expr *Ex,
case CK_ZeroToOCLOpaqueType:
case CK_IntToOCLSampler:
case CK_LValueBitCast:
+ case CK_FloatingToFixedPoint:
+ case CK_FixedPointToFloating:
case CK_FixedPointCast:
case CK_FixedPointToBoolean:
case CK_FixedPointToIntegral:
@@ -991,10 +993,11 @@ 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)) {
+ if (isa<CXXMethodDecl>(VD) || isa<FieldDecl>(VD) ||
+ isa<IndirectFieldDecl>(VD)) {
ProgramStateRef State = (*I)->getState();
const LocationContext *LCtx = (*I)->getLocationContext();
- SVal SV = svalBuilder.getMemberPointer(cast<DeclaratorDecl>(VD));
+ SVal SV = svalBuilder.getMemberPointer(cast<NamedDecl>(VD));
Bldr.generateNode(U, *I, State->BindExpr(U, LCtx, SV));
break;
}
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
index 38a680eb04c0..cab65687444b 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCXX.cpp
@@ -132,10 +132,20 @@ SVal ExprEngine::computeObjectUnderConstruction(
case ConstructionContext::SimpleConstructorInitializerKind: {
const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
const auto *Init = ICC->getCXXCtorInitializer();
- assert(Init->isAnyMemberInitializer());
const CXXMethodDecl *CurCtor = cast<CXXMethodDecl>(LCtx->getDecl());
Loc ThisPtr = SVB.getCXXThis(CurCtor, LCtx->getStackFrame());
SVal ThisVal = State->getSVal(ThisPtr);
+ if (Init->isBaseInitializer()) {
+ const auto *ThisReg = cast<SubRegion>(ThisVal.getAsRegion());
+ const CXXRecordDecl *BaseClass =
+ Init->getBaseClass()->getAsCXXRecordDecl();
+ const auto *BaseReg =
+ MRMgr.getCXXBaseObjectRegion(BaseClass, ThisReg,
+ Init->isBaseVirtual());
+ return SVB.makeLoc(BaseReg);
+ }
+ if (Init->isDelegatingInitializer())
+ return ThisVal;
const ValueDecl *Field;
SVal FieldVal;
@@ -364,8 +374,12 @@ ProgramStateRef ExprEngine::updateObjectsUnderConstruction(
case ConstructionContext::CXX17ElidedCopyConstructorInitializerKind:
case ConstructionContext::SimpleConstructorInitializerKind: {
const auto *ICC = cast<ConstructorInitializerConstructionContext>(CC);
- return addObjectUnderConstruction(State, ICC->getCXXCtorInitializer(),
- LCtx, V);
+ const auto *Init = ICC->getCXXCtorInitializer();
+ // Base and delegating initializers handled above
+ assert(Init->isAnyMemberInitializer() &&
+ "Base and delegating initializers should have been handled by"
+ "computeObjectUnderConstruction()");
+ return addObjectUnderConstruction(State, Init, LCtx, V);
}
case ConstructionContext::NewAllocatedObjectKind: {
return State;
@@ -602,11 +616,11 @@ void ExprEngine::handleConstructor(const Expr *E,
*Call, *this);
ExplodedNodeSet DstEvaluated;
- StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);
if (CE && CE->getConstructor()->isTrivial() &&
CE->getConstructor()->isCopyOrMoveConstructor() &&
!CallOpts.IsArrayCtorOrDtor) {
+ StmtNodeBuilder Bldr(DstPreCall, DstEvaluated, *currBldrCtx);
// FIXME: Handle other kinds of trivial constructors as well.
for (ExplodedNodeSet::iterator I = DstPreCall.begin(), E = DstPreCall.end();
I != E; ++I)
@@ -626,6 +640,8 @@ void ExprEngine::handleConstructor(const Expr *E,
// in the CFG, would be called at the end of the full expression or
// later (for life-time extended temporaries) -- but avoids infeasible
// paths when no-return temporary destructors are used for assertions.
+ ExplodedNodeSet DstEvaluatedPostProcessed;
+ StmtNodeBuilder Bldr(DstEvaluated, DstEvaluatedPostProcessed, *currBldrCtx);
const AnalysisDeclContext *ADC = LCtx->getAnalysisDeclContext();
if (!ADC->getCFGBuildOptions().AddTemporaryDtors) {
if (llvm::isa_and_nonnull<CXXTempObjectRegion>(TargetRegion) &&
@@ -655,7 +671,7 @@ void ExprEngine::handleConstructor(const Expr *E,
}
ExplodedNodeSet DstPostArgumentCleanup;
- for (ExplodedNode *I : DstEvaluated)
+ for (ExplodedNode *I : DstEvaluatedPostProcessed)
finishArgumentConstruction(DstPostArgumentCleanup, I, *Call);
// If there were other constructors called for object-type arguments
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
index 52ba17d59ae0..996d3644e018 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineCallAndReturn.cpp
@@ -842,19 +842,7 @@ ExprEngine::mayInlineCallKind(const CallEvent &Call, const ExplodedNode *Pred,
static bool hasMember(const ASTContext &Ctx, const CXXRecordDecl *RD,
StringRef Name) {
const IdentifierInfo &II = Ctx.Idents.get(Name);
- DeclarationName DeclName = Ctx.DeclarationNames.getIdentifier(&II);
- if (!RD->lookup(DeclName).empty())
- return true;
-
- CXXBasePaths Paths(false, false, false);
- if (RD->lookupInBases(
- [DeclName](const CXXBaseSpecifier *Specifier, CXXBasePath &Path) {
- return CXXRecordDecl::FindOrdinaryMember(Specifier, Path, DeclName);
- },
- Paths))
- return true;
-
- return false;
+ return RD->hasMemberName(Ctx.DeclarationNames.getIdentifier(&II));
}
/// Returns true if the given C++ class is a container or iterator.
diff --git a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
index eb9a0be2e5d6..5a55e81497b0 100644
--- a/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ExprEngineObjC.cpp
@@ -53,10 +53,8 @@ static void populateObjCForDestinationSet(
ProgramStateRef state = Pred->getState();
const LocationContext *LCtx = Pred->getLocationContext();
- SVal hasElementsV = svalBuilder.makeTruthVal(hasElements);
-
- // FIXME: S is not an expression. We should not be binding values to it.
- ProgramStateRef nextState = state->BindExpr(S, LCtx, hasElementsV);
+ ProgramStateRef nextState =
+ ExprEngine::setWhetherHasMoreIteration(state, S, LCtx, hasElements);
if (auto MV = elementV.getAs<loc::MemRegionVal>())
if (const auto *R = dyn_cast<TypedValueRegion>(MV->getRegion())) {
@@ -93,10 +91,9 @@ void ExprEngine::VisitObjCForCollectionStmt(const ObjCForCollectionStmt *S,
// (1) binds the next container value to 'element'. This creates a new
// node in the ExplodedGraph.
//
- // (2) binds the value 0/1 to the ObjCForCollectionStmt* itself, indicating
- // whether or not the container has any more elements. This value
- // will be tested in ProcessBranch. We need to explicitly bind
- // this value because a container can contain nil elements.
+ // (2) note whether the collection has any more elements (or in other words,
+ // whether the loop has more iterations). This will be tested in
+ // processBranch.
//
// FIXME: Eventually this logic should actually do dispatches to
// 'countByEnumeratingWithState:objects:count:' (NSFastEnumeration).
diff --git a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
index bc7c41d039c4..149459cf986a 100644
--- a/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/HTMLDiagnostics.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Analysis/IssueHash.h"
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
@@ -23,8 +24,6 @@
#include "clang/Lex/Token.h"
#include "clang/Rewrite/Core/HTMLRewrite.h"
#include "clang/Rewrite/Core/Rewriter.h"
-#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
-#include "clang/StaticAnalyzer/Core/IssueHash.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/ArrayRef.h"
#include "llvm/ADT/SmallString.h"
@@ -58,17 +57,18 @@ using namespace ento;
namespace {
class HTMLDiagnostics : public PathDiagnosticConsumer {
+ PathDiagnosticConsumerOptions DiagOpts;
std::string Directory;
bool createdDir = false;
bool noDir = false;
const Preprocessor &PP;
- AnalyzerOptions &AnalyzerOpts;
const bool SupportsCrossFileDiagnostics;
public:
- HTMLDiagnostics(AnalyzerOptions &AnalyzerOpts, const std::string &OutputDir,
- const Preprocessor &pp, bool supportsMultipleFiles)
- : Directory(OutputDir), PP(pp), AnalyzerOpts(AnalyzerOpts),
+ HTMLDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
+ const std::string &OutputDir, const Preprocessor &pp,
+ bool supportsMultipleFiles)
+ : DiagOpts(std::move(DiagOpts)), Directory(OutputDir), PP(pp),
SupportsCrossFileDiagnostics(supportsMultipleFiles) {}
~HTMLDiagnostics() override { FlushDiagnostics(nullptr); }
@@ -133,7 +133,7 @@ private:
} // namespace
void ento::createHTMLDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &OutputDir, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
@@ -142,37 +142,38 @@ void ento::createHTMLDiagnosticConsumer(
// output mode. This doesn't make much sense, we should have the minimal text
// as our default. In the case of backward compatibility concerns, this could
// be preserved with -analyzer-config-compatibility-mode=true.
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputDir, PP, CTU);
+ createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU);
// TODO: Emit an error here.
if (OutputDir.empty())
return;
- C.push_back(new HTMLDiagnostics(AnalyzerOpts, OutputDir, PP, true));
+ C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, true));
}
void ento::createHTMLSingleFileDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &OutputDir, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
+ createTextMinimalPathDiagnosticConsumer(DiagOpts, C, OutputDir, PP, CTU);
// TODO: Emit an error here.
if (OutputDir.empty())
return;
- C.push_back(new HTMLDiagnostics(AnalyzerOpts, OutputDir, PP, false));
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputDir, PP, CTU);
+ C.push_back(new HTMLDiagnostics(std::move(DiagOpts), OutputDir, PP, false));
}
void ento::createPlistHTMLDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &prefix, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
createHTMLDiagnosticConsumer(
- AnalyzerOpts, C, std::string(llvm::sys::path::parent_path(prefix)), PP,
+ DiagOpts, C, std::string(llvm::sys::path::parent_path(prefix)), PP,
CTU);
- createPlistMultiFileDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU);
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, prefix, PP, CTU);
+ createPlistMultiFileDiagnosticConsumer(DiagOpts, C, prefix, PP, CTU);
+ createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, prefix, PP,
+ CTU);
}
//===----------------------------------------------------------------------===//
@@ -245,7 +246,7 @@ void HTMLDiagnostics::ReportDiag(const PathDiagnostic& D,
int FD;
SmallString<128> Model, ResultPath;
- if (!AnalyzerOpts.ShouldWriteStableReportFilename) {
+ if (!DiagOpts.ShouldWriteStableReportFilename) {
llvm::sys::path::append(Model, Directory, "report-%%%%%%.html");
if (std::error_code EC =
llvm::sys::fs::make_absolute(Model)) {
@@ -535,7 +536,7 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
<input type="checkbox" class="spoilerhider" id="showinvocation" />
<label for="showinvocation" >Show analyzer invocation</label>
<div class="spoiler">clang -cc1 )<<<";
- os << html::EscapeText(AnalyzerOpts.FullCompilerInvocation);
+ os << html::EscapeText(DiagOpts.ToolInvocation);
os << R"<<<(
</div>
<div id='tooltiphint' hidden="true">
@@ -582,8 +583,8 @@ void HTMLDiagnostics::FinalizeHTML(const PathDiagnostic& D, Rewriter &R,
os << "\n<!-- FUNCTIONNAME " << declName << " -->\n";
os << "\n<!-- ISSUEHASHCONTENTOFLINEINCONTEXT "
- << GetIssueHash(SMgr, L, D.getCheckerName(), D.getBugType(),
- DeclWithIssue, PP.getLangOpts())
+ << getIssueHash(L, D.getCheckerName(), D.getBugType(), DeclWithIssue,
+ PP.getLangOpts())
<< " -->\n";
os << "\n<!-- BUGLINE "
@@ -786,8 +787,8 @@ void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
if (LPosInfo.first != BugFileID)
return;
- const llvm::MemoryBuffer *Buf = SM.getBuffer(LPosInfo.first);
- const char* FileStart = Buf->getBufferStart();
+ llvm::MemoryBufferRef Buf = SM.getBufferOrFake(LPosInfo.first);
+ const char *FileStart = Buf.getBufferStart();
// Compute the column number. Rewind from the current position to the start
// of the line.
@@ -797,7 +798,7 @@ void HTMLDiagnostics::HandlePiece(Rewriter &R, FileID BugFileID,
// Compute LineEnd.
const char *LineEnd = TokInstantiationPtr;
- const char* FileEnd = Buf->getBufferEnd();
+ const char *FileEnd = Buf.getBufferEnd();
while (*LineEnd != '\n' && LineEnd != FileEnd)
++LineEnd;
diff --git a/clang/lib/StaticAnalyzer/Core/IssueHash.cpp b/clang/lib/StaticAnalyzer/Core/IssueHash.cpp
deleted file mode 100644
index e7497f3fbdaa..000000000000
--- a/clang/lib/StaticAnalyzer/Core/IssueHash.cpp
+++ /dev/null
@@ -1,204 +0,0 @@
-//===---------- IssueHash.cpp - Generate identification hashes --*- 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
-//
-//===----------------------------------------------------------------------===//
-#include "clang/StaticAnalyzer/Core/IssueHash.h"
-#include "clang/AST/ASTContext.h"
-#include "clang/AST/Decl.h"
-#include "clang/AST/DeclCXX.h"
-#include "clang/Basic/SourceManager.h"
-#include "clang/Basic/Specifiers.h"
-#include "clang/Lex/Lexer.h"
-#include "llvm/ADT/StringExtras.h"
-#include "llvm/ADT/StringRef.h"
-#include "llvm/ADT/Twine.h"
-#include "llvm/Support/LineIterator.h"
-#include "llvm/Support/MD5.h"
-#include "llvm/Support/Path.h"
-
-#include <functional>
-#include <sstream>
-#include <string>
-
-using namespace clang;
-
-// Get a string representation of the parts of the signature that can be
-// overloaded on.
-static std::string GetSignature(const FunctionDecl *Target) {
- if (!Target)
- return "";
- std::string Signature;
-
- // When a flow sensitive bug happens in templated code we should not generate
- // distinct hash value for every instantiation. Use the signature from the
- // primary template.
- if (const FunctionDecl *InstantiatedFrom =
- Target->getTemplateInstantiationPattern())
- Target = InstantiatedFrom;
-
- if (!isa<CXXConstructorDecl>(Target) && !isa<CXXDestructorDecl>(Target) &&
- !isa<CXXConversionDecl>(Target))
- Signature.append(Target->getReturnType().getAsString()).append(" ");
- Signature.append(Target->getQualifiedNameAsString()).append("(");
-
- for (int i = 0, paramsCount = Target->getNumParams(); i < paramsCount; ++i) {
- if (i)
- Signature.append(", ");
- Signature.append(Target->getParamDecl(i)->getType().getAsString());
- }
-
- if (Target->isVariadic())
- Signature.append(", ...");
- Signature.append(")");
-
- const auto *TargetT =
- llvm::dyn_cast_or_null<FunctionType>(Target->getType().getTypePtr());
-
- if (!TargetT || !isa<CXXMethodDecl>(Target))
- return Signature;
-
- if (TargetT->isConst())
- Signature.append(" const");
- if (TargetT->isVolatile())
- Signature.append(" volatile");
- if (TargetT->isRestrict())
- Signature.append(" restrict");
-
- if (const auto *TargetPT =
- dyn_cast_or_null<FunctionProtoType>(Target->getType().getTypePtr())) {
- switch (TargetPT->getRefQualifier()) {
- case RQ_LValue:
- Signature.append(" &");
- break;
- case RQ_RValue:
- Signature.append(" &&");
- break;
- default:
- break;
- }
- }
-
- return Signature;
-}
-
-static std::string GetEnclosingDeclContextSignature(const Decl *D) {
- if (!D)
- return "";
-
- if (const auto *ND = dyn_cast<NamedDecl>(D)) {
- std::string DeclName;
-
- switch (ND->getKind()) {
- case Decl::Namespace:
- case Decl::Record:
- case Decl::CXXRecord:
- case Decl::Enum:
- DeclName = ND->getQualifiedNameAsString();
- break;
- case Decl::CXXConstructor:
- case Decl::CXXDestructor:
- case Decl::CXXConversion:
- case Decl::CXXMethod:
- case Decl::Function:
- DeclName = GetSignature(dyn_cast_or_null<FunctionDecl>(ND));
- break;
- case Decl::ObjCMethod:
- // ObjC Methods can not be overloaded, qualified name uniquely identifies
- // the method.
- DeclName = ND->getQualifiedNameAsString();
- break;
- default:
- break;
- }
-
- return DeclName;
- }
-
- return "";
-}
-
-static StringRef GetNthLineOfFile(const llvm::MemoryBuffer *Buffer, int Line) {
- if (!Buffer)
- return "";
-
- llvm::line_iterator LI(*Buffer, false);
- for (; !LI.is_at_eof() && LI.line_number() != Line; ++LI)
- ;
-
- return *LI;
-}
-
-static std::string NormalizeLine(const SourceManager &SM, FullSourceLoc &L,
- const LangOptions &LangOpts) {
- static StringRef Whitespaces = " \t\n";
-
- StringRef Str = GetNthLineOfFile(SM.getBuffer(L.getFileID(), L),
- L.getExpansionLineNumber());
- StringRef::size_type col = Str.find_first_not_of(Whitespaces);
- if (col == StringRef::npos)
- col = 1; // The line only contains whitespace.
- else
- col++;
- SourceLocation StartOfLine =
- SM.translateLineCol(SM.getFileID(L), L.getExpansionLineNumber(), col);
- const llvm::MemoryBuffer *Buffer =
- SM.getBuffer(SM.getFileID(StartOfLine), StartOfLine);
- if (!Buffer)
- return {};
-
- const char *BufferPos = SM.getCharacterData(StartOfLine);
-
- Token Token;
- Lexer Lexer(SM.getLocForStartOfFile(SM.getFileID(StartOfLine)), LangOpts,
- Buffer->getBufferStart(), BufferPos, Buffer->getBufferEnd());
-
- size_t NextStart = 0;
- std::ostringstream LineBuff;
- while (!Lexer.LexFromRawLexer(Token) && NextStart < 2) {
- if (Token.isAtStartOfLine() && NextStart++ > 0)
- continue;
- LineBuff << std::string(SM.getCharacterData(Token.getLocation()),
- Token.getLength());
- }
-
- return LineBuff.str();
-}
-
-static llvm::SmallString<32> GetHashOfContent(StringRef Content) {
- llvm::MD5 Hash;
- llvm::MD5::MD5Result MD5Res;
- SmallString<32> Res;
-
- Hash.update(Content);
- Hash.final(MD5Res);
- llvm::MD5::stringifyResult(MD5Res, Res);
-
- return Res;
-}
-
-std::string clang::GetIssueString(const SourceManager &SM,
- FullSourceLoc &IssueLoc,
- StringRef CheckerName, StringRef BugType,
- const Decl *D,
- const LangOptions &LangOpts) {
- static StringRef Delimiter = "$";
-
- return (llvm::Twine(CheckerName) + Delimiter +
- GetEnclosingDeclContextSignature(D) + Delimiter +
- Twine(IssueLoc.getExpansionColumnNumber()) + Delimiter +
- NormalizeLine(SM, IssueLoc, LangOpts) + Delimiter + BugType)
- .str();
-}
-
-SmallString<32> clang::GetIssueHash(const SourceManager &SM,
- FullSourceLoc &IssueLoc,
- StringRef CheckerName, StringRef BugType,
- const Decl *D,
- const LangOptions &LangOpts) {
-
- return GetHashOfContent(
- GetIssueString(SM, IssueLoc, CheckerName, BugType, D, LangOpts));
-}
diff --git a/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
index ed62778623a8..35e320c7755f 100644
--- a/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/PlistDiagnostics.cpp
@@ -10,6 +10,7 @@
//
//===----------------------------------------------------------------------===//
+#include "clang/Analysis/IssueHash.h"
#include "clang/Analysis/PathDiagnostic.h"
#include "clang/Basic/FileManager.h"
#include "clang/Basic/PlistSupport.h"
@@ -20,13 +21,12 @@
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/TokenConcatenation.h"
#include "clang/Rewrite/Core/HTMLRewrite.h"
-#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
-#include "clang/StaticAnalyzer/Core/IssueHash.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/SmallPtrSet.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/ADT/Statistic.h"
#include "llvm/Support/Casting.h"
+#include <memory>
using namespace clang;
using namespace ento;
@@ -39,13 +39,17 @@ using namespace markup;
namespace {
class PlistDiagnostics : public PathDiagnosticConsumer {
+ PathDiagnosticConsumerOptions DiagOpts;
const std::string OutputFile;
const Preprocessor &PP;
const cross_tu::CrossTranslationUnitContext &CTU;
- AnalyzerOptions &AnOpts;
const bool SupportsCrossFileDiagnostics;
+
+ void printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
+ const PathPieces &Path);
+
public:
- PlistDiagnostics(AnalyzerOptions &AnalyzerOpts,
+ PlistDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
const std::string &OutputFile, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU,
bool supportsMultipleFiles);
@@ -74,23 +78,19 @@ namespace {
/// A helper class for emitting a single report.
class PlistPrinter {
const FIDMap& FM;
- AnalyzerOptions &AnOpts;
const Preprocessor &PP;
const cross_tu::CrossTranslationUnitContext &CTU;
llvm::SmallVector<const PathDiagnosticMacroPiece *, 0> MacroPieces;
public:
- PlistPrinter(const FIDMap& FM, AnalyzerOptions &AnOpts,
+ PlistPrinter(const FIDMap& FM,
const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU)
- : FM(FM), AnOpts(AnOpts), PP(PP), CTU(CTU) {
+ : FM(FM), PP(PP), CTU(CTU) {
}
void ReportDiag(raw_ostream &o, const PathDiagnosticPiece& P) {
ReportPiece(o, P, /*indent*/ 4, /*depth*/ 0, /*includeControlFlow*/ true);
-
- // Don't emit a warning about an unused private field.
- (void)AnOpts;
}
/// Print the expansions of the collected macro pieces.
@@ -165,11 +165,6 @@ struct ExpansionInfo {
} // end of anonymous namespace
-static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
- AnalyzerOptions &AnOpts, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &CTU,
- const PathPieces &Path);
-
/// Print coverage information to output stream {@code o}.
/// May modify the used list of files {@code Fids} by inserting new ones.
static void printCoverage(const PathDiagnostic *D,
@@ -520,11 +515,53 @@ static void printCoverage(const PathDiagnostic *D,
assert(IndentLevel == InputIndentLevel);
}
-static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
- AnalyzerOptions &AnOpts, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &CTU,
- const PathPieces &Path) {
- PlistPrinter Printer(FM, AnOpts, PP, CTU);
+//===----------------------------------------------------------------------===//
+// Methods of PlistDiagnostics.
+//===----------------------------------------------------------------------===//
+
+PlistDiagnostics::PlistDiagnostics(
+ PathDiagnosticConsumerOptions DiagOpts, const std::string &output,
+ const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU,
+ bool supportsMultipleFiles)
+ : DiagOpts(std::move(DiagOpts)), OutputFile(output), PP(PP), CTU(CTU),
+ SupportsCrossFileDiagnostics(supportsMultipleFiles) {
+ // FIXME: Will be used by a later planned change.
+ (void)this->CTU;
+}
+
+void ento::createPlistDiagnosticConsumer(
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
+ const std::string &OutputFile, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+
+ // TODO: Emit an error here.
+ if (OutputFile.empty())
+ return;
+
+ C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
+ /*supportsMultipleFiles=*/false));
+ createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
+ PP, CTU);
+}
+
+void ento::createPlistMultiFileDiagnosticConsumer(
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
+ const std::string &OutputFile, const Preprocessor &PP,
+ const cross_tu::CrossTranslationUnitContext &CTU) {
+
+ // TODO: Emit an error here.
+ if (OutputFile.empty())
+ return;
+
+ C.push_back(new PlistDiagnostics(DiagOpts, OutputFile, PP, CTU,
+ /*supportsMultipleFiles=*/true));
+ createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, OutputFile,
+ PP, CTU);
+}
+
+void PlistDiagnostics::printBugPath(llvm::raw_ostream &o, const FIDMap &FM,
+ const PathPieces &Path) {
+ PlistPrinter Printer(FM, PP, CTU);
assert(std::is_partitioned(Path.begin(), Path.end(),
[](const PathDiagnosticPieceRef &E) {
return E->getKind() == PathDiagnosticPiece::Note;
@@ -557,7 +594,7 @@ static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
o << " </array>\n";
- if (!AnOpts.ShouldDisplayMacroExpansions)
+ if (!DiagOpts.ShouldDisplayMacroExpansions)
return;
o << " <key>macro_expansions</key>\n"
@@ -566,48 +603,6 @@ static void printBugPath(llvm::raw_ostream &o, const FIDMap& FM,
o << " </array>\n";
}
-//===----------------------------------------------------------------------===//
-// Methods of PlistDiagnostics.
-//===----------------------------------------------------------------------===//
-
-PlistDiagnostics::PlistDiagnostics(
- AnalyzerOptions &AnalyzerOpts, const std::string &output,
- const Preprocessor &PP, const cross_tu::CrossTranslationUnitContext &CTU,
- bool supportsMultipleFiles)
- : OutputFile(output), PP(PP), CTU(CTU), AnOpts(AnalyzerOpts),
- SupportsCrossFileDiagnostics(supportsMultipleFiles) {
- // FIXME: Will be used by a later planned change.
- (void)this->CTU;
-}
-
-void ento::createPlistDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &OutputFile, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &CTU) {
-
- // TODO: Emit an error here.
- if (OutputFile.empty())
- return;
-
- C.push_back(new PlistDiagnostics(AnalyzerOpts, OutputFile, PP, CTU,
- /*supportsMultipleFiles*/ false));
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputFile, PP, CTU);
-}
-
-void ento::createPlistMultiFileDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
- const std::string &OutputFile, const Preprocessor &PP,
- const cross_tu::CrossTranslationUnitContext &CTU) {
-
- // TODO: Emit an error here.
- if (OutputFile.empty())
- return;
-
- C.push_back(new PlistDiagnostics(AnalyzerOpts, OutputFile, PP, CTU,
- /*supportsMultipleFiles*/ true));
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, OutputFile, PP, CTU);
-}
-
void PlistDiagnostics::FlushDiagnosticsImpl(
std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) {
@@ -682,7 +677,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
o << " <dict>\n";
const PathDiagnostic *D = *DI;
- printBugPath(o, FM, AnOpts, PP, CTU, D->path);
+ printBugPath(o, FM, D->path);
// Output the bug type and bug category.
o << " <key>description</key>";
@@ -702,7 +697,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
: D->getLocation().asLocation()),
SM);
const Decl *DeclWithIssue = D->getDeclWithIssue();
- EmitString(o, GetIssueHash(SM, L, D->getCheckerName(), D->getBugType(),
+ EmitString(o, getIssueHash(L, D->getCheckerName(), D->getBugType(),
DeclWithIssue, LangOpts))
<< '\n';
@@ -806,7 +801,7 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
EmitString(o << " ", SM.getFileEntryForID(FID)->getName()) << '\n';
o << " </array>\n";
- if (llvm::AreStatisticsEnabled() && AnOpts.ShouldSerializeStats) {
+ if (llvm::AreStatisticsEnabled() && DiagOpts.ShouldSerializeStats) {
o << " <key>statistics</key>\n";
std::string stats;
llvm::raw_string_ostream os(stats);
@@ -825,22 +820,36 @@ void PlistDiagnostics::FlushDiagnosticsImpl(
namespace {
-using ExpArgTokens = llvm::SmallVector<Token, 2>;
+using ArgTokensTy = llvm::SmallVector<Token, 2>;
+
+} // end of anonymous namespace
+
+LLVM_DUMP_METHOD static void dumpArgTokensToStream(llvm::raw_ostream &Out,
+ const Preprocessor &PP,
+ const ArgTokensTy &Toks);
-/// Maps unexpanded macro arguments to expanded arguments. A macro argument may
+namespace {
+/// Maps unexpanded macro parameters to expanded arguments. A macro argument may
/// need to expanded further when it is nested inside another macro.
-class MacroArgMap : public std::map<const IdentifierInfo *, ExpArgTokens> {
+class MacroParamMap : public std::map<const IdentifierInfo *, ArgTokensTy> {
public:
- void expandFromPrevMacro(const MacroArgMap &Super);
+ void expandFromPrevMacro(const MacroParamMap &Super);
+
+ LLVM_DUMP_METHOD void dump(const Preprocessor &PP) const {
+ dumpToStream(llvm::errs(), PP);
+ }
+
+ LLVM_DUMP_METHOD void dumpToStream(llvm::raw_ostream &Out,
+ const Preprocessor &PP) const;
};
-struct MacroNameAndArgs {
+struct MacroExpansionInfo {
std::string Name;
const MacroInfo *MI = nullptr;
- MacroArgMap Args;
+ MacroParamMap ParamMap;
- MacroNameAndArgs(std::string N, const MacroInfo *MI, MacroArgMap M)
- : Name(std::move(N)), MI(MI), Args(std::move(M)) {}
+ MacroExpansionInfo(std::string N, const MacroInfo *MI, MacroParamMap M)
+ : Name(std::move(N)), MI(MI), ParamMap(std::move(M)) {}
};
class TokenPrinter {
@@ -860,6 +869,46 @@ public:
void printToken(const Token &Tok);
};
+/// Wrapper around a Lexer object that can lex tokens one-by-one. Its possible
+/// to "inject" a range of tokens into the stream, in which case the next token
+/// is retrieved from the next element of the range, until the end of the range
+/// is reached.
+class TokenStream {
+public:
+ TokenStream(SourceLocation ExpanLoc, const SourceManager &SM,
+ const LangOptions &LangOpts)
+ : ExpanLoc(ExpanLoc) {
+ FileID File;
+ unsigned Offset;
+ std::tie(File, Offset) = SM.getDecomposedLoc(ExpanLoc);
+ llvm::MemoryBufferRef MB = SM.getBufferOrFake(File);
+ const char *MacroNameTokenPos = MB.getBufferStart() + Offset;
+
+ RawLexer = std::make_unique<Lexer>(SM.getLocForStartOfFile(File), LangOpts,
+ MB.getBufferStart(), MacroNameTokenPos,
+ MB.getBufferEnd());
+ }
+
+ void next(Token &Result) {
+ if (CurrTokenIt == TokenRange.end()) {
+ RawLexer->LexFromRawLexer(Result);
+ return;
+ }
+ Result = *CurrTokenIt;
+ CurrTokenIt++;
+ }
+
+ void injectRange(const ArgTokensTy &Range) {
+ TokenRange = Range;
+ CurrTokenIt = TokenRange.begin();
+ }
+
+ std::unique_ptr<Lexer> RawLexer;
+ ArgTokensTy TokenRange;
+ ArgTokensTy::iterator CurrTokenIt = TokenRange.begin();
+ SourceLocation ExpanLoc;
+};
+
} // end of anonymous namespace
/// The implementation method of getMacroExpansion: It prints the expansion of
@@ -878,7 +927,7 @@ public:
///
/// As we expand the last line, we'll immediately replace PRINT(str) with
/// print(x). The information that both 'str' and 'x' refers to the same string
-/// is an information we have to forward, hence the argument \p PrevArgs.
+/// is an information we have to forward, hence the argument \p PrevParamMap.
///
/// To avoid infinite recursion we maintain the already processed tokens in
/// a set. This is carried as a parameter through the recursive calls. The set
@@ -888,13 +937,11 @@ public:
/// #define f(y) x
/// #define x f(x)
static std::string getMacroNameAndPrintExpansion(
- TokenPrinter &Printer,
- SourceLocation MacroLoc,
- const Preprocessor &PP,
- const MacroArgMap &PrevArgs,
+ TokenPrinter &Printer, SourceLocation MacroLoc, const Preprocessor &PP,
+ const MacroParamMap &PrevParamMap,
llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens);
-/// Retrieves the name of the macro and what it's arguments expand into
+/// Retrieves the name of the macro and what it's parameters expand into
/// at \p ExpanLoc.
///
/// For example, for the following macro expansion:
@@ -916,8 +963,9 @@ static std::string getMacroNameAndPrintExpansion(
/// When \p ExpanLoc references "SET_TO_NULL(a)" within the definition of
/// "NOT_SUSPICOUS", the macro name "SET_TO_NULL" and the MacroArgMap map
/// { (x, a) } will be returned.
-static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
- const Preprocessor &PP);
+static MacroExpansionInfo
+getMacroExpansionInfo(const MacroParamMap &PrevParamMap,
+ SourceLocation ExpanLoc, const Preprocessor &PP);
/// Retrieves the ')' token that matches '(' \p It points to.
static MacroInfo::tokens_iterator getMatchingRParen(
@@ -951,21 +999,20 @@ getExpandedMacro(SourceLocation MacroLoc, const Preprocessor &PP,
llvm::SmallPtrSet<IdentifierInfo*, 8> AlreadyProcessedTokens;
std::string MacroName = getMacroNameAndPrintExpansion(
- Printer, MacroLoc, *PPToUse, MacroArgMap{}, AlreadyProcessedTokens);
+ Printer, MacroLoc, *PPToUse, MacroParamMap{}, AlreadyProcessedTokens);
return {MacroName, std::string(OS.str())};
}
static std::string getMacroNameAndPrintExpansion(
- TokenPrinter &Printer,
- SourceLocation MacroLoc,
- const Preprocessor &PP,
- const MacroArgMap &PrevArgs,
+ TokenPrinter &Printer, SourceLocation MacroLoc, const Preprocessor &PP,
+ const MacroParamMap &PrevParamMap,
llvm::SmallPtrSet<IdentifierInfo *, 8> &AlreadyProcessedTokens) {
const SourceManager &SM = PP.getSourceManager();
- MacroNameAndArgs Info = getMacroNameAndArgs(SM.getExpansionLoc(MacroLoc), PP);
- IdentifierInfo* IDInfo = PP.getIdentifierInfo(Info.Name);
+ MacroExpansionInfo MExpInfo =
+ getMacroExpansionInfo(PrevParamMap, SM.getExpansionLoc(MacroLoc), PP);
+ IdentifierInfo *MacroNameII = PP.getIdentifierInfo(MExpInfo.Name);
// TODO: If the macro definition contains another symbol then this function is
// called recursively. In case this symbol is the one being defined, it will
@@ -973,18 +1020,18 @@ static std::string getMacroNameAndPrintExpansion(
// in this case we don't get the full expansion text in the Plist file. See
// the test file where "value" is expanded to "garbage_" instead of
// "garbage_value".
- if (!AlreadyProcessedTokens.insert(IDInfo).second)
- return Info.Name;
+ if (!AlreadyProcessedTokens.insert(MacroNameII).second)
+ return MExpInfo.Name;
- if (!Info.MI)
- return Info.Name;
+ if (!MExpInfo.MI)
+ return MExpInfo.Name;
// Manually expand its arguments from the previous macro.
- Info.Args.expandFromPrevMacro(PrevArgs);
+ MExpInfo.ParamMap.expandFromPrevMacro(PrevParamMap);
// Iterate over the macro's tokens and stringify them.
- for (auto It = Info.MI->tokens_begin(), E = Info.MI->tokens_end(); It != E;
- ++It) {
+ for (auto It = MExpInfo.MI->tokens_begin(), E = MExpInfo.MI->tokens_end();
+ It != E; ++It) {
Token T = *It;
// If this token is not an identifier, we only need to print it.
@@ -1000,8 +1047,8 @@ static std::string getMacroNameAndPrintExpansion(
// If this token is a macro that should be expanded inside the current
// macro.
if (getMacroInfoForLocation(PP, SM, II, T.getLocation())) {
- getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP, Info.Args,
- AlreadyProcessedTokens);
+ getMacroNameAndPrintExpansion(Printer, T.getLocation(), PP,
+ MExpInfo.ParamMap, AlreadyProcessedTokens);
// If this is a function-like macro, skip its arguments, as
// getExpandedMacro() already printed them. If this is the case, let's
@@ -1013,10 +1060,10 @@ static std::string getMacroNameAndPrintExpansion(
}
// If this token is the current macro's argument, we should expand it.
- auto ArgMapIt = Info.Args.find(II);
- if (ArgMapIt != Info.Args.end()) {
- for (MacroInfo::tokens_iterator ArgIt = ArgMapIt->second.begin(),
- ArgEnd = ArgMapIt->second.end();
+ auto ParamToArgIt = MExpInfo.ParamMap.find(II);
+ if (ParamToArgIt != MExpInfo.ParamMap.end()) {
+ for (MacroInfo::tokens_iterator ArgIt = ParamToArgIt->second.begin(),
+ ArgEnd = ParamToArgIt->second.end();
ArgIt != ArgEnd; ++ArgIt) {
// These tokens may still be macros, if that is the case, handle it the
@@ -1034,7 +1081,8 @@ static std::string getMacroNameAndPrintExpansion(
}
getMacroNameAndPrintExpansion(Printer, ArgIt->getLocation(), PP,
- Info.Args, AlreadyProcessedTokens);
+ MExpInfo.ParamMap,
+ AlreadyProcessedTokens);
// Peek the next token if it is a tok::l_paren. This way we can decide
// if this is the application or just a reference to a function maxro
// symbol:
@@ -1055,34 +1103,30 @@ static std::string getMacroNameAndPrintExpansion(
Printer.printToken(T);
}
- AlreadyProcessedTokens.erase(IDInfo);
+ AlreadyProcessedTokens.erase(MacroNameII);
- return Info.Name;
+ return MExpInfo.Name;
}
-static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
- const Preprocessor &PP) {
+static MacroExpansionInfo
+getMacroExpansionInfo(const MacroParamMap &PrevParamMap,
+ SourceLocation ExpanLoc, const Preprocessor &PP) {
const SourceManager &SM = PP.getSourceManager();
const LangOptions &LangOpts = PP.getLangOpts();
// First, we create a Lexer to lex *at the expansion location* the tokens
// referring to the macro's name and its arguments.
- std::pair<FileID, unsigned> LocInfo = SM.getDecomposedLoc(ExpanLoc);
- const llvm::MemoryBuffer *MB = SM.getBuffer(LocInfo.first);
- const char *MacroNameTokenPos = MB->getBufferStart() + LocInfo.second;
-
- Lexer RawLexer(SM.getLocForStartOfFile(LocInfo.first), LangOpts,
- MB->getBufferStart(), MacroNameTokenPos, MB->getBufferEnd());
+ TokenStream TStream(ExpanLoc, SM, LangOpts);
// Acquire the macro's name.
Token TheTok;
- RawLexer.LexFromRawLexer(TheTok);
+ TStream.next(TheTok);
std::string MacroName = PP.getSpelling(TheTok);
const auto *II = PP.getIdentifierInfo(MacroName);
- assert(II && "Failed to acquire the IndetifierInfo for the macro!");
+ assert(II && "Failed to acquire the IdentifierInfo for the macro!");
const MacroInfo *MI = getMacroInfoForLocation(PP, SM, II, ExpanLoc);
// assert(MI && "The macro must've been defined at it's expansion location!");
@@ -1094,18 +1138,18 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
if (!MI)
return { MacroName, MI, {} };
- // Acquire the macro's arguments.
+ // Acquire the macro's arguments at the expansion point.
//
// The rough idea here is to lex from the first left parentheses to the last
- // right parentheses, and map the macro's unexpanded arguments to what they
- // will be expanded to. An expanded macro argument may contain several tokens
- // (like '3 + 4'), so we'll lex until we find a tok::comma or tok::r_paren, at
- // which point we start lexing the next argument or finish.
- ArrayRef<const IdentifierInfo *> MacroArgs = MI->params();
- if (MacroArgs.empty())
+ // right parentheses, and map the macro's parameter to what they will be
+ // expanded to. A macro argument may contain several token (like '3 + 4'), so
+ // we'll lex until we find a tok::comma or tok::r_paren, at which point we
+ // start lexing the next argument or finish.
+ ArrayRef<const IdentifierInfo *> MacroParams = MI->params();
+ if (MacroParams.empty())
return { MacroName, MI, {} };
- RawLexer.LexFromRawLexer(TheTok);
+ TStream.next(TheTok);
// When this is a token which expands to another macro function then its
// parentheses are not at its expansion locaiton. For example:
//
@@ -1117,9 +1161,9 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
if (TheTok.isNot(tok::l_paren))
return { MacroName, MI, {} };
- MacroArgMap Args;
+ MacroParamMap ParamMap;
- // When the macro's argument is a function call, like
+ // When the argument is a function call, like
// CALL_FN(someFunctionName(param1, param2))
// we will find tok::l_paren, tok::r_paren, and tok::comma that do not divide
// actual macro arguments, or do not represent the macro argument's closing
@@ -1130,12 +1174,19 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
// * > 1, then tok::comma is a part of the current arg.
int ParenthesesDepth = 1;
- // If we encounter __VA_ARGS__, we will lex until the closing tok::r_paren,
- // even if we lex a tok::comma and ParanthesesDepth == 1.
- const IdentifierInfo *__VA_ARGS__II = PP.getIdentifierInfo("__VA_ARGS__");
+ // If we encounter the variadic arg, we will lex until the closing
+ // tok::r_paren, even if we lex a tok::comma and ParanthesesDepth == 1.
+ const IdentifierInfo *VariadicParamII = PP.getIdentifierInfo("__VA_ARGS__");
+ if (MI->isGNUVarargs()) {
+ // If macro uses GNU-style variadic args, the param name is user-supplied,
+ // an not "__VA_ARGS__". E.g.:
+ // #define FOO(a, b, myvargs...)
+ // In this case, just use the last parameter:
+ VariadicParamII = *(MacroParams.rbegin());
+ }
- for (const IdentifierInfo *UnexpArgII : MacroArgs) {
- MacroArgMap::mapped_type ExpandedArgTokens;
+ for (const IdentifierInfo *CurrParamII : MacroParams) {
+ MacroParamMap::mapped_type ArgTokens;
// One could also simply not supply a single argument to __VA_ARGS__ -- this
// results in a preprocessor warning, but is not an error:
@@ -1149,10 +1200,10 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
if (ParenthesesDepth != 0) {
// Lex the first token of the next macro parameter.
- RawLexer.LexFromRawLexer(TheTok);
+ TStream.next(TheTok);
- while (!(ParenthesesDepth == 1 &&
- (UnexpArgII == __VA_ARGS__II ? false : TheTok.is(tok::comma)))) {
+ while (CurrParamII == VariadicParamII || ParenthesesDepth != 1 ||
+ !TheTok.is(tok::comma)) {
assert(TheTok.isNot(tok::eof) &&
"EOF encountered while looking for expanded macro args!");
@@ -1165,24 +1216,51 @@ static MacroNameAndArgs getMacroNameAndArgs(SourceLocation ExpanLoc,
if (ParenthesesDepth == 0)
break;
- if (TheTok.is(tok::raw_identifier))
+ if (TheTok.is(tok::raw_identifier)) {
PP.LookUpIdentifierInfo(TheTok);
+ // This token is a variadic parameter:
+ //
+ // #define PARAMS_RESOLVE_TO_VA_ARGS(i, fmt) foo(i, fmt); \
+ // i = 0;
+ // #define DISPATCH(...) \
+ // PARAMS_RESOLVE_TO_VA_ARGS(__VA_ARGS__);
+ // // ^~~~~~~~~~~ Variadic parameter here
+ //
+ // void multipleParamsResolveToVA_ARGS(void) {
+ // int x = 1;
+ // DISPATCH(x, "LF1M healer"); // Multiple arguments are mapped to
+ // // a single __VA_ARGS__ parameter.
+ // (void)(10 / x);
+ // }
+ //
+ // We will stumble across this while trying to expand
+ // PARAMS_RESOLVE_TO_VA_ARGS. By this point, we already noted during
+ // the processing of DISPATCH what __VA_ARGS__ maps to, so we'll
+ // retrieve the next series of tokens from that.
+ if (TheTok.getIdentifierInfo() == VariadicParamII) {
+ TStream.injectRange(PrevParamMap.at(VariadicParamII));
+ TStream.next(TheTok);
+ continue;
+ }
+ }
- ExpandedArgTokens.push_back(TheTok);
- RawLexer.LexFromRawLexer(TheTok);
+ ArgTokens.push_back(TheTok);
+ TStream.next(TheTok);
}
} else {
- assert(UnexpArgII == __VA_ARGS__II);
+ assert(CurrParamII == VariadicParamII &&
+ "No more macro arguments are found, but the current parameter "
+ "isn't the variadic arg!");
}
- Args.emplace(UnexpArgII, std::move(ExpandedArgTokens));
+ ParamMap.emplace(CurrParamII, std::move(ArgTokens));
}
assert(TheTok.is(tok::r_paren) &&
"Expanded macro argument acquisition failed! After the end of the loop"
" this token should be ')'!");
- return { MacroName, MI, Args };
+ return {MacroName, MI, ParamMap};
}
static MacroInfo::tokens_iterator getMatchingRParen(
@@ -1222,14 +1300,14 @@ static const MacroInfo *getMacroInfoForLocation(const Preprocessor &PP,
return MD->findDirectiveAtLoc(Loc, SM).getMacroInfo();
}
-void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) {
+void MacroParamMap::expandFromPrevMacro(const MacroParamMap &Super) {
for (value_type &Pair : *this) {
- ExpArgTokens &CurrExpArgTokens = Pair.second;
+ ArgTokensTy &CurrArgTokens = Pair.second;
// For each token in the expanded macro argument.
- auto It = CurrExpArgTokens.begin();
- while (It != CurrExpArgTokens.end()) {
+ auto It = CurrArgTokens.begin();
+ while (It != CurrArgTokens.end()) {
if (It->isNot(tok::identifier)) {
++It;
continue;
@@ -1244,17 +1322,43 @@ void MacroArgMap::expandFromPrevMacro(const MacroArgMap &Super) {
continue;
}
- const ExpArgTokens &SuperExpArgTokens = Super.at(II);
+ const ArgTokensTy &SuperArgTokens = Super.at(II);
- It = CurrExpArgTokens.insert(
- It, SuperExpArgTokens.begin(), SuperExpArgTokens.end());
- std::advance(It, SuperExpArgTokens.size());
- It = CurrExpArgTokens.erase(It);
+ It = CurrArgTokens.insert(It, SuperArgTokens.begin(),
+ SuperArgTokens.end());
+ std::advance(It, SuperArgTokens.size());
+ It = CurrArgTokens.erase(It);
}
}
}
+void MacroParamMap::dumpToStream(llvm::raw_ostream &Out,
+ const Preprocessor &PP) const {
+ for (const std::pair<const IdentifierInfo *, ArgTokensTy> Pair : *this) {
+ Out << Pair.first->getName() << " -> ";
+ dumpArgTokensToStream(Out, PP, Pair.second);
+ Out << '\n';
+ }
+}
+
+static void dumpArgTokensToStream(llvm::raw_ostream &Out,
+ const Preprocessor &PP,
+ const ArgTokensTy &Toks) {
+ TokenPrinter Printer(Out, PP);
+ for (Token Tok : Toks)
+ Printer.printToken(Tok);
+}
+
void TokenPrinter::printToken(const Token &Tok) {
+ // TODO: Handle GNU extensions where hash and hashhash occurs right before
+ // __VA_ARGS__.
+ // cppreference.com: "some compilers offer an extension that allows ## to
+ // appear after a comma and before __VA_ARGS__, in which case the ## does
+ // nothing when the variable arguments are present, but removes the comma when
+ // the variable arguments are not present: this makes it possible to define
+ // macros such as fprintf (stderr, format, ##__VA_ARGS__)"
+ // FIXME: Handle named variadic macro parameters (also a GNU extension).
+
// If this is the first token to be printed, don't print space.
if (PrevTok.isNot(tok::unknown)) {
// If the tokens were already space separated, or if they must be to avoid
diff --git a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
index 006a4006b7fc..1ccb0de92fba 100644
--- a/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
+++ b/clang/lib/StaticAnalyzer/Core/ProgramState.cpp
@@ -582,9 +582,6 @@ bool ScanReachableSymbols::scan(SVal val) {
if (SymbolRef Sym = val.getAsSymbol())
return scan(Sym);
- if (const SymExpr *Sym = val.getAsSymbolicExpression())
- return scan(Sym);
-
if (Optional<nonloc::CompoundVal> X = val.getAs<nonloc::CompoundVal>())
return scan(*X);
diff --git a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
index cb6f61e86ae3..a481bde1651b 100644
--- a/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RangeConstraintManager.cpp
@@ -89,7 +89,7 @@ public:
}
TriStateKind getCmpOpState(BinaryOperatorKind CurrentOP,
- BinaryOperatorKind QueriedOP) const {
+ BinaryOperatorKind QueriedOP) const {
return CmpOpTable[getIndexFromOp(CurrentOP)][getIndexFromOp(QueriedOP)];
}
@@ -364,6 +364,18 @@ RangeSet RangeSet::Negate(BasicValueFactory &BV, Factory &F) const {
return newRanges;
}
+RangeSet RangeSet::Delete(BasicValueFactory &BV, Factory &F,
+ const llvm::APSInt &Point) const {
+ llvm::APSInt Upper = Point;
+ llvm::APSInt Lower = Point;
+
+ ++Upper;
+ --Lower;
+
+ // Notice that the lower bound is greater than the upper bound.
+ return Intersect(BV, F, Upper, Lower);
+}
+
void RangeSet::print(raw_ostream &os) const {
bool isFirst = true;
os << "{ ";
@@ -379,7 +391,315 @@ void RangeSet::print(raw_ostream &os) const {
os << " }";
}
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(SymbolSet, SymbolRef)
+
namespace {
+class EquivalenceClass;
+} // end anonymous namespace
+
+REGISTER_MAP_WITH_PROGRAMSTATE(ClassMap, SymbolRef, EquivalenceClass)
+REGISTER_MAP_WITH_PROGRAMSTATE(ClassMembers, EquivalenceClass, SymbolSet)
+REGISTER_MAP_WITH_PROGRAMSTATE(ConstraintRange, EquivalenceClass, RangeSet)
+
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(ClassSet, EquivalenceClass)
+REGISTER_MAP_WITH_PROGRAMSTATE(DisequalityMap, EquivalenceClass, ClassSet)
+
+namespace {
+/// This class encapsulates a set of symbols equal to each other.
+///
+/// The main idea of the approach requiring such classes is in narrowing
+/// and sharing constraints between symbols within the class. Also we can
+/// conclude that there is no practical need in storing constraints for
+/// every member of the class separately.
+///
+/// Main terminology:
+///
+/// * "Equivalence class" is an object of this class, which can be efficiently
+/// compared to other classes. It represents the whole class without
+/// storing the actual in it. The members of the class however can be
+/// retrieved from the state.
+///
+/// * "Class members" are the symbols corresponding to the class. This means
+/// that A == B for every member symbols A and B from the class. Members of
+/// each class are stored in the state.
+///
+/// * "Trivial class" is a class that has and ever had only one same symbol.
+///
+/// * "Merge operation" merges two classes into one. It is the main operation
+/// to produce non-trivial classes.
+/// If, at some point, we can assume that two symbols from two distinct
+/// classes are equal, we can merge these classes.
+class EquivalenceClass : public llvm::FoldingSetNode {
+public:
+ /// Find equivalence class for the given symbol in the given state.
+ LLVM_NODISCARD static inline EquivalenceClass find(ProgramStateRef State,
+ SymbolRef Sym);
+
+ /// Merge classes for the given symbols and return a new state.
+ LLVM_NODISCARD static inline ProgramStateRef
+ merge(BasicValueFactory &BV, RangeSet::Factory &F, ProgramStateRef State,
+ SymbolRef First, SymbolRef Second);
+ // Merge this class with the given class and return a new state.
+ LLVM_NODISCARD inline ProgramStateRef merge(BasicValueFactory &BV,
+ RangeSet::Factory &F,
+ ProgramStateRef State,
+ EquivalenceClass Other);
+
+ /// Return a set of class members for the given state.
+ LLVM_NODISCARD inline SymbolSet getClassMembers(ProgramStateRef State);
+ /// Return true if the current class is trivial in the given state.
+ LLVM_NODISCARD inline bool isTrivial(ProgramStateRef State);
+ /// Return true if the current class is trivial and its only member is dead.
+ LLVM_NODISCARD inline bool isTriviallyDead(ProgramStateRef State,
+ SymbolReaper &Reaper);
+
+ LLVM_NODISCARD static inline ProgramStateRef
+ markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, SymbolRef First, SymbolRef Second);
+ LLVM_NODISCARD static inline ProgramStateRef
+ markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, EquivalenceClass First,
+ EquivalenceClass Second);
+ LLVM_NODISCARD inline ProgramStateRef
+ markDisequal(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, EquivalenceClass Other) const;
+ LLVM_NODISCARD static inline ClassSet
+ getDisequalClasses(ProgramStateRef State, SymbolRef Sym);
+ LLVM_NODISCARD inline ClassSet
+ getDisequalClasses(ProgramStateRef State) const;
+ LLVM_NODISCARD inline ClassSet
+ getDisequalClasses(DisequalityMapTy Map, ClassSet::Factory &Factory) const;
+
+ LLVM_NODISCARD static inline Optional<bool>
+ areEqual(ProgramStateRef State, SymbolRef First, SymbolRef Second);
+
+ /// Check equivalence data for consistency.
+ LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED static bool
+ isClassDataConsistent(ProgramStateRef State);
+
+ LLVM_NODISCARD QualType getType() const {
+ return getRepresentativeSymbol()->getType();
+ }
+
+ EquivalenceClass() = delete;
+ EquivalenceClass(const EquivalenceClass &) = default;
+ EquivalenceClass &operator=(const EquivalenceClass &) = delete;
+ EquivalenceClass(EquivalenceClass &&) = default;
+ EquivalenceClass &operator=(EquivalenceClass &&) = delete;
+
+ bool operator==(const EquivalenceClass &Other) const {
+ return ID == Other.ID;
+ }
+ bool operator<(const EquivalenceClass &Other) const { return ID < Other.ID; }
+ bool operator!=(const EquivalenceClass &Other) const {
+ return !operator==(Other);
+ }
+
+ static void Profile(llvm::FoldingSetNodeID &ID, uintptr_t CID) {
+ ID.AddInteger(CID);
+ }
+
+ void Profile(llvm::FoldingSetNodeID &ID) const { Profile(ID, this->ID); }
+
+private:
+ /* implicit */ EquivalenceClass(SymbolRef Sym)
+ : ID(reinterpret_cast<uintptr_t>(Sym)) {}
+
+ /// This function is intended to be used ONLY within the class.
+ /// The fact that ID is a pointer to a symbol is an implementation detail
+ /// and should stay that way.
+ /// In the current implementation, we use it to retrieve the only member
+ /// of the trivial class.
+ SymbolRef getRepresentativeSymbol() const {
+ return reinterpret_cast<SymbolRef>(ID);
+ }
+ static inline SymbolSet::Factory &getMembersFactory(ProgramStateRef State);
+
+ inline ProgramStateRef mergeImpl(BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, SymbolSet Members,
+ EquivalenceClass Other,
+ SymbolSet OtherMembers);
+ static inline void
+ addToDisequalityInfo(DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
+ BasicValueFactory &BV, RangeSet::Factory &F,
+ ProgramStateRef State, EquivalenceClass First,
+ EquivalenceClass Second);
+
+ /// This is a unique identifier of the class.
+ uintptr_t ID;
+};
+
+//===----------------------------------------------------------------------===//
+// Constraint functions
+//===----------------------------------------------------------------------===//
+
+LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
+ EquivalenceClass Class) {
+ return State->get<ConstraintRange>(Class);
+}
+
+LLVM_NODISCARD inline const RangeSet *getConstraint(ProgramStateRef State,
+ SymbolRef Sym) {
+ return getConstraint(State, EquivalenceClass::find(State, Sym));
+}
+
+//===----------------------------------------------------------------------===//
+// Equality/diseqiality abstraction
+//===----------------------------------------------------------------------===//
+
+/// A small helper structure representing symbolic equality.
+///
+/// Equality check can have different forms (like a == b or a - b) and this
+/// class encapsulates those away if the only thing the user wants to check -
+/// whether it's equality/diseqiality or not and have an easy access to the
+/// compared symbols.
+struct EqualityInfo {
+public:
+ SymbolRef Left, Right;
+ // true for equality and false for disequality.
+ bool IsEquality = true;
+
+ void invert() { IsEquality = !IsEquality; }
+ /// Extract equality information from the given symbol and the constants.
+ ///
+ /// This function assumes the following expression Sym + Adjustment != Int.
+ /// It is a default because the most widespread case of the equality check
+ /// is (A == B) + 0 != 0.
+ static Optional<EqualityInfo> extract(SymbolRef Sym, const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ // As of now, the only equality form supported is Sym + 0 != 0.
+ if (!Int.isNullValue() || !Adjustment.isNullValue())
+ return llvm::None;
+
+ return extract(Sym);
+ }
+ /// Extract equality information from the given symbol.
+ static Optional<EqualityInfo> extract(SymbolRef Sym) {
+ return EqualityExtractor().Visit(Sym);
+ }
+
+private:
+ class EqualityExtractor
+ : public SymExprVisitor<EqualityExtractor, Optional<EqualityInfo>> {
+ public:
+ Optional<EqualityInfo> VisitSymSymExpr(const SymSymExpr *Sym) const {
+ switch (Sym->getOpcode()) {
+ case BO_Sub:
+ // This case is: A - B != 0 -> disequality check.
+ return EqualityInfo{Sym->getLHS(), Sym->getRHS(), false};
+ case BO_EQ:
+ // This case is: A == B != 0 -> equality check.
+ return EqualityInfo{Sym->getLHS(), Sym->getRHS(), true};
+ case BO_NE:
+ // This case is: A != B != 0 -> diseqiality check.
+ return EqualityInfo{Sym->getLHS(), Sym->getRHS(), false};
+ default:
+ return llvm::None;
+ }
+ }
+ };
+};
+
+//===----------------------------------------------------------------------===//
+// Intersection functions
+//===----------------------------------------------------------------------===//
+
+template <class SecondTy, class... RestTy>
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail);
+
+template <class... RangeTy> struct IntersectionTraits;
+
+template <class... TailTy> struct IntersectionTraits<RangeSet, TailTy...> {
+ // Found RangeSet, no need to check any further
+ using Type = RangeSet;
+};
+
+template <> struct IntersectionTraits<> {
+ // We ran out of types, and we didn't find any RangeSet, so the result should
+ // be optional.
+ using Type = Optional<RangeSet>;
+};
+
+template <class OptionalOrPointer, class... TailTy>
+struct IntersectionTraits<OptionalOrPointer, TailTy...> {
+ // If current type is Optional or a raw pointer, we should keep looking.
+ using Type = typename IntersectionTraits<TailTy...>::Type;
+};
+
+template <class EndTy>
+LLVM_NODISCARD inline EndTy intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, EndTy End) {
+ // If the list contains only RangeSet or Optional<RangeSet>, simply return
+ // that range set.
+ return End;
+}
+
+LLVM_NODISCARD LLVM_ATTRIBUTE_UNUSED inline Optional<RangeSet>
+intersect(BasicValueFactory &BV, RangeSet::Factory &F, const RangeSet *End) {
+ // This is an extraneous conversion from a raw pointer into Optional<RangeSet>
+ if (End) {
+ return *End;
+ }
+ return llvm::None;
+}
+
+template <class... RestTy>
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ RangeSet Second, RestTy... Tail) {
+ // Here we call either the <RangeSet,RangeSet,...> or <RangeSet,...> version
+ // of the function and can be sure that the result is RangeSet.
+ return intersect(BV, F, Head.Intersect(BV, F, Second), Tail...);
+}
+
+template <class SecondTy, class... RestTy>
+LLVM_NODISCARD inline RangeSet intersect(BasicValueFactory &BV,
+ RangeSet::Factory &F, RangeSet Head,
+ SecondTy Second, RestTy... Tail) {
+ if (Second) {
+ // Here we call the <RangeSet,RangeSet,...> version of the function...
+ return intersect(BV, F, Head, *Second, Tail...);
+ }
+ // ...and here it is either <RangeSet,RangeSet,...> or <RangeSet,...>, which
+ // means that the result is definitely RangeSet.
+ return intersect(BV, F, Head, Tail...);
+}
+
+/// Main generic intersect function.
+/// It intersects all of the given range sets. If some of the given arguments
+/// don't hold a range set (nullptr or llvm::None), the function will skip them.
+///
+/// Available representations for the arguments are:
+/// * RangeSet
+/// * Optional<RangeSet>
+/// * RangeSet *
+/// Pointer to a RangeSet is automatically assumed to be nullable and will get
+/// checked as well as the optional version. If this behaviour is undesired,
+/// please dereference the pointer in the call.
+///
+/// Return type depends on the arguments' types. If we can be sure in compile
+/// time that there will be a range set as a result, the returning type is
+/// simply RangeSet, in other cases we have to back off to Optional<RangeSet>.
+///
+/// Please, prefer optional range sets to raw pointers. If the last argument is
+/// a raw pointer and all previous arguments are None, it will cost one
+/// additional check to convert RangeSet * into Optional<RangeSet>.
+template <class HeadTy, class SecondTy, class... RestTy>
+LLVM_NODISCARD inline
+ typename IntersectionTraits<HeadTy, SecondTy, RestTy...>::Type
+ intersect(BasicValueFactory &BV, RangeSet::Factory &F, HeadTy Head,
+ SecondTy Second, RestTy... Tail) {
+ if (Head) {
+ return intersect(BV, F, *Head, Second, Tail...);
+ }
+ return intersect(BV, F, Second, Tail...);
+}
+
+//===----------------------------------------------------------------------===//
+// Symbolic reasoning logic
+//===----------------------------------------------------------------------===//
/// A little component aggregating all of the reasoning we have about
/// the ranges of symbolic expressions.
@@ -389,10 +709,11 @@ namespace {
class SymbolicRangeInferrer
: public SymExprVisitor<SymbolicRangeInferrer, RangeSet> {
public:
+ template <class SourceType>
static RangeSet inferRange(BasicValueFactory &BV, RangeSet::Factory &F,
- ProgramStateRef State, SymbolRef Sym) {
+ ProgramStateRef State, SourceType Origin) {
SymbolicRangeInferrer Inferrer(BV, F, State);
- return Inferrer.infer(Sym);
+ return Inferrer.infer(Origin);
}
RangeSet VisitSymExpr(SymbolRef Sym) {
@@ -442,37 +763,35 @@ private:
}
RangeSet infer(SymbolRef Sym) {
- const RangeSet *AssociatedRange = State->get<ConstraintRange>(Sym);
-
- // If Sym is a difference of symbols A - B, then maybe we have range set
- // stored for B - A.
- const RangeSet *RangeAssociatedWithNegatedSym =
- getRangeForMinusSymbol(State, Sym);
-
- // If we have range set stored for both A - B and B - A then calculate the
- // effective range set by intersecting the range set for A - B and the
- // negated range set of B - A.
- if (AssociatedRange && RangeAssociatedWithNegatedSym)
- return AssociatedRange->Intersect(
- ValueFactory, RangeFactory,
- RangeAssociatedWithNegatedSym->Negate(ValueFactory, RangeFactory));
-
- if (AssociatedRange)
- return *AssociatedRange;
-
- if (RangeAssociatedWithNegatedSym)
- return RangeAssociatedWithNegatedSym->Negate(ValueFactory, RangeFactory);
+ if (Optional<RangeSet> ConstraintBasedRange = intersect(
+ ValueFactory, RangeFactory, getConstraint(State, Sym),
+ // If Sym is a difference of symbols A - B, then maybe we have range
+ // set stored for B - A.
+ //
+ // If we have range set stored for both A - B and B - A then
+ // calculate the effective range set by intersecting the range set
+ // for A - B and the negated range set of B - A.
+ getRangeForNegatedSub(Sym), getRangeForEqualities(Sym))) {
+ return *ConstraintBasedRange;
+ }
// If Sym is a comparison expression (except <=>),
// find any other comparisons with the same operands.
// See function description.
- const RangeSet CmpRangeSet = getRangeForComparisonSymbol(State, Sym);
- if (!CmpRangeSet.isEmpty())
- return CmpRangeSet;
+ if (Optional<RangeSet> CmpRangeSet = getRangeForComparisonSymbol(Sym)) {
+ return *CmpRangeSet;
+ }
return Visit(Sym);
}
+ RangeSet infer(EquivalenceClass Class) {
+ if (const RangeSet *AssociatedConstraint = getConstraint(State, Class))
+ return *AssociatedConstraint;
+
+ return infer(Class.getType());
+ }
+
/// Infer range information solely from the type.
RangeSet infer(QualType T) {
// Lazily generate a new RangeSet representing all possible values for the
@@ -621,8 +940,7 @@ private:
/// Return a range set subtracting zero from \p Domain.
RangeSet assumeNonZero(RangeSet Domain, QualType T) {
APSIntType IntType = ValueFactory.getAPSIntType(T);
- return Domain.Intersect(ValueFactory, RangeFactory,
- ++IntType.getZeroValue(), --IntType.getZeroValue());
+ return Domain.Delete(ValueFactory, RangeFactory, IntType.getZeroValue());
}
// FIXME: Once SValBuilder supports unary minus, we should use SValBuilder to
@@ -630,23 +948,26 @@ private:
// symbol manually. This will allow us to support finding ranges of not
// only negated SymSymExpr-type expressions, but also of other, simpler
// expressions which we currently do not know how to negate.
- const RangeSet *getRangeForMinusSymbol(ProgramStateRef State, SymbolRef Sym) {
+ Optional<RangeSet> getRangeForNegatedSub(SymbolRef Sym) {
if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
if (SSE->getOpcode() == BO_Sub) {
QualType T = Sym->getType();
+
+ // Do not negate unsigned ranges
+ if (!T->isUnsignedIntegerOrEnumerationType() &&
+ !T->isSignedIntegerOrEnumerationType())
+ return llvm::None;
+
SymbolManager &SymMgr = State->getSymbolManager();
- SymbolRef negSym =
+ SymbolRef NegatedSym =
SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), T);
- if (const RangeSet *negV = State->get<ConstraintRange>(negSym)) {
- // Unsigned range set cannot be negated, unless it is [0, 0].
- if (T->isUnsignedIntegerOrEnumerationType() ||
- T->isSignedIntegerOrEnumerationType())
- return negV;
+ if (const RangeSet *NegatedRange = getConstraint(State, NegatedSym)) {
+ return NegatedRange->Negate(ValueFactory, RangeFactory);
}
}
}
- return nullptr;
+ return llvm::None;
}
// Returns ranges only for binary comparison operators (except <=>)
@@ -659,18 +980,16 @@ private:
// It covers all possible combinations (see CmpOpTable description).
// Note that `x` and `y` can also stand for subexpressions,
// not only for actual symbols.
- RangeSet getRangeForComparisonSymbol(ProgramStateRef State, SymbolRef Sym) {
- const RangeSet EmptyRangeSet = RangeFactory.getEmptySet();
-
- auto SSE = dyn_cast<SymSymExpr>(Sym);
+ Optional<RangeSet> getRangeForComparisonSymbol(SymbolRef Sym) {
+ const auto *SSE = dyn_cast<SymSymExpr>(Sym);
if (!SSE)
- return EmptyRangeSet;
+ return llvm::None;
BinaryOperatorKind CurrentOP = SSE->getOpcode();
// We currently do not support <=> (C++20).
if (!BinaryOperator::isComparisonOp(CurrentOP) || (CurrentOP == BO_Cmp))
- return EmptyRangeSet;
+ return llvm::None;
static const OperatorRelationsTable CmpOpTable{};
@@ -679,10 +998,6 @@ private:
QualType T = SSE->getType();
SymbolManager &SymMgr = State->getSymbolManager();
- const llvm::APSInt &Zero = ValueFactory.getValue(0, T);
- const llvm::APSInt &One = ValueFactory.getValue(1, T);
- const RangeSet TrueRangeSet(RangeFactory, One, One);
- const RangeSet FalseRangeSet(RangeFactory, Zero, Zero);
int UnknownStates = 0;
@@ -693,7 +1008,7 @@ private:
// Let's find an expression e.g. (x < y).
BinaryOperatorKind QueriedOP = OperatorRelationsTable::getOpFromIndex(i);
const SymSymExpr *SymSym = SymMgr.getSymSymExpr(LHS, QueriedOP, RHS, T);
- const RangeSet *QueriedRangeSet = State->get<ConstraintRange>(SymSym);
+ const RangeSet *QueriedRangeSet = getConstraint(State, SymSym);
// If ranges were not previously found,
// try to find a reversed expression (y > x).
@@ -701,7 +1016,7 @@ private:
const BinaryOperatorKind ROP =
BinaryOperator::reverseComparisonOp(QueriedOP);
SymSym = SymMgr.getSymSymExpr(RHS, ROP, LHS, T);
- QueriedRangeSet = State->get<ConstraintRange>(SymSym);
+ QueriedRangeSet = getConstraint(State, SymSym);
}
if (!QueriedRangeSet || QueriedRangeSet->isEmpty())
@@ -732,11 +1047,38 @@ private:
continue;
}
- return (BranchState == OperatorRelationsTable::True) ? TrueRangeSet
- : FalseRangeSet;
+ return (BranchState == OperatorRelationsTable::True) ? getTrueRange(T)
+ : getFalseRange(T);
+ }
+
+ return llvm::None;
+ }
+
+ Optional<RangeSet> getRangeForEqualities(SymbolRef Sym) {
+ Optional<EqualityInfo> Equality = EqualityInfo::extract(Sym);
+
+ if (!Equality)
+ return llvm::None;
+
+ if (Optional<bool> AreEqual = EquivalenceClass::areEqual(
+ State, Equality->Left, Equality->Right)) {
+ if (*AreEqual == Equality->IsEquality) {
+ return getTrueRange(Sym->getType());
+ }
+ return getFalseRange(Sym->getType());
}
- return EmptyRangeSet;
+ return llvm::None;
+ }
+
+ RangeSet getTrueRange(QualType T) {
+ RangeSet TypeRange = infer(T);
+ return assumeNonZero(TypeRange, T);
+ }
+
+ RangeSet getFalseRange(QualType T) {
+ const llvm::APSInt &Zero = ValueFactory.getValue(0, T);
+ return RangeSet(RangeFactory, Zero);
}
BasicValueFactory &ValueFactory;
@@ -744,6 +1086,10 @@ private:
ProgramStateRef State;
};
+//===----------------------------------------------------------------------===//
+// Range-based reasoning about symbolic operations
+//===----------------------------------------------------------------------===//
+
template <>
RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Or>(Range LHS, Range RHS,
QualType T) {
@@ -904,6 +1250,10 @@ RangeSet SymbolicRangeInferrer::VisitBinaryOperator<BO_Rem>(Range LHS,
return {RangeFactory, ValueFactory.getValue(Min), ValueFactory.getValue(Max)};
}
+//===----------------------------------------------------------------------===//
+// Constraint manager implementation details
+//===----------------------------------------------------------------------===//
+
class RangeConstraintManager : public RangedConstraintManager {
public:
RangeConstraintManager(ExprEngine *EE, SValBuilder &SVB)
@@ -915,7 +1265,11 @@ public:
bool haveEqualConstraints(ProgramStateRef S1,
ProgramStateRef S2) const override {
- return S1->get<ConstraintRange>() == S2->get<ConstraintRange>();
+ // 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;
@@ -971,6 +1325,7 @@ private:
RangeSet::Factory F;
RangeSet getRange(ProgramStateRef State, SymbolRef Sym);
+ RangeSet getRange(ProgramStateRef State, EquivalenceClass Class);
RangeSet getSymLTRange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
@@ -987,6 +1342,87 @@ private:
RangeSet getSymGERange(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment);
+
+ //===------------------------------------------------------------------===//
+ // Equality tracking implementation
+ //===------------------------------------------------------------------===//
+
+ ProgramStateRef trackEQ(RangeSet NewConstraint, ProgramStateRef State,
+ SymbolRef Sym, const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ return track<true>(NewConstraint, State, Sym, Int, Adjustment);
+ }
+
+ ProgramStateRef trackNE(RangeSet NewConstraint, ProgramStateRef State,
+ SymbolRef Sym, const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ return track<false>(NewConstraint, State, Sym, Int, Adjustment);
+ }
+
+ template <bool EQ>
+ ProgramStateRef track(RangeSet NewConstraint, ProgramStateRef State,
+ SymbolRef Sym, const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
+ if (NewConstraint.isEmpty())
+ // This is an infeasible assumption.
+ return nullptr;
+
+ ProgramStateRef NewState = setConstraint(State, Sym, NewConstraint);
+ if (auto Equality = EqualityInfo::extract(Sym, Int, Adjustment)) {
+ // If the original assumption is not Sym + Adjustment !=/</> Int,
+ // we should invert IsEquality flag.
+ Equality->IsEquality = Equality->IsEquality != EQ;
+ return track(NewState, *Equality);
+ }
+
+ return NewState;
+ }
+
+ ProgramStateRef track(ProgramStateRef State, EqualityInfo ToTrack) {
+ if (ToTrack.IsEquality) {
+ return trackEquality(State, ToTrack.Left, ToTrack.Right);
+ }
+ return trackDisequality(State, ToTrack.Left, ToTrack.Right);
+ }
+
+ ProgramStateRef trackDisequality(ProgramStateRef State, SymbolRef LHS,
+ SymbolRef RHS) {
+ return EquivalenceClass::markDisequal(getBasicVals(), F, State, LHS, RHS);
+ }
+
+ ProgramStateRef trackEquality(ProgramStateRef State, SymbolRef LHS,
+ SymbolRef RHS) {
+ return EquivalenceClass::merge(getBasicVals(), F, State, LHS, RHS);
+ }
+
+ LLVM_NODISCARD inline ProgramStateRef setConstraint(ProgramStateRef State,
+ EquivalenceClass Class,
+ RangeSet Constraint) {
+ ConstraintRangeTy Constraints = State->get<ConstraintRange>();
+ ConstraintRangeTy::Factory &CF = State->get_context<ConstraintRange>();
+
+ // Add new constraint.
+ Constraints = CF.add(Constraints, Class, Constraint);
+
+ // There is a chance that we might need to update constraints for the
+ // classes that are known to be disequal to Class.
+ //
+ // In order for this to be even possible, the new constraint should
+ // be simply a constant because we can't reason about range disequalities.
+ if (const llvm::APSInt *Point = Constraint.getConcreteValue())
+ for (EquivalenceClass DisequalClass : Class.getDisequalClasses(State)) {
+ RangeSet UpdatedConstraint =
+ getRange(State, DisequalClass).Delete(getBasicVals(), F, *Point);
+ Constraints = CF.add(Constraints, DisequalClass, UpdatedConstraint);
+ }
+
+ return State->set<ConstraintRange>(Constraints);
+ }
+
+ LLVM_NODISCARD inline ProgramStateRef
+ setConstraint(ProgramStateRef State, SymbolRef Sym, RangeSet Constraint) {
+ return setConstraint(State, EquivalenceClass::find(State, Sym), Constraint);
+ }
};
} // end anonymous namespace
@@ -997,6 +1433,372 @@ ento::CreateRangeConstraintManager(ProgramStateManager &StMgr,
return std::make_unique<RangeConstraintManager>(Eng, StMgr.getSValBuilder());
}
+ConstraintMap ento::getConstraintMap(ProgramStateRef State) {
+ ConstraintMap::Factory &F = State->get_context<ConstraintMap>();
+ ConstraintMap Result = F.getEmptyMap();
+
+ ConstraintRangeTy Constraints = State->get<ConstraintRange>();
+ for (std::pair<EquivalenceClass, RangeSet> ClassConstraint : Constraints) {
+ EquivalenceClass Class = ClassConstraint.first;
+ SymbolSet ClassMembers = Class.getClassMembers(State);
+ assert(!ClassMembers.isEmpty() &&
+ "Class must always have at least one member!");
+
+ SymbolRef Representative = *ClassMembers.begin();
+ Result = F.add(Result, Representative, ClassConstraint.second);
+ }
+
+ return Result;
+}
+
+//===----------------------------------------------------------------------===//
+// EqualityClass implementation details
+//===----------------------------------------------------------------------===//
+
+inline EquivalenceClass EquivalenceClass::find(ProgramStateRef State,
+ SymbolRef Sym) {
+ // We store far from all Symbol -> Class mappings
+ if (const EquivalenceClass *NontrivialClass = State->get<ClassMap>(Sym))
+ return *NontrivialClass;
+
+ // This is a trivial class of Sym.
+ return Sym;
+}
+
+inline ProgramStateRef EquivalenceClass::merge(BasicValueFactory &BV,
+ RangeSet::Factory &F,
+ ProgramStateRef State,
+ SymbolRef First,
+ SymbolRef Second) {
+ EquivalenceClass FirstClass = find(State, First);
+ EquivalenceClass SecondClass = find(State, Second);
+
+ return FirstClass.merge(BV, F, State, SecondClass);
+}
+
+inline ProgramStateRef EquivalenceClass::merge(BasicValueFactory &BV,
+ RangeSet::Factory &F,
+ ProgramStateRef State,
+ EquivalenceClass Other) {
+ // It is already the same class.
+ if (*this == Other)
+ return State;
+
+ // FIXME: As of now, we support only equivalence classes of the same type.
+ // This limitation is connected to the lack of explicit casts in
+ // our symbolic expression model.
+ //
+ // That means that for `int x` and `char y` we don't distinguish
+ // between these two very different cases:
+ // * `x == y`
+ // * `(char)x == y`
+ //
+ // The moment we introduce symbolic casts, this restriction can be
+ // lifted.
+ if (getType() != Other.getType())
+ return State;
+
+ SymbolSet Members = getClassMembers(State);
+ SymbolSet OtherMembers = Other.getClassMembers(State);
+
+ // We estimate the size of the class by the height of tree containing
+ // its members. Merging is not a trivial operation, so it's easier to
+ // merge the smaller class into the bigger one.
+ if (Members.getHeight() >= OtherMembers.getHeight()) {
+ return mergeImpl(BV, F, State, Members, Other, OtherMembers);
+ } else {
+ return Other.mergeImpl(BV, F, State, OtherMembers, *this, Members);
+ }
+}
+
+inline ProgramStateRef
+EquivalenceClass::mergeImpl(BasicValueFactory &ValueFactory,
+ RangeSet::Factory &RangeFactory,
+ ProgramStateRef State, SymbolSet MyMembers,
+ EquivalenceClass Other, SymbolSet OtherMembers) {
+ // Essentially what we try to recreate here is some kind of union-find
+ // data structure. It does have certain limitations due to persistence
+ // and the need to remove elements from classes.
+ //
+ // In this setting, EquialityClass object is the representative of the class
+ // or the parent element. ClassMap is a mapping of class members to their
+ // parent. Unlike the union-find structure, they all point directly to the
+ // class representative because we don't have an opportunity to actually do
+ // path compression when dealing with immutability. This means that we
+ // compress paths every time we do merges. It also means that we lose
+ // the main amortized complexity benefit from the original data structure.
+ ConstraintRangeTy Constraints = State->get<ConstraintRange>();
+ ConstraintRangeTy::Factory &CRF = State->get_context<ConstraintRange>();
+
+ // 1. If the merged classes have any constraints associated with them, we
+ // need to transfer them to the class we have left.
+ //
+ // Intersection here makes perfect sense because both of these constraints
+ // must hold for the whole new class.
+ if (Optional<RangeSet> NewClassConstraint =
+ intersect(ValueFactory, RangeFactory, getConstraint(State, *this),
+ getConstraint(State, Other))) {
+ // NOTE: Essentially, NewClassConstraint should NEVER be infeasible because
+ // range inferrer shouldn't generate ranges incompatible with
+ // equivalence classes. However, at the moment, due to imperfections
+ // in the solver, it is possible and the merge function can also
+ // return infeasible states aka null states.
+ if (NewClassConstraint->isEmpty())
+ // Infeasible state
+ return nullptr;
+
+ // No need in tracking constraints of a now-dissolved class.
+ Constraints = CRF.remove(Constraints, Other);
+ // Assign new constraints for this class.
+ Constraints = CRF.add(Constraints, *this, *NewClassConstraint);
+
+ State = State->set<ConstraintRange>(Constraints);
+ }
+
+ // 2. Get ALL equivalence-related maps
+ ClassMapTy Classes = State->get<ClassMap>();
+ ClassMapTy::Factory &CMF = State->get_context<ClassMap>();
+
+ ClassMembersTy Members = State->get<ClassMembers>();
+ ClassMembersTy::Factory &MF = State->get_context<ClassMembers>();
+
+ DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>();
+ DisequalityMapTy::Factory &DF = State->get_context<DisequalityMap>();
+
+ ClassSet::Factory &CF = State->get_context<ClassSet>();
+ SymbolSet::Factory &F = getMembersFactory(State);
+
+ // 2. Merge members of the Other class into the current class.
+ SymbolSet NewClassMembers = MyMembers;
+ for (SymbolRef Sym : OtherMembers) {
+ NewClassMembers = F.add(NewClassMembers, Sym);
+ // *this is now the class for all these new symbols.
+ Classes = CMF.add(Classes, Sym, *this);
+ }
+
+ // 3. Adjust member mapping.
+ //
+ // No need in tracking members of a now-dissolved class.
+ Members = MF.remove(Members, Other);
+ // Now only the current class is mapped to all the symbols.
+ Members = MF.add(Members, *this, NewClassMembers);
+
+ // 4. Update disequality relations
+ ClassSet DisequalToOther = Other.getDisequalClasses(DisequalityInfo, CF);
+ if (!DisequalToOther.isEmpty()) {
+ ClassSet DisequalToThis = getDisequalClasses(DisequalityInfo, CF);
+ DisequalityInfo = DF.remove(DisequalityInfo, Other);
+
+ for (EquivalenceClass DisequalClass : DisequalToOther) {
+ DisequalToThis = CF.add(DisequalToThis, DisequalClass);
+
+ // Disequality is a symmetric relation meaning that if
+ // DisequalToOther not null then the set for DisequalClass is not
+ // empty and has at least Other.
+ ClassSet OriginalSetLinkedToOther =
+ *DisequalityInfo.lookup(DisequalClass);
+
+ // Other will be eliminated and we should replace it with the bigger
+ // united class.
+ ClassSet NewSet = CF.remove(OriginalSetLinkedToOther, Other);
+ NewSet = CF.add(NewSet, *this);
+
+ DisequalityInfo = DF.add(DisequalityInfo, DisequalClass, NewSet);
+ }
+
+ DisequalityInfo = DF.add(DisequalityInfo, *this, DisequalToThis);
+ State = State->set<DisequalityMap>(DisequalityInfo);
+ }
+
+ // 5. Update the state
+ State = State->set<ClassMap>(Classes);
+ State = State->set<ClassMembers>(Members);
+
+ return State;
+}
+
+inline SymbolSet::Factory &
+EquivalenceClass::getMembersFactory(ProgramStateRef State) {
+ return State->get_context<SymbolSet>();
+}
+
+SymbolSet EquivalenceClass::getClassMembers(ProgramStateRef State) {
+ if (const SymbolSet *Members = State->get<ClassMembers>(*this))
+ return *Members;
+
+ // This class is trivial, so we need to construct a set
+ // with just that one symbol from the class.
+ SymbolSet::Factory &F = getMembersFactory(State);
+ return F.add(F.getEmptySet(), getRepresentativeSymbol());
+}
+
+bool EquivalenceClass::isTrivial(ProgramStateRef State) {
+ return State->get<ClassMembers>(*this) == nullptr;
+}
+
+bool EquivalenceClass::isTriviallyDead(ProgramStateRef State,
+ SymbolReaper &Reaper) {
+ return isTrivial(State) && Reaper.isDead(getRepresentativeSymbol());
+}
+
+inline ProgramStateRef EquivalenceClass::markDisequal(BasicValueFactory &VF,
+ RangeSet::Factory &RF,
+ ProgramStateRef State,
+ SymbolRef First,
+ SymbolRef Second) {
+ return markDisequal(VF, RF, State, find(State, First), find(State, Second));
+}
+
+inline ProgramStateRef EquivalenceClass::markDisequal(BasicValueFactory &VF,
+ RangeSet::Factory &RF,
+ ProgramStateRef State,
+ EquivalenceClass First,
+ EquivalenceClass Second) {
+ return First.markDisequal(VF, RF, State, Second);
+}
+
+inline ProgramStateRef
+EquivalenceClass::markDisequal(BasicValueFactory &VF, RangeSet::Factory &RF,
+ ProgramStateRef State,
+ EquivalenceClass Other) const {
+ // If we know that two classes are equal, we can only produce an infeasible
+ // state.
+ if (*this == Other) {
+ return nullptr;
+ }
+
+ DisequalityMapTy DisequalityInfo = State->get<DisequalityMap>();
+ ConstraintRangeTy Constraints = State->get<ConstraintRange>();
+
+ // Disequality is a symmetric relation, so if we mark A as disequal to B,
+ // we should also mark B as disequalt to A.
+ addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, *this,
+ Other);
+ addToDisequalityInfo(DisequalityInfo, Constraints, VF, RF, State, Other,
+ *this);
+
+ State = State->set<DisequalityMap>(DisequalityInfo);
+ State = State->set<ConstraintRange>(Constraints);
+
+ return State;
+}
+
+inline void EquivalenceClass::addToDisequalityInfo(
+ DisequalityMapTy &Info, ConstraintRangeTy &Constraints,
+ BasicValueFactory &VF, RangeSet::Factory &RF, ProgramStateRef State,
+ EquivalenceClass First, EquivalenceClass Second) {
+
+ // 1. Get all of the required factories.
+ DisequalityMapTy::Factory &F = State->get_context<DisequalityMap>();
+ ClassSet::Factory &CF = State->get_context<ClassSet>();
+ ConstraintRangeTy::Factory &CRF = State->get_context<ConstraintRange>();
+
+ // 2. Add Second to the set of classes disequal to First.
+ const ClassSet *CurrentSet = Info.lookup(First);
+ ClassSet NewSet = CurrentSet ? *CurrentSet : CF.getEmptySet();
+ NewSet = CF.add(NewSet, Second);
+
+ Info = F.add(Info, First, NewSet);
+
+ // 3. If Second is known to be a constant, we can delete this point
+ // from the constraint asociated with First.
+ //
+ // So, if Second == 10, it means that First != 10.
+ // At the same time, the same logic does not apply to ranges.
+ if (const RangeSet *SecondConstraint = Constraints.lookup(Second))
+ if (const llvm::APSInt *Point = SecondConstraint->getConcreteValue()) {
+
+ RangeSet FirstConstraint = SymbolicRangeInferrer::inferRange(
+ VF, RF, State, First.getRepresentativeSymbol());
+
+ FirstConstraint = FirstConstraint.Delete(VF, RF, *Point);
+ Constraints = CRF.add(Constraints, First, FirstConstraint);
+ }
+}
+
+inline Optional<bool> EquivalenceClass::areEqual(ProgramStateRef State,
+ SymbolRef FirstSym,
+ SymbolRef SecondSym) {
+ EquivalenceClass First = find(State, FirstSym);
+ EquivalenceClass Second = find(State, SecondSym);
+
+ // The same equivalence class => symbols are equal.
+ if (First == Second)
+ return true;
+
+ // Let's check if we know anything about these two classes being not equal to
+ // each other.
+ ClassSet DisequalToFirst = First.getDisequalClasses(State);
+ if (DisequalToFirst.contains(Second))
+ return false;
+
+ // It is not clear.
+ return llvm::None;
+}
+
+inline ClassSet EquivalenceClass::getDisequalClasses(ProgramStateRef State,
+ SymbolRef Sym) {
+ return find(State, Sym).getDisequalClasses(State);
+}
+
+inline ClassSet
+EquivalenceClass::getDisequalClasses(ProgramStateRef State) const {
+ return getDisequalClasses(State->get<DisequalityMap>(),
+ State->get_context<ClassSet>());
+}
+
+inline ClassSet
+EquivalenceClass::getDisequalClasses(DisequalityMapTy Map,
+ ClassSet::Factory &Factory) const {
+ if (const ClassSet *DisequalClasses = Map.lookup(*this))
+ return *DisequalClasses;
+
+ return Factory.getEmptySet();
+}
+
+bool EquivalenceClass::isClassDataConsistent(ProgramStateRef State) {
+ ClassMembersTy Members = State->get<ClassMembers>();
+
+ for (std::pair<EquivalenceClass, SymbolSet> ClassMembersPair : Members) {
+ for (SymbolRef Member : ClassMembersPair.second) {
+ // Every member of the class should have a mapping back to the class.
+ if (find(State, Member) == ClassMembersPair.first) {
+ continue;
+ }
+
+ return false;
+ }
+ }
+
+ DisequalityMapTy Disequalities = State->get<DisequalityMap>();
+ for (std::pair<EquivalenceClass, ClassSet> DisequalityInfo : Disequalities) {
+ EquivalenceClass Class = DisequalityInfo.first;
+ ClassSet DisequalClasses = DisequalityInfo.second;
+
+ // There is no use in keeping empty sets in the map.
+ if (DisequalClasses.isEmpty())
+ return false;
+
+ // Disequality is symmetrical, i.e. for every Class A and B that A != B,
+ // B != A should also be true.
+ for (EquivalenceClass DisequalClass : DisequalClasses) {
+ const ClassSet *DisequalToDisequalClasses =
+ Disequalities.lookup(DisequalClass);
+
+ // It should be a set of at least one element: Class
+ if (!DisequalToDisequalClasses ||
+ !DisequalToDisequalClasses->contains(Class))
+ return false;
+ }
+ }
+
+ return true;
+}
+
+//===----------------------------------------------------------------------===//
+// RangeConstraintManager implementation
+//===----------------------------------------------------------------------===//
+
bool RangeConstraintManager::canReasonAbout(SVal X) const {
Optional<nonloc::SymbolVal> SymVal = X.getAs<nonloc::SymbolVal>();
if (SymVal && SymVal->isExpression()) {
@@ -1045,7 +1847,7 @@ bool RangeConstraintManager::canReasonAbout(SVal X) const {
ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State,
SymbolRef Sym) {
- const RangeSet *Ranges = State->get<ConstraintRange>(Sym);
+ const RangeSet *Ranges = getConstraint(State, Sym);
// If we don't have any information about this symbol, it's underconstrained.
if (!Ranges)
@@ -1069,28 +1871,148 @@ ConditionTruthVal RangeConstraintManager::checkNull(ProgramStateRef State,
const llvm::APSInt *RangeConstraintManager::getSymVal(ProgramStateRef St,
SymbolRef Sym) const {
- const ConstraintRangeTy::data_type *T = St->get<ConstraintRange>(Sym);
+ const RangeSet *T = getConstraint(St, Sym);
return T ? T->getConcreteValue() : nullptr;
}
+//===----------------------------------------------------------------------===//
+// Remove dead symbols from existing constraints
+//===----------------------------------------------------------------------===//
+
/// Scan all symbols referenced by the constraints. If the symbol is not alive
/// as marked in LSymbols, mark it as dead in DSymbols.
ProgramStateRef
RangeConstraintManager::removeDeadBindings(ProgramStateRef State,
SymbolReaper &SymReaper) {
- bool Changed = false;
- ConstraintRangeTy CR = State->get<ConstraintRange>();
- ConstraintRangeTy::Factory &CRFactory = State->get_context<ConstraintRange>();
+ ClassMembersTy ClassMembersMap = State->get<ClassMembers>();
+ ClassMembersTy NewClassMembersMap = ClassMembersMap;
+ ClassMembersTy::Factory &EMFactory = State->get_context<ClassMembers>();
+ SymbolSet::Factory &SetFactory = State->get_context<SymbolSet>();
+
+ ConstraintRangeTy Constraints = State->get<ConstraintRange>();
+ ConstraintRangeTy NewConstraints = Constraints;
+ ConstraintRangeTy::Factory &ConstraintFactory =
+ State->get_context<ConstraintRange>();
+
+ ClassMapTy Map = State->get<ClassMap>();
+ ClassMapTy NewMap = Map;
+ ClassMapTy::Factory &ClassFactory = State->get_context<ClassMap>();
+
+ DisequalityMapTy Disequalities = State->get<DisequalityMap>();
+ DisequalityMapTy::Factory &DisequalityFactory =
+ State->get_context<DisequalityMap>();
+ ClassSet::Factory &ClassSetFactory = State->get_context<ClassSet>();
+
+ bool ClassMapChanged = false;
+ bool MembersMapChanged = false;
+ bool ConstraintMapChanged = false;
+ bool DisequalitiesChanged = false;
+
+ auto removeDeadClass = [&](EquivalenceClass Class) {
+ // Remove associated constraint ranges.
+ Constraints = ConstraintFactory.remove(Constraints, Class);
+ ConstraintMapChanged = true;
+
+ // Update disequality information to not hold any information on the
+ // removed class.
+ ClassSet DisequalClasses =
+ Class.getDisequalClasses(Disequalities, ClassSetFactory);
+ if (!DisequalClasses.isEmpty()) {
+ for (EquivalenceClass DisequalClass : DisequalClasses) {
+ ClassSet DisequalToDisequalSet =
+ DisequalClass.getDisequalClasses(Disequalities, ClassSetFactory);
+ // DisequalToDisequalSet is guaranteed to be non-empty for consistent
+ // disequality info.
+ assert(!DisequalToDisequalSet.isEmpty());
+ ClassSet NewSet = ClassSetFactory.remove(DisequalToDisequalSet, Class);
+
+ // No need in keeping an empty set.
+ if (NewSet.isEmpty()) {
+ Disequalities =
+ DisequalityFactory.remove(Disequalities, DisequalClass);
+ } else {
+ Disequalities =
+ DisequalityFactory.add(Disequalities, DisequalClass, NewSet);
+ }
+ }
+ // Remove the data for the class
+ Disequalities = DisequalityFactory.remove(Disequalities, Class);
+ DisequalitiesChanged = true;
+ }
+ };
+
+ // 1. Let's see if dead symbols are trivial and have associated constraints.
+ for (std::pair<EquivalenceClass, RangeSet> ClassConstraintPair :
+ Constraints) {
+ EquivalenceClass Class = ClassConstraintPair.first;
+ if (Class.isTriviallyDead(State, SymReaper)) {
+ // If this class is trivial, we can remove its constraints right away.
+ removeDeadClass(Class);
+ }
+ }
+
+ // 2. We don't need to track classes for dead symbols.
+ for (std::pair<SymbolRef, EquivalenceClass> SymbolClassPair : Map) {
+ SymbolRef Sym = SymbolClassPair.first;
- for (ConstraintRangeTy::iterator I = CR.begin(), E = CR.end(); I != E; ++I) {
- SymbolRef Sym = I.getKey();
if (SymReaper.isDead(Sym)) {
- Changed = true;
- CR = CRFactory.remove(CR, Sym);
+ ClassMapChanged = true;
+ NewMap = ClassFactory.remove(NewMap, Sym);
+ }
+ }
+
+ // 3. Remove dead members from classes and remove dead non-trivial classes
+ // and their constraints.
+ for (std::pair<EquivalenceClass, SymbolSet> ClassMembersPair :
+ ClassMembersMap) {
+ EquivalenceClass Class = ClassMembersPair.first;
+ SymbolSet LiveMembers = ClassMembersPair.second;
+ bool MembersChanged = false;
+
+ for (SymbolRef Member : ClassMembersPair.second) {
+ if (SymReaper.isDead(Member)) {
+ MembersChanged = true;
+ LiveMembers = SetFactory.remove(LiveMembers, Member);
+ }
+ }
+
+ // Check if the class changed.
+ if (!MembersChanged)
+ continue;
+
+ MembersMapChanged = true;
+
+ if (LiveMembers.isEmpty()) {
+ // The class is dead now, we need to wipe it out of the members map...
+ NewClassMembersMap = EMFactory.remove(NewClassMembersMap, Class);
+
+ // ...and remove all of its constraints.
+ removeDeadClass(Class);
+ } else {
+ // We need to change the members associated with the class.
+ NewClassMembersMap =
+ EMFactory.add(NewClassMembersMap, Class, LiveMembers);
}
}
- return Changed ? State->set<ConstraintRange>(CR) : State;
+ // 4. Update the state with new maps.
+ //
+ // Here we try to be humble and update a map only if it really changed.
+ if (ClassMapChanged)
+ State = State->set<ClassMap>(NewMap);
+
+ if (MembersMapChanged)
+ State = State->set<ClassMembers>(NewClassMembersMap);
+
+ if (ConstraintMapChanged)
+ State = State->set<ConstraintRange>(Constraints);
+
+ if (DisequalitiesChanged)
+ State = State->set<DisequalityMap>(Disequalities);
+
+ assert(EquivalenceClass::isClassDataConsistent(State));
+
+ return State;
}
RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
@@ -1098,6 +2020,11 @@ RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Sym);
}
+RangeSet RangeConstraintManager::getRange(ProgramStateRef State,
+ EquivalenceClass Class) {
+ return SymbolicRangeInferrer::inferRange(getBasicVals(), F, State, Class);
+}
+
//===------------------------------------------------------------------------===
// assumeSymX methods: protected interface for RangeConstraintManager.
//===------------------------------------------------------------------------===/
@@ -1119,15 +2046,11 @@ RangeConstraintManager::assumeSymNE(ProgramStateRef St, SymbolRef Sym,
if (AdjustmentType.testInRange(Int, true) != APSIntType::RTR_Within)
return St;
- llvm::APSInt Lower = AdjustmentType.convert(Int) - Adjustment;
- llvm::APSInt Upper = Lower;
- --Lower;
- ++Upper;
+ llvm::APSInt Point = AdjustmentType.convert(Int) - Adjustment;
- // [Int-Adjustment+1, Int-Adjustment-1]
- // Notice that the lower bound is greater than the upper bound.
- RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, Upper, Lower);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+ RangeSet New = getRange(St, Sym).Delete(getBasicVals(), F, Point);
+
+ return trackNE(New, St, Sym, Int, Adjustment);
}
ProgramStateRef
@@ -1142,7 +2065,8 @@ RangeConstraintManager::assumeSymEQ(ProgramStateRef St, SymbolRef Sym,
// [Int-Adjustment, Int-Adjustment]
llvm::APSInt AdjInt = AdjustmentType.convert(Int) - Adjustment;
RangeSet New = getRange(St, Sym).Intersect(getBasicVals(), F, AdjInt, AdjInt);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+
+ return trackEQ(New, St, Sym, Int, Adjustment);
}
RangeSet RangeConstraintManager::getSymLTRange(ProgramStateRef St,
@@ -1178,7 +2102,7 @@ RangeConstraintManager::assumeSymLT(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymLTRange(St, Sym, Int, Adjustment);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+ return trackNE(New, St, Sym, Int, Adjustment);
}
RangeSet RangeConstraintManager::getSymGTRange(ProgramStateRef St,
@@ -1214,7 +2138,7 @@ RangeConstraintManager::assumeSymGT(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymGTRange(St, Sym, Int, Adjustment);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+ return trackNE(New, St, Sym, Int, Adjustment);
}
RangeSet RangeConstraintManager::getSymGERange(ProgramStateRef St,
@@ -1250,13 +2174,13 @@ RangeConstraintManager::assumeSymGE(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymGERange(St, Sym, Int, Adjustment);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+ return New.isEmpty() ? nullptr : setConstraint(St, Sym, New);
}
-RangeSet RangeConstraintManager::getSymLERange(
- llvm::function_ref<RangeSet()> RS,
- const llvm::APSInt &Int,
- const llvm::APSInt &Adjustment) {
+RangeSet
+RangeConstraintManager::getSymLERange(llvm::function_ref<RangeSet()> RS,
+ const llvm::APSInt &Int,
+ const llvm::APSInt &Adjustment) {
// Before we do any real work, see if the value can even show up.
APSIntType AdjustmentType(Adjustment);
switch (AdjustmentType.testInRange(Int, true)) {
@@ -1293,7 +2217,7 @@ RangeConstraintManager::assumeSymLE(ProgramStateRef St, SymbolRef Sym,
const llvm::APSInt &Int,
const llvm::APSInt &Adjustment) {
RangeSet New = getSymLERange(St, Sym, Int, Adjustment);
- return New.isEmpty() ? nullptr : St->set<ConstraintRange>(Sym, New);
+ return New.isEmpty() ? nullptr : setConstraint(St, Sym, New);
}
ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange(
@@ -1303,7 +2227,7 @@ ProgramStateRef RangeConstraintManager::assumeSymWithinInclusiveRange(
if (New.isEmpty())
return nullptr;
RangeSet Out = getSymLERange([&] { return New; }, To, Adjustment);
- return Out.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, Out);
+ return Out.isEmpty() ? nullptr : setConstraint(State, Sym, Out);
}
ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange(
@@ -1312,7 +2236,7 @@ ProgramStateRef RangeConstraintManager::assumeSymOutsideInclusiveRange(
RangeSet RangeLT = getSymLTRange(State, Sym, From, Adjustment);
RangeSet RangeGT = getSymGTRange(State, Sym, To, Adjustment);
RangeSet New(RangeLT.addRange(F, RangeGT));
- return New.isEmpty() ? nullptr : State->set<ConstraintRange>(Sym, New);
+ return New.isEmpty() ? nullptr : setConstraint(State, Sym, New);
}
//===----------------------------------------------------------------------===//
@@ -1332,17 +2256,25 @@ void RangeConstraintManager::printJson(raw_ostream &Out, ProgramStateRef State,
++Space;
Out << '[' << NL;
- for (ConstraintRangeTy::iterator I = Constraints.begin();
- I != Constraints.end(); ++I) {
- Indent(Out, Space, IsDot)
- << "{ \"symbol\": \"" << I.getKey() << "\", \"range\": \"";
- I.getData().print(Out);
- Out << "\" }";
+ bool First = true;
+ for (std::pair<EquivalenceClass, RangeSet> P : Constraints) {
+ SymbolSet ClassMembers = P.first.getClassMembers(State);
- if (std::next(I) != Constraints.end())
- Out << ',';
- Out << NL;
+ // We can print the same constraint for every class member.
+ for (SymbolRef ClassMember : ClassMembers) {
+ if (First) {
+ First = false;
+ } else {
+ Out << ',';
+ Out << NL;
+ }
+ Indent(Out, Space, IsDot)
+ << "{ \"symbol\": \"" << ClassMember << "\", \"range\": \"";
+ P.second.print(Out);
+ Out << "\" }";
+ }
}
+ Out << NL;
--Space;
Indent(Out, Space, IsDot) << "]," << NL;
diff --git a/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
index 4748c106eb55..e7a03e6ed582 100644
--- a/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/RangedConstraintManager.cpp
@@ -40,19 +40,20 @@ ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State,
}
} else if (const SymSymExpr *SSE = dyn_cast<SymSymExpr>(Sym)) {
- // Translate "a != b" to "(b - a) != 0".
- // We invert the order of the operands as a heuristic for how loop
- // conditions are usually written ("begin != end") as compared to length
- // calculations ("end - begin"). The more correct thing to do would be to
- // canonicalize "a - b" and "b - a", which would allow us to treat
- // "a != b" and "b != a" the same.
- SymbolManager &SymMgr = getSymbolManager();
BinaryOperator::Opcode Op = SSE->getOpcode();
assert(BinaryOperator::isComparisonOp(Op));
- // For now, we only support comparing pointers.
+ // We convert equality operations for pointers only.
if (Loc::isLocType(SSE->getLHS()->getType()) &&
Loc::isLocType(SSE->getRHS()->getType())) {
+ // Translate "a != b" to "(b - a) != 0".
+ // We invert the order of the operands as a heuristic for how loop
+ // conditions are usually written ("begin != end") as compared to length
+ // calculations ("end - begin"). The more correct thing to do would be to
+ // canonicalize "a - b" and "b - a", which would allow us to treat
+ // "a != b" and "b != a" the same.
+
+ SymbolManager &SymMgr = getSymbolManager();
QualType DiffTy = SymMgr.getContext().getPointerDiffType();
SymbolRef Subtraction =
SymMgr.getSymSymExpr(SSE->getRHS(), BO_Sub, SSE->getLHS(), DiffTy);
@@ -63,6 +64,25 @@ ProgramStateRef RangedConstraintManager::assumeSym(ProgramStateRef State,
Op = BinaryOperator::negateComparisonOp(Op);
return assumeSymRel(State, Subtraction, Op, Zero);
}
+
+ if (BinaryOperator::isEqualityOp(Op)) {
+ SymbolManager &SymMgr = getSymbolManager();
+
+ QualType ExprType = SSE->getType();
+ SymbolRef CanonicalEquality =
+ SymMgr.getSymSymExpr(SSE->getLHS(), BO_EQ, SSE->getRHS(), ExprType);
+
+ bool WasEqual = SSE->getOpcode() == BO_EQ;
+ bool IsExpectedEqual = WasEqual == Assumption;
+
+ const llvm::APSInt &Zero = getBasicVals().getValue(0, ExprType);
+
+ if (IsExpectedEqual) {
+ return assumeSymNE(State, CanonicalEquality, Zero, Zero);
+ }
+
+ return assumeSymEQ(State, CanonicalEquality, Zero, Zero);
+ }
}
// If we get here, there's nothing else we can do but treat the symbol as
@@ -199,11 +219,6 @@ void RangedConstraintManager::computeAdjustment(SymbolRef &Sym,
}
}
-void *ProgramStateTrait<ConstraintRange>::GDMIndex() {
- static int Index;
- return &Index;
-}
-
} // end of namespace ento
} // end of namespace clang
diff --git a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
index c00a2c8ba8a2..72b8ada1dfab 100644
--- a/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SValBuilder.cpp
@@ -236,10 +236,11 @@ SValBuilder::getDerivedRegionValueSymbolVal(SymbolRef parentSymbol,
return nonloc::SymbolVal(sym);
}
-DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) {
- assert(!DD || isa<CXXMethodDecl>(DD) || isa<FieldDecl>(DD));
+DefinedSVal SValBuilder::getMemberPointer(const NamedDecl *ND) {
+ assert(!ND || isa<CXXMethodDecl>(ND) || isa<FieldDecl>(ND) ||
+ isa<IndirectFieldDecl>(ND));
- if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(DD)) {
+ if (const auto *MD = dyn_cast_or_null<CXXMethodDecl>(ND)) {
// Sema treats pointers to static member functions as have function pointer
// type, so return a function pointer for the method.
// We don't need to play a similar trick for static member fields
@@ -249,7 +250,7 @@ DefinedSVal SValBuilder::getMemberPointer(const DeclaratorDecl *DD) {
return getFunctionPointer(MD);
}
- return nonloc::PointerToMember(DD);
+ return nonloc::PointerToMember(ND);
}
DefinedSVal SValBuilder::getFunctionPointer(const FunctionDecl *func) {
@@ -305,6 +306,14 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
return makeLoc(getRegionManager().getStringRegion(SL));
}
+ case Stmt::PredefinedExprClass: {
+ const auto *PE = cast<PredefinedExpr>(E);
+ assert(PE->getFunctionName() &&
+ "Since we analyze only instantiated functions, PredefinedExpr "
+ "should have a function name.");
+ return makeLoc(getRegionManager().getStringRegion(PE->getFunctionName()));
+ }
+
// Fast-path some expressions to avoid the overhead of going through the AST's
// constant evaluator
case Stmt::CharacterLiteralClass: {
@@ -377,8 +386,8 @@ Optional<SVal> SValBuilder::getConstantVal(const Expr *E) {
SVal SValBuilder::makeSymExprValNN(BinaryOperator::Opcode Op,
NonLoc LHS, NonLoc RHS,
QualType ResultTy) {
- const SymExpr *symLHS = LHS.getAsSymExpr();
- const SymExpr *symRHS = RHS.getAsSymExpr();
+ SymbolRef symLHS = LHS.getAsSymbol();
+ SymbolRef symRHS = RHS.getAsSymbol();
// 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.
@@ -492,7 +501,7 @@ SVal SValBuilder::evalIntegralCast(ProgramStateRef state, SVal val,
if (getContext().getTypeSize(castTy) >= getContext().getTypeSize(originalTy))
return evalCast(val, castTy, originalTy);
- const SymExpr *se = val.getAsSymbolicExpression();
+ SymbolRef se = val.getAsSymbol();
if (!se) // Let evalCast handle non symbolic expressions.
return evalCast(val, castTy, originalTy);
diff --git a/clang/lib/StaticAnalyzer/Core/SVals.cpp b/clang/lib/StaticAnalyzer/Core/SVals.cpp
index 9b5de6c3eb92..252596887e4f 100644
--- a/clang/lib/StaticAnalyzer/Core/SVals.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SVals.cpp
@@ -84,16 +84,12 @@ const FunctionDecl *SVal::getAsFunctionDecl() const {
/// the first symbolic parent region is returned.
SymbolRef SVal::getAsLocSymbol(bool IncludeBaseRegions) const {
// FIXME: should we consider SymbolRef wrapped in CodeTextRegion?
- if (Optional<nonloc::LocAsInteger> X = getAs<nonloc::LocAsInteger>())
- return X->getLoc().getAsLocSymbol(IncludeBaseRegions);
-
- if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>()) {
- const MemRegion *R = X->getRegion();
- if (const SymbolicRegion *SymR = IncludeBaseRegions ?
- R->getSymbolicBase() :
- dyn_cast<SymbolicRegion>(R->StripCasts()))
+ if (const MemRegion *R = getAsRegion())
+ if (const SymbolicRegion *SymR =
+ IncludeBaseRegions ? R->getSymbolicBase()
+ : dyn_cast<SymbolicRegion>(R->StripCasts()))
return SymR->getSymbol();
- }
+
return nullptr;
}
@@ -116,8 +112,6 @@ SymbolRef SVal::getLocSymbolInBase() const {
return nullptr;
}
-// TODO: The next 3 functions have to be simplified.
-
/// If this SVal wraps a symbol return that SymbolRef.
/// Otherwise, return 0.
///
@@ -132,22 +126,6 @@ SymbolRef SVal::getAsSymbol(bool IncludeBaseRegions) const {
return getAsLocSymbol(IncludeBaseRegions);
}
-/// getAsSymbolicExpression - If this Sval wraps a symbolic expression then
-/// return that expression. Otherwise return NULL.
-const SymExpr *SVal::getAsSymbolicExpression() const {
- if (Optional<nonloc::SymbolVal> X = getAs<nonloc::SymbolVal>())
- return X->getSymbol();
-
- return getAsSymbol();
-}
-
-const SymExpr* SVal::getAsSymExpr() const {
- const SymExpr* Sym = getAsSymbol();
- if (!Sym)
- Sym = getAsSymbolicExpression();
- return Sym;
-}
-
const MemRegion *SVal::getAsRegion() const {
if (Optional<loc::MemRegionVal> X = getAs<loc::MemRegionVal>())
return X->getRegion();
@@ -175,18 +153,18 @@ bool nonloc::PointerToMember::isNullMemberPointer() const {
return getPTMData().isNull();
}
-const DeclaratorDecl *nonloc::PointerToMember::getDecl() const {
+const NamedDecl *nonloc::PointerToMember::getDecl() const {
const auto PTMD = this->getPTMData();
if (PTMD.isNull())
return nullptr;
- const DeclaratorDecl *DD = nullptr;
- if (PTMD.is<const DeclaratorDecl *>())
- DD = PTMD.get<const DeclaratorDecl *>();
+ const NamedDecl *ND = nullptr;
+ if (PTMD.is<const NamedDecl *>())
+ ND = PTMD.get<const NamedDecl *>();
else
- DD = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl();
+ ND = PTMD.get<const PointerToMemberData *>()->getDeclaratorDecl();
- return DD;
+ return ND;
}
//===----------------------------------------------------------------------===//
@@ -203,14 +181,14 @@ nonloc::CompoundVal::iterator nonloc::CompoundVal::end() const {
nonloc::PointerToMember::iterator nonloc::PointerToMember::begin() const {
const PTMDataType PTMD = getPTMData();
- if (PTMD.is<const DeclaratorDecl *>())
+ if (PTMD.is<const NamedDecl *>())
return {};
return PTMD.get<const PointerToMemberData *>()->begin();
}
nonloc::PointerToMember::iterator nonloc::PointerToMember::end() const {
const PTMDataType PTMD = getPTMData();
- if (PTMD.is<const DeclaratorDecl *>())
+ if (PTMD.is<const NamedDecl *>())
return {};
return PTMD.get<const PointerToMemberData *>()->end();
}
diff --git a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
index 8c2e85601576..f93d04ccd61a 100644
--- a/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SarifDiagnostics.cpp
@@ -14,7 +14,6 @@
#include "clang/Basic/FileManager.h"
#include "clang/Basic/Version.h"
#include "clang/Lex/Preprocessor.h"
-#include "clang/StaticAnalyzer/Core/AnalyzerOptions.h"
#include "clang/StaticAnalyzer/Core/PathDiagnosticConsumers.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/ADT/StringMap.h"
@@ -32,8 +31,7 @@ class SarifDiagnostics : public PathDiagnosticConsumer {
const LangOptions &LO;
public:
- SarifDiagnostics(AnalyzerOptions &, const std::string &Output,
- const LangOptions &LO)
+ SarifDiagnostics(const std::string &Output, const LangOptions &LO)
: OutputFile(Output), LO(LO) {}
~SarifDiagnostics() override = default;
@@ -48,7 +46,7 @@ public:
} // end anonymous namespace
void ento::createSarifDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &Output, const Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
@@ -56,8 +54,9 @@ void ento::createSarifDiagnosticConsumer(
if (Output.empty())
return;
- C.push_back(new SarifDiagnostics(AnalyzerOpts, Output, PP.getLangOpts()));
- createTextMinimalPathDiagnosticConsumer(AnalyzerOpts, C, Output, PP, CTU);
+ C.push_back(new SarifDiagnostics(Output, PP.getLangOpts()));
+ createTextMinimalPathDiagnosticConsumer(std::move(DiagOpts), C, Output, PP,
+ CTU);
}
static StringRef getFileName(const FileEntry &FE) {
@@ -160,9 +159,8 @@ static unsigned int adjustColumnPos(const SourceManager &SM, SourceLocation Loc,
assert(LocInfo.second > SM.getExpansionColumnNumber(Loc) &&
"position in file is before column number?");
- bool InvalidBuffer = false;
- const MemoryBuffer *Buf = SM.getBuffer(LocInfo.first, &InvalidBuffer);
- assert(!InvalidBuffer && "got an invalid buffer for the location's file");
+ Optional<MemoryBufferRef> Buf = SM.getBufferOrNone(LocInfo.first);
+ assert(Buf && "got an invalid buffer for the location's file");
assert(Buf->getBufferSize() >= (LocInfo.second + TokenLen) &&
"token extends past end of buffer?");
diff --git a/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp b/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
index 3709106ad44c..f96974f97dcc 100644
--- a/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SimpleConstraintManager.cpp
@@ -57,7 +57,7 @@ ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State,
// SymIntExprs.
if (!canReasonAbout(Cond)) {
// Just add the constraint to the expression without trying to simplify.
- SymbolRef Sym = Cond.getAsSymExpr();
+ SymbolRef Sym = Cond.getAsSymbol();
assert(Sym);
return assumeSymUnsupported(State, Sym, Assumption);
}
@@ -101,7 +101,7 @@ ProgramStateRef SimpleConstraintManager::assumeInclusiveRange(
if (!canReasonAbout(Value)) {
// Just add the constraint to the expression without trying to simplify.
- SymbolRef Sym = Value.getAsSymExpr();
+ SymbolRef Sym = Value.getAsSymbol();
assert(Sym);
return assumeSymInclusiveRange(State, Sym, From, To, InRange);
}
diff --git a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
index 2e269f6a596e..facadaf1225f 100644
--- a/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SimpleSValBuilder.cpp
@@ -86,7 +86,7 @@ SVal SimpleSValBuilder::evalCastFromNonLoc(NonLoc val, QualType castTy) {
return makeLocAsInteger(LI->getLoc(), castSize);
}
- if (const SymExpr *se = val.getAsSymbolicExpression()) {
+ if (SymbolRef se = val.getAsSymbol()) {
QualType T = Context.getCanonicalType(se->getType());
// If types are the same or both are integers, ignore the cast.
// FIXME: Remove this hack when we support symbolic truncation/extension.
@@ -1106,19 +1106,28 @@ SVal SimpleSValBuilder::evalBinOpLL(ProgramStateRef state,
}
SVal SimpleSValBuilder::evalBinOpLN(ProgramStateRef state,
- BinaryOperator::Opcode op,
- Loc lhs, NonLoc rhs, QualType resultTy) {
+ BinaryOperator::Opcode op, Loc lhs,
+ NonLoc rhs, QualType resultTy) {
if (op >= BO_PtrMemD && op <= BO_PtrMemI) {
if (auto PTMSV = rhs.getAs<nonloc::PointerToMember>()) {
if (PTMSV->isNullMemberPointer())
return UndefinedVal();
- if (const FieldDecl *FD = PTMSV->getDeclAs<FieldDecl>()) {
+
+ auto getFieldLValue = [&](const auto *FD) -> SVal {
SVal Result = lhs;
for (const auto &I : *PTMSV)
Result = StateMgr.getStoreManager().evalDerivedToBase(
- Result, I->getType(),I->isVirtual());
+ Result, I->getType(), I->isVirtual());
+
return state->getLValue(FD, Result);
+ };
+
+ if (const auto *FD = PTMSV->getDeclAs<FieldDecl>()) {
+ return getFieldLValue(FD);
+ }
+ if (const auto *FD = PTMSV->getDeclAs<IndirectFieldDecl>()) {
+ return getFieldLValue(FD);
}
}
diff --git a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
index 6ca7aec9caec..79a8eef30576 100644
--- a/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
+++ b/clang/lib/StaticAnalyzer/Core/SymbolManager.cpp
@@ -14,6 +14,7 @@
#include "clang/StaticAnalyzer/Core/PathSensitive/SymbolManager.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
+#include "clang/AST/StmtObjC.h"
#include "clang/Analysis/Analyses/LiveVariables.h"
#include "clang/Analysis/AnalysisDeclContext.h"
#include "clang/Basic/LLVM.h"
@@ -34,6 +35,12 @@ using namespace ento;
void SymExpr::anchor() {}
+StringRef SymbolConjured::getKindStr() const { return "conj_$"; }
+StringRef SymbolDerived::getKindStr() const { return "derived_$"; }
+StringRef SymbolExtent::getKindStr() const { return "extent_$"; }
+StringRef SymbolMetadata::getKindStr() const { return "meta_$"; }
+StringRef SymbolRegionValue::getKindStr() const { return "reg_$"; }
+
LLVM_DUMP_METHOD void SymExpr::dump() const { dumpToStream(llvm::errs()); }
void BinarySymExpr::dumpToStreamImpl(raw_ostream &OS, const SymExpr *Sym) {
@@ -64,7 +71,7 @@ void SymbolCast::dumpToStream(raw_ostream &os) const {
}
void SymbolConjured::dumpToStream(raw_ostream &os) const {
- os << "conj_$" << getSymbolID() << '{' << T.getAsString() << ", LC"
+ os << getKindStr() << getSymbolID() << '{' << T.getAsString() << ", LC"
<< LCtx->getID();
if (S)
os << ", S" << S->getID(LCtx->getDecl()->getASTContext());
@@ -74,24 +81,24 @@ void SymbolConjured::dumpToStream(raw_ostream &os) const {
}
void SymbolDerived::dumpToStream(raw_ostream &os) const {
- os << "derived_$" << getSymbolID() << '{'
- << getParentSymbol() << ',' << getRegion() << '}';
+ os << getKindStr() << getSymbolID() << '{' << getParentSymbol() << ','
+ << getRegion() << '}';
}
void SymbolExtent::dumpToStream(raw_ostream &os) const {
- os << "extent_$" << getSymbolID() << '{' << getRegion() << '}';
+ os << getKindStr() << getSymbolID() << '{' << getRegion() << '}';
}
void SymbolMetadata::dumpToStream(raw_ostream &os) const {
- os << "meta_$" << getSymbolID() << '{'
- << getRegion() << ',' << T.getAsString() << '}';
+ os << getKindStr() << getSymbolID() << '{' << getRegion() << ','
+ << T.getAsString() << '}';
}
void SymbolData::anchor() {}
void SymbolRegionValue::dumpToStream(raw_ostream &os) const {
- os << "reg_$" << getSymbolID()
- << '<' << getType().getAsString() << ' ' << R << '>';
+ os << getKindStr() << getSymbolID() << '<' << getType().getAsString() << ' '
+ << R << '>';
}
bool SymExpr::symbol_iterator::operator==(const symbol_iterator &X) const {
@@ -482,7 +489,7 @@ bool SymbolReaper::isLive(SymbolRef sym) {
}
bool
-SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
+SymbolReaper::isLive(const Expr *ExprVal, const LocationContext *ELCtx) const {
if (LCtx == nullptr)
return false;
@@ -494,7 +501,8 @@ SymbolReaper::isLive(const Stmt *ExprVal, const LocationContext *ELCtx) const {
return true;
}
- // If no statement is provided, everything is this and parent contexts is live.
+ // If no statement is provided, everything in this and parent contexts is
+ // live.
if (!Loc)
return true;
diff --git a/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
index f4c7e5978e19..ae2bad7ee77c 100644
--- a/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
+++ b/clang/lib/StaticAnalyzer/Core/TextDiagnostics.cpp
@@ -34,20 +34,17 @@ namespace {
/// type to the standard error, or to to compliment many others. Emits detailed
/// diagnostics in textual format for the 'text' output type.
class TextDiagnostics : public PathDiagnosticConsumer {
+ PathDiagnosticConsumerOptions DiagOpts;
DiagnosticsEngine &DiagEng;
const LangOptions &LO;
- const bool IncludePath = false;
- const bool ShouldEmitAsError = false;
- const bool ApplyFixIts = false;
- const bool ShouldDisplayCheckerName = false;
+ bool ShouldDisplayPathNotes;
public:
- TextDiagnostics(DiagnosticsEngine &DiagEng, const LangOptions &LO,
- bool ShouldIncludePath, const AnalyzerOptions &AnOpts)
- : DiagEng(DiagEng), LO(LO), IncludePath(ShouldIncludePath),
- ShouldEmitAsError(AnOpts.AnalyzerWerror),
- ApplyFixIts(AnOpts.ShouldApplyFixIts),
- ShouldDisplayCheckerName(AnOpts.ShouldDisplayCheckerNameForText) {}
+ TextDiagnostics(PathDiagnosticConsumerOptions DiagOpts,
+ DiagnosticsEngine &DiagEng, const LangOptions &LO,
+ bool ShouldDisplayPathNotes)
+ : DiagOpts(std::move(DiagOpts)), DiagEng(DiagEng), LO(LO),
+ ShouldDisplayPathNotes(ShouldDisplayPathNotes) {}
~TextDiagnostics() override {}
StringRef getName() const override { return "TextDiagnostics"; }
@@ -56,13 +53,13 @@ public:
bool supportsCrossFileDiagnostics() const override { return true; }
PathGenerationScheme getGenerationScheme() const override {
- return IncludePath ? Minimal : None;
+ return ShouldDisplayPathNotes ? Minimal : None;
}
void FlushDiagnosticsImpl(std::vector<const PathDiagnostic *> &Diags,
FilesMade *filesMade) override {
unsigned WarnID =
- ShouldEmitAsError
+ DiagOpts.ShouldDisplayWarningsAsErrors
? DiagEng.getCustomDiagID(DiagnosticsEngine::Error, "%0")
: DiagEng.getCustomDiagID(DiagnosticsEngine::Warning, "%0");
unsigned NoteID = DiagEng.getCustomDiagID(DiagnosticsEngine::Note, "%0");
@@ -72,7 +69,7 @@ public:
auto reportPiece = [&](unsigned ID, FullSourceLoc Loc, StringRef String,
ArrayRef<SourceRange> Ranges,
ArrayRef<FixItHint> Fixits) {
- if (!ApplyFixIts) {
+ if (!DiagOpts.ShouldApplyFixIts) {
DiagEng.Report(Loc, ID) << String << Ranges << Fixits;
return;
}
@@ -92,9 +89,10 @@ public:
E = Diags.end();
I != E; ++I) {
const PathDiagnostic *PD = *I;
- std::string WarningMsg =
- (ShouldDisplayCheckerName ? " [" + PD->getCheckerName() + "]" : "")
- .str();
+ std::string WarningMsg = (DiagOpts.ShouldDisplayDiagnosticName
+ ? " [" + PD->getCheckerName() + "]"
+ : "")
+ .str();
reportPiece(WarnID, PD->getLocation().asLocation(),
(PD->getShortDescription() + WarningMsg).str(),
@@ -110,7 +108,7 @@ public:
Piece->getFixits());
}
- if (!IncludePath)
+ if (!ShouldDisplayPathNotes)
continue;
// Then, add the path notes if necessary.
@@ -125,7 +123,7 @@ public:
}
}
- if (!ApplyFixIts || Repls.empty())
+ if (Repls.empty())
return;
Rewriter Rewrite(SM, LO);
@@ -139,18 +137,19 @@ public:
} // end anonymous namespace
void ento::createTextPathDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &Prefix, const clang::Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
- C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(),
- /*ShouldIncludePath*/ true, AnalyzerOpts));
+ C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(),
+ PP.getLangOpts(),
+ /*ShouldDisplayPathNotes=*/true));
}
void ento::createTextMinimalPathDiagnosticConsumer(
- AnalyzerOptions &AnalyzerOpts, PathDiagnosticConsumers &C,
+ PathDiagnosticConsumerOptions DiagOpts, PathDiagnosticConsumers &C,
const std::string &Prefix, const clang::Preprocessor &PP,
const cross_tu::CrossTranslationUnitContext &CTU) {
- C.emplace_back(new TextDiagnostics(PP.getDiagnostics(), PP.getLangOpts(),
- /*ShouldIncludePath*/ false,
- AnalyzerOpts));
+ C.emplace_back(new TextDiagnostics(std::move(DiagOpts), PP.getDiagnostics(),
+ PP.getLangOpts(),
+ /*ShouldDisplayPathNotes=*/false));
}