
The goal of this patch is to refine how the `SVal` base and sub-kinds are represented by forming one unified enum describing the possible SVals. This means that the `unsigned SVal::Kind` and the attached bit-packing semantics would be replaced by a single unified enum. This is more conventional and leads to a better debugging experience by default. This eases the need of using debug pretty-printers, or the use of runtime functions doing the printing for us like we do today by calling `Val.dump()` whenever we inspect the values. Previously, the first 2 bits of the `unsigned SVal::Kind` discriminated the following quartet: `UndefinedVal`, `UnknownVal`, `Loc`, or `NonLoc`. The rest of the upper bits represented the sub-kind, where the value represented the index among only the `Loc`s or `NonLoc`s, effectively attaching 2 meanings of the upper bits depending on the base-kind. We don't need to pack these bits, as we have plenty even if we would use just a plan-old `unsigned char`. Consequently, in this patch, I propose to lay out all the (non-abstract) `SVal` kinds into a single enum, along with some metadata (`BEGIN_Loc`, `END_Loc`, `BEGIN_NonLoc`, `END_NonLoc`) artificial enum values, similar how we do with the `MemRegions`. Note that in the unified `SVal::Kind` enum, to differentiate `nonloc::ConcreteInt` from `loc::ConcreteInt`, I had to prefix them with `Loc` and `NonLoc` to resolve this ambiguity. This should not surface in general, because I'm replacing the `nonloc::Kind` enum items with `inline constexpr` global constants to mimic the original behavior - and offer nicer spelling to these enum values. Some `SVal` constructors were not marked explicit, which I now mark as such to follow best practices, and marked others as `/*implicit*/` to clarify the intent. During refactoring, I also found at least one function not marked `LLVM_ATTRIBUTE_RETURNS_NONNULL`, so I did that. The `TypeRetrievingVisitor` visitor had some accidental dead code, namely: `VisitNonLocConcreteInt` and `VisitLocConcreteInt`. Previously, the `SValVisitor` expected visit handlers of `VisitNonLocXXXXX(nonloc::XXXXX)` and `VisitLocXXXXX(loc::XXXXX)`, where I felt that envoding `NonLoc` and `Loc` in the name is not necessary as the type of the parameter would select the right overload anyways, so I simplified the naming of those visit functions. The rest of the diff is a lot of times just formatting, because `getKind()` by nature, frequently appears in switches, which means that the whole switch gets automatically reformatted. I could probably undo the formatting, but I didn't want to deviate from the rule unless explicitly requested.
134 lines
4.6 KiB
C++
134 lines
4.6 KiB
C++
//== SimpleConstraintManager.cpp --------------------------------*- C++ -*--==//
|
|
//
|
|
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
|
// See https://llvm.org/LICENSE.txt for license information.
|
|
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
//
|
|
// This file defines SimpleConstraintManager, a class that provides a
|
|
// simplified constraint manager interface, compared to ConstraintManager.
|
|
//
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/SimpleConstraintManager.h"
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/APSIntType.h"
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ExprEngine.h"
|
|
#include "clang/StaticAnalyzer/Core/PathSensitive/ProgramState.h"
|
|
#include <optional>
|
|
|
|
namespace clang {
|
|
|
|
namespace ento {
|
|
|
|
SimpleConstraintManager::~SimpleConstraintManager() {}
|
|
|
|
ProgramStateRef SimpleConstraintManager::assumeInternal(ProgramStateRef State,
|
|
DefinedSVal Cond,
|
|
bool Assumption) {
|
|
// If we have a Loc value, cast it to a bool NonLoc first.
|
|
if (std::optional<Loc> LV = Cond.getAs<Loc>()) {
|
|
SValBuilder &SVB = State->getStateManager().getSValBuilder();
|
|
QualType T;
|
|
const MemRegion *MR = LV->getAsRegion();
|
|
if (const TypedRegion *TR = dyn_cast_or_null<TypedRegion>(MR))
|
|
T = TR->getLocationType();
|
|
else
|
|
T = SVB.getContext().VoidPtrTy;
|
|
|
|
Cond = SVB.evalCast(*LV, SVB.getContext().BoolTy, T).castAs<DefinedSVal>();
|
|
}
|
|
|
|
return assume(State, Cond.castAs<NonLoc>(), Assumption);
|
|
}
|
|
|
|
ProgramStateRef SimpleConstraintManager::assume(ProgramStateRef State,
|
|
NonLoc Cond, bool Assumption) {
|
|
State = assumeAux(State, Cond, Assumption);
|
|
if (EE)
|
|
return EE->processAssume(State, Cond, Assumption);
|
|
return State;
|
|
}
|
|
|
|
ProgramStateRef SimpleConstraintManager::assumeAux(ProgramStateRef State,
|
|
NonLoc Cond,
|
|
bool Assumption) {
|
|
|
|
// We cannot reason about SymSymExprs, and can only reason about some
|
|
// SymIntExprs.
|
|
if (!canReasonAbout(Cond)) {
|
|
// Just add the constraint to the expression without trying to simplify.
|
|
SymbolRef Sym = Cond.getAsSymbol();
|
|
assert(Sym);
|
|
return assumeSymUnsupported(State, Sym, Assumption);
|
|
}
|
|
|
|
switch (Cond.getKind()) {
|
|
default:
|
|
llvm_unreachable("'Assume' not implemented for this NonLoc");
|
|
|
|
case nonloc::SymbolValKind: {
|
|
nonloc::SymbolVal SV = Cond.castAs<nonloc::SymbolVal>();
|
|
SymbolRef Sym = SV.getSymbol();
|
|
assert(Sym);
|
|
return assumeSym(State, Sym, Assumption);
|
|
}
|
|
|
|
case nonloc::ConcreteIntKind: {
|
|
bool b = Cond.castAs<nonloc::ConcreteInt>().getValue() != 0;
|
|
bool isFeasible = b ? Assumption : !Assumption;
|
|
return isFeasible ? State : nullptr;
|
|
}
|
|
|
|
case nonloc::PointerToMemberKind: {
|
|
bool IsNull = !Cond.castAs<nonloc::PointerToMember>().isNullMemberPointer();
|
|
bool IsFeasible = IsNull ? Assumption : !Assumption;
|
|
return IsFeasible ? State : nullptr;
|
|
}
|
|
|
|
case nonloc::LocAsIntegerKind:
|
|
return assumeInternal(State, Cond.castAs<nonloc::LocAsInteger>().getLoc(),
|
|
Assumption);
|
|
} // end switch
|
|
}
|
|
|
|
ProgramStateRef SimpleConstraintManager::assumeInclusiveRangeInternal(
|
|
ProgramStateRef State, NonLoc Value, const llvm::APSInt &From,
|
|
const llvm::APSInt &To, bool InRange) {
|
|
|
|
assert(From.isUnsigned() == To.isUnsigned() &&
|
|
From.getBitWidth() == To.getBitWidth() &&
|
|
"Values should have same types!");
|
|
|
|
if (!canReasonAbout(Value)) {
|
|
// Just add the constraint to the expression without trying to simplify.
|
|
SymbolRef Sym = Value.getAsSymbol();
|
|
assert(Sym);
|
|
return assumeSymInclusiveRange(State, Sym, From, To, InRange);
|
|
}
|
|
|
|
switch (Value.getKind()) {
|
|
default:
|
|
llvm_unreachable("'assumeInclusiveRange' is not implemented"
|
|
"for this NonLoc");
|
|
|
|
case nonloc::LocAsIntegerKind:
|
|
case nonloc::SymbolValKind: {
|
|
if (SymbolRef Sym = Value.getAsSymbol())
|
|
return assumeSymInclusiveRange(State, Sym, From, To, InRange);
|
|
return State;
|
|
} // end switch
|
|
|
|
case nonloc::ConcreteIntKind: {
|
|
const llvm::APSInt &IntVal = Value.castAs<nonloc::ConcreteInt>().getValue();
|
|
bool IsInRange = IntVal >= From && IntVal <= To;
|
|
bool isFeasible = (IsInRange == InRange);
|
|
return isFeasible ? State : nullptr;
|
|
}
|
|
} // end switch
|
|
}
|
|
|
|
} // end of namespace ento
|
|
|
|
} // end of namespace clang
|