[llvm-branch-commits] [clang] 22bce84 - [analyzer] pr47037: CastValueChecker: Support for the new variadic isa<>.
Hans Wennborg via llvm-branch-commits
llvm-branch-commits at lists.llvm.org
Fri Aug 28 02:34:38 PDT 2020
Author: Adam Balogh
Date: 2020-08-28T11:32:08+02:00
New Revision: 22bce848a0b27b5ce7ef1e086054522a39c70651
URL: https://github.com/llvm/llvm-project/commit/22bce848a0b27b5ce7ef1e086054522a39c70651
DIFF: https://github.com/llvm/llvm-project/commit/22bce848a0b27b5ce7ef1e086054522a39c70651.diff
LOG: [analyzer] pr47037: CastValueChecker: Support for the new variadic isa<>.
llvm::isa<>() and llvm::isa_and_not_null<>() template functions recently became
variadic. Unfortunately this causes crashes in case of isa_and_not_null<>()
and incorrect behavior in isa<>(). This patch fixes this issue.
Differential Revision: https://reviews.llvm.org/D85728
(cherry picked from commit 4448affede5100658530aea8793ae7a7bc05a110)
Added:
Modified:
clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
clang/test/Analysis/Inputs/llvm.h
clang/test/Analysis/cast-value-logic.cpp
clang/test/Analysis/cast-value-notes.cpp
Removed:
################################################################################
diff --git a/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
index 3d1721f04875..528f68c6c429 100644
--- a/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
+++ b/clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp
@@ -135,6 +135,47 @@ static const NoteTag *getNoteTag(CheckerContext &C,
/*IsPrunable=*/true);
}
+static const NoteTag *getNoteTag(CheckerContext &C,
+ SmallVector<QualType, 4> CastToTyVec,
+ const Expr *Object,
+ bool IsKnownCast) {
+ Object = Object->IgnoreParenImpCasts();
+
+ return C.getNoteTag(
+ [=]() -> std::string {
+ SmallString<128> Msg;
+ llvm::raw_svector_ostream Out(Msg);
+
+ if (!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 '")
+ << ME->getMemberDecl()->getNameAsString() << '\'';
+ } else {
+ Out << (IsKnownCast ? "The object" : "the object");
+ }
+ Out << " is";
+
+ bool First = true;
+ for (QualType CastToTy: CastToTyVec) {
+ std::string CastToName =
+ CastToTy->getAsCXXRecordDecl() ?
+ CastToTy->getAsCXXRecordDecl()->getNameAsString() :
+ CastToTy->getPointeeCXXRecordDecl()->getNameAsString();
+ Out << ' ' << ((CastToTyVec.size() == 1) ? "not" :
+ (First ? "neither" : "nor")) << " a '" << CastToName
+ << '\'';
+ First = false;
+ }
+
+ return std::string(Out.str());
+ },
+ /*IsPrunable=*/true);
+}
+
//===----------------------------------------------------------------------===//
// Main logic to evaluate a cast.
//===----------------------------------------------------------------------===//
@@ -220,40 +261,76 @@ static void addInstanceOfTransition(const CallEvent &Call,
bool IsInstanceOf) {
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())
- CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext());
- else
- return;
+ SmallVector<QualType, 4> CastToTyVec;
+ for (unsigned idx = 0; idx < FD->getTemplateSpecializationArgs()->size() - 1;
+ ++idx) {
+ TemplateArgument CastToTempArg =
+ FD->getTemplateSpecializationArgs()->get(idx);
+ switch (CastToTempArg.getKind()) {
+ default:
+ return;
+ case TemplateArgument::Type:
+ CastToTyVec.push_back(CastToTempArg.getAsType());
+ break;
+ case TemplateArgument::Pack:
+ for (TemplateArgument ArgInPack: CastToTempArg.pack_elements())
+ CastToTyVec.push_back(ArgInPack.getAsType());
+ break;
+ }
+ }
const MemRegion *MR = DV.getAsRegion();
- const DynamicCastInfo *CastInfo =
- getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
+ if (MR && CastFromTy->isReferenceType())
+ MR = State->getSVal(DV.castAs<Loc>()).getAsRegion();
+
+ bool Success = false;
+ bool IsAnyKnown = false;
+ for (QualType CastToTy: CastToTyVec) {
+ if (CastFromTy->isPointerType())
+ CastToTy = C.getASTContext().getPointerType(CastToTy);
+ else if (CastFromTy->isReferenceType())
+ CastToTy = alignReferenceTypes(CastToTy, CastFromTy, C.getASTContext());
+ else
+ return;
- bool CastSucceeds;
- if (CastInfo)
- CastSucceeds = IsInstanceOf && CastInfo->succeeds();
- else
- CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;
+ const DynamicCastInfo *CastInfo =
+ getDynamicCastInfo(State, MR, CastFromTy, CastToTy);
- if (isInfeasibleCast(CastInfo, CastSucceeds)) {
- C.generateSink(State, C.getPredecessor());
- return;
+ bool CastSucceeds;
+ if (CastInfo)
+ CastSucceeds = IsInstanceOf && CastInfo->succeeds();
+ else
+ CastSucceeds = IsInstanceOf || CastFromTy == CastToTy;
+
+ // Store the type and the cast information.
+ bool IsKnownCast = CastInfo || CastFromTy == CastToTy;
+ IsAnyKnown = IsAnyKnown || IsKnownCast;
+ ProgramStateRef NewState = State;
+ if (!IsKnownCast)
+ NewState = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
+ IsInstanceOf);
+
+ if (CastSucceeds) {
+ Success = true;
+ C.addTransition(
+ NewState->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
+ C.getSValBuilder().makeTruthVal(true)),
+ getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), true,
+ IsKnownCast));
+ if (IsKnownCast)
+ return;
+ } else if (CastInfo && CastInfo->succeeds()) {
+ C.generateSink(NewState, C.getPredecessor());
+ return;
+ }
}
- // Store the type and the cast information.
- bool IsKnownCast = CastInfo || CastFromTy == CastToTy;
- if (!IsKnownCast)
- State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy,
- IsInstanceOf);
-
- C.addTransition(
- State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
- C.getSValBuilder().makeTruthVal(CastSucceeds)),
- getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), CastSucceeds,
- IsKnownCast));
+ if (!Success) {
+ C.addTransition(
+ State->BindExpr(Call.getOriginExpr(), C.getLocationContext(),
+ C.getSValBuilder().makeTruthVal(false)),
+ getNoteTag(C, CastToTyVec, Call.getArgExpr(0), IsAnyKnown));
+ }
}
//===----------------------------------------------------------------------===//
@@ -402,8 +479,9 @@ bool CastValueChecker::evalCall(const CallEvent &Call,
QualType ParamT = Call.parameters()[0]->getType();
QualType ResultT = Call.getResultType();
if (!(ParamT->isPointerType() && ResultT->isPointerType()) &&
- !(ParamT->isReferenceType() && ResultT->isReferenceType()))
+ !(ParamT->isReferenceType() && ResultT->isReferenceType())) {
return false;
+ }
DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
break;
diff --git a/clang/test/Analysis/Inputs/llvm.h b/clang/test/Analysis/Inputs/llvm.h
index c9d66ba2374d..b80567bcb586 100644
--- a/clang/test/Analysis/Inputs/llvm.h
+++ b/clang/test/Analysis/Inputs/llvm.h
@@ -19,11 +19,19 @@ const X *dyn_cast_or_null(Y *Value);
template <class X, class Y>
const X *dyn_cast_or_null(Y &Value);
-template <class X, class Y>
-bool isa(Y Value);
-
-template <class X, class Y>
-bool isa_and_nonnull(Y Value);
+template <class X, class Y> inline bool isa(const Y &Val);
+
+template <typename First, typename Second, typename... Rest, typename Y>
+inline bool isa(const Y &Val) {
+ return isa<First>(Val) || isa<Second, Rest...>(Val);
+}
+
+template <typename... X, class Y>
+inline bool isa_and_nonnull(const Y &Val) {
+ if (!Val)
+ return false;
+ return isa<X...>(Val);
+}
template <typename X, typename Y>
std::unique_ptr<X> cast(std::unique_ptr<Y> &&Value);
diff --git a/clang/test/Analysis/cast-value-logic.cpp b/clang/test/Analysis/cast-value-logic.cpp
index 1411ede92e36..52a94f24fba6 100644
--- a/clang/test/Analysis/cast-value-logic.cpp
+++ b/clang/test/Analysis/cast-value-logic.cpp
@@ -19,6 +19,8 @@ struct Shape {
virtual double area();
};
class Triangle : public Shape {};
+class Rectangle : public Shape {};
+class Hexagon : public Shape {};
class Circle : public Shape {
public:
~Circle();
@@ -39,6 +41,23 @@ void test_regions_isa(const Shape *A, const Shape *B) {
clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
}
+void test_regions_isa_variadic(const Shape *A, const Shape *B) {
+ if (isa<Triangle, Rectangle, Hexagon>(A) &&
+ !isa<Rectangle, Hexagon, Circle>(B))
+ clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
+void test_regions_isa_and_nonnull(const Shape *A, const Shape *B) {
+ if (isa_and_nonnull<Circle>(A) && !isa_and_nonnull<Circle>(B))
+ clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
+void test_regions_isa_and_nonnull_variadic(const Shape *A, const Shape *B) {
+ if (isa_and_nonnull<Triangle, Rectangle, Hexagon>(A) &&
+ !isa_and_nonnull<Rectangle, Hexagon, Circle>(B))
+ clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
+}
+
namespace test_cast {
void evalLogic(const Shape *S) {
const Circle *C = cast<Circle>(S);
diff --git a/clang/test/Analysis/cast-value-notes.cpp b/clang/test/Analysis/cast-value-notes.cpp
index eb5d1b3d3fe2..a09586309fb4 100644
--- a/clang/test/Analysis/cast-value-notes.cpp
+++ b/clang/test/Analysis/cast-value-notes.cpp
@@ -13,6 +13,8 @@ struct Shape {
const T *getAs() const;
};
class Triangle : public Shape {};
+class Rectangle : public Shape {};
+class Hexagon : public Shape {};
class Circle : public Shape {};
} // namespace clang
@@ -27,7 +29,6 @@ void evalReferences(const Shape &S) {
}
void evalNonNullParamNonNullReturnReference(const Shape &S) {
- // Unmodeled cast from reference to pointer.
const auto *C = dyn_cast_or_null<Circle>(S);
// expected-note at -1 {{'C' initialized here}}
@@ -43,13 +44,37 @@ void evalNonNullParamNonNullReturnReference(const Shape &S) {
return;
}
+ if (dyn_cast_or_null<Rectangle>(C)) {
+ // expected-note at -1 {{Assuming 'C' is not a 'Rectangle'}}
+ // expected-note at -2 {{Taking false branch}}
+ return;
+ }
+
+ if (dyn_cast_or_null<Hexagon>(C)) {
+ // expected-note at -1 {{Assuming 'C' is not a 'Hexagon'}}
+ // expected-note at -2 {{Taking false branch}}
+ return;
+ }
+
if (isa<Triangle>(C)) {
// expected-note at -1 {{'C' is not a 'Triangle'}}
// expected-note at -2 {{Taking false branch}}
return;
}
- if (isa<Circle>(C)) {
+ if (isa<Triangle, Rectangle>(C)) {
+ // expected-note at -1 {{'C' is neither a 'Triangle' nor a 'Rectangle'}}
+ // expected-note at -2 {{Taking false branch}}
+ return;
+ }
+
+ if (isa<Triangle, Rectangle, Hexagon>(C)) {
+ // expected-note at -1 {{'C' is neither a 'Triangle' nor a 'Rectangle' nor a 'Hexagon'}}
+ // expected-note at -2 {{Taking false branch}}
+ return;
+ }
+
+ if (isa<Circle, Rectangle, Hexagon>(C)) {
// expected-note at -1 {{'C' is a 'Circle'}}
// expected-note at -2 {{Taking true branch}}
@@ -65,22 +90,57 @@ void evalNonNullParamNonNullReturn(const Shape *S) {
// expected-note at -1 {{'S' is a 'Circle'}}
// expected-note at -2 {{'C' initialized here}}
- if (!isa<Triangle>(C)) {
- // expected-note at -1 {{Assuming 'C' is a 'Triangle'}}
+ if (!dyn_cast_or_null<Circle>(C)) {
+ // expected-note at -1 {{'C' is a 'Circle'}}
// expected-note at -2 {{Taking false branch}}
return;
}
- if (!isa<Triangle>(C)) {
- // expected-note at -1 {{'C' is a 'Triangle'}}
+ if (dyn_cast_or_null<Triangle>(C)) {
+ // expected-note at -1 {{Assuming 'C' is not a 'Triangle'}}
// expected-note at -2 {{Taking false branch}}
return;
}
- (void)(1 / !C);
- // expected-note at -1 {{'C' is non-null}}
- // expected-note at -2 {{Division by zero}}
- // expected-warning at -3 {{Division by zero}}
+ if (dyn_cast_or_null<Rectangle>(C)) {
+ // expected-note at -1 {{Assuming 'C' is not a 'Rectangle'}}
+ // expected-note at -2 {{Taking false branch}}
+ return;
+ }
+
+ if (dyn_cast_or_null<Hexagon>(C)) {
+ // expected-note at -1 {{Assuming 'C' is not a 'Hexagon'}}
+ // expected-note at -2 {{Taking false branch}}
+ return;
+ }
+
+ if (isa<Triangle>(C)) {
+ // expected-note at -1 {{'C' is not a 'Triangle'}}
+ // expected-note at -2 {{Taking false branch}}
+ return;
+ }
+
+ if (isa<Triangle, Rectangle>(C)) {
+ // expected-note at -1 {{'C' is neither a 'Triangle' nor a 'Rectangle'}}
+ // expected-note at -2 {{Taking false branch}}
+ return;
+ }
+
+ if (isa<Triangle, Rectangle, Hexagon>(C)) {
+ // expected-note at -1 {{'C' is neither a 'Triangle' nor a 'Rectangle' nor a 'Hexagon'}}
+ // expected-note at -2 {{Taking false branch}}
+ return;
+ }
+
+ if (isa<Circle, Rectangle, Hexagon>(C)) {
+ // expected-note at -1 {{'C' is a 'Circle'}}
+ // expected-note at -2 {{Taking true branch}}
+
+ (void)(1 / !C);
+ // expected-note at -1 {{'C' is non-null}}
+ // expected-note at -2 {{Division by zero}}
+ // expected-warning at -3 {{Division by zero}}
+ }
}
void evalNonNullParamNullReturn(const Shape *S) {
More information about the llvm-branch-commits
mailing list