blob: 6c81070bb9dbaed3cd0fab7da6af1eba332d77f9 [file] [log] [blame]
Wei Yi Tee8b58e192022-08-02 10:15:40 -07001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
5#include "nullability_verification/pointer_nullability_diagnosis.h"
6
Googler59d7fc02022-12-16 09:45:39 -08007#include <optional>
Dani Ferreira Franco Mouraf9dae202023-01-09 10:15:18 -08008#include <string>
Googler59d7fc02022-12-16 09:45:39 -08009
Wei Yi Tee8b58e192022-08-02 10:15:40 -070010#include "nullability_verification/pointer_nullability.h"
11#include "nullability_verification/pointer_nullability_matchers.h"
Wei Yi Tee9c161612022-08-19 14:20:02 -070012#include "clang/AST/ASTContext.h"
Wei Yi Tee566e2df2022-09-19 11:59:03 -070013#include "clang/AST/DeclCXX.h"
Wei Yi Tee8b58e192022-08-02 10:15:40 -070014#include "clang/AST/Expr.h"
Wei Yi Tee1bf11162022-08-26 03:16:53 -070015#include "clang/AST/ExprCXX.h"
Wei Yi Tee9c161612022-08-19 14:20:02 -070016#include "clang/AST/Stmt.h"
Wei Yi Tee8b58e192022-08-02 10:15:40 -070017#include "clang/ASTMatchers/ASTMatchFinder.h"
Wei Yi Tee217eb5f2022-09-15 03:18:28 -070018#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
Wei Yi Tee9c161612022-08-19 14:20:02 -070019#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
Wei Yi Tee036efdf2022-08-19 14:16:26 -070020#include "clang/Basic/Specifiers.h"
Wei Yi Tee8b58e192022-08-02 10:15:40 -070021
22namespace clang {
23namespace tidy {
24namespace nullability {
25
26using ast_matchers::MatchFinder;
Wei Yi Tee217eb5f2022-09-15 03:18:28 -070027using dataflow::CFGMatchSwitchBuilder;
Wei Yi Tee8b58e192022-08-02 10:15:40 -070028using dataflow::Environment;
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -080029using dataflow::TransferStateForDiagnostics;
Wei Yi Tee8b58e192022-08-02 10:15:40 -070030
31namespace {
32
Wei Yi Tee9c161612022-08-19 14:20:02 -070033// Returns true if `Expr` is uninterpreted or known to be nullable.
34bool isNullableOrUntracked(const Expr* E, const Environment& Env) {
35 auto* ActualVal = getPointerValueFromExpr(E, Env);
Dani Ferreira Franco Moura58f7c2f2023-01-12 09:31:30 -080036 if (ActualVal == nullptr) {
37 llvm::dbgs()
38 << "The dataflow analysis framework does not model a PointerValue for "
39 "the following Expr, and thus its dereference is marked as unsafe:";
40 E->dump();
41 }
Wei Yi Tee9c161612022-08-19 14:20:02 -070042 return !ActualVal || isNullable(*ActualVal, Env);
43}
44
45// Returns true if an uninterpreted or nullable `Expr` was assigned to a
46// construct with a non-null `DeclaredType`.
47bool isIncompatibleAssignment(QualType DeclaredType, const Expr* E,
48 const Environment& Env, ASTContext& Ctx) {
49 assert(DeclaredType->isAnyPointerType());
50 return getNullabilityKind(DeclaredType, Ctx) == NullabilityKind::NonNull &&
51 isNullableOrUntracked(E, Env);
Wei Yi Tee8b58e192022-08-02 10:15:40 -070052}
53
Googlerbad70522023-01-31 04:37:38 -080054std::optional<CFGElement> diagnoseDereference(
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -080055 const UnaryOperator* UnaryOp, const MatchFinder::MatchResult&,
56 const TransferStateForDiagnostics<PointerNullabilityLattice>& State) {
57 if (isNullableOrUntracked(UnaryOp->getSubExpr(), State.Env)) {
Googlerbad70522023-01-31 04:37:38 -080058 return std::optional<CFGElement>(CFGStmt(UnaryOp));
Wei Yi Tee9c161612022-08-19 14:20:02 -070059 }
Googler59d7fc02022-12-16 09:45:39 -080060 return std::nullopt;
Wei Yi Tee8b58e192022-08-02 10:15:40 -070061}
62
Googlerbad70522023-01-31 04:37:38 -080063std::optional<CFGElement> diagnoseArrow(
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -080064 const MemberExpr* MemberExpr, const MatchFinder::MatchResult& Result,
65 const TransferStateForDiagnostics<PointerNullabilityLattice>& State) {
66 if (isNullableOrUntracked(MemberExpr->getBase(), State.Env)) {
Googlerbad70522023-01-31 04:37:38 -080067 return std::optional<CFGElement>(CFGStmt(MemberExpr));
Wei Yi Tee9c161612022-08-19 14:20:02 -070068 }
Googler59d7fc02022-12-16 09:45:39 -080069 return std::nullopt;
Wei Yi Tee8b58e192022-08-02 10:15:40 -070070}
71
Wei Yi Tee1bf11162022-08-26 03:16:53 -070072bool isIncompatibleArgumentList(ArrayRef<QualType> ParamTypes,
73 ArrayRef<const Expr*> Args,
74 const Environment& Env, ASTContext& Ctx) {
75 assert(ParamTypes.size() == Args.size());
76 for (unsigned int I = 0; I < Args.size(); ++I) {
77 auto ParamType = ParamTypes[I].getNonReferenceType();
78 if (!ParamType->isAnyPointerType()) {
79 continue;
80 }
81 if (isIncompatibleAssignment(ParamType, Args[I], Env, Ctx)) {
82 return true;
83 }
84 }
85 return false;
86}
87
Dani Ferreira Franco Mouraf9dae202023-01-09 10:15:18 -080088NullabilityKind parseNullabilityKind(StringRef EnumName) {
89 return llvm::StringSwitch<NullabilityKind>(EnumName)
90 .Case("NK_nonnull", NullabilityKind::NonNull)
91 .Case("NK_nullable", NullabilityKind::Nullable)
92 .Case("NK_unspecified", NullabilityKind::Unspecified)
93 .Default(NullabilityKind::Unspecified);
94}
95
Dani Ferreira Franco Mouraf9dae202023-01-09 10:15:18 -080096/// Evaluates the `__assert_nullability` call by comparing the expected
97/// nullability to the nullability computed by the dataflow analysis.
98///
99/// If the function being diagnosed is called `__assert_nullability`, we assume
100/// it is a call of the shape __assert_nullability<a, b, c, ...>(p), where `p`
101/// is an expression that contains pointers and a, b, c ... represent each of
102/// the NullabilityKinds in `p`'s expected nullability. An expression's
103/// nullability can be expressed as a vector of NullabilityKinds, where each
104/// vector element corresponds to one of the pointers contained in the
105/// expression.
106///
107/// For example:
108/// \code
109/// enum NullabilityKind {
110/// NK_nonnull,
111/// NK_nullable,
112/// NK_unspecified,
113/// };
114///
115/// template<NullabilityKind ...NK, typename T>
116/// void __assert_nullability(T&);
117///
118/// template<typename T0, typename T1>
119/// struct Struct2Arg {
120/// T0 arg0;
121/// T1 arg1;
122/// };
123///
124/// void target(Struct2Arg<int *, int * _Nullable> p) {
125/// __assert_nullability<NK_unspecified, NK_nullable>(p);
126/// }
127/// \endcode
128bool diagnoseAssertNullabilityCall(
129 const CallExpr* CE,
130 const TransferStateForDiagnostics<PointerNullabilityLattice>& State,
131 ASTContext& Ctx) {
132 auto* DRE = cast<DeclRefExpr>(CE->getCallee()->IgnoreImpCasts());
133
134 // Extract the expected nullability from the template parameter pack.
135 std::vector<NullabilityKind> Expected;
136 for (auto P : DRE->template_arguments()) {
137 if (P.getArgument().getKind() == TemplateArgument::Expression) {
138 if (auto* EnumDRE = dyn_cast<DeclRefExpr>(P.getSourceExpression())) {
139 Expected.push_back(parseNullabilityKind(EnumDRE->getDecl()->getName()));
140 }
141 }
142 }
143
144 // Compare the nullability computed by nullability analysis with the
145 // expected one.
146 const Expr* GivenExpr = CE->getArg(0);
Googlerbad70522023-01-31 04:37:38 -0800147 std::optional<ArrayRef<NullabilityKind>> MaybeComputed =
Dani Ferreira Franco Mouraf9dae202023-01-09 10:15:18 -0800148 State.Lattice.getExprNullability(GivenExpr);
149 if (!MaybeComputed.has_value()) {
150 llvm::dbgs()
151 << "Could not evaluate __assert_nullability. Could not find the "
152 "nullability of the argument expression: ";
153 CE->dump();
154 return false;
155 }
156 if (MaybeComputed->vec() == Expected) return true;
157 // The computed and expected nullabilities differ. Print both to aid
158 // debugging.
159 llvm::dbgs() << "__assert_nullability failed at location: ";
160 CE->getExprLoc().print(llvm::dbgs(), Ctx.getSourceManager());
161 llvm::dbgs() << "\nExpression:\n";
162 GivenExpr->dump();
163 llvm::dbgs() << "Expected nullability: ";
164 llvm::dbgs() << nullabilityToString(Expected) << "\n";
165 llvm::dbgs() << "Computed nullability: ";
166 llvm::dbgs() << nullabilityToString(*MaybeComputed) << "\n";
167 return false;
168}
169
Wei Yi Tee036efdf2022-08-19 14:16:26 -0700170// TODO(b/233582219): Handle call expressions whose callee is not a decl (e.g.
171// a function returned from another function), or when the callee cannot be
172// interpreted as a function type (e.g. a pointer to a function pointer).
Googlerbad70522023-01-31 04:37:38 -0800173std::optional<CFGElement> diagnoseCallExpr(
Wei Yi Tee036efdf2022-08-19 14:16:26 -0700174 const CallExpr* CE, const MatchFinder::MatchResult& Result,
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -0800175 const TransferStateForDiagnostics<PointerNullabilityLattice>& State) {
Wei Yi Tee036efdf2022-08-19 14:16:26 -0700176 auto* Callee = CE->getCalleeDecl();
Googler59d7fc02022-12-16 09:45:39 -0800177 if (!Callee) return std::nullopt;
Wei Yi Tee036efdf2022-08-19 14:16:26 -0700178
179 auto* CalleeType = Callee->getFunctionType();
Googler59d7fc02022-12-16 09:45:39 -0800180 if (!CalleeType) return std::nullopt;
Wei Yi Tee036efdf2022-08-19 14:16:26 -0700181
Dani Ferreira Franco Mouraf9dae202023-01-09 10:15:18 -0800182 if (auto* FD = Callee->getAsFunction()) {
183 if (FD->getDeclName().isIdentifier() &&
184 FD->getName() == "__assert_nullability" &&
185 !diagnoseAssertNullabilityCall(CE, State, *Result.Context)) {
186 // TODO: Handle __assert_nullability failures differently from regular
187 // diagnostic ([[unsafe]]) failures.
Googlerbad70522023-01-31 04:37:38 -0800188 return std::optional<CFGElement>(CFGStmt(CE));
Dani Ferreira Franco Mouraf9dae202023-01-09 10:15:18 -0800189 }
190 }
191
Wei Yi Tee036efdf2022-08-19 14:16:26 -0700192 auto ParamTypes = CalleeType->getAs<FunctionProtoType>()->getParamTypes();
Wei Yi Tee1bf11162022-08-26 03:16:53 -0700193 ArrayRef<const Expr*> Args(CE->getArgs(), CE->getNumArgs());
194 if (isa<CXXOperatorCallExpr>(CE)) {
195 // The first argument of an operator call expression is the operand which
196 // does not appear in the list of parameter types.
197 Args = Args.drop_front();
Wei Yi Tee036efdf2022-08-19 14:16:26 -0700198 }
199
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -0800200 return isIncompatibleArgumentList(ParamTypes, Args, State.Env,
201 *Result.Context)
Googlerbad70522023-01-31 04:37:38 -0800202 ? std::optional<CFGElement>(CFGStmt(CE))
Googler59d7fc02022-12-16 09:45:39 -0800203 : std::nullopt;
Wei Yi Tee036efdf2022-08-19 14:16:26 -0700204}
205
Googlerbad70522023-01-31 04:37:38 -0800206std::optional<CFGElement> diagnoseConstructExpr(
Wei Yi Teeafbca012022-09-19 11:54:28 -0700207 const CXXConstructExpr* CE, const MatchFinder::MatchResult& Result,
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -0800208 const TransferStateForDiagnostics<PointerNullabilityLattice>& State) {
Wei Yi Teeafbca012022-09-19 11:54:28 -0700209 auto ConstructorParamTypes = CE->getConstructor()
210 ->getType()
211 ->getAs<FunctionProtoType>()
212 ->getParamTypes();
213 ArrayRef<const Expr*> ConstructorArgs(CE->getArgs(), CE->getNumArgs());
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -0800214 return isIncompatibleArgumentList(ConstructorParamTypes, ConstructorArgs,
215 State.Env, *Result.Context)
Googlerbad70522023-01-31 04:37:38 -0800216 ? std::optional<CFGElement>(CFGStmt(CE))
Googler59d7fc02022-12-16 09:45:39 -0800217 : std::nullopt;
Wei Yi Teeafbca012022-09-19 11:54:28 -0700218}
219
Googlerbad70522023-01-31 04:37:38 -0800220std::optional<CFGElement> diagnoseReturn(
Wei Yi Teeed480d62022-08-26 03:19:54 -0700221 const ReturnStmt* RS, const MatchFinder::MatchResult& Result,
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -0800222 const TransferStateForDiagnostics<PointerNullabilityLattice>& State) {
223 auto ReturnType = cast<FunctionDecl>(State.Env.getDeclCtx())->getReturnType();
Dani Ferreira Franco Mourab88f19f2023-01-26 04:47:09 -0800224
225 // TODO: Handle non-pointer return types.
226 if (!ReturnType->isPointerType()) {
227 return std::nullopt;
228 }
Wei Yi Teeed480d62022-08-26 03:19:54 -0700229
230 auto* ReturnExpr = RS->getRetValue();
231 assert(ReturnExpr->getType()->isPointerType());
232
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -0800233 return isIncompatibleAssignment(ReturnType, ReturnExpr, State.Env,
234 *Result.Context)
Googlerbad70522023-01-31 04:37:38 -0800235 ? std::optional<CFGElement>(CFGStmt(RS))
Googler59d7fc02022-12-16 09:45:39 -0800236 : std::nullopt;
Wei Yi Teeed480d62022-08-26 03:19:54 -0700237}
238
Googlerbad70522023-01-31 04:37:38 -0800239std::optional<CFGElement> diagnoseMemberInitializer(
Wei Yi Tee566e2df2022-09-19 11:59:03 -0700240 const CXXCtorInitializer* CI, const MatchFinder::MatchResult& Result,
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -0800241 const TransferStateForDiagnostics<PointerNullabilityLattice>& State) {
Wei Yi Tee566e2df2022-09-19 11:59:03 -0700242 assert(CI->isAnyMemberInitializer());
243 auto MemberType = CI->getAnyMember()->getType();
244 if (!MemberType->isAnyPointerType()) {
Googler59d7fc02022-12-16 09:45:39 -0800245 return std::nullopt;
Wei Yi Tee566e2df2022-09-19 11:59:03 -0700246 }
247 auto MemberInitExpr = CI->getInit();
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -0800248 return isIncompatibleAssignment(MemberType, MemberInitExpr, State.Env,
Wei Yi Tee566e2df2022-09-19 11:59:03 -0700249 *Result.Context)
Googlerbad70522023-01-31 04:37:38 -0800250 ? std::optional<CFGElement>(CFGInitializer(CI))
Googler59d7fc02022-12-16 09:45:39 -0800251 : std::nullopt;
Wei Yi Tee566e2df2022-09-19 11:59:03 -0700252}
253
Wei Yi Tee8b58e192022-08-02 10:15:40 -0700254auto buildDiagnoser() {
Dani Ferreira Franco Moura4a73ca72022-12-14 10:23:56 -0800255 return CFGMatchSwitchBuilder<const dataflow::TransferStateForDiagnostics<
256 PointerNullabilityLattice>,
Googlerbad70522023-01-31 04:37:38 -0800257 std::optional<CFGElement>>()
Wei Yi Tee8b58e192022-08-02 10:15:40 -0700258 // (*)
Wei Yi Tee217eb5f2022-09-15 03:18:28 -0700259 .CaseOfCFGStmt<UnaryOperator>(isPointerDereference(), diagnoseDereference)
Wei Yi Tee8b58e192022-08-02 10:15:40 -0700260 // (->)
Wei Yi Tee217eb5f2022-09-15 03:18:28 -0700261 .CaseOfCFGStmt<MemberExpr>(isPointerArrow(), diagnoseArrow)
Wei Yi Tee036efdf2022-08-19 14:16:26 -0700262 // Check compatibility of parameter assignments
Wei Yi Tee217eb5f2022-09-15 03:18:28 -0700263 .CaseOfCFGStmt<CallExpr>(isCallExpr(), diagnoseCallExpr)
264 .CaseOfCFGStmt<ReturnStmt>(isPointerReturn(), diagnoseReturn)
Wei Yi Teeafbca012022-09-19 11:54:28 -0700265 .CaseOfCFGStmt<CXXConstructExpr>(isConstructExpr(), diagnoseConstructExpr)
Wei Yi Tee566e2df2022-09-19 11:59:03 -0700266 .CaseOfCFGInit<CXXCtorInitializer>(isCtorMemberInitializer(),
267 diagnoseMemberInitializer)
Wei Yi Tee8b58e192022-08-02 10:15:40 -0700268 .Build();
269}
270
271} // namespace
272
273PointerNullabilityDiagnoser::PointerNullabilityDiagnoser()
274 : Diagnoser(buildDiagnoser()) {}
275
276} // namespace nullability
277} // namespace tidy
Wei Yi Tee8374c8f2022-08-11 01:18:41 -0700278} // namespace clang