Index: clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp @@ -16,6 +16,7 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/DeclTemplate.h" #include "clang/Lex/Lexer.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/Checker.h" @@ -31,7 +32,7 @@ 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); + + std::string ObjectName = Lexer::getSourceText( + CharSourceRange::getTokenRange(Call.getArgExpr(0)->getSourceRange()), + C.getSourceManager(), C.getASTContext().getLangOpts()); + + C.addTransition( + State->BindExpr(Call.getOriginExpr(), C.getLocationContext(), + C.getSValBuilder().makeTruthVal(CastSucceeds)), + getNoteTag(C, CastInfo, CastToTy, ObjectName, CastSucceeds, IsKnownCast)); +} + //===----------------------------------------------------------------------===// // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null. //===----------------------------------------------------------------------===// @@ -276,6 +328,41 @@ 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. //===----------------------------------------------------------------------===// @@ -286,12 +373,14 @@ 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) { @@ -303,6 +392,15 @@ 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) Index: clang/test/Analysis/Inputs/llvm.h =================================================================== --- clang/test/Analysis/Inputs/llvm.h +++ clang/test/Analysis/Inputs/llvm.h @@ -16,4 +16,10 @@ 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 Index: clang/test/Analysis/cast-value-logic.cpp =================================================================== --- clang/test/Analysis/cast-value-logic.cpp +++ clang/test/Analysis/cast-value-logic.cpp @@ -23,11 +23,16 @@ 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); Index: clang/test/Analysis/cast-value-notes.cpp =================================================================== --- clang/test/Analysis/cast-value-notes.cpp +++ clang/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 @@ 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 @@ // 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;