[analyzer][Casting] Support isa, cast, dyn_cast of SVals

This change specializes the LLVM RTTI mechanism for SVals.
After this change, we can use the well-known `isa`, `cast`, `dyn_cast`.

Examples:

  // SVal V = ...;
  // Loc MyLoc = ...;

  bool IsInteresting = isa<loc::MemRegionVal, loc::GotoLabel>(MyLoc);
  auto MRV = cast<loc::MemRegionVal>(MyLoc);
  Optional<loc::MemRegionVal> MaybeMRV = dyn_cast<loc::MemRegionVal>(V)

The current `SVal::getAs` and `castAs` member functions are redundant at
this point, but I believe that they are still handy.

The member function version is terse and reads left-to-right, which IMO
is a great plus. However, we should probably add a variadic `isa` member
function version to have the same casting API in both cases.

Thanks for the extensive TMP help @bzcheeseman!

Reviewed By: bzcheeseman

Differential Revision: https://reviews.llvm.org/D125709
This commit is contained in:
Balazs Benics 2022-06-14 13:43:04 +02:00
parent 6bf2791814
commit de6ba9704d
2 changed files with 29 additions and 12 deletions

View File

@ -98,19 +98,12 @@ public:
/// Convert to the specified SVal type, asserting that this SVal is of /// Convert to the specified SVal type, asserting that this SVal is of
/// the desired type. /// the desired type.
template<typename T> template <typename T> T castAs() const { return llvm::cast<T>(*this); }
T castAs() const {
assert(T::classof(*this));
return *static_cast<const T *>(this);
}
/// Convert to the specified SVal type, returning None if this SVal is /// Convert to the specified SVal type, returning None if this SVal is
/// not of the desired type. /// not of the desired type.
template<typename T> template <typename T> Optional<T> getAs() const {
Optional<T> getAs() const { return llvm::dyn_cast<T>(*this);
if (!T::classof(*this))
return None;
return *static_cast<const T *>(this);
} }
unsigned getRawKind() const { return Kind; } unsigned getRawKind() const { return Kind; }
@ -564,4 +557,28 @@ public:
} // namespace ento } // namespace ento
} // namespace clang } // namespace clang
namespace llvm {
template <typename To, typename From>
struct CastInfo<
To, From,
std::enable_if_t<std::is_base_of<::clang::ento::SVal, From>::value>>
: public CastIsPossible<To, ::clang::ento::SVal> {
using Self = CastInfo<
To, From,
std::enable_if_t<std::is_base_of<::clang::ento::SVal, From>::value>>;
static bool isPossible(const From &V) {
return To::classof(*static_cast<const ::clang::ento::SVal *>(&V));
}
static Optional<To> castFailed() { return Optional<To>{}; }
static To doCast(const From &f) {
return *static_cast<const To *>(cast<::clang::ento::SVal>(&f));
}
static Optional<To> doCastIfPossible(const From &f) {
if (!Self::isPossible(f))
return Self::castFailed();
return doCast(f);
}
};
} // namespace llvm
#endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H #endif // LLVM_CLANG_STATICANALYZER_CORE_PATHSENSITIVE_SVALS_H

View File

@ -446,7 +446,7 @@ void CFNumberChecker::checkPreStmt(const CallExpr *CE,
// FIXME: We really should allow ranges of valid theType values, and // FIXME: We really should allow ranges of valid theType values, and
// bifurcate the state appropriately. // bifurcate the state appropriately.
Optional<nonloc::ConcreteInt> V = TheTypeVal.getAs<nonloc::ConcreteInt>(); Optional<nonloc::ConcreteInt> V = dyn_cast<nonloc::ConcreteInt>(TheTypeVal);
if (!V) if (!V)
return; return;
@ -907,7 +907,7 @@ static ProgramStateRef checkElementNonNil(CheckerContext &C,
// Go ahead and assume the value is non-nil. // Go ahead and assume the value is non-nil.
SVal Val = State->getSVal(*ElementLoc); SVal Val = State->getSVal(*ElementLoc);
return State->assume(Val.castAs<DefinedOrUnknownSVal>(), true); return State->assume(cast<DefinedOrUnknownSVal>(Val), true);
} }
/// Returns NULL state if the collection is known to contain elements /// Returns NULL state if the collection is known to contain elements