Charusso created this revision.
Charusso added a reviewer: NoQ.
Charusso added a project: clang.
Herald added subscribers: cfe-commits, dkrupp, donat.nagy, Szelethus,
mikhail.ramalho, a.sidorin, szepet, baloghadamsoftware, xazax.hun.
The key is `CXXRecordDecl::isDerivedFrom()`.
Repository:
rC Clang
https://reviews.llvm.org/D67079
Files:
clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
clang/lib/StaticAnalyzer/Core/DynamicType.cpp
clang/test/Analysis/cast-value-hierarchy.cpp
clang/test/Analysis/cast-value-notes.cpp
clang/test/Analysis/cast-value-state-dump.cpp
clang/test/Analysis/expr-inspection.c
Index: clang/test/Analysis/expr-inspection.c
===================================================================
--- clang/test/Analysis/expr-inspection.c
+++ clang/test/Analysis/expr-inspection.c
@@ -38,7 +38,7 @@
// CHECK-NEXT: { "symbol": "reg_$0<int x>", "range": "{ [-2147483648, 13] }" }
// CHECK-NEXT: ],
// CHECK-NEXT: "dynamic_types": null,
-// CHECK-NEXT: "dynamic_casts": null,
+// CHECK-NEXT: "failed_casts": null,
// CHECK-NEXT: "constructing_objects": null,
// CHECK-NEXT: "checker_messages": null
// CHECK-NEXT: }
Index: clang/test/Analysis/cast-value-state-dump.cpp
===================================================================
--- clang/test/Analysis/cast-value-state-dump.cpp
+++ clang/test/Analysis/cast-value-state-dump.cpp
@@ -11,19 +11,31 @@
class Triangle : public Shape {};
class Circle : public Shape {};
class Square : public Shape {};
+class Octagram : public Shape {};
} // namespace clang
using namespace llvm;
using namespace clang;
void evalNonNullParamNonNullReturn(const Shape *S) {
+ if (isa<Triangle>(S)) {
+ // expected-note@-1 {{Assuming 'S' is not a 'Triangle'}}
+ // expected-note@-2 {{Taking false branch}}
+ return;
+ }
+
+ if (isa<Octagram>(S)) {
+ // expected-note@-1 {{Assuming 'S' is not an 'Octagram'}}
+ // expected-note@-2 {{Taking false branch}}
+ return;
+ }
+
const auto *C = dyn_cast_or_null<Circle>(S);
// expected-note@-1 {{Assuming 'S' is a 'Circle'}}
// expected-note@-2 {{'C' initialized here}}
- // FIXME: We assumed that 'S' is a 'Circle' therefore it is not a 'Square'.
if (dyn_cast_or_null<Square>(S)) {
- // expected-note@-1 {{Assuming 'S' is not a 'Square'}}
+ // expected-note@-1 {{'S' is not a 'Square'}}
// expected-note@-2 {{Taking false branch}}
return;
}
@@ -33,11 +45,11 @@
// CHECK: "dynamic_types": [
// CHECK-NEXT: { "region": "SymRegion{reg_$0<const struct clang::Shape * S>}", "dyn_type": "const class clang::Circle", "sub_classable": true }
// CHECK-NEXT: ],
- // CHECK-NEXT: "dynamic_casts": [
- // CHECK: { "region": "SymRegion{reg_$0<const struct clang::Shape * S>}", "casts": [
- // CHECK-NEXT: { "from": "const struct clang::Shape *", "to": "const class clang::Circle *", "kind": "success" },
- // CHECK-NEXT: { "from": "const struct clang::Shape *", "to": "const class clang::Square *", "kind": "fail" }
+ // CHECK-NEXT: "failed_casts": [
+ // CHECK-NEXT: { "region": "SymRegion{reg_$0<const struct clang::Shape * S>}", "casts": [
+ // CHECK-NEXT: "const class clang::Square *", "class clang::Triangle *", "class clang::Octagram *"
// CHECK-NEXT: ]}
+ // CHECK-NEXT: ],
(void)(1 / !C);
// expected-note@-1 {{'C' is non-null}}
Index: clang/test/Analysis/cast-value-notes.cpp
===================================================================
--- clang/test/Analysis/cast-value-notes.cpp
+++ clang/test/Analysis/cast-value-notes.cpp
@@ -37,12 +37,6 @@
return;
}
- if (dyn_cast_or_null<Triangle>(C)) {
- // expected-note@-1 {{Assuming 'C' is not a 'Triangle'}}
- // expected-note@-2 {{Taking false branch}}
- return;
- }
-
if (isa<Triangle>(C)) {
// expected-note@-1 {{'C' is not a 'Triangle'}}
// expected-note@-2 {{Taking false branch}}
@@ -65,14 +59,8 @@
// expected-note@-1 {{'S' is a 'Circle'}}
// expected-note@-2 {{'C' initialized here}}
- if (!isa<Triangle>(C)) {
- // expected-note@-1 {{Assuming 'C' is a 'Triangle'}}
- // expected-note@-2 {{Taking false branch}}
- return;
- }
-
- if (!isa<Triangle>(C)) {
- // expected-note@-1 {{'C' is a 'Triangle'}}
+ if (isa<Triangle>(C)) {
+ // expected-note@-1 {{'C' is not a 'Triangle'}}
// expected-note@-2 {{Taking false branch}}
return;
}
Index: clang/test/Analysis/cast-value-hierarchy.cpp
===================================================================
--- /dev/null
+++ clang/test/Analysis/cast-value-hierarchy.cpp
@@ -0,0 +1,54 @@
+// RUN: %clang_analyze_cc1 \
+// RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\
+// RUN: -verify %s
+
+#include "Inputs/llvm.h"
+
+using namespace llvm;
+
+void clang_analyzer_numTimesReached();
+void clang_analyzer_printState();
+void clang_analyzer_warnIfReached();
+void clang_analyzer_eval(bool);
+
+struct A {}; // A
+struct B : A {}; // / \.
+struct C : A {}; // B C
+struct D : B {}; // / \.
+struct E : B {}; // D E
+struct F : D, E {}; // \ / \.
+struct G : E {}; // F G
+
+void test_downcast(const A *a) {
+ const B *b = dyn_cast<B>(a);
+
+ if (dyn_cast<E>(b))
+ clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
+void test_isa_downcast(const A *a) {
+ if (isa<B>(a)) {
+ if (isa<E>(a))
+ clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+ }
+}
+
+void test_diamond(const A *a) {
+ if (isa<B>(a))
+ if (isa<D>(a) || isa<E>(a))
+ if (isa<F>(a))
+ clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
+void test_upcast(const A *a) {
+ const E *e = dyn_cast<E>(a);
+
+ if (dyn_cast<B>(e))
+ clang_analyzer_warnIfReached(); // no-warning
+}
+
+void test_isa_upcast(const A *a) {
+ if (isa<E>(a))
+ if (isa<B>(a))
+ clang_analyzer_warnIfReached(); // no-warning
+}
Index: clang/lib/StaticAnalyzer/Core/DynamicType.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Core/DynamicType.cpp
+++ clang/lib/StaticAnalyzer/Core/DynamicType.cpp
@@ -27,18 +27,22 @@
REGISTER_MAP_WITH_PROGRAMSTATE(DynamicTypeMap, const clang::ento::MemRegion *,
clang::ento::DynamicTypeInfo)
-/// A set factory of dynamic cast informations.
-REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicCastInfo)
+/// A set factory of dynamic cast informations as type informations.
+REGISTER_SET_FACTORY_WITH_PROGRAMSTATE(CastSet, clang::ento::DynamicTypeInfo)
-/// A map from symbols to cast informations.
-REGISTER_MAP_WITH_PROGRAMSTATE(DynamicCastMap, const clang::ento::MemRegion *,
+/// A map from symbols to failed cast informations.
+REGISTER_MAP_WITH_PROGRAMSTATE(FailedCastMap, const clang::ento::MemRegion *,
CastSet)
namespace clang {
namespace ento {
+const MemRegion *getSimplifiedRegion(const MemRegion *MR) {
+ return MR->StripCasts();
+}
+
DynamicTypeInfo getDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR) {
- MR = MR->StripCasts();
+ MR = getSimplifiedRegion(MR);
// Look up the dynamic type in the GDM.
if (const DynamicTypeInfo *DTI = State->get<DynamicTypeMap>(MR))
@@ -58,27 +62,40 @@
const DynamicTypeInfo *getRawDynamicTypeInfo(ProgramStateRef State,
const MemRegion *MR) {
+ if (!MR)
+ return nullptr;
+
+ MR = getSimplifiedRegion(MR);
return State->get<DynamicTypeMap>(MR);
}
-const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
- const MemRegion *MR,
- QualType CastFromTy,
- QualType CastToTy) {
- const auto *Lookup = State->get<DynamicCastMap>().lookup(MR);
+const DynamicTypeInfo *getFailedCastInfo(ProgramStateRef State,
+ const MemRegion *MR,
+ QualType CastToTy) {
+ if (!MR)
+ return nullptr;
+
+ MR = getSimplifiedRegion(MR);
+ const auto *Lookup = State->get<FailedCastMap>().lookup(MR);
if (!Lookup)
return nullptr;
- for (const DynamicCastInfo &Cast : *Lookup)
- if (Cast.equals(CastFromTy, CastToTy))
+ for (const DynamicTypeInfo &Cast : *Lookup) {
+ if (Cast.getType()->getPointeeCXXRecordDecl() ==
+ CastToTy->getPointeeCXXRecordDecl())
return &Cast;
+ }
return nullptr;
}
ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
DynamicTypeInfo NewTy) {
- State = State->set<DynamicTypeMap>(MR->StripCasts(), NewTy);
+ assert((NewTy.getType()->isAnyPointerType() ||
+ NewTy.getType()->isReferenceType()) &&
+ "DynamicTypeInfo should always be a pointer.");
+ MR = getSimplifiedRegion(MR);
+ State = State->set<DynamicTypeMap>(MR, NewTy);
assert(State);
return State;
}
@@ -90,29 +107,31 @@
ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
const MemRegion *MR,
- QualType CastFromTy,
QualType CastToTy,
+ const DynamicTypeInfo *CastInfo,
bool CastSucceeds) {
if (!MR)
return State;
- if (CastSucceeds) {
- assert((CastToTy->isAnyPointerType() || CastToTy->isReferenceType()) &&
- "DynamicTypeInfo should always be a pointer.");
- State = State->set<DynamicTypeMap>(MR, CastToTy);
- }
+ MR = getSimplifiedRegion(MR);
- DynamicCastInfo::CastResult ResultKind =
- CastSucceeds ? DynamicCastInfo::CastResult::Success
- : DynamicCastInfo::CastResult::Failure;
+ if (CastSucceeds) {
+ State = setDynamicTypeInfo(State, MR, CastToTy);
+ } else {
+ CastSet::Factory &F = State->get_context<CastSet>();
- CastSet::Factory &F = State->get_context<CastSet>();
+ const CastSet *TempSet = State->get<FailedCastMap>(MR);
+ CastSet Set = TempSet ? *TempSet : F.getEmptySet();
- const CastSet *TempSet = State->get<DynamicCastMap>(MR);
- CastSet Set = TempSet ? *TempSet : F.getEmptySet();
+ // If the 'CastToTy' is more derived remove the old cast information.
+ if (CastInfo && CastToTy->getPointeeCXXRecordDecl()->isDerivedFrom(
+ CastInfo->getType()->getPointeeCXXRecordDecl())) {
+ Set = F.remove(Set, *CastInfo);
+ }
- Set = F.add(Set, {CastFromTy, CastToTy, ResultKind});
- State = State->set<DynamicCastMap>(MR, Set);
+ Set = F.add(Set, CastToTy);
+ State = State->set<FailedCastMap>(MR, Set);
+ }
assert(State);
return State;
@@ -123,7 +142,7 @@
SymbolReaper &SR) {
for (const auto &Elem : Map)
if (!SR.isLiveRegion(Elem.first))
- State = State->remove<DynamicCastMap>(Elem.first);
+ State = State->remove<FailedCastMap>(Elem.first);
return State;
}
@@ -133,7 +152,7 @@
}
ProgramStateRef removeDeadCasts(ProgramStateRef State, SymbolReaper &SR) {
- return removeDead(State, State->get<DynamicCastMap>(), SR);
+ return removeDead(State, State->get<FailedCastMap>(), SR);
}
static void printDynamicTypesJson(raw_ostream &Out, ProgramStateRef State,
@@ -175,9 +194,9 @@
static void printDynamicCastsJson(raw_ostream &Out, ProgramStateRef State,
const char *NL, unsigned int Space,
bool IsDot) {
- Indent(Out, Space, IsDot) << "\"dynamic_casts\": ";
+ Indent(Out, Space, IsDot) << "\"failed_casts\": ";
- const DynamicCastMapTy &Map = State->get<DynamicCastMap>();
+ const FailedCastMapTy &Map = State->get<FailedCastMap>();
if (Map.isEmpty()) {
Out << "null," << NL;
return;
@@ -185,7 +204,7 @@
++Space;
Out << '[' << NL;
- for (DynamicCastMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
+ for (FailedCastMapTy::iterator I = Map.begin(); I != Map.end(); ++I) {
const MemRegion *MR = I->first;
const CastSet &Set = I->second;
@@ -195,16 +214,14 @@
} else {
++Space;
Out << '[' << NL;
+ Indent(Out, Space, IsDot);
for (CastSet::iterator SI = Set.begin(); SI != Set.end(); ++SI) {
- Indent(Out, Space, IsDot)
- << "{ \"from\": \"" << SI->from().getAsString() << "\", \"to\": \""
- << SI->to().getAsString() << "\", \"kind\": \""
- << (SI->succeeds() ? "success" : "fail") << "\" }";
+ Out << '\"' << SI->getType().getAsString() << '\"';
if (std::next(SI) != Set.end())
- Out << ',';
- Out << NL;
+ Out << ", ";
}
+ Out << NL;
--Space;
Indent(Out, Space, IsDot) << ']';
}
Index: clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
===================================================================
--- clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -8,12 +8,6 @@
//
// This defines CastValueChecker which models casts of custom RTTIs.
//
-// TODO list:
-// - It only allows one succesful cast between two types however in the wild
-// the object could be casted to multiple types.
-// - It needs to check the most likely type information from the dynamic type
-// map to increase precision of dynamic casting.
-//
//===----------------------------------------------------------------------===//
#include "clang/AST/DeclTemplate.h"
@@ -92,21 +86,103 @@
};
} // namespace
-static bool isInfeasibleCast(const DynamicCastInfo *CastInfo,
+static bool isDerivedFrom(QualType X, QualType Y) {
+ const CXXRecordDecl *XRD = X->isPointerType() ? X->getPointeeCXXRecordDecl()
+ : X->getAsCXXRecordDecl();
+ const CXXRecordDecl *YRD = Y->isPointerType() ? Y->getPointeeCXXRecordDecl()
+ : Y->getAsCXXRecordDecl();
+ if (!XRD || !YRD)
+ return true;
+
+ return XRD->isDerivedFrom(YRD);
+}
+
+static bool isInfeasibleCast(const DynamicTypeInfo *SucceededCastInfo,
+ const DynamicTypeInfo *FailedCastInfo,
+ QualType CastFromTy, QualType CastToTy,
bool CastSucceeds) {
- if (!CastInfo)
- return false;
+ // Load the proper dynamic cast information.
+ if (CastSucceeds && SucceededCastInfo)
+ CastFromTy = SucceededCastInfo->getType();
+ else if (!CastSucceeds && FailedCastInfo)
+ CastFromTy = FailedCastInfo->getType();
+
+ // Upcast is infeasible.
+ if (CastSucceeds && isDerivedFrom(CastFromTy, CastToTy))
+ return true;
- return CastSucceeds ? CastInfo->fails() : CastInfo->succeeds();
+ return false;
}
-static const NoteTag *getNoteTag(CheckerContext &C,
- const DynamicCastInfo *CastInfo,
- QualType CastToTy, const Expr *Object,
- bool CastSucceeds, bool IsKnownCast) {
+static bool isSucceededDowncast(const DynamicTypeInfo *CastInfo,
+ QualType CastToTy) {
+ const CXXRecordDecl *CurrentRD = CastToTy->getPointeeCXXRecordDecl();
+ const CXXRecordDecl *PreviousRD =
+ CastInfo->getType()->getPointeeCXXRecordDecl();
+
+ if (CurrentRD == PreviousRD)
+ return true;
+
+ // If the casts have a common anchestor it could not be a succeeded downcast.
+ for (const auto &PreviousBase : PreviousRD->bases())
+ for (const auto &CurrentBase : CurrentRD->bases())
+ if (PreviousBase.getType()->getAsCXXRecordDecl() ==
+ CurrentBase.getType()->getAsCXXRecordDecl())
+ return false;
+
+ return true;
+}
+
+namespace {
+struct CastContext {
+ CastContext(ProgramStateRef State, const MemRegion *MR, QualType CastFromTy,
+ QualType CastToTy, bool Assumption, bool IsCheckedCast) {
+ const DynamicTypeInfo *SucceededCastInfo = getRawDynamicTypeInfo(State, MR);
+ const DynamicTypeInfo *FailedCastInfo =
+ getFailedCastInfo(State, MR, CastToTy);
+
+ // We assume that every checked cast succeeds.
+ bool IsAlwaysSucceeds =
+ IsCheckedCast || CastFromTy->getPointeeCXXRecordDecl() ==
+ CastToTy->getPointeeCXXRecordDecl();
+
+ CastSucceeds = Assumption || IsAlwaysSucceeds;
+
+ // We need to check if the current new succeeded cast is a new downcast.
+ IsKnownCast = true;
+ if (CastSucceeds && SucceededCastInfo && !IsAlwaysSucceeds) {
+ CastSucceeds = isSucceededDowncast(SucceededCastInfo, CastToTy);
+ if (CastSucceeds) // A succeeded downcast is a new assumption.
+ IsKnownCast = false;
+ }
+
+ if (IsKnownCast)
+ IsKnownCast = IsAlwaysSucceeds || SucceededCastInfo ||
+ (!CastSucceeds && FailedCastInfo);
+
+ CastInfo = CastSucceeds ? SucceededCastInfo : FailedCastInfo;
+
+ IsInfeasible = isInfeasibleCast(SucceededCastInfo, FailedCastInfo,
+ CastFromTy, CastToTy, CastSucceeds);
+ }
+
+ bool IsKnownCast, CastSucceeds, IsInfeasible;
+ const DynamicTypeInfo *CastInfo;
+};
+} // namespace
+
+constexpr llvm::StringLiteral Vowels = "aeiou";
+
+static const NoteTag *getNoteTag(CheckerContext &C, const Expr *Object,
+ QualType CastToTy, CastContext &CastCtx) {
+ QualType KnownCastToTy;
+ if (CastCtx.CastInfo)
+ KnownCastToTy = CastCtx.CastInfo->getType();
+
std::string CastToName =
- CastInfo ? CastInfo->to()->getPointeeCXXRecordDecl()->getNameAsString()
- : CastToTy->getPointeeCXXRecordDecl()->getNameAsString();
+ CastCtx.CastInfo && CastCtx.IsKnownCast
+ ? KnownCastToTy->getPointeeCXXRecordDecl()->getNameAsString()
+ : CastToTy->getPointeeCXXRecordDecl()->getNameAsString();
Object = Object->IgnoreParenImpCasts();
return C.getNoteTag(
@@ -114,24 +190,25 @@
SmallString<128> Msg;
llvm::raw_svector_ostream Out(Msg);
- if (!IsKnownCast)
+ if (!CastCtx.IsKnownCast)
Out << "Assuming ";
if (const auto *DRE = dyn_cast<DeclRefExpr>(Object)) {
Out << '\'' << DRE->getDecl()->getNameAsString() << '\'';
} else if (const auto *ME = dyn_cast<MemberExpr>(Object)) {
- Out << (IsKnownCast ? "Field '" : "field '")
+ Out << (CastCtx.IsKnownCast ? "Field '" : "field '")
<< ME->getMemberDecl()->getNameAsString() << '\'';
} else {
- Out << (IsKnownCast ? "The object" : "the object");
+ Out << (CastCtx.IsKnownCast ? "The object" : "the object");
}
- Out << ' ' << (CastSucceeds ? "is a" : "is not a") << " '" << CastToName
- << '\'';
+ Out << ' ' << (CastCtx.CastSucceeds ? "is " : "is not ")
+ << (Vowels.contains_lower(CastToName[0]) ? "an" : "a") << " '"
+ << CastToName << '\'';
return Out.str();
},
- /*IsPrunable=*/true);
+ /*IsPrunable=*/!CastCtx.CastSucceeds);
}
//===----------------------------------------------------------------------===//
@@ -157,12 +234,14 @@
bool IsNonNullReturn,
bool IsCheckedCast = false) {
ProgramStateRef State = C.getState()->assume(DV, IsNonNullParam);
- if (!State)
+ if (!State) {
+ C.generateSink(C.getState(), C.getPredecessor());
return;
+ }
const Expr *Object;
QualType CastFromTy;
- QualType CastToTy = Call.getResultType();
+ QualType CastToTy = Call.getResultType().getCanonicalType();
if (Call.getNumArgs() > 0) {
Object = Call.getArgExpr(0);
@@ -182,35 +261,24 @@
}
const MemRegion *MR = DV.getAsRegion();
- const DynamicCastInfo *CastInfo =
- getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
-
- // We assume that every checked cast succeeds.
- bool CastSucceeds = IsCheckedCast || CastFromTy == CastToTy;
- if (!CastSucceeds) {
- if (CastInfo)
- CastSucceeds = IsNonNullReturn && CastInfo->succeeds();
- else
- CastSucceeds = IsNonNullReturn;
- }
+ CastContext CastCtx(State, MR, CastFromTy, CastToTy,
+ /*Assumption=*/IsNonNullReturn, IsCheckedCast);
- // Check for infeasible casts.
- if (isInfeasibleCast(CastInfo, CastSucceeds)) {
+ if (CastCtx.IsInfeasible) {
C.generateSink(State, C.getPredecessor());
return;
}
- // Store the type and the cast information.
- bool IsKnownCast = CastInfo || IsCheckedCast || CastFromTy == CastToTy;
- if (!IsKnownCast || IsCheckedCast)
- State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
- CastSucceeds);
+ State = setDynamicTypeAndCastInfo(State, MR, CastToTy, CastCtx.CastInfo,
+ CastCtx.CastSucceeds);
+
+ SVal V = CastCtx.CastSucceeds
+ ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)
+ : C.getSValBuilder().makeNull();
- SVal V = CastSucceeds ? C.getSValBuilder().evalCast(DV, CastToTy, CastFromTy)
- : C.getSValBuilder().makeNull();
C.addTransition(
State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), V, false),
- getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
+ getNoteTag(C, Object, CastToTy, CastCtx));
}
static void addInstanceOfTransition(const CallEvent &Call,
@@ -220,6 +288,7 @@
const FunctionDecl *FD = Call.getDecl()->getAsFunction();
QualType CastFromTy = Call.parameters()[0]->getType();
QualType CastToTy = FD->getTemplateSpecializationArgs()->get(0).getAsType();
+
if (CastFromTy->isPointerType())
CastToTy = C.getASTContext().getPointerType(CastToTy);
else if (CastFromTy->isReferenceType())
@@ -228,31 +297,21 @@
return;
const MemRegion *MR = DV.getAsRegion();
- const DynamicCastInfo *CastInfo =
- getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
-
- bool CastSucceeds;
- if (CastInfo)
- CastSucceeds = IsInstanceOf && CastInfo->succeeds();
- else
- CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;
+ CastContext CastCtx(State, MR, CastFromTy, CastToTy,
+ /*Assumption=*/IsInstanceOf, /*IsCheckedCast=*/false);
- if (isInfeasibleCast(CastInfo, CastSucceeds)) {
+ if (CastCtx.IsInfeasible) {
C.generateSink(State, C.getPredecessor());
return;
}
- // Store the type and the cast information.
- bool IsKnownCast = CastInfo || CastFromTy == CastToTy;
- if (!IsKnownCast)
- State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
- IsInstanceOf);
+ State = setDynamicTypeAndCastInfo(State, MR, CastToTy, CastCtx.CastInfo,
+ CastCtx.CastSucceeds);
C.addTransition(
State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
- C.getSValBuilder().makeTruthVal(CastSucceeds)),
- getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), CastSucceeds,
- IsKnownCast));
+ C.getSValBuilder().makeTruthVal(CastCtx.CastSucceeds)),
+ getNoteTag(C, Call.getArgExpr(0), CastToTy, CastCtx));
}
//===----------------------------------------------------------------------===//
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h
@@ -18,32 +18,33 @@
/// of a region in a given state along the analysis path.
class DynamicTypeInfo {
public:
- DynamicTypeInfo() : DynTy(QualType()) {}
+ DynamicTypeInfo() : Ty(QualType()) {}
- DynamicTypeInfo(QualType Ty, bool CanBeSub = true)
- : DynTy(Ty), CanBeASubClass(CanBeSub) {}
+ DynamicTypeInfo(QualType ty, bool CanBeSub = true)
+ : Ty(ty), CanBeASubClass(CanBeSub) {}
/// Returns false if the type information is precise (the type 'DynTy' is
/// the only type in the lattice), true otherwise.
bool canBeASubClass() const { return CanBeASubClass; }
/// Returns true if the dynamic type info is available.
- bool isValid() const { return !DynTy.isNull(); }
+ bool isValid() const { return !Ty.isNull(); }
/// Returns the currently inferred upper bound on the runtime type.
- QualType getType() const { return DynTy; }
+ QualType getType() const { return Ty; }
bool operator==(const DynamicTypeInfo &RHS) const {
- return DynTy == RHS.DynTy && CanBeASubClass == RHS.CanBeASubClass;
+ return Ty == RHS.Ty && CanBeASubClass == RHS.CanBeASubClass;
}
+ bool operator<(const DynamicTypeInfo &RHS) const { return Ty < RHS.Ty; }
void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.Add(DynTy);
+ ID.Add(Ty);
ID.AddBoolean(CanBeASubClass);
}
private:
- QualType DynTy;
+ QualType Ty;
bool CanBeASubClass;
};
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
+++ clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicType.h
@@ -16,7 +16,6 @@
#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICTYPE_H
#include "clang/AST/Type.h"
-#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/DynamicTypeInfo.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramStateTrait.h"
@@ -37,10 +36,9 @@
const MemRegion *MR);
/// Get dynamic cast information from \p CastFromTy to \p CastToTy of \p MR.
-const DynamicCastInfo *getDynamicCastInfo(ProgramStateRef State,
- const MemRegion *MR,
- QualType CastFromTy,
- QualType CastToTy);
+const DynamicTypeInfo *getFailedCastInfo(ProgramStateRef State,
+ const MemRegion *MR,
+ QualType CastToTy);
/// Set dynamic type information of the region; return the new state.
ProgramStateRef setDynamicTypeInfo(ProgramStateRef State, const MemRegion *MR,
@@ -53,9 +51,9 @@
/// Set dynamic type and cast information of the region; return the new state.
ProgramStateRef setDynamicTypeAndCastInfo(ProgramStateRef State,
const MemRegion *MR,
- QualType CastFromTy,
QualType CastToTy,
- bool IsCastSucceeds);
+ const DynamicTypeInfo *CastInfo,
+ bool CastSucceeds);
/// Removes the dead type informations from \p State.
ProgramStateRef removeDeadTypes(ProgramStateRef State, SymbolReaper &SR);
Index: clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h
===================================================================
--- clang/include/clang/StaticAnalyzer/Core/PathSensitive/DynamicCastInfo.h
+++ /dev/null
@@ -1,55 +0,0 @@
-//===- DynamicCastInfo.h - Runtime cast information -------------*- 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
-//
-//===----------------------------------------------------------------------===//
-
-#ifndef LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H
-#define LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H
-
-#include "clang/AST/Type.h"
-
-namespace clang {
-namespace ento {
-
-class DynamicCastInfo {
-public:
- enum CastResult { Success, Failure };
-
- DynamicCastInfo(QualType from, QualType to, CastResult resultKind)
- : From(from), To(to), ResultKind(resultKind) {}
-
- QualType from() const { return From; }
- QualType to() const { return To; }
-
- bool equals(QualType from, QualType to) const {
- return From == from && To == to;
- }
-
- bool succeeds() const { return ResultKind == CastResult::Success; }
- bool fails() const { return ResultKind == CastResult::Failure; }
-
- bool operator==(const DynamicCastInfo &RHS) const {
- return From == RHS.From && To == RHS.To;
- }
- bool operator<(const DynamicCastInfo &RHS) const {
- return From < RHS.From && To < RHS.To;
- }
-
- void Profile(llvm::FoldingSetNodeID &ID) const {
- ID.Add(From);
- ID.Add(To);
- ID.AddInteger(ResultKind);
- }
-
-private:
- QualType From, To;
- CastResult ResultKind;
-};
-
-} // namespace ento
-} // namespace clang
-
-#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_DYNAMICCASTINFO_H
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits