blob: 54daf2bd5f49fba40910f397450867c5d93c5798 [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/pointer_nullability.h"
#include <cassert>
#include <optional>
#include "nullability/type_nullability.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/Formula.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/StringRef.h"
namespace clang::tidy::nullability {
using dataflow::AtomicBoolValue;
using dataflow::BoolValue;
using dataflow::DataflowAnalysisContext;
using dataflow::Environment;
using dataflow::Formula;
using dataflow::PointerValue;
using dataflow::Value;
/// The nullness information of a pointer is represented by two properties
/// which indicate if its source was nullable, and if its value is null.
constexpr llvm::StringLiteral kFromNullable = "from_nullable";
constexpr llvm::StringLiteral kNull = "is_null";
NullabilityKind getNullabilityKind(QualType Type, ASTContext &Ctx) {
return Type->getNullability().value_or(NullabilityKind::Unspecified);
}
PointerValue *getPointerValueFromExpr(const Expr *PointerExpr,
const Environment &Env) {
return cast_or_null<PointerValue>(Env.getValue(*PointerExpr));
}
bool hasPointerNullState(const dataflow::PointerValue &PointerVal) {
return PointerVal.getProperty(kFromNullable) != nullptr &&
PointerVal.getProperty(kNull) != nullptr;
}
PointerNullState getPointerNullState(const PointerValue &PointerVal) {
auto &FromNullable = *cast<BoolValue>(PointerVal.getProperty(kFromNullable));
auto &Null = *cast<BoolValue>(PointerVal.getProperty(kNull));
return {FromNullable.formula(), Null.formula()};
}
static bool tryCreatePointerNullState(PointerValue &PointerVal,
dataflow::Arena &A,
const Formula *FromNullable = nullptr,
const Formula *IsNull = nullptr) {
// TODO: for now we assume that we have both nullability properties, or none.
// We'll need to relax this when properties can be independently widened away.
if (hasPointerNullState(PointerVal)) return false;
if (!FromNullable) FromNullable = &A.makeAtomRef(A.makeAtom());
if (!IsNull) IsNull = &A.makeAtomRef(A.makeAtom());
PointerVal.setProperty(kFromNullable, A.makeBoolValue(*FromNullable));
PointerVal.setProperty(kNull, A.makeBoolValue(*IsNull));
return true;
}
void initPointerNullState(PointerValue &PointerVal,
DataflowAnalysisContext &Ctx,
std::optional<PointerTypeNullability> Source) {
auto &A = Ctx.arena();
if (tryCreatePointerNullState(PointerVal, A,
Source ? &Source->isNullable(A) : nullptr)) {
// The symbolic/nonnull check is not needed for correctness, but it avoids
// adding meaningless (false => !null) invariant clauses.
// TODO: remove this once such clauses are recognized and dropped.
if (Source &&
(Source->isSymbolic() || Source == NullabilityKind::NonNull)) {
const Formula &IsNull = getPointerNullState(PointerVal).IsNull;
Ctx.addInvariant(A.makeImplies(Source->isNonnull(A), A.makeNot(IsNull)));
}
}
}
void initNullPointer(PointerValue &PointerVal, DataflowAnalysisContext &Ctx) {
tryCreatePointerNullState(PointerVal, Ctx.arena(),
/*FromNullable=*/&Ctx.arena().makeLiteral(true),
/*IsNull=*/&Ctx.arena().makeLiteral(true));
}
bool isNullable(const PointerValue &PointerVal, const Environment &Env,
const dataflow::Formula *AdditionalConstraints) {
auto &A = Env.getDataflowAnalysisContext().arena();
auto [FromNullable, Null] = getPointerNullState(PointerVal);
auto *ForseeablyNull = &A.makeAnd(FromNullable, Null);
if (AdditionalConstraints)
ForseeablyNull = &A.makeAnd(*AdditionalConstraints, *ForseeablyNull);
return !Env.flowConditionImplies(A.makeNot(*ForseeablyNull));
}
NullabilityKind getNullability(const dataflow::PointerValue &PointerVal,
const dataflow::Environment &Env,
const dataflow::Formula *AdditionalConstraints) {
auto &A = Env.getDataflowAnalysisContext().arena();
auto *Null = &getPointerNullState(PointerVal).IsNull;
if (AdditionalConstraints) Null = &A.makeAnd(*AdditionalConstraints, *Null);
if (Env.flowConditionImplies(A.makeNot(*Null)))
return NullabilityKind::NonNull;
return isNullable(PointerVal, Env, AdditionalConstraints)
? NullabilityKind::Nullable
: NullabilityKind::Unspecified;
}
} // namespace clang::tidy::nullability