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/ASTMatchers/ASTMatchers.h"
21 #include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
22 #include "clang/Analysis/FlowSensitive/MatchSwitch.h"
23 #include "clang/Analysis/FlowSensitive/NoopLattice.h"
24 #include "clang/Analysis/FlowSensitive/Value.h"
25 #include "clang/Basic/SourceLocation.h"
26 #include "llvm/ADT/StringRef.h"
27 #include "llvm/Support/Casting.h"
28 #include <cassert>
29 #include <memory>
30 #include <utility>
31 #include <vector>
32
33 namespace clang {
34 namespace dataflow {
35 namespace {
36
37 using namespace ::clang::ast_matchers;
38 using LatticeTransferState = TransferState<NoopLattice>;
39
optionalClass()40 DeclarationMatcher optionalClass() {
41 return classTemplateSpecializationDecl(
42 anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"),
43 hasName("__optional_destruct_base"), hasName("absl::optional"),
44 hasName("base::Optional")),
45 hasTemplateArgument(0, refersToType(type().bind("T"))));
46 }
47
optionalOrAliasType()48 auto optionalOrAliasType() {
49 return hasUnqualifiedDesugaredType(
50 recordType(hasDeclaration(optionalClass())));
51 }
52
53 /// Matches any of the spellings of the optional types and sugar, aliases, etc.
hasOptionalType()54 auto hasOptionalType() { return hasType(optionalOrAliasType()); }
55
isOptionalMemberCallWithName(llvm::StringRef MemberName,llvm::Optional<StatementMatcher> Ignorable=llvm::None)56 auto isOptionalMemberCallWithName(
57 llvm::StringRef MemberName,
58 llvm::Optional<StatementMatcher> Ignorable = llvm::None) {
59 auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr()))
60 : cxxThisExpr());
61 return cxxMemberCallExpr(
62 on(expr(Exception)),
63 callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass()))));
64 }
65
isOptionalOperatorCallWithName(llvm::StringRef operator_name,llvm::Optional<StatementMatcher> Ignorable=llvm::None)66 auto isOptionalOperatorCallWithName(
67 llvm::StringRef operator_name,
68 llvm::Optional<StatementMatcher> Ignorable = llvm::None) {
69 return cxxOperatorCallExpr(
70 hasOverloadedOperatorName(operator_name),
71 callee(cxxMethodDecl(ofClass(optionalClass()))),
72 Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr());
73 }
74
isMakeOptionalCall()75 auto isMakeOptionalCall() {
76 return callExpr(
77 callee(functionDecl(hasAnyName(
78 "std::make_optional", "base::make_optional", "absl::make_optional"))),
79 hasOptionalType());
80 }
81
hasNulloptType()82 auto hasNulloptType() {
83 return hasType(namedDecl(
84 hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t")));
85 }
86
inPlaceClass()87 auto inPlaceClass() {
88 return recordDecl(
89 hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t"));
90 }
91
isOptionalNulloptConstructor()92 auto isOptionalNulloptConstructor() {
93 return cxxConstructExpr(hasOptionalType(), argumentCountIs(1),
94 hasArgument(0, hasNulloptType()));
95 }
96
isOptionalInPlaceConstructor()97 auto isOptionalInPlaceConstructor() {
98 return cxxConstructExpr(hasOptionalType(),
99 hasArgument(0, hasType(inPlaceClass())));
100 }
101
isOptionalValueOrConversionConstructor()102 auto isOptionalValueOrConversionConstructor() {
103 return cxxConstructExpr(
104 hasOptionalType(),
105 unless(hasDeclaration(
106 cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))),
107 argumentCountIs(1), hasArgument(0, unless(hasNulloptType())));
108 }
109
isOptionalValueOrConversionAssignment()110 auto isOptionalValueOrConversionAssignment() {
111 return cxxOperatorCallExpr(
112 hasOverloadedOperatorName("="),
113 callee(cxxMethodDecl(ofClass(optionalClass()))),
114 unless(hasDeclaration(cxxMethodDecl(
115 anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))),
116 argumentCountIs(2), hasArgument(1, unless(hasNulloptType())));
117 }
118
isOptionalNulloptAssignment()119 auto isOptionalNulloptAssignment() {
120 return cxxOperatorCallExpr(hasOverloadedOperatorName("="),
121 callee(cxxMethodDecl(ofClass(optionalClass()))),
122 argumentCountIs(2),
123 hasArgument(1, hasNulloptType()));
124 }
125
isStdSwapCall()126 auto isStdSwapCall() {
127 return callExpr(callee(functionDecl(hasName("std::swap"))),
128 argumentCountIs(2), hasArgument(0, hasOptionalType()),
129 hasArgument(1, hasOptionalType()));
130 }
131
132 constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall";
133
isValueOrStringEmptyCall()134 auto isValueOrStringEmptyCall() {
135 // `opt.value_or("").empty()`
136 return cxxMemberCallExpr(
137 callee(cxxMethodDecl(hasName("empty"))),
138 onImplicitObjectArgument(ignoringImplicit(
139 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
140 callee(cxxMethodDecl(hasName("value_or"),
141 ofClass(optionalClass()))),
142 hasArgument(0, stringLiteral(hasSize(0))))
143 .bind(ValueOrCallID))));
144 }
145
isValueOrNotEqX()146 auto isValueOrNotEqX() {
147 auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) {
148 return hasOperands(
149 ignoringImplicit(
150 cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))),
151 callee(cxxMethodDecl(hasName("value_or"),
152 ofClass(optionalClass()))),
153 hasArgument(0, Arg))
154 .bind(ValueOrCallID)),
155 ignoringImplicit(Arg));
156 };
157
158 // `opt.value_or(X) != X`, for X is `nullptr`, `""`, or `0`. Ideally, we'd
159 // support this pattern for any expression, but the AST does not have a
160 // generic expression comparison facility, so we specialize to common cases
161 // seen in practice. FIXME: define a matcher that compares values across
162 // nodes, which would let us generalize this to any `X`.
163 return binaryOperation(hasOperatorName("!="),
164 anyOf(ComparesToSame(cxxNullPtrLiteralExpr()),
165 ComparesToSame(stringLiteral(hasSize(0))),
166 ComparesToSame(integerLiteral(equals(0)))));
167 }
168
isCallReturningOptional()169 auto isCallReturningOptional() {
170 return callExpr(hasType(qualType(anyOf(
171 optionalOrAliasType(), referenceType(pointee(optionalOrAliasType()))))));
172 }
173
174 /// Sets `HasValueVal` as the symbolic value that represents the "has_value"
175 /// property of the optional value `OptionalVal`.
setHasValue(Value & OptionalVal,BoolValue & HasValueVal)176 void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) {
177 OptionalVal.setProperty("has_value", HasValueVal);
178 }
179
180 /// Creates a symbolic value for an `optional` value using `HasValueVal` as the
181 /// symbolic value of its "has_value" property.
createOptionalValue(Environment & Env,BoolValue & HasValueVal)182 StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) {
183 auto OptionalVal = std::make_unique<StructValue>();
184 setHasValue(*OptionalVal, HasValueVal);
185 return Env.takeOwnership(std::move(OptionalVal));
186 }
187
188 /// Returns the symbolic value that represents the "has_value" property of the
189 /// optional value `OptionalVal`. Returns null if `OptionalVal` is null.
getHasValue(Environment & Env,Value * OptionalVal)190 BoolValue *getHasValue(Environment &Env, Value *OptionalVal) {
191 if (OptionalVal != nullptr) {
192 auto *HasValueVal =
193 cast_or_null<BoolValue>(OptionalVal->getProperty("has_value"));
194 if (HasValueVal == nullptr) {
195 HasValueVal = &Env.makeAtomicBoolValue();
196 OptionalVal->setProperty("has_value", *HasValueVal);
197 }
198 return HasValueVal;
199 }
200 return nullptr;
201 }
202
203 /// If `Type` is a reference type, returns the type of its pointee. Otherwise,
204 /// returns `Type` itself.
stripReference(QualType Type)205 QualType stripReference(QualType Type) {
206 return Type->isReferenceType() ? Type->getPointeeType() : Type;
207 }
208
209 /// Returns true if and only if `Type` is an optional type.
IsOptionalType(QualType Type)210 bool IsOptionalType(QualType Type) {
211 if (!Type->isRecordType())
212 return false;
213 // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call.
214 auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString();
215 return TypeName == "std::optional" || TypeName == "absl::optional" ||
216 TypeName == "base::Optional";
217 }
218
219 /// Returns the number of optional wrappers in `Type`.
220 ///
221 /// For example, if `Type` is `optional<optional<int>>`, the result of this
222 /// function will be 2.
countOptionalWrappers(const ASTContext & ASTCtx,QualType Type)223 int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) {
224 if (!IsOptionalType(Type))
225 return 0;
226 return 1 + countOptionalWrappers(
227 ASTCtx,
228 cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl())
229 ->getTemplateArgs()
230 .get(0)
231 .getAsType()
232 .getDesugaredType(ASTCtx));
233 }
234
235 /// Tries to initialize the `optional`'s value (that is, contents), and return
236 /// its location. Returns nullptr if the value can't be represented.
maybeInitializeOptionalValueMember(QualType Q,Value & OptionalVal,Environment & Env)237 StorageLocation *maybeInitializeOptionalValueMember(QualType Q,
238 Value &OptionalVal,
239 Environment &Env) {
240 // The "value" property represents a synthetic field. As such, it needs
241 // `StorageLocation`, like normal fields (and other variables). So, we model
242 // it with a `ReferenceValue`, since that includes a storage location. Once
243 // the property is set, it will be shared by all environments that access the
244 // `Value` representing the optional (here, `OptionalVal`).
245 if (auto *ValueProp = OptionalVal.getProperty("value")) {
246 auto *ValueRef = clang::cast<ReferenceValue>(ValueProp);
247 auto &ValueLoc = ValueRef->getReferentLoc();
248 if (Env.getValue(ValueLoc) == nullptr) {
249 // The property was previously set, but the value has been lost. This can
250 // happen, for example, because of an environment merge (where the two
251 // environments mapped the property to different values, which resulted in
252 // them both being discarded), or when two blocks in the CFG, with neither
253 // a dominator of the other, visit the same optional value, or even when a
254 // block is revisited during testing to collect per-statement state.
255 // FIXME: This situation means that the optional contents are not shared
256 // between branches and the like. Practically, this lack of sharing
257 // reduces the precision of the model when the contents are relevant to
258 // the check, like another optional or a boolean that influences control
259 // flow.
260 auto *ValueVal = Env.createValue(ValueLoc.getType());
261 if (ValueVal == nullptr)
262 return nullptr;
263 Env.setValue(ValueLoc, *ValueVal);
264 }
265 return &ValueLoc;
266 }
267
268 auto Ty = stripReference(Q);
269 auto *ValueVal = Env.createValue(Ty);
270 if (ValueVal == nullptr)
271 return nullptr;
272 auto &ValueLoc = Env.createStorageLocation(Ty);
273 Env.setValue(ValueLoc, *ValueVal);
274 auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc);
275 OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef)));
276 return &ValueLoc;
277 }
278
initializeOptionalReference(const Expr * OptionalExpr,const MatchFinder::MatchResult &,LatticeTransferState & State)279 void initializeOptionalReference(const Expr *OptionalExpr,
280 const MatchFinder::MatchResult &,
281 LatticeTransferState &State) {
282 if (auto *OptionalVal =
283 State.Env.getValue(*OptionalExpr, SkipPast::Reference)) {
284 if (OptionalVal->getProperty("has_value") == nullptr) {
285 setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue());
286 }
287 }
288 }
289
290 /// Returns true if and only if `OptionalVal` is initialized and known to be
291 /// empty in `Env.
isEmptyOptional(const Value & OptionalVal,const Environment & Env)292 bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) {
293 auto *HasValueVal =
294 cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
295 return HasValueVal != nullptr &&
296 Env.flowConditionImplies(Env.makeNot(*HasValueVal));
297 }
298
299 /// Returns true if and only if `OptionalVal` is initialized and known to be
300 /// non-empty in `Env.
isNonEmptyOptional(const Value & OptionalVal,const Environment & Env)301 bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) {
302 auto *HasValueVal =
303 cast_or_null<BoolValue>(OptionalVal.getProperty("has_value"));
304 return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal);
305 }
306
transferUnwrapCall(const Expr * UnwrapExpr,const Expr * ObjectExpr,LatticeTransferState & State)307 void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr,
308 LatticeTransferState &State) {
309 if (auto *OptionalVal =
310 State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
311 if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr)
312 if (auto *Loc = maybeInitializeOptionalValueMember(
313 UnwrapExpr->getType(), *OptionalVal, State.Env))
314 State.Env.setStorageLocation(*UnwrapExpr, *Loc);
315 }
316 }
317
transferMakeOptionalCall(const CallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)318 void transferMakeOptionalCall(const CallExpr *E,
319 const MatchFinder::MatchResult &,
320 LatticeTransferState &State) {
321 auto &Loc = State.Env.createStorageLocation(*E);
322 State.Env.setStorageLocation(*E, Loc);
323 State.Env.setValue(
324 Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true)));
325 }
326
transferOptionalHasValueCall(const CXXMemberCallExpr * CallExpr,const MatchFinder::MatchResult &,LatticeTransferState & State)327 void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr,
328 const MatchFinder::MatchResult &,
329 LatticeTransferState &State) {
330 if (auto *HasValueVal = getHasValue(
331 State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(),
332 SkipPast::ReferenceThenPointer))) {
333 auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr);
334 State.Env.setValue(CallExprLoc, *HasValueVal);
335 State.Env.setStorageLocation(*CallExpr, CallExprLoc);
336 }
337 }
338
339 /// `ModelPred` builds a logical formula relating the predicate in
340 /// `ValueOrPredExpr` to the optional's `has_value` property.
transferValueOrImpl(const clang::Expr * ValueOrPredExpr,const MatchFinder::MatchResult & Result,LatticeTransferState & State,BoolValue & (* ModelPred)(Environment & Env,BoolValue & ExprVal,BoolValue & HasValueVal))341 void transferValueOrImpl(const clang::Expr *ValueOrPredExpr,
342 const MatchFinder::MatchResult &Result,
343 LatticeTransferState &State,
344 BoolValue &(*ModelPred)(Environment &Env,
345 BoolValue &ExprVal,
346 BoolValue &HasValueVal)) {
347 auto &Env = State.Env;
348
349 const auto *ObjectArgumentExpr =
350 Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID)
351 ->getImplicitObjectArgument();
352
353 auto *HasValueVal = getHasValue(
354 State.Env,
355 State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer));
356 if (HasValueVal == nullptr)
357 return;
358
359 auto *ExprValue = cast_or_null<BoolValue>(
360 State.Env.getValue(*ValueOrPredExpr, SkipPast::None));
361 if (ExprValue == nullptr) {
362 auto &ExprLoc = State.Env.createStorageLocation(*ValueOrPredExpr);
363 ExprValue = &State.Env.makeAtomicBoolValue();
364 State.Env.setValue(ExprLoc, *ExprValue);
365 State.Env.setStorageLocation(*ValueOrPredExpr, ExprLoc);
366 }
367
368 Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal));
369 }
370
transferValueOrStringEmptyCall(const clang::Expr * ComparisonExpr,const MatchFinder::MatchResult & Result,LatticeTransferState & State)371 void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr,
372 const MatchFinder::MatchResult &Result,
373 LatticeTransferState &State) {
374 return transferValueOrImpl(ComparisonExpr, Result, State,
375 [](Environment &Env, BoolValue &ExprVal,
376 BoolValue &HasValueVal) -> BoolValue & {
377 // If the result is *not* empty, then we know the
378 // optional must have been holding a value. If
379 // `ExprVal` is true, though, we don't learn
380 // anything definite about `has_value`, so we
381 // don't add any corresponding implications to
382 // the flow condition.
383 return Env.makeImplication(Env.makeNot(ExprVal),
384 HasValueVal);
385 });
386 }
387
transferValueOrNotEqX(const Expr * ComparisonExpr,const MatchFinder::MatchResult & Result,LatticeTransferState & State)388 void transferValueOrNotEqX(const Expr *ComparisonExpr,
389 const MatchFinder::MatchResult &Result,
390 LatticeTransferState &State) {
391 transferValueOrImpl(ComparisonExpr, Result, State,
392 [](Environment &Env, BoolValue &ExprVal,
393 BoolValue &HasValueVal) -> BoolValue & {
394 // We know that if `(opt.value_or(X) != X)` then
395 // `opt.hasValue()`, even without knowing further
396 // details about the contents of `opt`.
397 return Env.makeImplication(ExprVal, HasValueVal);
398 });
399 }
400
transferCallReturningOptional(const CallExpr * E,const MatchFinder::MatchResult & Result,LatticeTransferState & State)401 void transferCallReturningOptional(const CallExpr *E,
402 const MatchFinder::MatchResult &Result,
403 LatticeTransferState &State) {
404 if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr)
405 return;
406
407 auto &Loc = State.Env.createStorageLocation(*E);
408 State.Env.setStorageLocation(*E, Loc);
409 State.Env.setValue(
410 Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue()));
411 }
412
assignOptionalValue(const Expr & E,LatticeTransferState & State,BoolValue & HasValueVal)413 void assignOptionalValue(const Expr &E, LatticeTransferState &State,
414 BoolValue &HasValueVal) {
415 if (auto *OptionalLoc =
416 State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) {
417 State.Env.setValue(*OptionalLoc,
418 createOptionalValue(State.Env, HasValueVal));
419 }
420 }
421
422 /// Returns a symbolic value for the "has_value" property of an `optional<T>`
423 /// value that is constructed/assigned from a value of type `U` or `optional<U>`
424 /// where `T` is constructible from `U`.
value_orConversionHasValue(const FunctionDecl & F,const Expr & E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)425 BoolValue &value_orConversionHasValue(const FunctionDecl &F, const Expr &E,
426 const MatchFinder::MatchResult &MatchRes,
427 LatticeTransferState &State) {
428 assert(F.getTemplateSpecializationArgs()->size() > 0);
429
430 const int TemplateParamOptionalWrappersCount = countOptionalWrappers(
431 *MatchRes.Context,
432 stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType()));
433 const int ArgTypeOptionalWrappersCount =
434 countOptionalWrappers(*MatchRes.Context, stripReference(E.getType()));
435
436 // Check if this is a constructor/assignment call for `optional<T>` with
437 // argument of type `U` such that `T` is constructible from `U`.
438 if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount)
439 return State.Env.getBoolLiteralValue(true);
440
441 // This is a constructor/assignment call for `optional<T>` with argument of
442 // type `optional<U>` such that `T` is constructible from `U`.
443 if (auto *HasValueVal =
444 getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference)))
445 return *HasValueVal;
446 return State.Env.makeAtomicBoolValue();
447 }
448
transferValueOrConversionConstructor(const CXXConstructExpr * E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)449 void transferValueOrConversionConstructor(
450 const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes,
451 LatticeTransferState &State) {
452 assert(E->getNumArgs() > 0);
453
454 assignOptionalValue(*E, State,
455 value_orConversionHasValue(*E->getConstructor(),
456 *E->getArg(0), MatchRes,
457 State));
458 }
459
transferAssignment(const CXXOperatorCallExpr * E,BoolValue & HasValueVal,LatticeTransferState & State)460 void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal,
461 LatticeTransferState &State) {
462 assert(E->getNumArgs() > 0);
463
464 auto *OptionalLoc =
465 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
466 if (OptionalLoc == nullptr)
467 return;
468
469 State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal));
470
471 // Assign a storage location for the whole expression.
472 State.Env.setStorageLocation(*E, *OptionalLoc);
473 }
474
transferValueOrConversionAssignment(const CXXOperatorCallExpr * E,const MatchFinder::MatchResult & MatchRes,LatticeTransferState & State)475 void transferValueOrConversionAssignment(
476 const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes,
477 LatticeTransferState &State) {
478 assert(E->getNumArgs() > 1);
479 transferAssignment(E,
480 value_orConversionHasValue(*E->getDirectCallee(),
481 *E->getArg(1), MatchRes, State),
482 State);
483 }
484
transferNulloptAssignment(const CXXOperatorCallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)485 void transferNulloptAssignment(const CXXOperatorCallExpr *E,
486 const MatchFinder::MatchResult &,
487 LatticeTransferState &State) {
488 transferAssignment(E, State.Env.getBoolLiteralValue(false), State);
489 }
490
transferSwap(const StorageLocation & OptionalLoc1,const StorageLocation & OptionalLoc2,LatticeTransferState & State)491 void transferSwap(const StorageLocation &OptionalLoc1,
492 const StorageLocation &OptionalLoc2,
493 LatticeTransferState &State) {
494 auto *OptionalVal1 = State.Env.getValue(OptionalLoc1);
495 assert(OptionalVal1 != nullptr);
496
497 auto *OptionalVal2 = State.Env.getValue(OptionalLoc2);
498 assert(OptionalVal2 != nullptr);
499
500 State.Env.setValue(OptionalLoc1, *OptionalVal2);
501 State.Env.setValue(OptionalLoc2, *OptionalVal1);
502 }
503
transferSwapCall(const CXXMemberCallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)504 void transferSwapCall(const CXXMemberCallExpr *E,
505 const MatchFinder::MatchResult &,
506 LatticeTransferState &State) {
507 assert(E->getNumArgs() == 1);
508
509 auto *OptionalLoc1 = State.Env.getStorageLocation(
510 *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer);
511 assert(OptionalLoc1 != nullptr);
512
513 auto *OptionalLoc2 =
514 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
515 assert(OptionalLoc2 != nullptr);
516
517 transferSwap(*OptionalLoc1, *OptionalLoc2, State);
518 }
519
transferStdSwapCall(const CallExpr * E,const MatchFinder::MatchResult &,LatticeTransferState & State)520 void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &,
521 LatticeTransferState &State) {
522 assert(E->getNumArgs() == 2);
523
524 auto *OptionalLoc1 =
525 State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference);
526 assert(OptionalLoc1 != nullptr);
527
528 auto *OptionalLoc2 =
529 State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference);
530 assert(OptionalLoc2 != nullptr);
531
532 transferSwap(*OptionalLoc1, *OptionalLoc2, State);
533 }
534
535 llvm::Optional<StatementMatcher>
ignorableOptional(const UncheckedOptionalAccessModelOptions & Options)536 ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) {
537 if (Options.IgnoreSmartPointerDereference)
538 return memberExpr(hasObjectExpression(ignoringParenImpCasts(
539 cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("->"),
540 hasOverloadedOperatorName("*")),
541 unless(hasArgument(0, expr(hasOptionalType())))))));
542 return llvm::None;
543 }
544
545 StatementMatcher
valueCall(llvm::Optional<StatementMatcher> & IgnorableOptional)546 valueCall(llvm::Optional<StatementMatcher> &IgnorableOptional) {
547 return isOptionalMemberCallWithName("value", IgnorableOptional);
548 }
549
550 StatementMatcher
valueOperatorCall(llvm::Optional<StatementMatcher> & IgnorableOptional)551 valueOperatorCall(llvm::Optional<StatementMatcher> &IgnorableOptional) {
552 return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional),
553 isOptionalOperatorCallWithName("->", IgnorableOptional)));
554 }
555
buildTransferMatchSwitch(const UncheckedOptionalAccessModelOptions & Options)556 auto buildTransferMatchSwitch(
557 const UncheckedOptionalAccessModelOptions &Options) {
558 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
559 // lot of duplicated work (e.g. string comparisons), consider providing APIs
560 // that avoid it through memoization.
561 auto IgnorableOptional = ignorableOptional(Options);
562 return MatchSwitchBuilder<LatticeTransferState>()
563 // Attach a symbolic "has_value" state to optional values that we see for
564 // the first time.
565 .CaseOf<Expr>(
566 expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()),
567 initializeOptionalReference)
568
569 // make_optional
570 .CaseOf<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall)
571
572 // optional::optional
573 .CaseOf<CXXConstructExpr>(
574 isOptionalInPlaceConstructor(),
575 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
576 LatticeTransferState &State) {
577 assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true));
578 })
579 .CaseOf<CXXConstructExpr>(
580 isOptionalNulloptConstructor(),
581 [](const CXXConstructExpr *E, const MatchFinder::MatchResult &,
582 LatticeTransferState &State) {
583 assignOptionalValue(*E, State,
584 State.Env.getBoolLiteralValue(false));
585 })
586 .CaseOf<CXXConstructExpr>(isOptionalValueOrConversionConstructor(),
587 transferValueOrConversionConstructor)
588
589 // optional::operator=
590 .CaseOf<CXXOperatorCallExpr>(isOptionalValueOrConversionAssignment(),
591 transferValueOrConversionAssignment)
592 .CaseOf<CXXOperatorCallExpr>(isOptionalNulloptAssignment(),
593 transferNulloptAssignment)
594
595 // optional::value
596 .CaseOf<CXXMemberCallExpr>(
597 valueCall(IgnorableOptional),
598 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
599 LatticeTransferState &State) {
600 transferUnwrapCall(E, E->getImplicitObjectArgument(), State);
601 })
602
603 // optional::operator*, optional::operator->
604 .CaseOf<CallExpr>(valueOperatorCall(IgnorableOptional),
605 [](const CallExpr *E, const MatchFinder::MatchResult &,
606 LatticeTransferState &State) {
607 transferUnwrapCall(E, E->getArg(0), State);
608 })
609
610 // optional::has_value
611 .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("has_value"),
612 transferOptionalHasValueCall)
613
614 // optional::operator bool
615 .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("operator bool"),
616 transferOptionalHasValueCall)
617
618 // optional::emplace
619 .CaseOf<CXXMemberCallExpr>(
620 isOptionalMemberCallWithName("emplace"),
621 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
622 LatticeTransferState &State) {
623 assignOptionalValue(*E->getImplicitObjectArgument(), State,
624 State.Env.getBoolLiteralValue(true));
625 })
626
627 // optional::reset
628 .CaseOf<CXXMemberCallExpr>(
629 isOptionalMemberCallWithName("reset"),
630 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
631 LatticeTransferState &State) {
632 assignOptionalValue(*E->getImplicitObjectArgument(), State,
633 State.Env.getBoolLiteralValue(false));
634 })
635
636 // optional::swap
637 .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"),
638 transferSwapCall)
639
640 // std::swap
641 .CaseOf<CallExpr>(isStdSwapCall(), transferStdSwapCall)
642
643 // opt.value_or("").empty()
644 .CaseOf<Expr>(isValueOrStringEmptyCall(), transferValueOrStringEmptyCall)
645
646 // opt.value_or(X) != X
647 .CaseOf<Expr>(isValueOrNotEqX(), transferValueOrNotEqX)
648
649 // returns optional
650 .CaseOf<CallExpr>(isCallReturningOptional(),
651 transferCallReturningOptional)
652
653 .Build();
654 }
655
diagnoseUnwrapCall(const Expr * UnwrapExpr,const Expr * ObjectExpr,const Environment & Env)656 std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr,
657 const Expr *ObjectExpr,
658 const Environment &Env) {
659 if (auto *OptionalVal =
660 Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) {
661 auto *Prop = OptionalVal->getProperty("has_value");
662 if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) {
663 if (Env.flowConditionImplies(*HasValueVal))
664 return {};
665 }
666 }
667
668 // Record that this unwrap is *not* provably safe.
669 // FIXME: include either the name of the optional (if applicable) or a source
670 // range of the access for easier interpretation of the result.
671 return {ObjectExpr->getBeginLoc()};
672 }
673
buildDiagnoseMatchSwitch(const UncheckedOptionalAccessModelOptions & Options)674 auto buildDiagnoseMatchSwitch(
675 const UncheckedOptionalAccessModelOptions &Options) {
676 // FIXME: Evaluate the efficiency of matchers. If using matchers results in a
677 // lot of duplicated work (e.g. string comparisons), consider providing APIs
678 // that avoid it through memoization.
679 auto IgnorableOptional = ignorableOptional(Options);
680 return MatchSwitchBuilder<const Environment, std::vector<SourceLocation>>()
681 // optional::value
682 .CaseOf<CXXMemberCallExpr>(
683 valueCall(IgnorableOptional),
684 [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &,
685 const Environment &Env) {
686 return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env);
687 })
688
689 // optional::operator*, optional::operator->
690 .CaseOf<CallExpr>(
691 valueOperatorCall(IgnorableOptional),
692 [](const CallExpr *E, const MatchFinder::MatchResult &,
693 const Environment &Env) {
694 return diagnoseUnwrapCall(E, E->getArg(0), Env);
695 })
696 .Build();
697 }
698
699 } // namespace
700
701 ast_matchers::DeclarationMatcher
optionalClassDecl()702 UncheckedOptionalAccessModel::optionalClassDecl() {
703 return optionalClass();
704 }
705
UncheckedOptionalAccessModel(ASTContext & Ctx,UncheckedOptionalAccessModelOptions Options)706 UncheckedOptionalAccessModel::UncheckedOptionalAccessModel(
707 ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options)
708 : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx),
709 TransferMatchSwitch(buildTransferMatchSwitch(Options)) {}
710
transfer(const Stmt * S,NoopLattice & L,Environment & Env)711 void UncheckedOptionalAccessModel::transfer(const Stmt *S, NoopLattice &L,
712 Environment &Env) {
713 LatticeTransferState State(L, Env);
714 TransferMatchSwitch(*S, getASTContext(), State);
715 }
716
compareEquivalent(QualType Type,const Value & Val1,const Environment & Env1,const Value & Val2,const Environment & Env2)717 bool UncheckedOptionalAccessModel::compareEquivalent(QualType Type,
718 const Value &Val1,
719 const Environment &Env1,
720 const Value &Val2,
721 const Environment &Env2) {
722 return isNonEmptyOptional(Val1, Env1) == isNonEmptyOptional(Val2, Env2);
723 }
724
merge(QualType Type,const Value & Val1,const Environment & Env1,const Value & Val2,const Environment & Env2,Value & MergedVal,Environment & MergedEnv)725 bool UncheckedOptionalAccessModel::merge(QualType Type, const Value &Val1,
726 const Environment &Env1,
727 const Value &Val2,
728 const Environment &Env2,
729 Value &MergedVal,
730 Environment &MergedEnv) {
731 if (!IsOptionalType(Type))
732 return true;
733
734 auto &HasValueVal = MergedEnv.makeAtomicBoolValue();
735 if (isNonEmptyOptional(Val1, Env1) && isNonEmptyOptional(Val2, Env2))
736 MergedEnv.addToFlowCondition(HasValueVal);
737 else if (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2))
738 MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal));
739 setHasValue(MergedVal, HasValueVal);
740 return true;
741 }
742
UncheckedOptionalAccessDiagnoser(UncheckedOptionalAccessModelOptions Options)743 UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser(
744 UncheckedOptionalAccessModelOptions Options)
745 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
746
diagnose(ASTContext & Context,const Stmt * Stmt,const Environment & Env)747 std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose(
748 ASTContext &Context, const Stmt *Stmt, const Environment &Env) {
749 return DiagnoseMatchSwitch(*Stmt, Context, Env);
750 }
751
752 } // namespace dataflow
753 } // namespace clang
754