Index: clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp =================================================================== --- clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp +++ clang/lib/StaticAnalyzer/Checkers/CastValueChecker.cpp @@ -10,6 +10,8 @@ // //===----------------------------------------------------------------------===// +#include "clang/AST/DeclTemplate.h" +#include "clang/Lex/Lexer.h" #include "clang/StaticAnalyzer/Checkers/BuiltinCheckerRegistration.h" #include "clang/StaticAnalyzer/Core/Checker.h" #include "clang/StaticAnalyzer/Core/CheckerManager.h" @@ -24,7 +26,7 @@ namespace { class CastValueChecker : public Checker { - enum class CallKind { Function, Method }; + enum class CallKind { Function, Method, InstanceOf }; using CastCheck = std::functionisFails() : Cast->isSucceeds(); +} + //===----------------------------------------------------------------------===// // Main logic to evaluate a cast. //===----------------------------------------------------------------------===// @@ -114,13 +135,9 @@ else IsCastSucceeds = IsCheckedCast || IsNonNullReturn || CastFromTy == CastToTy; - // Check for infeasible casts. - if (!IsCheckedCast && Cast) { - if ((IsCastSucceeds && Cast->isFails()) || - (!IsCastSucceeds && Cast->isSucceeds())) { - C.generateSink(State, C.getPredecessor()); - return; - } + if (!IsCheckedCast && isInfeasibleCast(Cast, IsCastSucceeds)) { + C.generateSink(State, C.getPredecessor()); + return; } // Store the type and the cast information. @@ -154,6 +171,75 @@ Tag); } +static void addInstanceOfTransition(const CallEvent &Call, + DefinedOrUnknownSVal DV, + ProgramStateRef State, CheckerContext &C, + bool IsInstanceOf) { + const auto *CE = cast(Call.getOriginExpr()); + + const DeclRefExpr *DRE = nullptr; + const Stmt *Body = CE->getCalleeDecl()->getBody(); + if (Body) + DRE = dyn_cast(Body); + + if (!Body || !DRE) + DRE = dyn_cast(CE->getCallee()->IgnoreParenImpCasts()); + + QualType CastToTy = DRE->getTemplateArgs()->getArgument().getAsType(); + QualType CastFromTy = getRecordType(Call.parameters()[0]->getType()); + + const DynamicCastInfo *Cast = getDynamicCastInfo(State, CastFromTy, CastToTy); + + bool IsCastSucceeds; + if (Cast) + IsCastSucceeds = IsInstanceOf && Cast->isSucceeds(); + else + IsCastSucceeds = IsInstanceOf || CastFromTy == CastToTy; + + if (isInfeasibleCast(Cast, IsCastSucceeds)) { + C.generateSink(State, C.getPredecessor()); + return; + } + + // Store the type and the cast information. + const MemRegion *MR = DV.getAsRegion(); + bool IsKnownCast = Cast || CastFromTy == CastToTy; + if (!IsKnownCast) + State = setDynamicTypeAndCastInfo(State, MR, CastFromTy, CastToTy, + Call.getResultType(), IsInstanceOf); + + std::string ObjectName = Lexer::getSourceText( + CharSourceRange::getTokenRange(CE->getArg(0)->getSourceRange()), + C.getSourceManager(), C.getASTContext().getLangOpts()); + + const NoteTag *Tag = C.getNoteTag( + [=](BugReport &) -> std::string { + SmallString<128> Msg; + llvm::raw_svector_ostream Out(Msg); + + Out << (IsKnownCast ? "'" : "Assuming '") << ObjectName + << "' with type '" + << CastFromTy->getAsCXXRecordDecl()->getNameAsString() << "' "; + + if (CastFromTy != CastToTy) { + Out << (IsInstanceOf ? "is also an" : "is not the") << " instance of"; + } else { + Out << "is a"; + }; + + Out << " '" << CastToTy->getAsCXXRecordDecl()->getNameAsString() + << '\''; + + return Out.str(); + }, + /*IsPrunable=*/true); + + C.addTransition( + State->BindExpr(CE, C.getLocationContext(), + C.getSValBuilder().makeTruthVal(IsInstanceOf)), + Tag); +} + //===----------------------------------------------------------------------===// // Evaluating cast, dyn_cast, cast_or_null, dyn_cast_or_null. //===----------------------------------------------------------------------===// @@ -242,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. //===----------------------------------------------------------------------===// @@ -252,12 +373,15 @@ 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 (!Call.getResultType()->getPointeeCXXRecordDecl()) + QualType ResultTy = Call.getResultType(); + if (Kind != CallKind::InstanceOf && !ResultTy->getPointeeCXXRecordDecl()) return false; - const CastCheck &Check = Lookup->first; - CallKind Kind = Lookup->second; + const auto *CE = cast(Call.getOriginExpr()); Optional DV; switch (Kind) { @@ -270,6 +394,23 @@ DV = Call.getArgSVal(0).getAs(); break; } + case CallKind::InstanceOf: { + // If we cannot obtain the call's 'DRE' we cannot be sure how to model it. + const DeclRefExpr *DRE = nullptr; + const Stmt *Body = CE->getCalleeDecl()->getBody(); + if (Body) + DRE = dyn_cast(Body); + + if (!Body || !DRE) + DRE = dyn_cast(CE->getCallee()->IgnoreParenImpCasts()); + + // We have to obtain the only template argument to determinte the type. + if (!DRE || !DRE->getTemplateArgs()) + return false; + + DV = Call.getArgSVal(0).getAs(); + break; + } case CallKind::Method: const auto *InstanceCall = dyn_cast(&Call); if (!InstanceCall) 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 namespace llvm { @@ -18,6 +18,12 @@ 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 namespace clang { @@ -79,10 +85,21 @@ 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' with type 'Circle' is not the instance of 'Triangle'}} + // expected-note@-2 {{Taking false branch}} + return; + } + + if (isa(C)) { + // expected-note@-1 {{'C' with type 'Circle' 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) { @@ -90,8 +107,14 @@ // expected-note@-1 {{Checked cast from 'Shape' to 'Circle' succeeds}} // expected-note@-2 {{'C' initialized here}} - if (!cast(C)) { - // expected-note@-1 {{Checked cast from 'Circle' to 'Triangle' succeeds}} + if (isa(C)) { + // expected-note@-1 {{Assuming 'C' with type 'Circle' is not the instance of 'Triangle'}} + // expected-note@-2 {{Taking false branch}} + return; + } + + if (isa(C)) { + // expected-note@-1 {{'C' with type 'Circle' is not the instance of 'Triangle'}} // expected-note@-2 {{Taking false branch}} return; }