forked from OSchip/llvm-project
[analyzer] CastValueChecker: Model isa(), isa_and_nonnull()
Summary: - Reviewed By: NoQ Differential Revision: https://reviews.llvm.org/D66423 llvm-svn: 369615
This commit is contained in:
parent
12002fbd21
commit
4d71600c11
|
@ -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<eval::Call> {
|
||||
enum class CallKind { Function, Method };
|
||||
enum class CallKind { Function, Method, InstanceOf };
|
||||
|
||||
using CastCheck =
|
||||
std::function<void(const CastValueChecker *, const CallEvent &Call,
|
||||
|
@ -45,6 +46,10 @@ public:
|
|||
//
|
||||
// 4) castAs: Has no parameter, the return value is non-null.
|
||||
// 5) getAs: Has no parameter, the return value is null or non-null.
|
||||
//
|
||||
// We have two cases to check the parameter is an instance of the given type.
|
||||
// 1) isa: The parameter is non-null, returns boolean.
|
||||
// 2) isa_and_nonnull: The parameter is null or non-null, returns boolean.
|
||||
bool evalCall(const CallEvent &Call, CheckerContext &C) const;
|
||||
void checkDeadSymbols(SymbolReaper &SR, CheckerContext &C) const;
|
||||
|
||||
|
@ -63,7 +68,11 @@ private:
|
|||
{{{"clang", "castAs"}, 0},
|
||||
{&CastValueChecker::evalCastAs, CallKind::Method}},
|
||||
{{{"clang", "getAs"}, 0},
|
||||
{&CastValueChecker::evalGetAs, CallKind::Method}}};
|
||||
{&CastValueChecker::evalGetAs, CallKind::Method}},
|
||||
{{{"llvm", "isa"}, 1},
|
||||
{&CastValueChecker::evalIsa, CallKind::InstanceOf}},
|
||||
{{{"llvm", "isa_and_nonnull"}, 1},
|
||||
{&CastValueChecker::evalIsaAndNonNull, CallKind::InstanceOf}}};
|
||||
|
||||
void evalCast(const CallEvent &Call, DefinedOrUnknownSVal DV,
|
||||
CheckerContext &C) const;
|
||||
|
@ -77,6 +86,10 @@ private:
|
|||
CheckerContext &C) const;
|
||||
void evalGetAs(const CallEvent &Call, DefinedOrUnknownSVal DV,
|
||||
CheckerContext &C) const;
|
||||
void evalIsa(const CallEvent &Call, DefinedOrUnknownSVal DV,
|
||||
CheckerContext &C) const;
|
||||
void evalIsaAndNonNull(const CallEvent &Call, DefinedOrUnknownSVal DV,
|
||||
CheckerContext &C) const;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
|
@ -189,6 +202,42 @@ static void addCastTransition(const CallEvent &Call, DefinedOrUnknownSVal DV,
|
|||
getNoteTag(C, CastInfo, CastToTy, Object, CastSucceeds, IsKnownCast));
|
||||
}
|
||||
|
||||
static void addInstanceOfTransition(const CallEvent &Call,
|
||||
DefinedOrUnknownSVal DV,
|
||||
ProgramStateRef State, CheckerContext &C,
|
||||
bool IsInstanceOf) {
|
||||
const FunctionDecl *FD = Call.getDecl()->getAsFunction();
|
||||
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;
|
||||
|
||||
// We need to obtain the record type of the call's result to model it.
|
||||
if (!getRecordType(Call.getResultType())->isRecordType())
|
||||
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 (Kind != CallKind::InstanceOf &&
|
||||
!getRecordType(Call.getResultType())->isRecordType())
|
||||
return false;
|
||||
|
||||
Optional<DefinedOrUnknownSVal> DV;
|
||||
|
||||
switch (Kind) {
|
||||
|
@ -304,6 +390,15 @@ bool CastValueChecker::evalCall(const CallEvent &Call,
|
|||
DV = Call.getArgSVal(0).getAs<DefinedOrUnknownSVal>();
|
||||
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<DefinedOrUnknownSVal>();
|
||||
break;
|
||||
}
|
||||
case CallKind::Method:
|
||||
const auto *InstanceCall = dyn_cast<CXXInstanceCall>(&Call);
|
||||
if (!InstanceCall)
|
||||
|
|
|
@ -16,4 +16,10 @@ template <class X, class Y>
|
|||
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);
|
||||
} // namespace llvm
|
||||
|
|
|
@ -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<Circle>(A) && !dyn_cast<Circle>(B))
|
||||
clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
|
||||
}
|
||||
|
||||
void test_regions_isa(const Shape *A, const Shape *B) {
|
||||
if (isa<Circle>(A) && !isa<Circle>(B))
|
||||
clang_analyzer_warnIfReached(); // expected-warning {{REACHABLE}}
|
||||
}
|
||||
|
||||
namespace test_cast {
|
||||
void evalLogic(const Shape *S) {
|
||||
const Circle *C = cast<Circle>(S);
|
||||
|
|
|
@ -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<Triangle>(C)) {
|
||||
if (isa<Triangle>(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<Circle>(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<Triangle>(C)) {
|
||||
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'}}
|
||||
// expected-note@-2 {{Taking false branch}}
|
||||
return;
|
||||
|
|
Loading…
Reference in New Issue