blob: 07455673d3c3b923b509264fd31912194c51642d [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 "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/AST/OperationKinds.h"
#include "clang/AST/Stmt.h"
#include "clang/AST/Type.h"
#include "clang/ASTMatchers/ASTMatchFinder.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 {
BoolValue& getPointerNotNullProperty(
const Expr* PointerExpr, TransferState<PointerNullabilityLattice>& State) {
auto* PointerVal =
cast<PointerValue>(State.Env.getValue(*PointerExpr, SkipPast::Reference));
CHECK(State.Lattice.hasPointerNotNullProperty(PointerVal));
return *State.Lattice.getPointerNotNullProperty(PointerVal);
}
void initialisePointerNotNullProperty(
const Expr* PointerExpr, TransferState<PointerNullabilityLattice>& State,
BoolValue* NotNullProperty = nullptr) {
if (auto* PointerVal = cast_or_null<PointerValue>(
State.Env.getValue(*PointerExpr, SkipPast::Reference))) {
if (!State.Lattice.hasPointerNotNullProperty(PointerVal)) {
State.Lattice.setPointerNotNullProperty(
PointerVal,
NotNullProperty ? NotNullProperty : &State.Env.makeAtomicBoolValue());
}
}
}
void transferInitPointerVariableReference(
const Expr* PointerExpr, const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
initialisePointerNotNullProperty(PointerExpr, State);
}
void transferInitCXXThisExpr(const Expr* ThisExpr,
const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
initialisePointerNotNullProperty(ThisExpr, State,
&State.Env.getBoolLiteralValue(true));
}
void transferNullPointerLiteral(
const Expr* NullPointer, const MatchFinder::MatchResult& Result,
TransferState<PointerNullabilityLattice>& State) {
auto* NullPointerVal = cast_or_null<PointerValue>(
State.Env.getValue(*NullPointer, SkipPast::None));
if (NullPointerVal == nullptr) {
// Create storage location and value for null pointer if it doesn't exist
auto& NullPointerLoc = State.Env.createStorageLocation(*NullPointer);
NullPointerVal = &State.Env.takeOwnership(
std::make_unique<PointerValue>(NullPointerLoc));
State.Env.setStorageLocation(*NullPointer, NullPointerLoc);
State.Env.setValue(NullPointerLoc, *NullPointerVal);
}
if (!State.Lattice.hasPointerNotNullProperty(NullPointerVal)) {
// Set null pointer to be known null if not already set
State.Lattice.setPointerNotNullProperty(
NullPointerVal, &State.Env.getBoolLiteralValue(false));
}
}
void transferAddrOf(const UnaryOperator* UnaryOp,
const MatchFinder::MatchResult& Result,
TransferState<PointerNullabilityLattice>& State) {
auto* PointerVal =
cast<PointerValue>(State.Env.getValue(*UnaryOp, SkipPast::None));
State.Lattice.setPointerNotNullProperty(PointerVal,
&State.Env.getBoolLiteralValue(true));
}
void transferPointerAccess(const Expr* PointerExpr,
TransferState<PointerNullabilityLattice>& State) {
auto& PointerNotNull = getPointerNotNullProperty(PointerExpr, State);
if (!State.Env.flowConditionImplies(PointerNotNull)) {
State.Lattice.addViolation(PointerExpr);
}
}
void transferDereference(const UnaryOperator* UnaryOp,
const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
transferPointerAccess(UnaryOp->getSubExpr(), State);
}
void transferMemberExprInvolvingPointers(
const MemberExpr* MemberExpr, const MatchFinder::MatchResult&,
TransferState<PointerNullabilityLattice>& State) {
if (MemberExpr->isArrow()) {
// Base expr is a pointer, check that (->) access is safe
transferPointerAccess(MemberExpr->getBase(), State);
}
if (MemberExpr->getType()->isAnyPointerType()) {
// Accessed member is a pointer, initialise its nullability
initialisePointerNotNullProperty(MemberExpr, State);
}
}
void transferNullCheckComparison(
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& LHSNotNull = getPointerNotNullProperty(BinaryOp->getLHS(), State);
auto& RHSNotNull = getPointerNotNullProperty(BinaryOp->getRHS(), State);
// !LHS && !RHS => LHS == RHS
State.Env.addToFlowCondition(State.Env.makeImplication(
State.Env.makeAnd(State.Env.makeNot(LHSNotNull),
State.Env.makeNot(RHSNotNull)),
PointerEQ));
// !LHS && RHS => LHS != RHS
State.Env.addToFlowCondition(State.Env.makeImplication(
State.Env.makeAnd(State.Env.makeNot(LHSNotNull), RHSNotNull), PointerNE));
// LHS && !RHS => LHS != RHS
State.Env.addToFlowCondition(State.Env.makeImplication(
State.Env.makeAnd(LHSNotNull, State.Env.makeNot(RHSNotNull)), PointerNE));
}
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* PointerNotNull = State.Lattice.getPointerNotNullProperty(PointerVal);
CHECK(PointerNotNull != nullptr);
auto& CastExprLoc = State.Env.createStorageLocation(*CastExpr);
State.Env.setValue(CastExprLoc, *PointerNotNull);
State.Env.setStorageLocation(*CastExpr, CastExprLoc);
}
}
auto buildTransferer() {
return MatchSwitchBuilder<TransferState<PointerNullabilityLattice>>()
// Handles initialization of the null states of pointers
.CaseOf<Expr>(isPointerVariableReference(),
transferInitPointerVariableReference)
.CaseOf<Expr>(isCXXThisExpr(), transferInitCXXThisExpr)
// Handles initialization of null states of member pointers and safety of
// member access (->) on pointers
.CaseOf<MemberExpr>(isMemberExprInvolvingPointers(),
transferMemberExprInvolvingPointers)
// Handles nullptr
.CaseOf<Expr>(isNullPointerLiteral(), transferNullPointerLiteral)
// Handles address of operator (&var)
.CaseOf<UnaryOperator>(isAddrOf(), transferAddrOf)
// Handles pointer dereferencing (*ptr)
.CaseOf<UnaryOperator>(isPointerDereference(), transferDereference)
// Handles comparison between 2 pointers
.CaseOf<BinaryOperator>(isPointerCheckBinOp(),
transferNullCheckComparison)
// Handles checking of pointer as boolean
.CaseOf<Expr>(isImplicitCastPointerToBool(),
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