blob: 5b739ab194a1c456083274c4bca14290b81a9e1b [file] [log] [blame]
// Part of the Crubit project, under the Apache License v2.0 with LLVM
// Exceptions. See /LICENSE for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
#include "nullability_verification/pointer_nullability_analysis.h"
#include <string>
#include "absl/log/check.h"
#include "nullability_verification/pointer_nullability.h"
#include "nullability_verification/pointer_nullability_lattice.h"
#include "nullability_verification/pointer_nullability_matchers.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Expr.h"
#include "clang/AST/OperationKinds.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/AST/TypeVisitor.h"
#include "clang/ASTMatchers/ASTMatchFinder.h"
#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Specifiers.h"
namespace clang {
namespace tidy {
namespace nullability {
using ast_matchers::MatchFinder;
using dataflow::BoolValue;
using dataflow::CFGMatchSwitchBuilder;
using dataflow::Environment;
using dataflow::PointerValue;
using dataflow::SkipPast;
using dataflow::TransferState;
using dataflow::Value;
namespace {
class GetNullabilityAnnotationsFromTypeVisitor
: public TypeVisitor<GetNullabilityAnnotationsFromTypeVisitor> {
std::vector<NullabilityKind> NullabilityAnnotations;
public:
std::vector<NullabilityKind> getNullabilityAnnotations() && {
return std::move(NullabilityAnnotations);
}
void Visit(QualType T) { TypeVisitor::Visit(T.getTypePtr()); }
void VisitElaboratedType(const ElaboratedType* ET) {
Visit(ET->getNamedType());
}
void VisitTemplateSpecializationType(const TemplateSpecializationType* TST) {
for (auto TA : TST->template_arguments()) {
if (TA.getKind() == TemplateArgument::Type) {
Visit(TA.getAsType());
}
}
}
void VisitAttributedType(const AttributedType* AT) {
Optional<NullabilityKind> NK = AT->getImmediateNullability();
if (NK.has_value()) {
NullabilityAnnotations.push_back(AT->getImmediateNullability().value());
QualType MT = AT->getModifiedType();
if (auto PT = MT->getAs<PointerType>()) {
Visit(PT->getPointeeType());
} else {
// TODO: Handle this unusual yet possible (e.g. through typedefs)
// case.
llvm::dbgs() << "\nThe type " << AT
<< "contains a nullability annotation that is not "
<< "succeeded by a pointer type. "
<< "This occurence is not currently handled.\n";
}
} else {
Visit(AT->getModifiedType());
}
}
void VisitPointerType(const PointerType* PT) {
NullabilityAnnotations.push_back(NullabilityKind::Unspecified);
Visit(PT->getPointeeType());
}
};
/// Traverse over a type to get its nullability. For example, if T is the type
/// Struct3Arg<int * _Nonnull, int, pair<int * _Nullable, int *>> * _Nonnull,
/// the resulting nullability annotations will be {_Nonnull, _Nonnull,
/// _Nullable, _Unknown}. Note that non-pointer elements (e.g., the second
/// argument of Struct3Arg) do not get a nullability annotation.
std::vector<NullabilityKind> getNullabilityAnnotationsFromType(QualType T) {
GetNullabilityAnnotationsFromTypeVisitor AnnotationVisitor;
AnnotationVisitor.Visit(T);
return std::move(AnnotationVisitor).getNullabilityAnnotations();
}
class CountPointersInTypeVisitor
: public TypeVisitor<CountPointersInTypeVisitor> {
unsigned count = 0;
public:
CountPointersInTypeVisitor() {}
unsigned getCount() { return count; }
void Visit(QualType T) {
CHECK(T.isCanonical());
TypeVisitor::Visit(T.getTypePtrOrNull());
}
void VisitPointerType(const PointerType* PT) {
count += 1;
Visit(PT->getPointeeType());
}
void Visit(TemplateArgument TA) {
if (TA.getKind() == TemplateArgument::Type) {
Visit(TA.getAsType());
}
}
void VisitRecordType(const RecordType* RT) {
if (auto* CTSD = dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl())) {
for (auto& TA : CTSD->getTemplateArgs().asArray()) {
Visit(TA);
}
}
}
};
unsigned countPointersInType(QualType T) {
CountPointersInTypeVisitor PointerCountVisitor;
PointerCountVisitor.Visit(T.getCanonicalType());
return PointerCountVisitor.getCount();
}
unsigned countPointersInType(TemplateArgument TA) {
if (TA.getKind() == TemplateArgument::Type) {
return countPointersInType(TA.getAsType().getCanonicalType());
}
return 0;
}
class SubstituteNullabilityAnnotationsInTemplateVisitor
: public TypeVisitor<SubstituteNullabilityAnnotationsInTemplateVisitor> {
std::vector<NullabilityKind> NullabilityAnnotations;
std::function<std::vector<NullabilityKind>(
const SubstTemplateTypeParmType* ST)>
GetSubstitutedNullability;
public:
explicit SubstituteNullabilityAnnotationsInTemplateVisitor(
std::function<
std::vector<NullabilityKind>(const SubstTemplateTypeParmType* ST)>
GetSubstitutedNullability)
: GetSubstitutedNullability(std::move(GetSubstitutedNullability)) {}
std::vector<NullabilityKind> getNullabilityAnnotations() && {
return std::move(NullabilityAnnotations);
}
void Visit(QualType T) { TypeVisitor::Visit(T.getTypePtr()); }
void VisitFunctionProtoType(const FunctionProtoType* FPT) {
Visit(FPT->getReturnType());
// TODO: Visit arguments.
}
void VisitSubstTemplateTypeParmType(const SubstTemplateTypeParmType* ST) {
for (auto NK : GetSubstitutedNullability(ST)) {
NullabilityAnnotations.push_back(NK);
}
}
void VisitPointerType(const PointerType* PT) {
NullabilityAnnotations.push_back(NullabilityKind::Unspecified);
Visit(PT->getPointeeType());
}
void VisitElaboratedType(const ElaboratedType* ET) {
Visit(ET->getNamedType());
}
void VisitTemplateSpecializationType(const TemplateSpecializationType* TST) {
for (auto TA : TST->template_arguments()) {
if (TA.getKind() == TemplateArgument::Type) {
Visit(TA.getAsType());
}
}
}
};
/// Compute the nullability annotation of type `T`, which contains types
/// originally written as a class template type parameter.
///
/// Example:
///
/// \code
/// template <typename F, typename S>
/// struct pair {
/// S *_Nullable getNullablePtrToSecond();
/// };
/// \endcode
///
/// Consider the following member call:
///
/// \code
/// pair<int *, int *_Nonnull> x;
/// x.getNullablePtrToSecond();
/// \endcode
///
/// The class template specialization `x` has the following substitutions:
///
/// F=int *, whose nullability is [_Unspecified]
/// S=int * _Nonnull, whose nullability is [_Nonnull]
///
/// The return type of the member call `x.getNullablePtrToSecond()` is
/// S * _Nullable.
///
/// When we call `substituteNullabilityAnnotationsInClassTemplate` with the type
/// `S * _Nullable` and the `base` node of the member call (in this case, a
/// `DeclRefExpr`), it returns the nullability of the given type after applying
/// substitutions, which in this case is [_Nullable, _Nonnull].
std::vector<NullabilityKind> substituteNullabilityAnnotationsInClassTemplate(
QualType T, ArrayRef<NullabilityKind> BaseNullabilityAnnotations,
QualType BaseType) {
SubstituteNullabilityAnnotationsInTemplateVisitor AnnotationVisitor(
[&](const SubstTemplateTypeParmType* ST) {
unsigned PointerCount = 0;
unsigned ArgIndex = ST->getIndex();
if (auto RT = BaseType->getAs<RecordType>()) {
if (auto CTSD =
dyn_cast<ClassTemplateSpecializationDecl>(RT->getDecl())) {
auto TemplateArgs = CTSD->getTemplateArgs().asArray();
// TODO: Correctly handle the indexing of nested templates (e.g.
// PointerNullabilityTest.MemberFunctionTemplateOfTemplateStruct),
// then remove this fallback.
if (TemplateArgs.size() <= ArgIndex &&
ST->getReplacedParameter()->getDepth() == 0) {
return std::vector<NullabilityKind>();
}
for (auto TA : TemplateArgs.take_front(ArgIndex)) {
PointerCount += countPointersInType(TA);
}
unsigned SliceSize = countPointersInType(TemplateArgs[ArgIndex]);
if (BaseNullabilityAnnotations.size() < PointerCount + SliceSize) {
// TODO: Currently, BaseNullabilityAnnotations can be erroneously
// empty due to lack of expression coverage. Use the dataflow
// lattice to retrieve correct base type annotations. Then, remove
// this fallback.
return std::vector<NullabilityKind>();
} else {
return BaseNullabilityAnnotations.slice(PointerCount, SliceSize)
.vec();
}
}
}
return std::vector<NullabilityKind>();
});
AnnotationVisitor.Visit(T);
return std::move(AnnotationVisitor).getNullabilityAnnotations();
}
/// Compute nullability annotations of `T`, which might contain template type
/// variable substitutions bound by the call `CE`.
///
/// Example:
///
/// \code
/// template<typename F, typename S>
/// std::pair<S, F> flip(std::pair<F, S> p);
/// \endcode
///
/// Consider the following CallExpr:
///
/// \code
/// flip<int * _Nonnull, int * _Nullable>(std::make_pair(&x, &y));
/// \endcode
///
/// This CallExpr has the following substitutions:
/// F=int * _Nonnull, whose nullability is [_Nonnull]
/// S=int * _Nullable, whose nullability is [_Nullable]
///
/// The return type of this CallExpr is `std::pair<S, F>`.
///
/// When we call `substituteNullabilityAnnotationsInFunctionTemplate` with the
/// type `std::pair<S, F>` and the above CallExpr, it returns the nullability
/// the given type after applying substitutions, which in this case is
/// [_Nullable, _Nonnull].
std::vector<NullabilityKind> substituteNullabilityAnnotationsInFunctionTemplate(
QualType T, const CallExpr* CE) {
SubstituteNullabilityAnnotationsInTemplateVisitor AnnotationVisitor(
[&](const SubstTemplateTypeParmType* ST) {
// TODO: Handle calls that use template argument deduction.
// TODO: Handle nested templates (...->getDepth() > 0).
if (auto* DRE =
dyn_cast<DeclRefExpr>(CE->getCallee()->IgnoreImpCasts());
ST->getReplacedParameter()->getDepth() == 0 &&
DRE->hasExplicitTemplateArgs()) {
return getNullabilityAnnotationsFromType(
DRE->template_arguments()[ST->getIndex()]
.getTypeSourceInfo()
->getType());
}
return std::vector<NullabilityKind>();
});
AnnotationVisitor.Visit(T);
return std::move(AnnotationVisitor).getNullabilityAnnotations();
}
NullabilityKind getPointerNullability(const Expr* E,
PointerNullabilityAnalysis::Lattice& L) {
QualType ExprType = E->getType();
Optional<NullabilityKind> Nullability = ExprType->getNullability();
// If the expression's type does not contain nullability information, it may
// be a template instantiation. Look up the nullability in the
// `ExprToNullability` map.
if (Nullability.value_or(NullabilityKind::Unspecified) ==
NullabilityKind::Unspecified) {
if (auto MaybeNullability = L.getExprNullability(E)) {
if (!MaybeNullability->empty()) {
// Return the nullability of the topmost pointer in the type.
Nullability = (*MaybeNullability)[0];
}
}
}
return Nullability.value_or(NullabilityKind::Unspecified);
}
void initPointerFromAnnotations(
PointerValue& PointerVal, const Expr* E,
TransferState<PointerNullabilityLattice>& State) {
NullabilityKind Nullability = getPointerNullability(E, State.Lattice);
switch (Nullability) {
case NullabilityKind::NonNull:
initNotNullPointer(PointerVal, State.Env);
break;
case NullabilityKind::Nullable:
initNullablePointer(PointerVal, State.Env);
break;
default:
initUnknownPointer(PointerVal, State.Env);
}
}
void transferFlowSensitiveNullPointer(
const Expr* NullPointer, const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
if (auto* PointerVal = getPointerValueFromExpr(NullPointer, State.Env)) {
initNullPointer(*PointerVal, State.Env);
}
}
void transferFlowSensitiveNotNullPointer(
const Expr* NotNullPointer, const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
if (auto* PointerVal = getPointerValueFromExpr(NotNullPointer, State.Env)) {
initNotNullPointer(*PointerVal, State.Env);
}
}
void transferFlowSensitivePointer(
const Expr* PointerExpr, const MatchFinder::MatchResult& Result,
TransferState<PointerNullabilityLattice>& State) {
if (auto* PointerVal = getPointerValueFromExpr(PointerExpr, State.Env)) {
initPointerFromAnnotations(*PointerVal, PointerExpr, State);
}
}
// TODO(b/233582219): Implement promotion of nullability knownness for initially
// unknown pointers when there is evidence that it is nullable, for example
// when the pointer is compared to nullptr, or casted to boolean.
void transferFlowSensitiveNullCheckComparison(
const BinaryOperator* BinaryOp, const MatchFinder::MatchResult& result,
TransferState<PointerNullabilityLattice>& State) {
// Boolean representing the comparison between the two pointer values,
// automatically created by the dataflow framework.
auto& PointerComparison =
*cast<BoolValue>(State.Env.getValue(*BinaryOp, SkipPast::None));
CHECK(BinaryOp->getOpcode() == BO_EQ || BinaryOp->getOpcode() == BO_NE);
auto& PointerEQ = BinaryOp->getOpcode() == BO_EQ
? PointerComparison
: State.Env.makeNot(PointerComparison);
auto& PointerNE = BinaryOp->getOpcode() == BO_EQ
? State.Env.makeNot(PointerComparison)
: PointerComparison;
auto* LHS = getPointerValueFromExpr(BinaryOp->getLHS(), State.Env);
auto* RHS = getPointerValueFromExpr(BinaryOp->getRHS(), State.Env);
if (!LHS || !RHS) return;
auto [LHSKnown, LHSNull] = getPointerNullState(*LHS, State.Env);
auto [RHSKnown, RHSNull] = getPointerNullState(*RHS, State.Env);
auto& LHSKnownNotNull =
State.Env.makeAnd(LHSKnown, State.Env.makeNot(LHSNull));
auto& RHSKnownNotNull =
State.Env.makeAnd(RHSKnown, State.Env.makeNot(RHSNull));
auto& LHSKnownNull = State.Env.makeAnd(LHSKnown, LHSNull);
auto& RHSKnownNull = State.Env.makeAnd(RHSKnown, RHSNull);
// nullptr == nullptr
State.Env.addToFlowCondition(State.Env.makeImplication(
State.Env.makeAnd(LHSKnownNull, RHSKnownNull), PointerEQ));
// nullptr != notnull
State.Env.addToFlowCondition(State.Env.makeImplication(
State.Env.makeAnd(LHSKnownNull, RHSKnownNotNull), PointerNE));
// notnull != nullptr
State.Env.addToFlowCondition(State.Env.makeImplication(
State.Env.makeAnd(LHSKnownNotNull, RHSKnownNull), PointerNE));
}
void transferFlowSensitiveNullCheckImplicitCastPtrToBool(
const Expr* CastExpr, const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
auto* PointerVal =
getPointerValueFromExpr(CastExpr->IgnoreImplicit(), State.Env);
if (!PointerVal) return;
auto [PointerKnown, PointerNull] =
getPointerNullState(*PointerVal, State.Env);
auto& CastExprLoc = State.Env.createStorageLocation(*CastExpr);
State.Env.setValue(CastExprLoc, State.Env.makeNot(PointerNull));
State.Env.setStorageLocation(*CastExpr, CastExprLoc);
}
void transferFlowSensitiveCallExpr(
const CallExpr* CallExpr, const MatchFinder::MatchResult& Result,
TransferState<PointerNullabilityLattice>& State) {
auto ReturnType = CallExpr->getType();
if (!ReturnType->isAnyPointerType()) return;
auto* PointerVal = getPointerValueFromExpr(CallExpr, State.Env);
if (!PointerVal) {
PointerVal = cast<PointerValue>(State.Env.createValue(ReturnType));
auto& CallExprLoc = State.Env.createStorageLocation(*CallExpr);
State.Env.setValue(CallExprLoc, *PointerVal);
State.Env.setStorageLocation(*CallExpr, CallExprLoc);
}
initPointerFromAnnotations(*PointerVal, CallExpr, State);
}
void transferNonFlowSensitiveDeclRefExpr(
const DeclRefExpr* DRE, const MatchFinder::MatchResult& MR,
TransferState<PointerNullabilityLattice>& State) {
State.Lattice.insertExprNullabilityIfAbsent(
DRE, [&]() { return getNullabilityAnnotationsFromType(DRE->getType()); });
}
void transferNonFlowSensitiveMemberExpr(
const MemberExpr* ME, const MatchFinder::MatchResult& MR,
TransferState<PointerNullabilityLattice>& State) {
State.Lattice.insertExprNullabilityIfAbsent(ME, [&]() {
auto BaseNullability = State.Lattice.getExprNullability(ME->getBase());
if (BaseNullability.has_value()) {
QualType MemberType = ME->getType();
// When a MemberExpr is a part of a member function call
// (a child of CXXMemberCallExpr), the MemberExpr models a
// partially-applied member function, which isn't a real C++ construct.
// The AST does not provide rich type information for such MemberExprs.
// Instead, the AST specifies a placeholder type, specifically
// BuiltinType::BoundMember. So we have to look at the type of the member
// function declaration.
if (ME->hasPlaceholderType(BuiltinType::BoundMember)) {
MemberType = ME->getMemberDecl()->getType();
}
return substituteNullabilityAnnotationsInClassTemplate(
MemberType, *BaseNullability, ME->getBase()->getType());
} else {
// Since we process child nodes before parents, we should already have
// computed the base (child) nullability. However, this is not true in all
// test cases. So, we return unspecified nullability annotations.
// TODO: Fix this issue, add a CHECK(BaseNullability.has_value()) and
// remove the else branch.
llvm::dbgs() << "Nullability of child node not found\n";
return std::vector<NullabilityKind>(countPointersInType(ME->getType()),
NullabilityKind::Unspecified);
}
});
}
void transferNonFlowSensitiveMemberCallExpr(
const CXXMemberCallExpr* MCE, const MatchFinder::MatchResult& MR,
TransferState<PointerNullabilityLattice>& State) {
State.Lattice.insertExprNullabilityIfAbsent(MCE, [&]() {
auto BaseNullability = State.Lattice.getExprNullability(MCE->getCallee());
if (BaseNullability.has_value()) {
return BaseNullability->vec();
} else {
// Since we process child nodes before parents, we should already have
// computed the base (child) nullability. However, this is not true in all
// test cases. So, we return unspecified nullability annotations.
// TODO: Fix this issue, add a CHECK(BaseNullability.has_value()) and
// remove the else branch.
llvm::dbgs() << "Nullability of child node not found\n";
return std::vector<NullabilityKind>(countPointersInType(MCE->getType()),
NullabilityKind::Unspecified);
}
});
}
void transferNonFlowSensitiveCastExpr(
const CastExpr* CE, const MatchFinder::MatchResult& MR,
TransferState<PointerNullabilityLattice>& State) {
// TODO: Handle casts where the input and output types can have different
// numbers of pointers, and therefore different nullability. For example, a
// reinterpret_cast from `int *` to int.
State.Lattice.insertExprNullabilityIfAbsent(CE, [&]() {
auto BaseNullability = State.Lattice.getExprNullability(CE->getSubExpr());
if (BaseNullability.has_value()) {
return BaseNullability->vec();
} else {
// Since we process child nodes before parents, we should already have
// computed the base (child) nullability. However, this is not true in all
// test cases. So, we return unspecified nullability annotations.
// TODO: Fix this issue, add a CHECK(BaseNullability.has_value()) and
// remove the else branch.
llvm::dbgs() << "Nullability of child node not found\n";
return std::vector<NullabilityKind>(countPointersInType(CE->getType()),
NullabilityKind::Unspecified);
}
});
}
void transferNonFlowSensitiveMaterializeTemporaryExpr(
const MaterializeTemporaryExpr* MTE, const MatchFinder::MatchResult& MR,
TransferState<PointerNullabilityLattice>& State) {
State.Lattice.insertExprNullabilityIfAbsent(MTE, [&]() {
auto BaseNullability = State.Lattice.getExprNullability(MTE->getSubExpr());
if (BaseNullability.has_value()) {
return BaseNullability->vec();
} else {
// Since we process child nodes before parents, we should already have
// computed the base (child) nullability. However, this is not true in all
// test cases. So, we return unspecified nullability annotations.
// TODO: Fix this issue, add a CHECK(BaseNullability.has_value()) and
// remove the else branch.
llvm::dbgs() << "Nullability of child node not found\n";
return std::vector<NullabilityKind>(countPointersInType(MTE->getType()),
NullabilityKind::Unspecified);
}
});
}
void transferNonFlowSensitiveCallExpr(
const CallExpr* CE, const MatchFinder::MatchResult& MR,
TransferState<PointerNullabilityLattice>& State) {
// TODO: Check CallExpr arguments in the diagnoser against the nullability of
// parameters.
State.Lattice.insertExprNullabilityIfAbsent(CE, [&]() {
return substituteNullabilityAnnotationsInFunctionTemplate(CE->getType(),
CE);
});
}
auto buildNonFlowSensitiveTransferer() {
return CFGMatchSwitchBuilder<TransferState<PointerNullabilityLattice>>()
.CaseOfCFGStmt<DeclRefExpr>(ast_matchers::declRefExpr(),
transferNonFlowSensitiveDeclRefExpr)
.CaseOfCFGStmt<MemberExpr>(ast_matchers::memberExpr(),
transferNonFlowSensitiveMemberExpr)
.CaseOfCFGStmt<CXXMemberCallExpr>(ast_matchers::cxxMemberCallExpr(),
transferNonFlowSensitiveMemberCallExpr)
.CaseOfCFGStmt<CastExpr>(ast_matchers::castExpr(),
transferNonFlowSensitiveCastExpr)
.CaseOfCFGStmt<MaterializeTemporaryExpr>(
ast_matchers::materializeTemporaryExpr(),
transferNonFlowSensitiveMaterializeTemporaryExpr)
.CaseOfCFGStmt<CallExpr>(ast_matchers::callExpr(),
transferNonFlowSensitiveCallExpr)
.Build();
}
auto buildFlowSensitiveTransferer() {
return CFGMatchSwitchBuilder<TransferState<PointerNullabilityLattice>>()
// Handles initialization of the null states of pointers.
.CaseOfCFGStmt<Expr>(isCXXThisExpr(), transferFlowSensitiveNotNullPointer)
.CaseOfCFGStmt<Expr>(isAddrOf(), transferFlowSensitiveNotNullPointer)
.CaseOfCFGStmt<Expr>(isNullPointerLiteral(),
transferFlowSensitiveNullPointer)
.CaseOfCFGStmt<CallExpr>(isCallExpr(), transferFlowSensitiveCallExpr)
.CaseOfCFGStmt<Expr>(isPointerExpr(), transferFlowSensitivePointer)
// Handles comparison between 2 pointers.
.CaseOfCFGStmt<BinaryOperator>(isPointerCheckBinOp(),
transferFlowSensitiveNullCheckComparison)
// Handles checking of pointer as boolean.
.CaseOfCFGStmt<Expr>(isImplicitCastPointerToBool(),
transferFlowSensitiveNullCheckImplicitCastPtrToBool)
.Build();
}
} // namespace
PointerNullabilityAnalysis::PointerNullabilityAnalysis(ASTContext& Context)
: DataflowAnalysis<PointerNullabilityAnalysis, PointerNullabilityLattice>(
Context),
NonFlowSensitiveTransferer(buildNonFlowSensitiveTransferer()),
FlowSensitiveTransferer(buildFlowSensitiveTransferer()) {}
void PointerNullabilityAnalysis::transfer(const CFGElement* Elt,
PointerNullabilityLattice& Lattice,
Environment& Env) {
TransferState<PointerNullabilityLattice> State(Lattice, Env);
NonFlowSensitiveTransferer(*Elt, getASTContext(), State);
FlowSensitiveTransferer(*Elt, getASTContext(), State);
}
BoolValue& mergeBoolValues(BoolValue& Bool1, const Environment& Env1,
BoolValue& Bool2, const Environment& Env2,
Environment& MergedEnv) {
if (&Bool1 == &Bool2) {
return Bool1;
}
auto& MergedBool = MergedEnv.makeAtomicBoolValue();
// If `Bool1` and `Bool2` is constrained to the same true / false value,
// `MergedBool` can be constrained similarly without needing to consider the
// path taken - this simplifies the flow condition tracked in `MergedEnv`.
// Otherwise, information about which path was taken is used to associate
// `MergedBool` with `Bool1` and `Bool2`.
if (Env1.flowConditionImplies(Bool1) && Env2.flowConditionImplies(Bool2)) {
MergedEnv.addToFlowCondition(MergedBool);
} else if (Env1.flowConditionImplies(Env1.makeNot(Bool1)) &&
Env2.flowConditionImplies(Env2.makeNot(Bool2))) {
MergedEnv.addToFlowCondition(MergedEnv.makeNot(MergedBool));
} else {
// TODO(b/233582219): Flow conditions are not necessarily mutually
// exclusive, a fix is in order: https://reviews.llvm.org/D130270. Update
// this section when the patch is commited.
auto& FC1 = Env1.getFlowConditionToken();
auto& FC2 = Env2.getFlowConditionToken();
MergedEnv.addToFlowCondition(MergedEnv.makeOr(
MergedEnv.makeAnd(FC1, MergedEnv.makeIff(MergedBool, Bool1)),
MergedEnv.makeAnd(FC2, MergedEnv.makeIff(MergedBool, Bool2))));
}
return MergedBool;
}
bool PointerNullabilityAnalysis::merge(QualType Type, const Value& Val1,
const Environment& Env1,
const Value& Val2,
const Environment& Env2,
Value& MergedVal,
Environment& MergedEnv) {
if (!Type->isAnyPointerType()) {
return false;
}
auto [Known1, Null1] = getPointerNullState(cast<PointerValue>(Val1), Env1);
auto [Known2, Null2] = getPointerNullState(cast<PointerValue>(Val2), Env2);
auto& Known = mergeBoolValues(Known1, Env1, Known2, Env2, MergedEnv);
auto& Null = mergeBoolValues(Null1, Env1, Null2, Env2, MergedEnv);
initPointerNullState(cast<PointerValue>(MergedVal), MergedEnv, &Known, &Null);
return true;
}
} // namespace nullability
} // namespace tidy
} // namespace clang