blob: afc368c2596c4907072758d3b55026c3450319f2 [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/inference/infer_nullability_constraints.h"
#include <optional>
#include <vector>
#include "absl/log/check.h"
#include "nullability/inference/inference.proto.h"
#include "nullability/inference/resolve_constraints.h"
#include "nullability/inference/safety_constraint_generator.h"
#include "nullability/pointer_nullability.h"
#include "nullability/pointer_nullability_analysis.h"
#include "nullability/pointer_nullability_lattice.h"
#include "nullability/type_nullability.h"
#include "clang/AST/ASTContext.h"
#include "clang/AST/Decl.h"
#include "clang/AST/DeclBase.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/Analysis/CFG.h"
#include "clang/Analysis/FlowSensitive/ControlFlowContext.h"
#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
#include "clang/Analysis/FlowSensitive/DataflowAnalysisContext.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/WatchedLiteralsSolver.h"
#include "clang/Basic/LLVM.h"
#include "clang/Basic/Specifiers.h"
#include "llvm/ADT/DenseMap.h"
#include "llvm/Support/Errc.h"
#include "llvm/Support/Error.h"
#include "llvm/Support/raw_ostream.h"
namespace clang::tidy::nullability {
namespace {
using ::clang::dataflow::DataflowAnalysisContext;
using ::clang::dataflow::Environment;
using ::clang::dataflow::PointerValue;
using ::clang::dataflow::Value;
std::optional<NullabilityConstraint> inferNullabilityConstraint(
const clang::ParmVarDecl &P, const Environment &Environment,
const SafetyConstraintGenerator &SafetyConstraintGenerator) {
auto *Val = clang::dyn_cast_or_null<PointerValue>(Environment.getValue(P));
// If there's no PointerValue or its null state wasn't evaluated by the
// analysis, produce no facts.
if (!Val || !hasPointerNullState(*Val)) {
return std::nullopt;
}
// Parameters already annotated NonNull must be NonNull. This enables
// production of edits to synchronize the annotation from definition to all
// declarations.
if (isNonNullAnnotated(P.getType())) {
NullabilityConstraint Constraint;
Constraint.set_must_be_nonnull(true);
return Constraint;
}
return resolveConstraints(SafetyConstraintGenerator.constraints(), *Val);
}
} // namespace
bool isNonNullAnnotated(clang::QualType Type) {
CHECK(Type.getNonReferenceType()->isPointerType());
if (const TypeNullability Nullability =
getNullabilityAnnotationsFromType(Type);
!Nullability.empty()) {
return Nullability[0] == clang::NullabilityKind::NonNull;
}
return false;
}
llvm::Expected<llvm::DenseMap<const clang::NamedDecl *, NullabilityConstraint>>
inferNullabilityConstraints(const clang::FunctionDecl &Func,
clang::ASTContext &Context) {
// We want to make sure we use the declaration that the body comes from,
// otherwise we will see references to `ParmVarDecl`s from a different
// declaration.
const clang::FunctionDecl *DeclWithBody = nullptr;
if (!Func.getBody(DeclWithBody)) {
return llvm::make_error<llvm::StringError>(llvm::errc::invalid_argument,
"Function has no body.");
}
CHECK(DeclWithBody);
llvm::DenseMap<const clang::NamedDecl *, NullabilityConstraint> Results;
llvm::Expected<clang::dataflow::ControlFlowContext> ControlFlowContext =
clang::dataflow::ControlFlowContext::build(*DeclWithBody);
if (!ControlFlowContext) return Results;
DataflowAnalysisContext AnalysisContext(
std::make_unique<clang::dataflow::WatchedLiteralsSolver>());
Environment Environment(AnalysisContext, *DeclWithBody);
PointerNullabilityAnalysis Analysis(Context);
SafetyConstraintGenerator SafetyConstraintGenerator;
llvm::Expected<std::vector<std::optional<
clang::dataflow::DataflowAnalysisState<PointerNullabilityLattice>>>>
BlockToOutputStateOrError = clang::dataflow::runDataflowAnalysis(
*ControlFlowContext, Analysis, Environment,
[&SafetyConstraintGenerator, &Context](
const clang::CFGElement &Element,
const clang::dataflow::DataflowAnalysisState<
PointerNullabilityLattice> &State) {
SafetyConstraintGenerator.collectConstraints(Element, State.Lattice,
State.Env, Context);
});
if (!BlockToOutputStateOrError) {
return Results;
}
for (const auto *P : DeclWithBody->parameters()) {
CHECK(P != nullptr);
if (std::optional<NullabilityConstraint> Constraint =
inferNullabilityConstraint(*P, Environment,
SafetyConstraintGenerator))
Results[P] = *Constraint;
}
return Results;
}
} // namespace clang::tidy::nullability