From 3a0426ea559d704305b6d1706341ad353b333d91 Mon Sep 17 00:00:00 2001 From: Csaba Dabis Date: Thu, 22 Aug 2019 02:57:59 +0000 Subject: [PATCH] [analyzer] CastValueChecker: Model isa(), isa_and_nonnull() Summary: - Reviewed By: NoQ Differential Revision: https://reviews.llvm.org/D66423 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@369615 91177308-0d34-0410-b5e6-96231b3b80d8 --- .../Checkers/CastValueChecker.cpp | 105 +++++++++++++++++- test/Analysis/Inputs/llvm.h | 6 + test/Analysis/cast-value-logic.cpp | 7 +- test/Analysis/cast-value-notes.cpp | 25 +++-- 4 files changed, 130 insertions(+), 13 deletions(-) diff --git a/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp b/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp index 1580f7f31c..5c4daa3e42 100644 --- a/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp +++ b/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp @@ -16,6 +16,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/DeclTemplate.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -30,7 +31,7 @@ using namespace ento; namespace { class CastValueChecker : public Checker { - enum class CallKind { Function, Method }; + enum class CallKind { Function, Method, InstanceOf }; using CastCheck = std::functiongetAsFunction(); + QualType CastToTy = FD->getTemplateSpecializationArgs()->get(0).getAsType(); + QualType CastFromTy = getRecordType(Call.parameters()[0]->getType()); + + 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; + + if (isInfeasibleCast(CastInfo, CastSucceeds)) { + 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, + Call.getResultType(), IsInstanceOf); + + C.addTransition( + State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeTruthVal(CastSucceeds)), + getNoteTag(C, CastInfo, CastToTy, Call.getArgExpr(0), CastSucceeds, + IsKnownCast)); +} + //===----------------------------------------------------------------------===// // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null. //===----------------------------------------------------------------------===// @@ -277,6 +326,41 @@ void CastValueChecker::evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV, evalZeroParamNullReturn(Call, DV, C); } +//===----------------------------------------------------------------------===// +// Evaluating isa, isa_and_nonnull. +//===----------------------------------------------------------------------===// + +void CastValueChecker::evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV, + CheckerContext &C) const { + ProgramStateRef NonNullState, NullState; + std::tie(NonNullState, NullState) = C.getState()->assume(DV); + + if (NonNullState) { + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); + } + + if (NullState) { + C.generateSink(NullState, C.getPredecessor()); + } +} + +void CastValueChecker::evalIsaAndNonNull(const CallEvent &Call, + DefinedOrUnknownSVal DV, + CheckerContext &C) const { + ProgramStateRef NonNullState, NullState; + std::tie(NonNullState, NullState) = C.getState()->assume(DV); + + if (NonNullState) { + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/true); + addInstanceOfTransition(Call, DV, NonNullState, C, /*IsInstanceOf=*/false); + } + + if (NullState) { + addInstanceOfTransition(Call, DV, NullState, C, /*IsInstanceOf=*/false); + } +} + //===----------------------------------------------------------------------===// // Main logic to evaluate a call. //===----------------------------------------------------------------------===// @@ -287,12 +371,14 @@ bool CastValueChecker::evalCall(const CallEvent &Call, if (!Lookup) return false; + const CastCheck &Check = Lookup->first; + CallKind Kind = Lookup->second; + // We need to obtain the record type of the call's result to model it. - if (!getRecordType(Call.getResultType())->isRecordType()) + if (Kind != CallKind::InstanceOf && + !getRecordType(Call.getResultType())->isRecordType()) return false; - const CastCheck &Check = Lookup->first; - CallKind Kind = Lookup->second; Optional DV; switch (Kind) { @@ -304,6 +390,15 @@ bool CastValueChecker::evalCall(const CallEvent &Call, DV = Call.getArgSVal(0).getAs(); break; } + case CallKind::InstanceOf: { + // We need to obtain the only template argument to determinte the type. + const FunctionDecl *FD = Call.getDecl()->getAsFunction(); + if (!FD || !FD->getTemplateSpecializationArgs()) + return false; + + DV = Call.getArgSVal(0).getAs(); + break; + } case CallKind::Method: const auto *InstanceCall = dyn_cast(&Call); if (!InstanceCall) diff --git a/test/Analysis/Inputs/llvm.h b/test/Analysis/Inputs/llvm.h index 9d465c65fd..d77b464810 100644 --- a/test/Analysis/Inputs/llvm.h +++ b/test/Analysis/Inputs/llvm.h @@ -16,4 +16,10 @@ template const X *dyn_cast_or_null(Y *Value); template const X *dyn_cast_or_null(Y &Value); + +template +bool isa(Y Value); + +template +bool isa_and_nonnull(Y Value); } // namespace llvm diff --git a/test/Analysis/cast-value-logic.cpp b/test/Analysis/cast-value-logic.cpp index 32001d8548..e001e4a65e 100644 --- a/test/Analysis/cast-value-logic.cpp +++ b/test/Analysis/cast-value-logic.cpp @@ -23,11 +23,16 @@ class Circle : public Shape {}; using namespace llvm; using namespace clang; -void test_regions(const Shape *A, const Shape *B) { +void test_regions_dyn_cast(const Shape *A, const Shape *B) { if (dyn_cast(A) && !dyn_cast(B)) clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} } +void test_regions_isa(const Shape *A, const Shape *B) { + if (isa(A) && !isa(B)) + clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}} +} + namespace test_cast { void evalLogic(const Shape *S) { const Circle *C = cast(S); diff --git a/test/Analysis/cast-value-notes.cpp b/test/Analysis/cast-value-notes.cpp index 77801d6993..6e7f6b01af 100644 --- a/test/Analysis/cast-value-notes.cpp +++ b/test/Analysis/cast-value-notes.cpp @@ -1,5 +1,5 @@ // RUN: %clang_analyze_cc1 \ -// RUN: -analyzer-checker=core,apiModeling.llvm.CastValue \ +// RUN: -analyzer-checker=core,apiModeling.llvm.CastValue,debug.ExprInspection\ // RUN: -analyzer-output=text -verify %s #include "Inputs/llvm.h" @@ -43,16 +43,21 @@ void evalNonNullParamNonNullReturnReference(const Shape &S) { return; } - if (dyn_cast_or_null(C)) { + if (isa(C)) { // expected-note@-1 {{'C' is not a 'Triangle'}} // expected-note@-2 {{Taking false branch}} return; } - (void)(1 / !C); - // expected-note@-1 {{'C' is non-null}} - // expected-note@-2 {{Division by zero}} - // expected-warning@-3 {{Division by zero}} + if (isa(C)) { + // expected-note@-1 {{'C' is a 'Circle'}} + // expected-note@-2 {{Taking true branch}} + + (void)(1 / !C); + // expected-note@-1 {{'C' is non-null}} + // expected-note@-2 {{Division by zero}} + // expected-warning@-3 {{Division by zero}} + } } void evalNonNullParamNonNullReturn(const Shape *S) { @@ -60,7 +65,13 @@ void evalNonNullParamNonNullReturn(const Shape *S) { // expected-note@-1 {{'S' is a 'Circle'}} // expected-note@-2 {{'C' initialized here}} - if (!cast(C)) { + if (!isa(C)) { + // expected-note@-1 {{Assuming 'C' is a 'Triangle'}} + // expected-note@-2 {{Taking false branch}} + return; + } + + if (!isa(C)) { // expected-note@-1 {{'C' is a 'Triangle'}} // expected-note@-2 {{Taking false branch}} return; -- 2.40.0