1 //===-- UncheckedOptionalAccessModel.cpp ------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // This file defines a dataflow analysis that detects unsafe uses of optional
10 // values.
11 //
12 //===----------------------------------------------------------------------===//
13
14 #include "clang/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.h"
15 #include "clang/AST/ASTContext.h"
16 #include "clang/AST/DeclCXX.h"
17 #include "clang/AST/Expr.h"
18 #include "clang/AST/ExprCXX.h"
19 #include "clang/AST/Stmt.h"
20 #include "clang/AST/Type.h"
21 #include "clang/ASTMatchers/ASTMatchers.h"
22 #include "clang/ASTMatchers/ASTMatchersMacros.h"
23 #include "clang/Analysis/CFG.h"
24 #include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
25 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
26 #include "clang/Analysis/FlowSensitive/Formula.h"
27 #include "clang/Analysis/FlowSensitive/RecordOps.h"
28 #include "clang/Analysis/FlowSensitive/SmartPointerAccessorCaching.h"
29 #include "clang/Analysis/FlowSensitive/StorageLocation.h"
30 #include "clang/Analysis/FlowSensitive/Value.h"
31 #include "clang/Basic/OperatorKinds.h"
32 #include "clang/Basic/SourceLocation.h"
33 #include "llvm/ADT/StringRef.h"
34 #include "llvm/Support/ErrorHandling.h"
35 #include <cassert>
36 #include <optional>
37
38 namespace clang {
39 namespace dataflow {
40
41 // Note: the Names appear in reverse order. E.g., to check
42 // if NS is foo::bar::, call isFullyQualifiedNamespaceEqualTo(NS, "bar", "foo")
43 template <class... NameTypes>
isFullyQualifiedNamespaceEqualTo(const NamespaceDecl & NS,llvm::StringRef Name,NameTypes...Names)44 static bool isFullyQualifiedNamespaceEqualTo(const NamespaceDecl &NS,
45 llvm::StringRef Name,
46 NameTypes... Names) {
47 if (!(NS.getDeclName().isIdentifier() && NS.getName() == Name &&
48 NS.getParent() != nullptr))
49 return false;
50
51 if constexpr (sizeof...(NameTypes) > 0) {
52 if (NS.getParent()->isTranslationUnit())
53 return false;
54 if (const auto *NextNS = dyn_cast_or_null<NamespaceDecl>(NS.getParent()))
55 return isFullyQualifiedNamespaceEqualTo(*NextNS, Names...);
56 return false;
57 } else {
58 return NS.getParent()->isTranslationUnit();
59 }
60 }
61
hasOptionalClassName(const CXXRecordDecl & RD)62 static bool hasOptionalClassName(const CXXRecordDecl &RD) {
63 if (!RD.getDeclName().isIdentifier())
64 return false;
65
66 if (RD.getName() == "optional") {
67 if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext()))
68 return N->isStdNamespace() ||
69 isFullyQualifiedNamespaceEqualTo(*N, "absl") ||
70 isFullyQualifiedNamespaceEqualTo(*N, "bsl");
71 return false;
72 }
73
74 if (RD.getName() == "Optional") {
75 // Check whether namespace is "::base" or "::folly".
76 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
77 return N != nullptr && (isFullyQualifiedNamespaceEqualTo(*N, "base") ||
78 isFullyQualifiedNamespaceEqualTo(*N, "folly"));
79 }
80
81 if (RD.getName() == "NullableValue") {
82 const auto *N = dyn_cast_or_null<NamespaceDecl>(RD.getDeclContext());
83 return N != nullptr &&
84 isFullyQualifiedNamespaceEqualTo(*N, "bdlb", "BloombergLP");
85 }
86
87 return false;
88 }
89
getOptionalBaseClass(const CXXRecordDecl * RD)90 static const CXXRecordDecl *getOptionalBaseClass(const CXXRecordDecl *RD) {
91 if (RD == nullptr)
92 return nullptr;
93 if (hasOptionalClassName(*RD))
94 return RD;
95
96 if (!RD->hasDefinition())
97 return nullptr;
98
99 for (const CXXBaseSpecifier &Base : RD->bases())
100 if (const CXXRecordDecl *BaseClass =
101 getOptionalBaseClass(Base.getType()->getAsCXXRecordDecl()))
102 return BaseClass;
103
104 return nullptr;
105 }
106
isSupportedOptionalType(QualType Ty)107 static bool isSupportedOptionalType(QualType Ty) {
108 const CXXRecordDecl *Optional =
109 getOptionalBaseClass(Ty->getAsCXXRecordDecl());
110 return Optional != nullptr;
111 }
112
113 namespace {
114
115 using namespace ::clang::ast_matchers;
116
117 using LatticeTransferState = TransferState<UncheckedOptionalAccessLattice>;
118
AST_MATCHER(CXXRecordDecl,optionalClass)119 AST_MATCHER(CXXRecordDecl, optionalClass) { return hasOptionalClassName(Node); }
120
AST_MATCHER(CXXRecordDecl,optionalOrDerivedClass)121 AST_MATCHER(CXXRecordDecl, optionalOrDerivedClass) {
122 return getOptionalBaseClass(&Node) != nullptr;
123 }
124
desugarsToOptionalType()125 auto desugarsToOptionalType() {
126 return hasUnqualifiedDesugaredType(
127 recordType(hasDeclaration(cxxRecordDecl(optionalClass()))));
128 }
129
desugarsToOptionalOrDerivedType()130 auto desugarsToOptionalOrDerivedType() {
131 return hasUnqualifiedDesugaredType(
132 recordType(hasDeclaration(cxxRecordDecl(optionalOrDerivedClass()))));
133 }
134
hasOptionalType()135 auto hasOptionalType() { return hasType(desugarsToOptionalType()); }
136
137 /// Matches any of the spellings of the optional types and sugar, aliases,
138 /// derived classes, etc.
hasOptionalOrDerivedType()139 auto hasOptionalOrDerivedType() {
140 return hasType(desugarsToOptionalOrDerivedType());
141 }
142
getPublicType(const Expr * E)143 QualType getPublicType(const Expr *E) {
144 auto *Cast = dyn_cast<ImplicitCastExpr>(E->IgnoreParens());
145 if (Cast == nullptr || Cast->getCastKind() != CK_UncheckedDerivedToBase) {
146 QualType Ty = E->getType();
147 if (Ty->isPointerType())
148 return Ty->getPointeeType();
149 return Ty;
150 }
151
152 // Is the derived type that we're casting from the type of `*this`? In this
153 // special case, we can upcast to the base class even if the base is
154 // non-public.
155 bool CastingFromThis = isa<CXXThisExpr>(Cast->getSubExpr());
156
157 // Find the least-derived type in the path (i.e. the last entry in the list)
158 // that we can access.
159 const CXXBaseSpecifier *PublicBase = nullptr;
160 for (const CXXBaseSpecifier *Base : Cast->path()) {
161 if (Base->getAccessSpecifier() != AS_public && !CastingFromThis)
162 break;
163 PublicBase = Base;
164 CastingFromThis = false;
165 }
166
167 if (PublicBase != nullptr)
168 return PublicBase->getType();
169
170 // We didn't find any public type that we could cast to. There may be more
171 // casts in `getSubExpr()`, so recurse. (If there aren't any more casts, this
172 // will return the type of `getSubExpr()`.)
173 return getPublicType(Cast->getSubExpr());
174 }
175
176 // Returns the least-derived type for the receiver of `MCE` that
177 // `MCE.getImplicitObjectArgument()->IgnoreParentImpCasts()` can be downcast to.
178 // Effectively, we upcast until we reach a non-public base class, unless that
179 // base is a base of `*this`.
180 //
181 // This is needed to correctly match methods called on types derived from
182 // `std::optional`.
183 //
184 // Say we have a `struct Derived : public std::optional<int> {} d;` For a call
185 // `d.has_value()`, the `getImplicitObjectArgument()` looks like this:
186 //
187 // ImplicitCastExpr 'const std::__optional_storage_base<int>' lvalue
188 // | <UncheckedDerivedToBase (optional -> __optional_storage_base)>
189 // `-DeclRefExpr 'Derived' lvalue Var 'd' 'Derived'
190 //
191 // The type of the implicit object argument is `__optional_storage_base`
192 // (since this is the internal type that `has_value()` is declared on). If we
193 // call `IgnoreParenImpCasts()` on the implicit object argument, we get the
194 // `DeclRefExpr`, which has type `Derived`. Neither of these types is
195 // `optional`, and hence neither is sufficient for querying whether we are
196 // calling a method on `optional`.
197 //
198 // Instead, starting with the most derived type, we need to follow the chain of
199 // casts
getPublicReceiverType(const CXXMemberCallExpr & MCE)200 QualType getPublicReceiverType(const CXXMemberCallExpr &MCE) {
201 return getPublicType(MCE.getImplicitObjectArgument());
202 }
203
AST_MATCHER_P(CXXMemberCallExpr,publicReceiverType,ast_matchers::internal::Matcher<QualType>,InnerMatcher)204 AST_MATCHER_P(CXXMemberCallExpr, publicReceiverType,
205 ast_matchers::internal::Matcher<QualType>, InnerMatcher) {
206 return InnerMatcher.matches(getPublicReceiverType(Node), Finder, Builder);
207 }
208
isOptionalMemberCallWithNameMatcher(ast_matchers::internal::Matcher<NamedDecl> matcher,const std::optional<StatementMatcher> & Ignorable=std::nullopt)209 auto isOptionalMemberCallWithNameMatcher(
210 ast_matchers::internal::Matcher<NamedDecl> matcher,
211 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
212 return cxxMemberCallExpr(Ignorable ? on(expr(unless(*Ignorable)))
213 : anything(),
214 publicReceiverType(desugarsToOptionalType()),
215 callee(cxxMethodDecl(matcher)));
216 }
217
isOptionalOperatorCallWithName(llvm::StringRef operator_name,const std::optional<StatementMatcher> & Ignorable=std::nullopt)218 auto isOptionalOperatorCallWithName(
219 llvm::StringRef operator_name,
220 const std::optional<StatementMatcher> &Ignorable = std::nullopt) {
221 return cxxOperatorCallExpr(
222 hasOverloadedOperatorName(operator_name),
223 callee(cxxMethodDecl(ofClass(optionalClass()))),
224 Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
225 }
226
isMakeOptionalCall()227 auto isMakeOptionalCall() {
228 return callExpr(
229 callee(functionDecl(hasAnyName(
230 "std::make_optional", "base::make_optional", "absl::make_optional",
231 "folly::make_optional", "bsl::make_optional"))),
232 hasOptionalType());
233 }
234
nulloptTypeDecl()235 auto nulloptTypeDecl() {
236 return namedDecl(hasAnyName("std::nullopt_t", "absl::nullopt_t",
237 "base::nullopt_t", "folly::None",
238 "bsl::nullopt_t"));
239 }
240
hasNulloptType()241 auto hasNulloptType() { return hasType(nulloptTypeDecl()); }
242
inPlaceClass()243 auto inPlaceClass() {
244 return recordDecl(hasAnyName("std::in_place_t", "absl::in_place_t",
245 "base::in_place_t", "folly::in_place_t",
246 "bsl::in_place_t"));
247 }
248
isOptionalNulloptConstructor()249 auto isOptionalNulloptConstructor() {
250 return cxxConstructExpr(
251 hasDeclaration(cxxConstructorDecl(parameterCountIs(1),
252 hasParameter(0, hasNulloptType()))),
253 hasOptionalOrDerivedType());
254 }
255
isOptionalInPlaceConstructor()256 auto isOptionalInPlaceConstructor() {
257 return cxxConstructExpr(hasArgument(0, hasType(inPlaceClass())),
258 hasOptionalOrDerivedType());
259 }
260
isOptionalValueOrConversionConstructor()261 auto isOptionalValueOrConversionConstructor() {
262 return cxxConstructExpr(
263 unless(hasDeclaration(
264 cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
265 argumentCountIs(1), hasArgument(0, unless(hasNulloptType())),
266 hasOptionalOrDerivedType());
267 }
268
isOptionalValueOrConversionAssignment()269 auto isOptionalValueOrConversionAssignment() {
270 return cxxOperatorCallExpr(
271 hasOverloadedOperatorName("="),
272 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
273 unless(hasDeclaration(cxxMethodDecl(
274 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
275 argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
276 }
277
isOptionalNulloptAssignment()278 auto isOptionalNulloptAssignment() {
279 return cxxOperatorCallExpr(
280 hasOverloadedOperatorName("="),
281 callee(cxxMethodDecl(ofClass(optionalOrDerivedClass()))),
282 argumentCountIs(2), hasArgument(1, hasNulloptType()));
283 }
284
isStdSwapCall()285 auto isStdSwapCall() {
286 return callExpr(callee(functionDecl(hasName("std::swap"))),
287 argumentCountIs(2),
288 hasArgument(0, hasOptionalOrDerivedType()),
289 hasArgument(1, hasOptionalOrDerivedType()));
290 }
291
isStdForwardCall()292 auto isStdForwardCall() {
293 return callExpr(callee(functionDecl(hasName("std::forward"))),
294 argumentCountIs(1),
295 hasArgument(0, hasOptionalOrDerivedType()));
296 }
297
298 constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
299
isValueOrStringEmptyCall()300 auto isValueOrStringEmptyCall() {
301 // `opt.value_or("").empty()`
302 return cxxMemberCallExpr(
303 callee(cxxMethodDecl(hasName("empty"))),
304 onImplicitObjectArgument(ignoringImplicit(
305 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
306 callee(cxxMethodDecl(hasName("value_or"),
307 ofClass(optionalClass()))),
308 hasArgument(0, stringLiteral(hasSize(0))))
309 .bind(ValueOrCallID))));
310 }
311
isValueOrNotEqX()312 auto isValueOrNotEqX() {
313 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
314 return hasOperands(
315 ignoringImplicit(
316 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
317 callee(cxxMethodDecl(hasName("value_or"),
318 ofClass(optionalClass()))),
319 hasArgument(0, Arg))
320 .bind(ValueOrCallID)),
321 ignoringImplicit(Arg));
322 };
323
324 // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
325 // support this pattern for any expression, but the AST does not have a
326 // generic expression comparison facility, so we specialize to common cases
327 // seen in practice. FIXME: define a matcher that compares values across
328 // nodes, which would let us generalize this to any `X`.
329 return binaryOperation(hasOperatorName("!="),
330 anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
331 ComparesToSame(stringLiteral(hasSize(0))),
332 ComparesToSame(integerLiteral(equals(0)))));
333 }
334
isZeroParamConstMemberCall()335 auto isZeroParamConstMemberCall() {
336 return cxxMemberCallExpr(
337 callee(cxxMethodDecl(parameterCountIs(0), isConst())));
338 }
339
isZeroParamConstMemberOperatorCall()340 auto isZeroParamConstMemberOperatorCall() {
341 return cxxOperatorCallExpr(
342 callee(cxxMethodDecl(parameterCountIs(0), isConst())));
343 }
344
isNonConstMemberCall()345 auto isNonConstMemberCall() {
346 return cxxMemberCallExpr(callee(cxxMethodDecl(unless(isConst()))));
347 }
348
isNonConstMemberOperatorCall()349 auto isNonConstMemberOperatorCall() {
350 return cxxOperatorCallExpr(callee(cxxMethodDecl(unless(isConst()))));
351 }
352
isCallReturningOptional()353 auto isCallReturningOptional() {
354 return callExpr(hasType(qualType(
355 anyOf(desugarsToOptionalOrDerivedType(),
356 referenceType(pointee(desugarsToOptionalOrDerivedType()))))));
357 }
358
359 template <typename L, typename R>
isComparisonOperatorCall(L lhs_arg_matcher,R rhs_arg_matcher)360 auto isComparisonOperatorCall(L lhs_arg_matcher, R rhs_arg_matcher) {
361 return cxxOperatorCallExpr(
362 anyOf(hasOverloadedOperatorName("=="), hasOverloadedOperatorName("!=")),
363 argumentCountIs(2), hasArgument(0, lhs_arg_matcher),
364 hasArgument(1, rhs_arg_matcher));
365 }
366
367 /// Ensures that `Expr` is mapped to a `BoolValue` and returns its formula.
forceBoolValue(Environment & Env,const Expr & Expr)368 const Formula &forceBoolValue(Environment &Env, const Expr &Expr) {
369 auto *Value = Env.get<BoolValue>(Expr);
370 if (Value != nullptr)
371 return Value->formula();
372
373 Value = &Env.makeAtomicBoolValue();
374 Env.setValue(Expr, *Value);
375 return Value->formula();
376 }
377
locForHasValue(const RecordStorageLocation & OptionalLoc)378 StorageLocation &locForHasValue(const RecordStorageLocation &OptionalLoc) {
379 return OptionalLoc.getSyntheticField("has_value");
380 }
381
locForValue(const RecordStorageLocation & OptionalLoc)382 StorageLocation &locForValue(const RecordStorageLocation &OptionalLoc) {
383 return OptionalLoc.getSyntheticField("value");
384 }
385
386 /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
387 /// property of the optional at `OptionalLoc`.
setHasValue(RecordStorageLocation & OptionalLoc,BoolValue & HasValueVal,Environment & Env)388 void setHasValue(RecordStorageLocation &OptionalLoc, BoolValue &HasValueVal,
389 Environment &Env) {
390 Env.setValue(locForHasValue(OptionalLoc), HasValueVal);
391 }
392
393 /// Returns the symbolic value that represents the "has_value" property of the
394 /// optional at `OptionalLoc`. Returns null if `OptionalLoc` is null.
getHasValue(Environment & Env,RecordStorageLocation * OptionalLoc)395 BoolValue *getHasValue(Environment &Env, RecordStorageLocation *OptionalLoc) {
396 if (OptionalLoc == nullptr)
397 return nullptr;
398 StorageLocation &HasValueLoc = locForHasValue(*OptionalLoc);
399 auto *HasValueVal = Env.get<BoolValue>(HasValueLoc);
400 if (HasValueVal == nullptr) {
401 HasValueVal = &Env.makeAtomicBoolValue();
402 Env.setValue(HasValueLoc, *HasValueVal);
403 }
404 return HasValueVal;
405 }
406
valueTypeFromOptionalDecl(const CXXRecordDecl & RD)407 QualType valueTypeFromOptionalDecl(const CXXRecordDecl &RD) {
408 auto &CTSD = cast<ClassTemplateSpecializationDecl>(RD);
409 return CTSD.getTemplateArgs()[0].getAsType();
410 }
411
412 /// Returns the number of optional wrappers in `Type`.
413 ///
414 /// For example, if `Type` is `optional<optional<int>>`, the result of this
415 /// function will be 2.
countOptionalWrappers(const ASTContext & ASTCtx,QualType Type)416 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
417 const CXXRecordDecl *Optional =
418 getOptionalBaseClass(Type->getAsCXXRecordDecl());
419 if (Optional == nullptr)
420 return 0;
421 return 1 + countOptionalWrappers(
422 ASTCtx,
423 valueTypeFromOptionalDecl(*Optional).getDesugaredType(ASTCtx));
424 }
425
getLocBehindPossiblePointer(const Expr & E,const Environment & Env)426 StorageLocation *getLocBehindPossiblePointer(const Expr &E,
427 const Environment &Env) {
428 if (E.isPRValue()) {
429 if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Env.getValue(E)))
430 return &PointerVal->getPointeeLoc();
431 return nullptr;
432 }
433 return Env.getStorageLocation(E);
434 }
435
transferUnwrapCall(const Expr * UnwrapExpr,const Expr * ObjectExpr,LatticeTransferState & State)436 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
437 LatticeTransferState &State) {
438 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
439 getLocBehindPossiblePointer(*ObjectExpr, State.Env))) {
440 if (State.Env.getStorageLocation(*UnwrapExpr) == nullptr)
441 State.Env.setStorageLocation(*UnwrapExpr, locForValue(*OptionalLoc));
442 }
443 }
444
transferArrowOpCall(const Expr * UnwrapExpr,const Expr * ObjectExpr,LatticeTransferState & State)445 void transferArrowOpCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
446 LatticeTransferState &State) {
447 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
448 getLocBehindPossiblePointer(*ObjectExpr, State.Env)))
449 State.Env.setValue(
450 *UnwrapExpr, State.Env.create<PointerValue>(locForValue(*OptionalLoc)));
451 }
452
transferMakeOptionalCall(const CallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)453 void transferMakeOptionalCall(const CallExpr *E,
454 const MatchFinder::MatchResult &,
455 LatticeTransferState &State) {
456 setHasValue(State.Env.getResultObjectLocation(*E),
457 State.Env.getBoolLiteralValue(true), State.Env);
458 }
459
transferOptionalHasValueCall(const CXXMemberCallExpr * CallExpr,const MatchFinder::MatchResult &,LatticeTransferState & State)460 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
461 const MatchFinder::MatchResult &,
462 LatticeTransferState &State) {
463 if (auto *HasValueVal = getHasValue(
464 State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
465 State.Env.setValue(*CallExpr, *HasValueVal);
466 }
467 }
468
transferOptionalIsNullCall(const CXXMemberCallExpr * CallExpr,const MatchFinder::MatchResult &,LatticeTransferState & State)469 void transferOptionalIsNullCall(const CXXMemberCallExpr *CallExpr,
470 const MatchFinder::MatchResult &,
471 LatticeTransferState &State) {
472 if (auto *HasValueVal = getHasValue(
473 State.Env, getImplicitObjectLocation(*CallExpr, State.Env))) {
474 State.Env.setValue(*CallExpr, State.Env.makeNot(*HasValueVal));
475 }
476 }
477
478 /// `ModelPred` builds a logical formula relating the predicate in
479 /// `ValueOrPredExpr` to the optional's `has_value` property.
transferValueOrImpl(const clang::Expr * ValueOrPredExpr,const MatchFinder::MatchResult & Result,LatticeTransferState & State,const Formula & (* ModelPred)(Environment & Env,const Formula & ExprVal,const Formula & HasValueVal))480 void transferValueOrImpl(
481 const clang::Expr *ValueOrPredExpr, const MatchFinder::MatchResult &Result,
482 LatticeTransferState &State,
483 const Formula &(*ModelPred)(Environment &Env, const Formula &ExprVal,
484 const Formula &HasValueVal)) {
485 auto &Env = State.Env;
486
487 const auto *MCE =
488 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID);
489
490 auto *HasValueVal =
491 getHasValue(State.Env, getImplicitObjectLocation(*MCE, State.Env));
492 if (HasValueVal == nullptr)
493 return;
494
495 Env.assume(ModelPred(Env, forceBoolValue(Env, *ValueOrPredExpr),
496 HasValueVal->formula()));
497 }
498
transferValueOrStringEmptyCall(const clang::Expr * ComparisonExpr,const MatchFinder::MatchResult & Result,LatticeTransferState & State)499 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
500 const MatchFinder::MatchResult &Result,
501 LatticeTransferState &State) {
502 return transferValueOrImpl(ComparisonExpr, Result, State,
503 [](Environment &Env, const Formula &ExprVal,
504 const Formula &HasValueVal) -> const Formula & {
505 auto &A = Env.arena();
506 // If the result is *not* empty, then we know the
507 // optional must have been holding a value. If
508 // `ExprVal` is true, though, we don't learn
509 // anything definite about `has_value`, so we
510 // don't add any corresponding implications to
511 // the flow condition.
512 return A.makeImplies(A.makeNot(ExprVal),
513 HasValueVal);
514 });
515 }
516
transferValueOrNotEqX(const Expr * ComparisonExpr,const MatchFinder::MatchResult & Result,LatticeTransferState & State)517 void transferValueOrNotEqX(const Expr *ComparisonExpr,
518 const MatchFinder::MatchResult &Result,
519 LatticeTransferState &State) {
520 transferValueOrImpl(ComparisonExpr, Result, State,
521 [](Environment &Env, const Formula &ExprVal,
522 const Formula &HasValueVal) -> const Formula & {
523 auto &A = Env.arena();
524 // We know that if `(opt.value_or(X) != X)` then
525 // `opt.hasValue()`, even without knowing further
526 // details about the contents of `opt`.
527 return A.makeImplies(ExprVal, HasValueVal);
528 });
529 }
530
transferCallReturningOptional(const CallExpr * E,const MatchFinder::MatchResult & Result,LatticeTransferState & State)531 void transferCallReturningOptional(const CallExpr *E,
532 const MatchFinder::MatchResult &Result,
533 LatticeTransferState &State) {
534 RecordStorageLocation *Loc = nullptr;
535 if (E->isPRValue()) {
536 Loc = &State.Env.getResultObjectLocation(*E);
537 } else {
538 Loc = State.Env.get<RecordStorageLocation>(*E);
539 if (Loc == nullptr) {
540 Loc = &cast<RecordStorageLocation>(State.Env.createStorageLocation(*E));
541 State.Env.setStorageLocation(*E, *Loc);
542 }
543 }
544
545 if (State.Env.getValue(locForHasValue(*Loc)) != nullptr)
546 return;
547
548 setHasValue(*Loc, State.Env.makeAtomicBoolValue(), State.Env);
549 }
550
551 // Returns true if the const accessor is handled by caching.
552 // Returns false if we could not cache. We should perform default handling
553 // in that case.
handleConstMemberCall(const CallExpr * CE,dataflow::RecordStorageLocation * RecordLoc,const MatchFinder::MatchResult & Result,LatticeTransferState & State)554 bool handleConstMemberCall(const CallExpr *CE,
555 dataflow::RecordStorageLocation *RecordLoc,
556 const MatchFinder::MatchResult &Result,
557 LatticeTransferState &State) {
558 if (RecordLoc == nullptr)
559 return false;
560
561 // Cache if the const method returns a reference.
562 if (CE->isGLValue()) {
563 const FunctionDecl *DirectCallee = CE->getDirectCallee();
564 if (DirectCallee == nullptr)
565 return false;
566
567 // Initialize the optional's "has_value" property to true if the type is
568 // optional, otherwise no-op. If we want to support const ref to pointers or
569 // bools we should initialize their values here too.
570 auto Init = [&](StorageLocation &Loc) {
571 if (isSupportedOptionalType(CE->getType()))
572 setHasValue(cast<RecordStorageLocation>(Loc),
573 State.Env.makeAtomicBoolValue(), State.Env);
574 };
575 StorageLocation &Loc =
576 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
577 *RecordLoc, DirectCallee, State.Env, Init);
578
579 State.Env.setStorageLocation(*CE, Loc);
580 return true;
581 }
582 // PRValue cases:
583 if (CE->getType()->isBooleanType() || CE->getType()->isPointerType()) {
584 // If the const method returns a boolean or pointer type.
585 Value *Val = State.Lattice.getOrCreateConstMethodReturnValue(*RecordLoc, CE,
586 State.Env);
587 if (Val == nullptr)
588 return false;
589 State.Env.setValue(*CE, *Val);
590 return true;
591 }
592 if (isSupportedOptionalType(CE->getType())) {
593 // If the const method returns an optional by value.
594 const FunctionDecl *DirectCallee = CE->getDirectCallee();
595 if (DirectCallee == nullptr)
596 return false;
597 StorageLocation &Loc =
598 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
599 *RecordLoc, DirectCallee, State.Env, [&](StorageLocation &Loc) {
600 setHasValue(cast<RecordStorageLocation>(Loc),
601 State.Env.makeAtomicBoolValue(), State.Env);
602 });
603 // Use copyRecord to link the optional to the result object of the call
604 // expression.
605 auto &ResultLoc = State.Env.getResultObjectLocation(*CE);
606 copyRecord(cast<RecordStorageLocation>(Loc), ResultLoc, State.Env);
607 return true;
608 }
609
610 return false;
611 }
612
handleConstMemberCallWithFallbacks(const CallExpr * CE,dataflow::RecordStorageLocation * RecordLoc,const MatchFinder::MatchResult & Result,LatticeTransferState & State)613 void handleConstMemberCallWithFallbacks(
614 const CallExpr *CE, dataflow::RecordStorageLocation *RecordLoc,
615 const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
616 if (handleConstMemberCall(CE, RecordLoc, Result, State))
617 return;
618 // Perform default handling if the call returns an optional, but wasn't
619 // handled by caching.
620 if (isSupportedOptionalType(CE->getType()))
621 transferCallReturningOptional(CE, Result, State);
622 }
623
transferConstMemberCall(const CXXMemberCallExpr * MCE,const MatchFinder::MatchResult & Result,LatticeTransferState & State)624 void transferConstMemberCall(const CXXMemberCallExpr *MCE,
625 const MatchFinder::MatchResult &Result,
626 LatticeTransferState &State) {
627 handleConstMemberCallWithFallbacks(
628 MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
629 }
630
transferConstMemberOperatorCall(const CXXOperatorCallExpr * OCE,const MatchFinder::MatchResult & Result,LatticeTransferState & State)631 void transferConstMemberOperatorCall(const CXXOperatorCallExpr *OCE,
632 const MatchFinder::MatchResult &Result,
633 LatticeTransferState &State) {
634 auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
635 State.Env.getStorageLocation(*OCE->getArg(0)));
636 handleConstMemberCallWithFallbacks(OCE, RecordLoc, Result, State);
637 }
638
handleNonConstMemberCall(const CallExpr * CE,dataflow::RecordStorageLocation * RecordLoc,const MatchFinder::MatchResult & Result,LatticeTransferState & State)639 void handleNonConstMemberCall(const CallExpr *CE,
640 dataflow::RecordStorageLocation *RecordLoc,
641 const MatchFinder::MatchResult &Result,
642 LatticeTransferState &State) {
643 if (RecordLoc != nullptr) {
644 // When a non-const member function is called, clear all (non-const)
645 // optional fields of the receiver. Const-qualified fields can't be
646 // changed (at least, not without UB).
647 for (const auto &[Field, FieldLoc] : RecordLoc->children()) {
648 QualType FieldType = Field->getType();
649 if (!FieldType.isConstQualified() &&
650 isSupportedOptionalType(Field->getType())) {
651 auto *FieldRecordLoc = cast_or_null<RecordStorageLocation>(FieldLoc);
652 if (FieldRecordLoc) {
653 setHasValue(*FieldRecordLoc, State.Env.makeAtomicBoolValue(),
654 State.Env);
655 }
656 }
657 }
658 State.Lattice.clearConstMethodReturnValues(*RecordLoc);
659 State.Lattice.clearConstMethodReturnStorageLocations(*RecordLoc);
660 }
661
662 // Perform default handling if the call returns an optional.
663 if (isSupportedOptionalType(CE->getType())) {
664 transferCallReturningOptional(CE, Result, State);
665 }
666 }
667
transferValue_NonConstMemberCall(const CXXMemberCallExpr * MCE,const MatchFinder::MatchResult & Result,LatticeTransferState & State)668 void transferValue_NonConstMemberCall(const CXXMemberCallExpr *MCE,
669 const MatchFinder::MatchResult &Result,
670 LatticeTransferState &State) {
671 handleNonConstMemberCall(
672 MCE, dataflow::getImplicitObjectLocation(*MCE, State.Env), Result, State);
673 }
674
transferValue_NonConstMemberOperatorCall(const CXXOperatorCallExpr * OCE,const MatchFinder::MatchResult & Result,LatticeTransferState & State)675 void transferValue_NonConstMemberOperatorCall(
676 const CXXOperatorCallExpr *OCE, const MatchFinder::MatchResult &Result,
677 LatticeTransferState &State) {
678 auto *RecordLoc = cast_or_null<dataflow::RecordStorageLocation>(
679 State.Env.getStorageLocation(*OCE->getArg(0)));
680 handleNonConstMemberCall(OCE, RecordLoc, Result, State);
681 }
682
constructOptionalValue(const Expr & E,Environment & Env,BoolValue & HasValueVal)683 void constructOptionalValue(const Expr &E, Environment &Env,
684 BoolValue &HasValueVal) {
685 RecordStorageLocation &Loc = Env.getResultObjectLocation(E);
686 setHasValue(Loc, HasValueVal, Env);
687 }
688
689 /// Returns a symbolic value for the "has_value" property of an `optional<T>`
690 /// value that is constructed/assigned from a value of type `U` or `optional<U>`
691 /// where `T` is constructible from `U`.
valueOrConversionHasValue(QualType DestType,const Expr & E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)692 BoolValue &valueOrConversionHasValue(QualType DestType, const Expr &E,
693 const MatchFinder::MatchResult &MatchRes,
694 LatticeTransferState &State) {
695 const int DestTypeOptionalWrappersCount =
696 countOptionalWrappers(*MatchRes.Context, DestType);
697 const int ArgTypeOptionalWrappersCount = countOptionalWrappers(
698 *MatchRes.Context, E.getType().getNonReferenceType());
699
700 // Is this an constructor of the form `template<class U> optional(U &&)` /
701 // assignment of the form `template<class U> optional& operator=(U &&)`
702 // (where `T` is assignable / constructible from `U`)?
703 // We recognize this because the number of optionals in the optional being
704 // assigned to is different from the function argument type.
705 if (DestTypeOptionalWrappersCount != ArgTypeOptionalWrappersCount)
706 return State.Env.getBoolLiteralValue(true);
707
708 // Otherwise, this must be a constructor of the form
709 // `template <class U> optional<optional<U> &&)` / assignment of the form
710 // `template <class U> optional& operator=(optional<U> &&)
711 // (where, again, `T` is assignable / constructible from `U`).
712 auto *Loc = State.Env.get<RecordStorageLocation>(E);
713 if (auto *HasValueVal = getHasValue(State.Env, Loc))
714 return *HasValueVal;
715 return State.Env.makeAtomicBoolValue();
716 }
717
transferValueOrConversionConstructor(const CXXConstructExpr * E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)718 void transferValueOrConversionConstructor(
719 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
720 LatticeTransferState &State) {
721 assert(E->getNumArgs() > 0);
722
723 constructOptionalValue(
724 *E, State.Env,
725 valueOrConversionHasValue(
726 E->getConstructor()->getThisType()->getPointeeType(), *E->getArg(0),
727 MatchRes, State));
728 }
729
transferAssignment(const CXXOperatorCallExpr * E,BoolValue & HasValueVal,LatticeTransferState & State)730 void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
731 LatticeTransferState &State) {
732 assert(E->getNumArgs() > 0);
733
734 if (auto *Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0))) {
735 setHasValue(*Loc, HasValueVal, State.Env);
736
737 // Assign a storage location for the whole expression.
738 State.Env.setStorageLocation(*E, *Loc);
739 }
740 }
741
transferValueOrConversionAssignment(const CXXOperatorCallExpr * E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)742 void transferValueOrConversionAssignment(
743 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
744 LatticeTransferState &State) {
745 assert(E->getNumArgs() > 1);
746 transferAssignment(
747 E,
748 valueOrConversionHasValue(E->getArg(0)->getType().getNonReferenceType(),
749 *E->getArg(1), MatchRes, State),
750 State);
751 }
752
transferNulloptAssignment(const CXXOperatorCallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)753 void transferNulloptAssignment(const CXXOperatorCallExpr *E,
754 const MatchFinder::MatchResult &,
755 LatticeTransferState &State) {
756 transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
757 }
758
transferSwap(RecordStorageLocation * Loc1,RecordStorageLocation * Loc2,Environment & Env)759 void transferSwap(RecordStorageLocation *Loc1, RecordStorageLocation *Loc2,
760 Environment &Env) {
761 // We account for cases where one or both of the optionals are not modeled,
762 // either lacking associated storage locations, or lacking values associated
763 // to such storage locations.
764
765 if (Loc1 == nullptr) {
766 if (Loc2 != nullptr)
767 setHasValue(*Loc2, Env.makeAtomicBoolValue(), Env);
768 return;
769 }
770 if (Loc2 == nullptr) {
771 setHasValue(*Loc1, Env.makeAtomicBoolValue(), Env);
772 return;
773 }
774
775 // Both expressions have locations, though they may not have corresponding
776 // values. In that case, we create a fresh value at this point. Note that if
777 // two branches both do this, they will not share the value, but it at least
778 // allows for local reasoning about the value. To avoid the above, we would
779 // need *lazy* value allocation.
780 // FIXME: allocate values lazily, instead of just creating a fresh value.
781 BoolValue *BoolVal1 = getHasValue(Env, Loc1);
782 if (BoolVal1 == nullptr)
783 BoolVal1 = &Env.makeAtomicBoolValue();
784
785 BoolValue *BoolVal2 = getHasValue(Env, Loc2);
786 if (BoolVal2 == nullptr)
787 BoolVal2 = &Env.makeAtomicBoolValue();
788
789 setHasValue(*Loc1, *BoolVal2, Env);
790 setHasValue(*Loc2, *BoolVal1, Env);
791 }
792
transferSwapCall(const CXXMemberCallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)793 void transferSwapCall(const CXXMemberCallExpr *E,
794 const MatchFinder::MatchResult &,
795 LatticeTransferState &State) {
796 assert(E->getNumArgs() == 1);
797 auto *OtherLoc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
798 transferSwap(getImplicitObjectLocation(*E, State.Env), OtherLoc, State.Env);
799 }
800
transferStdSwapCall(const CallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)801 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
802 LatticeTransferState &State) {
803 assert(E->getNumArgs() == 2);
804 auto *Arg0Loc = State.Env.get<RecordStorageLocation>(*E->getArg(0));
805 auto *Arg1Loc = State.Env.get<RecordStorageLocation>(*E->getArg(1));
806 transferSwap(Arg0Loc, Arg1Loc, State.Env);
807 }
808
transferStdForwardCall(const CallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)809 void transferStdForwardCall(const CallExpr *E, const MatchFinder::MatchResult &,
810 LatticeTransferState &State) {
811 assert(E->getNumArgs() == 1);
812
813 if (auto *Loc = State.Env.getStorageLocation(*E->getArg(0)))
814 State.Env.setStorageLocation(*E, *Loc);
815 }
816
evaluateEquality(Arena & A,const Formula & EqVal,const Formula & LHS,const Formula & RHS)817 const Formula &evaluateEquality(Arena &A, const Formula &EqVal,
818 const Formula &LHS, const Formula &RHS) {
819 // Logically, an optional<T> object is composed of two values - a `has_value`
820 // bit and a value of type T. Equality of optional objects compares both
821 // values. Therefore, merely comparing the `has_value` bits isn't sufficient:
822 // when two optional objects are engaged, the equality of their respective
823 // values of type T matters. Since we only track the `has_value` bits, we
824 // can't make any conclusions about equality when we know that two optional
825 // objects are engaged.
826 //
827 // We express this as two facts about the equality:
828 // a) EqVal => (LHS & RHS) v (!RHS & !LHS)
829 // If they are equal, then either both are set or both are unset.
830 // b) (!LHS & !RHS) => EqVal
831 // If neither is set, then they are equal.
832 // We rewrite b) as !EqVal => (LHS v RHS), for a more compact formula.
833 return A.makeAnd(
834 A.makeImplies(EqVal, A.makeOr(A.makeAnd(LHS, RHS),
835 A.makeAnd(A.makeNot(LHS), A.makeNot(RHS)))),
836 A.makeImplies(A.makeNot(EqVal), A.makeOr(LHS, RHS)));
837 }
838
transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr * CmpExpr,const MatchFinder::MatchResult &,LatticeTransferState & State)839 void transferOptionalAndOptionalCmp(const clang::CXXOperatorCallExpr *CmpExpr,
840 const MatchFinder::MatchResult &,
841 LatticeTransferState &State) {
842 Environment &Env = State.Env;
843 auto &A = Env.arena();
844 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
845 auto *Arg0Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(0));
846 if (auto *LHasVal = getHasValue(Env, Arg0Loc)) {
847 auto *Arg1Loc = Env.get<RecordStorageLocation>(*CmpExpr->getArg(1));
848 if (auto *RHasVal = getHasValue(Env, Arg1Loc)) {
849 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
850 CmpValue = &A.makeNot(*CmpValue);
851 Env.assume(evaluateEquality(A, *CmpValue, LHasVal->formula(),
852 RHasVal->formula()));
853 }
854 }
855 }
856
transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr * CmpExpr,const clang::Expr * E,Environment & Env)857 void transferOptionalAndValueCmp(const clang::CXXOperatorCallExpr *CmpExpr,
858 const clang::Expr *E, Environment &Env) {
859 auto &A = Env.arena();
860 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
861 auto *Loc = Env.get<RecordStorageLocation>(*E);
862 if (auto *HasVal = getHasValue(Env, Loc)) {
863 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
864 CmpValue = &A.makeNot(*CmpValue);
865 Env.assume(
866 evaluateEquality(A, *CmpValue, HasVal->formula(), A.makeLiteral(true)));
867 }
868 }
869
transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr * CmpExpr,const clang::Expr * E,Environment & Env)870 void transferOptionalAndNulloptCmp(const clang::CXXOperatorCallExpr *CmpExpr,
871 const clang::Expr *E, Environment &Env) {
872 auto &A = Env.arena();
873 auto *CmpValue = &forceBoolValue(Env, *CmpExpr);
874 auto *Loc = Env.get<RecordStorageLocation>(*E);
875 if (auto *HasVal = getHasValue(Env, Loc)) {
876 if (CmpExpr->getOperator() == clang::OO_ExclaimEqual)
877 CmpValue = &A.makeNot(*CmpValue);
878 Env.assume(evaluateEquality(A, *CmpValue, HasVal->formula(),
879 A.makeLiteral(false)));
880 }
881 }
882
883 std::optional<StatementMatcher>
ignorableOptional(const UncheckedOptionalAccessModelOptions & Options)884 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
885 if (Options.IgnoreSmartPointerDereference) {
886 auto SmartPtrUse = expr(ignoringParenImpCasts(cxxOperatorCallExpr(
887 anyOf(hasOverloadedOperatorName("->"), hasOverloadedOperatorName("*")),
888 unless(hasArgument(0, expr(hasOptionalType()))))));
889 return expr(
890 anyOf(SmartPtrUse, memberExpr(hasObjectExpression(SmartPtrUse))));
891 }
892 return std::nullopt;
893 }
894
895 StatementMatcher
valueCall(const std::optional<StatementMatcher> & IgnorableOptional)896 valueCall(const std::optional<StatementMatcher> &IgnorableOptional) {
897 return isOptionalMemberCallWithNameMatcher(hasName("value"),
898 IgnorableOptional);
899 }
900
901 StatementMatcher
valueOperatorCall(const std::optional<StatementMatcher> & IgnorableOptional)902 valueOperatorCall(const std::optional<StatementMatcher> &IgnorableOptional) {
903 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
904 isOptionalOperatorCallWithName("->", IgnorableOptional)));
905 }
906
buildTransferMatchSwitch()907 auto buildTransferMatchSwitch() {
908 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
909 // lot of duplicated work (e.g. string comparisons), consider providing APIs
910 // that avoid it through memoization.
911 return CFGMatchSwitchBuilder<LatticeTransferState>()
912 // make_optional
913 .CaseOfCFGStmt<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
914
915 // optional::optional (in place)
916 .CaseOfCFGStmt<CXXConstructExpr>(
917 isOptionalInPlaceConstructor(),
918 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
919 LatticeTransferState &State) {
920 constructOptionalValue(*E, State.Env,
921 State.Env.getBoolLiteralValue(true));
922 })
923 // optional::optional(nullopt_t)
924 .CaseOfCFGStmt<CXXConstructExpr>(
925 isOptionalNulloptConstructor(),
926 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
927 LatticeTransferState &State) {
928 constructOptionalValue(*E, State.Env,
929 State.Env.getBoolLiteralValue(false));
930 })
931 // optional::optional (value/conversion)
932 .CaseOfCFGStmt<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
933 transferValueOrConversionConstructor)
934
935 // optional::operator=
936 .CaseOfCFGStmt<CXXOperatorCallExpr>(
937 isOptionalValueOrConversionAssignment(),
938 transferValueOrConversionAssignment)
939 .CaseOfCFGStmt<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
940 transferNulloptAssignment)
941
942 // optional::value
943 .CaseOfCFGStmt<CXXMemberCallExpr>(
944 valueCall(std::nullopt),
945 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
946 LatticeTransferState &State) {
947 transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
948 })
949
950 // optional::operator*
951 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("*"),
952 [](const CallExpr *E,
953 const MatchFinder::MatchResult &,
954 LatticeTransferState &State) {
955 transferUnwrapCall(E, E->getArg(0), State);
956 })
957
958 // optional::operator->
959 .CaseOfCFGStmt<CallExpr>(isOptionalOperatorCallWithName("->"),
960 [](const CallExpr *E,
961 const MatchFinder::MatchResult &,
962 LatticeTransferState &State) {
963 transferArrowOpCall(E, E->getArg(0), State);
964 })
965
966 // optional::has_value, optional::hasValue
967 // Of the supported optionals only folly::Optional uses hasValue, but this
968 // will also pass for other types
969 .CaseOfCFGStmt<CXXMemberCallExpr>(
970 isOptionalMemberCallWithNameMatcher(
971 hasAnyName("has_value", "hasValue")),
972 transferOptionalHasValueCall)
973
974 // optional::operator bool
975 .CaseOfCFGStmt<CXXMemberCallExpr>(
976 isOptionalMemberCallWithNameMatcher(hasName("operator bool")),
977 transferOptionalHasValueCall)
978
979 // NullableValue::isNull
980 // Only NullableValue has isNull
981 .CaseOfCFGStmt<CXXMemberCallExpr>(
982 isOptionalMemberCallWithNameMatcher(hasName("isNull")),
983 transferOptionalIsNullCall)
984
985 // optional::emplace
986 .CaseOfCFGStmt<CXXMemberCallExpr>(
987 isOptionalMemberCallWithNameMatcher(hasName("emplace")),
988 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
989 LatticeTransferState &State) {
990 if (RecordStorageLocation *Loc =
991 getImplicitObjectLocation(*E, State.Env)) {
992 setHasValue(*Loc, State.Env.getBoolLiteralValue(true), State.Env);
993 }
994 })
995
996 // optional::reset
997 .CaseOfCFGStmt<CXXMemberCallExpr>(
998 isOptionalMemberCallWithNameMatcher(hasName("reset")),
999 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
1000 LatticeTransferState &State) {
1001 if (RecordStorageLocation *Loc =
1002 getImplicitObjectLocation(*E, State.Env)) {
1003 setHasValue(*Loc, State.Env.getBoolLiteralValue(false),
1004 State.Env);
1005 }
1006 })
1007
1008 // optional::swap
1009 .CaseOfCFGStmt<CXXMemberCallExpr>(
1010 isOptionalMemberCallWithNameMatcher(hasName("swap")),
1011 transferSwapCall)
1012
1013 // std::swap
1014 .CaseOfCFGStmt<CallExpr>(isStdSwapCall(), transferStdSwapCall)
1015
1016 // std::forward
1017 .CaseOfCFGStmt<CallExpr>(isStdForwardCall(), transferStdForwardCall)
1018
1019 // opt.value_or("").empty()
1020 .CaseOfCFGStmt<Expr>(isValueOrStringEmptyCall(),
1021 transferValueOrStringEmptyCall)
1022
1023 // opt.value_or(X) != X
1024 .CaseOfCFGStmt<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
1025
1026 // Comparisons (==, !=):
1027 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1028 isComparisonOperatorCall(hasOptionalType(), hasOptionalType()),
1029 transferOptionalAndOptionalCmp)
1030 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1031 isComparisonOperatorCall(hasOptionalType(), hasNulloptType()),
1032 [](const clang::CXXOperatorCallExpr *Cmp,
1033 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1034 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(0), State.Env);
1035 })
1036 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1037 isComparisonOperatorCall(hasNulloptType(), hasOptionalType()),
1038 [](const clang::CXXOperatorCallExpr *Cmp,
1039 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1040 transferOptionalAndNulloptCmp(Cmp, Cmp->getArg(1), State.Env);
1041 })
1042 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1043 isComparisonOperatorCall(
1044 hasOptionalType(),
1045 unless(anyOf(hasOptionalType(), hasNulloptType()))),
1046 [](const clang::CXXOperatorCallExpr *Cmp,
1047 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1048 transferOptionalAndValueCmp(Cmp, Cmp->getArg(0), State.Env);
1049 })
1050 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1051 isComparisonOperatorCall(
1052 unless(anyOf(hasOptionalType(), hasNulloptType())),
1053 hasOptionalType()),
1054 [](const clang::CXXOperatorCallExpr *Cmp,
1055 const MatchFinder::MatchResult &, LatticeTransferState &State) {
1056 transferOptionalAndValueCmp(Cmp, Cmp->getArg(1), State.Env);
1057 })
1058
1059 // Smart-pointer-like operator* and operator-> calls that may look like
1060 // const accessors (below) but need special handling to allow mixing
1061 // the accessor calls.
1062 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1063 isSmartPointerLikeOperatorStar(),
1064 [](const CXXOperatorCallExpr *E,
1065 const MatchFinder::MatchResult &Result,
1066 LatticeTransferState &State) {
1067 transferSmartPointerLikeCachedDeref(
1068 E,
1069 dyn_cast_or_null<RecordStorageLocation>(
1070 getLocBehindPossiblePointer(*E->getArg(0), State.Env)),
1071 State, [](StorageLocation &Loc) {});
1072 })
1073 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1074 isSmartPointerLikeOperatorArrow(),
1075 [](const CXXOperatorCallExpr *E,
1076 const MatchFinder::MatchResult &Result,
1077 LatticeTransferState &State) {
1078 transferSmartPointerLikeCachedGet(
1079 E,
1080 dyn_cast_or_null<RecordStorageLocation>(
1081 getLocBehindPossiblePointer(*E->getArg(0), State.Env)),
1082 State, [](StorageLocation &Loc) {});
1083 })
1084 .CaseOfCFGStmt<CXXMemberCallExpr>(
1085 isSmartPointerLikeValueMethodCall(),
1086 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
1087 LatticeTransferState &State) {
1088 transferSmartPointerLikeCachedDeref(
1089 E, getImplicitObjectLocation(*E, State.Env), State,
1090 [](StorageLocation &Loc) {});
1091 })
1092 .CaseOfCFGStmt<CXXMemberCallExpr>(
1093 isSmartPointerLikeGetMethodCall(),
1094 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
1095 LatticeTransferState &State) {
1096 transferSmartPointerLikeCachedGet(
1097 E, getImplicitObjectLocation(*E, State.Env), State,
1098 [](StorageLocation &Loc) {});
1099 })
1100
1101 // const accessor calls
1102 .CaseOfCFGStmt<CXXMemberCallExpr>(isZeroParamConstMemberCall(),
1103 transferConstMemberCall)
1104 .CaseOfCFGStmt<CXXOperatorCallExpr>(isZeroParamConstMemberOperatorCall(),
1105 transferConstMemberOperatorCall)
1106 // non-const member calls that may modify the state of an object.
1107 .CaseOfCFGStmt<CXXMemberCallExpr>(isNonConstMemberCall(),
1108 transferValue_NonConstMemberCall)
1109 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1110 isNonConstMemberOperatorCall(),
1111 transferValue_NonConstMemberOperatorCall)
1112
1113 // other cases of returning optional
1114 .CaseOfCFGStmt<CallExpr>(isCallReturningOptional(),
1115 transferCallReturningOptional)
1116
1117 .Build();
1118 }
1119
1120 llvm::SmallVector<UncheckedOptionalAccessDiagnostic>
diagnoseUnwrapCall(const Expr * ObjectExpr,const Environment & Env)1121 diagnoseUnwrapCall(const Expr *ObjectExpr, const Environment &Env) {
1122 if (auto *OptionalLoc = cast_or_null<RecordStorageLocation>(
1123 getLocBehindPossiblePointer(*ObjectExpr, Env))) {
1124 auto *Prop = Env.getValue(locForHasValue(*OptionalLoc));
1125 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
1126 if (Env.proves(HasValueVal->formula()))
1127 return {};
1128 }
1129 }
1130
1131 // Record that this unwrap is *not* provably safe.
1132 // FIXME: include the name of the optional (if applicable).
1133 auto Range = CharSourceRange::getTokenRange(ObjectExpr->getSourceRange());
1134 return {UncheckedOptionalAccessDiagnostic{Range}};
1135 }
1136
buildDiagnoseMatchSwitch(const UncheckedOptionalAccessModelOptions & Options)1137 auto buildDiagnoseMatchSwitch(
1138 const UncheckedOptionalAccessModelOptions &Options) {
1139 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
1140 // lot of duplicated work (e.g. string comparisons), consider providing APIs
1141 // that avoid it through memoization.
1142 auto IgnorableOptional = ignorableOptional(Options);
1143 return CFGMatchSwitchBuilder<
1144 const Environment,
1145 llvm::SmallVector<UncheckedOptionalAccessDiagnostic>>()
1146 // optional::value
1147 .CaseOfCFGStmt<CXXMemberCallExpr>(
1148 valueCall(IgnorableOptional),
1149 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
1150 const Environment &Env) {
1151 return diagnoseUnwrapCall(E->getImplicitObjectArgument(), Env);
1152 })
1153
1154 // optional::operator*, optional::operator->
1155 .CaseOfCFGStmt<CallExpr>(valueOperatorCall(IgnorableOptional),
1156 [](const CallExpr *E,
1157 const MatchFinder::MatchResult &,
1158 const Environment &Env) {
1159 return diagnoseUnwrapCall(E->getArg(0), Env);
1160 })
1161 .Build();
1162 }
1163
1164 } // namespace
1165
1166 ast_matchers::DeclarationMatcher
optionalClassDecl()1167 UncheckedOptionalAccessModel::optionalClassDecl() {
1168 return cxxRecordDecl(optionalClass());
1169 }
1170
UncheckedOptionalAccessModel(ASTContext & Ctx,Environment & Env)1171 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(ASTContext &Ctx,
1172 Environment &Env)
1173 : DataflowAnalysis<UncheckedOptionalAccessModel,
1174 UncheckedOptionalAccessLattice>(Ctx),
1175 TransferMatchSwitch(buildTransferMatchSwitch()) {
1176 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
1177 [&Ctx](QualType Ty) -> llvm::StringMap<QualType> {
1178 const CXXRecordDecl *Optional =
1179 getOptionalBaseClass(Ty->getAsCXXRecordDecl());
1180 if (Optional == nullptr)
1181 return {};
1182 return {{"value", valueTypeFromOptionalDecl(*Optional)},
1183 {"has_value", Ctx.BoolTy}};
1184 });
1185 }
1186
transfer(const CFGElement & Elt,UncheckedOptionalAccessLattice & L,Environment & Env)1187 void UncheckedOptionalAccessModel::transfer(const CFGElement &Elt,
1188 UncheckedOptionalAccessLattice &L,
1189 Environment &Env) {
1190 LatticeTransferState State(L, Env);
1191 TransferMatchSwitch(Elt, getASTContext(), State);
1192 }
1193
UncheckedOptionalAccessDiagnoser(UncheckedOptionalAccessModelOptions Options)1194 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
1195 UncheckedOptionalAccessModelOptions Options)
1196 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
1197
1198 } // namespace dataflow
1199 } // namespace clang
1200