blob: 5cde0c3f7be5a2182f327025c1f881904b12dfed [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 <iostream>
#include <string>
#include "common/check.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/ASTMatchers/ASTMatchFinder.h"
#include "clang/ASTMatchers/ASTMatchers.h"
#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
#include "clang/Analysis/FlowSensitive/MatchSwitch.h"
#include "clang/Analysis/FlowSensitive/Value.h"
#include "clang/Basic/LLVM.h"
namespace clang {
namespace tidy {
namespace nullability {
using ast_matchers::MatchFinder;
using dataflow::BoolValue;
using dataflow::Environment;
using dataflow::MatchSwitchBuilder;
using dataflow::PointerValue;
using dataflow::SkipPast;
using dataflow::TransferState;
using dataflow::Value;
namespace {
void initialisePointerNullability(
const Expr* Expr, const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
if (auto* PointerVal = cast_or_null<PointerValue>(
State.Env.getValue(*Expr, SkipPast::Reference))) {
if (!State.Lattice.hasPointerNullability(PointerVal)) {
State.Lattice.setPointerNullability(PointerVal,
&State.Env.makeAtomicBoolValue());
}
}
}
void transferDereference(const UnaryOperator* UnaryOp,
const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
auto* PointerExpr = UnaryOp->getSubExpr();
if (auto* PointerVal = cast_or_null<PointerValue>(
State.Env.getValue(*PointerExpr, SkipPast::Reference))) {
auto PointerNullability = State.Lattice.getPointerNullability(PointerVal);
CHECK(PointerNullability != nullptr);
if (State.Env.flowConditionImplies(*PointerNullability)) {
return;
}
}
State.Lattice.addViolation(PointerExpr);
}
void transferNullCheckComparison(
const Expr* NullCheck, const Expr* PointerExpr,
TransferState<PointerNullabilityLattice>& State) {
if (auto* PointerVal = cast_or_null<PointerValue>(
State.Env.getValue(*PointerExpr, SkipPast::Reference))) {
auto* PointerNullability = State.Lattice.getPointerNullability(PointerVal);
CHECK(PointerNullability != nullptr);
// For binary operations, the dataflow framework automatically creates a
// corresponding BoolVal
auto* ExistingDFVal =
cast_or_null<BoolValue>(State.Env.getValue(*NullCheck, SkipPast::None));
CHECK(ExistingDFVal != nullptr);
State.Env.addToFlowCondition(
State.Env.makeIff(*ExistingDFVal, *PointerNullability));
}
}
void transferNullCheckImplicitCastPtrToBool(
const Expr* CastExpr, const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
if (auto* PointerVal = cast_or_null<PointerValue>(State.Env.getValue(
*CastExpr->IgnoreImplicit(), SkipPast::Reference))) {
auto* PointerNullability = State.Lattice.getPointerNullability(PointerVal);
CHECK(PointerNullability != nullptr);
auto& CastExprLoc = State.Env.createStorageLocation(*CastExpr);
State.Env.setValue(CastExprLoc, *PointerNullability);
State.Env.setStorageLocation(*CastExpr, CastExprLoc);
}
}
auto buildTransferer() {
return MatchSwitchBuilder<TransferState<PointerNullabilityLattice>>()
// Initialise nullability state of pointers
.CaseOf<Expr>(isPointerExpr(), initialisePointerNullability)
// Pointer dereference
.CaseOf<UnaryOperator>(isPointerDereference(), transferDereference)
// Nullability check
.CaseOf<BinaryOperator>(
isNEQNullBinOp(/*BindID=*/"pointer"),
[](const BinaryOperator* binOp,
const MatchFinder::MatchResult& result,
TransferState<PointerNullabilityLattice>& State) {
transferNullCheckComparison(
binOp, result.Nodes.getNodeAs<Expr>("pointer"), State);
})
.CaseOf<Expr>(isImplicitCastPtrToBool(),
transferNullCheckImplicitCastPtrToBool)
.Build();
}
} // namespace
PointerNullabilityAnalysis::PointerNullabilityAnalysis(ASTContext& Context)
: DataflowAnalysis<PointerNullabilityAnalysis, PointerNullabilityLattice>(
Context),
Transferer(buildTransferer()) {}
void PointerNullabilityAnalysis::transfer(const Stmt* Stmt,
PointerNullabilityLattice& Lattice,
Environment& Env) {
TransferState<PointerNullabilityLattice> State(Lattice, Env);
Transferer(*Stmt, getASTContext(), State);
}
bool PointerNullabilityAnalysis::merge(QualType Type, const Value& Val1,
const Environment& Env1,
const Value& Val2,
const Environment& Env2,
Value& MergedVal,
Environment& MergedEnv) {
return false;
}
} // namespace nullability
} // namespace tidy
} // namespace clang