/Users/buildslave/jenkins/workspace/coverage/llvm-project/clang/lib/Analysis/FlowSensitive/Models/UncheckedOptionalAccessModel.cpp
Line | Count | Source (jump to first uncovered line) |
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 | | |
40 | 6.80k | DeclarationMatcher optionalClass() { |
41 | 6.80k | return classTemplateSpecializationDecl( |
42 | 6.80k | anyOf(hasName("std::optional"), hasName("std::__optional_storage_base"), |
43 | 6.80k | hasName("__optional_destruct_base"), hasName("absl::optional"), |
44 | 6.80k | hasName("base::Optional")), |
45 | 6.80k | hasTemplateArgument(0, refersToType(type().bind("T")))); |
46 | 6.80k | } |
47 | | |
48 | 2.67k | auto optionalOrAliasType() { |
49 | 2.67k | return hasUnqualifiedDesugaredType( |
50 | 2.67k | recordType(hasDeclaration(optionalClass()))); |
51 | 2.67k | } |
52 | | |
53 | | /// Matches any of the spellings of the optional types and sugar, aliases, etc. |
54 | 2.18k | auto hasOptionalType() { return hasType(optionalOrAliasType()); } |
55 | | |
56 | | auto isOptionalMemberCallWithName( |
57 | | llvm::StringRef MemberName, |
58 | 1.70k | llvm::Optional<StatementMatcher> Ignorable = llvm::None) { |
59 | 1.70k | auto Exception = unless(Ignorable ? expr(anyOf(*Ignorable, cxxThisExpr()))486 |
60 | 1.70k | : cxxThisExpr()1.21k ); |
61 | 1.70k | return cxxMemberCallExpr( |
62 | 1.70k | on(expr(Exception)), |
63 | 1.70k | callee(cxxMethodDecl(hasName(MemberName), ofClass(optionalClass())))); |
64 | 1.70k | } |
65 | | |
66 | | auto isOptionalOperatorCallWithName( |
67 | | llvm::StringRef operator_name, |
68 | 972 | llvm::Optional<StatementMatcher> Ignorable = llvm::None) { |
69 | 972 | return cxxOperatorCallExpr( |
70 | 972 | hasOverloadedOperatorName(operator_name), |
71 | 972 | callee(cxxMethodDecl(ofClass(optionalClass()))), |
72 | 972 | Ignorable ? callExpr(unless(hasArgument(0, *Ignorable))) : callExpr()0 ); |
73 | 972 | } |
74 | | |
75 | 243 | auto isMakeOptionalCall() { |
76 | 243 | return callExpr( |
77 | 243 | callee(functionDecl(hasAnyName( |
78 | 243 | "std::make_optional", "base::make_optional", "absl::make_optional"))), |
79 | 243 | hasOptionalType()); |
80 | 243 | } |
81 | | |
82 | 972 | auto hasNulloptType() { |
83 | 972 | return hasType(namedDecl( |
84 | 972 | hasAnyName("std::nullopt_t", "absl::nullopt_t", "base::nullopt_t"))); |
85 | 972 | } |
86 | | |
87 | 243 | auto inPlaceClass() { |
88 | 243 | return recordDecl( |
89 | 243 | hasAnyName("std::in_place_t", "absl::in_place_t", "base::in_place_t")); |
90 | 243 | } |
91 | | |
92 | 243 | auto isOptionalNulloptConstructor() { |
93 | 243 | return cxxConstructExpr(hasOptionalType(), argumentCountIs(1), |
94 | 243 | hasArgument(0, hasNulloptType())); |
95 | 243 | } |
96 | | |
97 | 243 | auto isOptionalInPlaceConstructor() { |
98 | 243 | return cxxConstructExpr(hasOptionalType(), |
99 | 243 | hasArgument(0, hasType(inPlaceClass()))); |
100 | 243 | } |
101 | | |
102 | 243 | auto isOptionalValueOrConversionConstructor() { |
103 | 243 | return cxxConstructExpr( |
104 | 243 | hasOptionalType(), |
105 | 243 | unless(hasDeclaration( |
106 | 243 | cxxConstructorDecl(anyOf(isCopyConstructor(), isMoveConstructor())))), |
107 | 243 | argumentCountIs(1), hasArgument(0, unless(hasNulloptType()))); |
108 | 243 | } |
109 | | |
110 | 243 | auto isOptionalValueOrConversionAssignment() { |
111 | 243 | return cxxOperatorCallExpr( |
112 | 243 | hasOverloadedOperatorName("="), |
113 | 243 | callee(cxxMethodDecl(ofClass(optionalClass()))), |
114 | 243 | unless(hasDeclaration(cxxMethodDecl( |
115 | 243 | anyOf(isCopyAssignmentOperator(), isMoveAssignmentOperator())))), |
116 | 243 | argumentCountIs(2), hasArgument(1, unless(hasNulloptType()))); |
117 | 243 | } |
118 | | |
119 | 243 | auto isOptionalNulloptAssignment() { |
120 | 243 | return cxxOperatorCallExpr(hasOverloadedOperatorName("="), |
121 | 243 | callee(cxxMethodDecl(ofClass(optionalClass()))), |
122 | 243 | argumentCountIs(2), |
123 | 243 | hasArgument(1, hasNulloptType())); |
124 | 243 | } |
125 | | |
126 | 243 | auto isStdSwapCall() { |
127 | 243 | return callExpr(callee(functionDecl(hasName("std::swap"))), |
128 | 243 | argumentCountIs(2), hasArgument(0, hasOptionalType()), |
129 | 243 | hasArgument(1, hasOptionalType())); |
130 | 243 | } |
131 | | |
132 | | constexpr llvm::StringLiteral ValueOrCallID = "ValueOrCall"; |
133 | | |
134 | 243 | auto isValueOrStringEmptyCall() { |
135 | | // `opt.value_or("").empty()` |
136 | 243 | return cxxMemberCallExpr( |
137 | 243 | callee(cxxMethodDecl(hasName("empty"))), |
138 | 243 | onImplicitObjectArgument(ignoringImplicit( |
139 | 243 | cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), |
140 | 243 | callee(cxxMethodDecl(hasName("value_or"), |
141 | 243 | ofClass(optionalClass()))), |
142 | 243 | hasArgument(0, stringLiteral(hasSize(0)))) |
143 | 243 | .bind(ValueOrCallID)))); |
144 | 243 | } |
145 | | |
146 | 243 | auto isValueOrNotEqX() { |
147 | 729 | auto ComparesToSame = [](ast_matchers::internal::Matcher<Stmt> Arg) { |
148 | 729 | return hasOperands( |
149 | 729 | ignoringImplicit( |
150 | 729 | cxxMemberCallExpr(on(expr(unless(cxxThisExpr()))), |
151 | 729 | callee(cxxMethodDecl(hasName("value_or"), |
152 | 729 | ofClass(optionalClass()))), |
153 | 729 | hasArgument(0, Arg)) |
154 | 729 | .bind(ValueOrCallID)), |
155 | 729 | ignoringImplicit(Arg)); |
156 | 729 | }; |
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 | 243 | return binaryOperation(hasOperatorName("!="), |
164 | 243 | anyOf(ComparesToSame(cxxNullPtrLiteralExpr()), |
165 | 243 | ComparesToSame(stringLiteral(hasSize(0))), |
166 | 243 | ComparesToSame(integerLiteral(equals(0))))); |
167 | 243 | } |
168 | | |
169 | 243 | auto isCallReturningOptional() { |
170 | 243 | return callExpr(hasType(qualType(anyOf( |
171 | 243 | optionalOrAliasType(), referenceType(pointee(optionalOrAliasType())))))); |
172 | 243 | } |
173 | | |
174 | | /// Sets `HasValueVal` as the symbolic value that represents the "has_value" |
175 | | /// property of the optional value `OptionalVal`. |
176 | 771 | void setHasValue(Value &OptionalVal, BoolValue &HasValueVal) { |
177 | 771 | OptionalVal.setProperty("has_value", HasValueVal); |
178 | 771 | } |
179 | | |
180 | | /// Creates a symbolic value for an `optional` value using `HasValueVal` as the |
181 | | /// symbolic value of its "has_value" property. |
182 | 510 | StructValue &createOptionalValue(Environment &Env, BoolValue &HasValueVal) { |
183 | 510 | auto OptionalVal = std::make_unique<StructValue>(); |
184 | 510 | setHasValue(*OptionalVal, HasValueVal); |
185 | 510 | return Env.takeOwnership(std::move(OptionalVal)); |
186 | 510 | } |
187 | | |
188 | | /// Returns the symbolic value that represents the "has_value" property of the |
189 | | /// optional value `OptionalVal`. Returns null if `OptionalVal` is null. |
190 | 237 | BoolValue *getHasValue(Environment &Env, Value *OptionalVal) { |
191 | 237 | if (OptionalVal != nullptr) { |
192 | 237 | auto *HasValueVal = |
193 | 237 | cast_or_null<BoolValue>(OptionalVal->getProperty("has_value")); |
194 | 237 | if (HasValueVal == nullptr) { |
195 | 24 | HasValueVal = &Env.makeAtomicBoolValue(); |
196 | 24 | OptionalVal->setProperty("has_value", *HasValueVal); |
197 | 24 | } |
198 | 237 | return HasValueVal; |
199 | 237 | } |
200 | 0 | return nullptr; |
201 | 237 | } |
202 | | |
203 | | /// If `Type` is a reference type, returns the type of its pointee. Otherwise, |
204 | | /// returns `Type` itself. |
205 | 897 | QualType stripReference(QualType Type) { |
206 | 897 | return Type->isReferenceType() ? Type->getPointeeType()18 : Type879 ; |
207 | 897 | } |
208 | | |
209 | | /// Returns true if and only if `Type` is an optional type. |
210 | 670 | bool IsOptionalType(QualType Type) { |
211 | 670 | if (!Type->isRecordType()) |
212 | 322 | return false; |
213 | | // FIXME: Optimize this by avoiding the `getQualifiedNameAsString` call. |
214 | 348 | auto TypeName = Type->getAsCXXRecordDecl()->getQualifiedNameAsString(); |
215 | 348 | return TypeName == "std::optional" || TypeName == "absl::optional"296 || |
216 | 348 | TypeName == "base::Optional"244 ; |
217 | 670 | } |
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. |
223 | 528 | int countOptionalWrappers(const ASTContext &ASTCtx, QualType Type) { |
224 | 528 | if (!IsOptionalType(Type)) |
225 | 468 | return 0; |
226 | 60 | return 1 + countOptionalWrappers( |
227 | 60 | ASTCtx, |
228 | 60 | cast<ClassTemplateSpecializationDecl>(Type->getAsRecordDecl()) |
229 | 60 | ->getTemplateArgs() |
230 | 60 | .get(0) |
231 | 60 | .getAsType() |
232 | 60 | .getDesugaredType(ASTCtx)); |
233 | 528 | } |
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. |
237 | | StorageLocation *maybeInitializeOptionalValueMember(QualType Q, |
238 | | Value &OptionalVal, |
239 | 618 | 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 | 618 | if (auto *ValueProp = OptionalVal.getProperty("value")) { |
246 | 189 | auto *ValueRef = clang::cast<ReferenceValue>(ValueProp); |
247 | 189 | auto &ValueLoc = ValueRef->getReferentLoc(); |
248 | 189 | 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 | 147 | auto *ValueVal = Env.createValue(ValueLoc.getType()); |
261 | 147 | if (ValueVal == nullptr) |
262 | 0 | return nullptr; |
263 | 147 | Env.setValue(ValueLoc, *ValueVal); |
264 | 147 | } |
265 | 189 | return &ValueLoc; |
266 | 189 | } |
267 | | |
268 | 429 | auto Ty = stripReference(Q); |
269 | 429 | auto *ValueVal = Env.createValue(Ty); |
270 | 429 | if (ValueVal == nullptr) |
271 | 6 | return nullptr; |
272 | 423 | auto &ValueLoc = Env.createStorageLocation(Ty); |
273 | 423 | Env.setValue(ValueLoc, *ValueVal); |
274 | 423 | auto ValueRef = std::make_unique<ReferenceValue>(ValueLoc); |
275 | 423 | OptionalVal.setProperty("value", Env.takeOwnership(std::move(ValueRef))); |
276 | 423 | return &ValueLoc; |
277 | 429 | } |
278 | | |
279 | | void initializeOptionalReference(const Expr *OptionalExpr, |
280 | | const MatchFinder::MatchResult &, |
281 | 993 | LatticeTransferState &State) { |
282 | 993 | if (auto *OptionalVal = |
283 | 993 | State.Env.getValue(*OptionalExpr, SkipPast::Reference)) { |
284 | 981 | if (OptionalVal->getProperty("has_value") == nullptr) { |
285 | 165 | setHasValue(*OptionalVal, State.Env.makeAtomicBoolValue()); |
286 | 165 | } |
287 | 981 | } |
288 | 993 | } |
289 | | |
290 | | /// Returns true if and only if `OptionalVal` is initialized and known to be |
291 | | /// empty in `Env. |
292 | 106 | bool isEmptyOptional(const Value &OptionalVal, const Environment &Env) { |
293 | 106 | auto *HasValueVal = |
294 | 106 | cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); |
295 | 106 | return HasValueVal != nullptr && |
296 | 106 | Env.flowConditionImplies(Env.makeNot(*HasValueVal)); |
297 | 106 | } |
298 | | |
299 | | /// Returns true if and only if `OptionalVal` is initialized and known to be |
300 | | /// non-empty in `Env. |
301 | 179 | bool isNonEmptyOptional(const Value &OptionalVal, const Environment &Env) { |
302 | 179 | auto *HasValueVal = |
303 | 179 | cast_or_null<BoolValue>(OptionalVal.getProperty("has_value")); |
304 | 179 | return HasValueVal != nullptr && Env.flowConditionImplies(*HasValueVal)167 ; |
305 | 179 | } |
306 | | |
307 | | void transferUnwrapCall(const Expr *UnwrapExpr, const Expr *ObjectExpr, |
308 | 618 | LatticeTransferState &State) { |
309 | 618 | if (auto *OptionalVal = |
310 | 618 | State.Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { |
311 | 618 | if (State.Env.getStorageLocation(*UnwrapExpr, SkipPast::None) == nullptr) |
312 | 618 | if (auto *Loc = maybeInitializeOptionalValueMember( |
313 | 618 | UnwrapExpr->getType(), *OptionalVal, State.Env)) |
314 | 612 | State.Env.setStorageLocation(*UnwrapExpr, *Loc); |
315 | 618 | } |
316 | 618 | } |
317 | | |
318 | | void transferMakeOptionalCall(const CallExpr *E, |
319 | | const MatchFinder::MatchResult &, |
320 | 24 | LatticeTransferState &State) { |
321 | 24 | auto &Loc = State.Env.createStorageLocation(*E); |
322 | 24 | State.Env.setStorageLocation(*E, Loc); |
323 | 24 | State.Env.setValue( |
324 | 24 | Loc, createOptionalValue(State.Env, State.Env.getBoolLiteralValue(true))); |
325 | 24 | } |
326 | | |
327 | | void transferOptionalHasValueCall(const CXXMemberCallExpr *CallExpr, |
328 | | const MatchFinder::MatchResult &, |
329 | 159 | LatticeTransferState &State) { |
330 | 159 | if (auto *HasValueVal = getHasValue( |
331 | 159 | State.Env, State.Env.getValue(*CallExpr->getImplicitObjectArgument(), |
332 | 159 | SkipPast::ReferenceThenPointer))) { |
333 | 159 | auto &CallExprLoc = State.Env.createStorageLocation(*CallExpr); |
334 | 159 | State.Env.setValue(CallExprLoc, *HasValueVal); |
335 | 159 | State.Env.setStorageLocation(*CallExpr, CallExprLoc); |
336 | 159 | } |
337 | 159 | } |
338 | | |
339 | | /// `ModelPred` builds a logical formula relating the predicate in |
340 | | /// `ValueOrPredExpr` to the optional's `has_value` property. |
341 | | void transferValueOrImpl(const clang::Expr *ValueOrPredExpr, |
342 | | const MatchFinder::MatchResult &Result, |
343 | | LatticeTransferState &State, |
344 | | BoolValue &(*ModelPred)(Environment &Env, |
345 | | BoolValue &ExprVal, |
346 | 30 | BoolValue &HasValueVal)) { |
347 | 30 | auto &Env = State.Env; |
348 | | |
349 | 30 | const auto *ObjectArgumentExpr = |
350 | 30 | Result.Nodes.getNodeAs<clang::CXXMemberCallExpr>(ValueOrCallID) |
351 | 30 | ->getImplicitObjectArgument(); |
352 | | |
353 | 30 | auto *HasValueVal = getHasValue( |
354 | 30 | State.Env, |
355 | 30 | State.Env.getValue(*ObjectArgumentExpr, SkipPast::ReferenceThenPointer)); |
356 | 30 | if (HasValueVal == nullptr) |
357 | 0 | return; |
358 | | |
359 | 30 | auto *ExprValue = cast_or_null<BoolValue>( |
360 | 30 | State.Env.getValue(*ValueOrPredExpr, SkipPast::None)); |
361 | 30 | if (ExprValue == nullptr) { |
362 | 12 | auto &ExprLoc = State.Env.createStorageLocation(*ValueOrPredExpr); |
363 | 12 | ExprValue = &State.Env.makeAtomicBoolValue(); |
364 | 12 | State.Env.setValue(ExprLoc, *ExprValue); |
365 | 12 | State.Env.setStorageLocation(*ValueOrPredExpr, ExprLoc); |
366 | 12 | } |
367 | | |
368 | 30 | Env.addToFlowCondition(ModelPred(Env, *ExprValue, *HasValueVal)); |
369 | 30 | } |
370 | | |
371 | | void transferValueOrStringEmptyCall(const clang::Expr *ComparisonExpr, |
372 | | const MatchFinder::MatchResult &Result, |
373 | 6 | LatticeTransferState &State) { |
374 | 6 | return transferValueOrImpl(ComparisonExpr, Result, State, |
375 | 6 | [](Environment &Env, BoolValue &ExprVal, |
376 | 6 | 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 | 6 | return Env.makeImplication(Env.makeNot(ExprVal), |
384 | 6 | HasValueVal); |
385 | 6 | }); |
386 | 6 | } |
387 | | |
388 | | void transferValueOrNotEqX(const Expr *ComparisonExpr, |
389 | | const MatchFinder::MatchResult &Result, |
390 | 24 | LatticeTransferState &State) { |
391 | 24 | transferValueOrImpl(ComparisonExpr, Result, State, |
392 | 24 | [](Environment &Env, BoolValue &ExprVal, |
393 | 24 | 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 | 24 | return Env.makeImplication(ExprVal, HasValueVal); |
398 | 24 | }); |
399 | 24 | } |
400 | | |
401 | | void transferCallReturningOptional(const CallExpr *E, |
402 | | const MatchFinder::MatchResult &Result, |
403 | 264 | LatticeTransferState &State) { |
404 | 264 | if (State.Env.getStorageLocation(*E, SkipPast::None) != nullptr) |
405 | 138 | return; |
406 | | |
407 | 126 | auto &Loc = State.Env.createStorageLocation(*E); |
408 | 126 | State.Env.setStorageLocation(*E, Loc); |
409 | 126 | State.Env.setValue( |
410 | 126 | Loc, createOptionalValue(State.Env, State.Env.makeAtomicBoolValue())); |
411 | 126 | } |
412 | | |
413 | | void assignOptionalValue(const Expr &E, LatticeTransferState &State, |
414 | 294 | BoolValue &HasValueVal) { |
415 | 294 | if (auto *OptionalLoc = |
416 | 294 | State.Env.getStorageLocation(E, SkipPast::ReferenceThenPointer)) { |
417 | 294 | State.Env.setValue(*OptionalLoc, |
418 | 294 | createOptionalValue(State.Env, HasValueVal)); |
419 | 294 | } |
420 | 294 | } |
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`. |
425 | | BoolValue &value_orConversionHasValue(const FunctionDecl &F, const Expr &E, |
426 | | const MatchFinder::MatchResult &MatchRes, |
427 | 234 | LatticeTransferState &State) { |
428 | 234 | assert(F.getTemplateSpecializationArgs()->size() > 0); |
429 | | |
430 | 0 | const int TemplateParamOptionalWrappersCount = countOptionalWrappers( |
431 | 234 | *MatchRes.Context, |
432 | 234 | stripReference(F.getTemplateSpecializationArgs()->get(0).getAsType())); |
433 | 234 | const int ArgTypeOptionalWrappersCount = |
434 | 234 | 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 | 234 | if (TemplateParamOptionalWrappersCount == ArgTypeOptionalWrappersCount) |
439 | 186 | 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 | 48 | if (auto *HasValueVal = |
444 | 48 | getHasValue(State.Env, State.Env.getValue(E, SkipPast::Reference))) |
445 | 48 | return *HasValueVal; |
446 | 0 | return State.Env.makeAtomicBoolValue(); |
447 | 48 | } |
448 | | |
449 | | void transferValueOrConversionConstructor( |
450 | | const CXXConstructExpr *E, const MatchFinder::MatchResult &MatchRes, |
451 | 198 | LatticeTransferState &State) { |
452 | 198 | assert(E->getNumArgs() > 0); |
453 | | |
454 | 0 | assignOptionalValue(*E, State, |
455 | 198 | value_orConversionHasValue(*E->getConstructor(), |
456 | 198 | *E->getArg(0), MatchRes, |
457 | 198 | State)); |
458 | 198 | } |
459 | | |
460 | | void transferAssignment(const CXXOperatorCallExpr *E, BoolValue &HasValueVal, |
461 | 66 | LatticeTransferState &State) { |
462 | 66 | assert(E->getNumArgs() > 0); |
463 | | |
464 | 0 | auto *OptionalLoc = |
465 | 66 | State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); |
466 | 66 | if (OptionalLoc == nullptr) |
467 | 0 | return; |
468 | | |
469 | 66 | State.Env.setValue(*OptionalLoc, createOptionalValue(State.Env, HasValueVal)); |
470 | | |
471 | | // Assign a storage location for the whole expression. |
472 | 66 | State.Env.setStorageLocation(*E, *OptionalLoc); |
473 | 66 | } |
474 | | |
475 | | void transferValueOrConversionAssignment( |
476 | | const CXXOperatorCallExpr *E, const MatchFinder::MatchResult &MatchRes, |
477 | 36 | LatticeTransferState &State) { |
478 | 36 | assert(E->getNumArgs() > 1); |
479 | 0 | transferAssignment(E, |
480 | 36 | value_orConversionHasValue(*E->getDirectCallee(), |
481 | 36 | *E->getArg(1), MatchRes, State), |
482 | 36 | State); |
483 | 36 | } |
484 | | |
485 | | void transferNulloptAssignment(const CXXOperatorCallExpr *E, |
486 | | const MatchFinder::MatchResult &, |
487 | 30 | LatticeTransferState &State) { |
488 | 30 | transferAssignment(E, State.Env.getBoolLiteralValue(false), State); |
489 | 30 | } |
490 | | |
491 | | void transferSwap(const StorageLocation &OptionalLoc1, |
492 | | const StorageLocation &OptionalLoc2, |
493 | 24 | LatticeTransferState &State) { |
494 | 24 | auto *OptionalVal1 = State.Env.getValue(OptionalLoc1); |
495 | 24 | assert(OptionalVal1 != nullptr); |
496 | | |
497 | 0 | auto *OptionalVal2 = State.Env.getValue(OptionalLoc2); |
498 | 24 | assert(OptionalVal2 != nullptr); |
499 | | |
500 | 0 | State.Env.setValue(OptionalLoc1, *OptionalVal2); |
501 | 24 | State.Env.setValue(OptionalLoc2, *OptionalVal1); |
502 | 24 | } |
503 | | |
504 | | void transferSwapCall(const CXXMemberCallExpr *E, |
505 | | const MatchFinder::MatchResult &, |
506 | 12 | LatticeTransferState &State) { |
507 | 12 | assert(E->getNumArgs() == 1); |
508 | | |
509 | 0 | auto *OptionalLoc1 = State.Env.getStorageLocation( |
510 | 12 | *E->getImplicitObjectArgument(), SkipPast::ReferenceThenPointer); |
511 | 12 | assert(OptionalLoc1 != nullptr); |
512 | | |
513 | 0 | auto *OptionalLoc2 = |
514 | 12 | State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); |
515 | 12 | assert(OptionalLoc2 != nullptr); |
516 | | |
517 | 0 | transferSwap(*OptionalLoc1, *OptionalLoc2, State); |
518 | 12 | } |
519 | | |
520 | | void transferStdSwapCall(const CallExpr *E, const MatchFinder::MatchResult &, |
521 | 12 | LatticeTransferState &State) { |
522 | 12 | assert(E->getNumArgs() == 2); |
523 | | |
524 | 0 | auto *OptionalLoc1 = |
525 | 12 | State.Env.getStorageLocation(*E->getArg(0), SkipPast::Reference); |
526 | 12 | assert(OptionalLoc1 != nullptr); |
527 | | |
528 | 0 | auto *OptionalLoc2 = |
529 | 12 | State.Env.getStorageLocation(*E->getArg(1), SkipPast::Reference); |
530 | 12 | assert(OptionalLoc2 != nullptr); |
531 | | |
532 | 0 | transferSwap(*OptionalLoc1, *OptionalLoc2, State); |
533 | 12 | } |
534 | | |
535 | | llvm::Optional<StatementMatcher> |
536 | 486 | ignorableOptional(const UncheckedOptionalAccessModelOptions &Options) { |
537 | 486 | if (Options.IgnoreSmartPointerDereference) |
538 | 486 | return memberExpr(hasObjectExpression(ignoringParenImpCasts( |
539 | 486 | cxxOperatorCallExpr(anyOf(hasOverloadedOperatorName("->"), |
540 | 486 | hasOverloadedOperatorName("*")), |
541 | 486 | unless(hasArgument(0, expr(hasOptionalType()))))))); |
542 | 0 | return llvm::None; |
543 | 486 | } |
544 | | |
545 | | StatementMatcher |
546 | 486 | valueCall(llvm::Optional<StatementMatcher> &IgnorableOptional) { |
547 | 486 | return isOptionalMemberCallWithName("value", IgnorableOptional); |
548 | 486 | } |
549 | | |
550 | | StatementMatcher |
551 | 486 | valueOperatorCall(llvm::Optional<StatementMatcher> &IgnorableOptional) { |
552 | 486 | return expr(anyOf(isOptionalOperatorCallWithName("*", IgnorableOptional), |
553 | 486 | isOptionalOperatorCallWithName("->", IgnorableOptional))); |
554 | 486 | } |
555 | | |
556 | | auto buildTransferMatchSwitch( |
557 | 243 | 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 | 243 | auto IgnorableOptional = ignorableOptional(Options); |
562 | 243 | return MatchSwitchBuilder<LatticeTransferState>() |
563 | | // Attach a symbolic "has_value" state to optional values that we see for |
564 | | // the first time. |
565 | 243 | .CaseOf<Expr>( |
566 | 243 | expr(anyOf(declRefExpr(), memberExpr()), hasOptionalType()), |
567 | 243 | initializeOptionalReference) |
568 | | |
569 | | // make_optional |
570 | 243 | .CaseOf<CallExpr>(isMakeOptionalCall(), transferMakeOptionalCall) |
571 | | |
572 | | // optional::optional |
573 | 243 | .CaseOf<CXXConstructExpr>( |
574 | 243 | isOptionalInPlaceConstructor(), |
575 | 243 | [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, |
576 | 243 | LatticeTransferState &State) { |
577 | 24 | assignOptionalValue(*E, State, State.Env.getBoolLiteralValue(true)); |
578 | 24 | }) |
579 | 243 | .CaseOf<CXXConstructExpr>( |
580 | 243 | isOptionalNulloptConstructor(), |
581 | 243 | [](const CXXConstructExpr *E, const MatchFinder::MatchResult &, |
582 | 243 | LatticeTransferState &State) { |
583 | 42 | assignOptionalValue(*E, State, |
584 | 42 | State.Env.getBoolLiteralValue(false)); |
585 | 42 | }) |
586 | 243 | .CaseOf<CXXConstructExpr>(isOptionalValueOrConversionConstructor(), |
587 | 243 | transferValueOrConversionConstructor) |
588 | | |
589 | | // optional::operator= |
590 | 243 | .CaseOf<CXXOperatorCallExpr>(isOptionalValueOrConversionAssignment(), |
591 | 243 | transferValueOrConversionAssignment) |
592 | 243 | .CaseOf<CXXOperatorCallExpr>(isOptionalNulloptAssignment(), |
593 | 243 | transferNulloptAssignment) |
594 | | |
595 | | // optional::value |
596 | 243 | .CaseOf<CXXMemberCallExpr>( |
597 | 243 | valueCall(IgnorableOptional), |
598 | 243 | [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, |
599 | 522 | LatticeTransferState &State) { |
600 | 522 | transferUnwrapCall(E, E->getImplicitObjectArgument(), State); |
601 | 522 | }) |
602 | | |
603 | | // optional::operator*, optional::operator-> |
604 | 243 | .CaseOf<CallExpr>(valueOperatorCall(IgnorableOptional), |
605 | 243 | [](const CallExpr *E, const MatchFinder::MatchResult &, |
606 | 243 | LatticeTransferState &State) { |
607 | 96 | transferUnwrapCall(E, E->getArg(0), State); |
608 | 96 | }) |
609 | | |
610 | | // optional::has_value |
611 | 243 | .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("has_value"), |
612 | 243 | transferOptionalHasValueCall) |
613 | | |
614 | | // optional::operator bool |
615 | 243 | .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("operator bool"), |
616 | 243 | transferOptionalHasValueCall) |
617 | | |
618 | | // optional::emplace |
619 | 243 | .CaseOf<CXXMemberCallExpr>( |
620 | 243 | isOptionalMemberCallWithName("emplace"), |
621 | 243 | [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, |
622 | 243 | LatticeTransferState &State) { |
623 | 12 | assignOptionalValue(*E->getImplicitObjectArgument(), State, |
624 | 12 | State.Env.getBoolLiteralValue(true)); |
625 | 12 | }) |
626 | | |
627 | | // optional::reset |
628 | 243 | .CaseOf<CXXMemberCallExpr>( |
629 | 243 | isOptionalMemberCallWithName("reset"), |
630 | 243 | [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, |
631 | 243 | LatticeTransferState &State) { |
632 | 18 | assignOptionalValue(*E->getImplicitObjectArgument(), State, |
633 | 18 | State.Env.getBoolLiteralValue(false)); |
634 | 18 | }) |
635 | | |
636 | | // optional::swap |
637 | 243 | .CaseOf<CXXMemberCallExpr>(isOptionalMemberCallWithName("swap"), |
638 | 243 | transferSwapCall) |
639 | | |
640 | | // std::swap |
641 | 243 | .CaseOf<CallExpr>(isStdSwapCall(), transferStdSwapCall) |
642 | | |
643 | | // opt.value_or("").empty() |
644 | 243 | .CaseOf<Expr>(isValueOrStringEmptyCall(), transferValueOrStringEmptyCall) |
645 | | |
646 | | // opt.value_or(X) != X |
647 | 243 | .CaseOf<Expr>(isValueOrNotEqX(), transferValueOrNotEqX) |
648 | | |
649 | | // returns optional |
650 | 243 | .CaseOf<CallExpr>(isCallReturningOptional(), |
651 | 243 | transferCallReturningOptional) |
652 | | |
653 | 243 | .Build(); |
654 | 243 | } |
655 | | |
656 | | std::vector<SourceLocation> diagnoseUnwrapCall(const Expr *UnwrapExpr, |
657 | | const Expr *ObjectExpr, |
658 | 306 | const Environment &Env) { |
659 | 306 | if (auto *OptionalVal = |
660 | 306 | Env.getValue(*ObjectExpr, SkipPast::ReferenceThenPointer)) { |
661 | 306 | auto *Prop = OptionalVal->getProperty("has_value"); |
662 | 306 | if (auto *HasValueVal = cast_or_null<BoolValue>(Prop)) { |
663 | 303 | if (Env.flowConditionImplies(*HasValueVal)) |
664 | 183 | return {}; |
665 | 303 | } |
666 | 306 | } |
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 | 123 | return {ObjectExpr->getBeginLoc()}; |
672 | 306 | } |
673 | | |
674 | | auto buildDiagnoseMatchSwitch( |
675 | 243 | 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 | 243 | auto IgnorableOptional = ignorableOptional(Options); |
680 | 243 | return MatchSwitchBuilder<const Environment, std::vector<SourceLocation>>() |
681 | | // optional::value |
682 | 243 | .CaseOf<CXXMemberCallExpr>( |
683 | 243 | valueCall(IgnorableOptional), |
684 | 243 | [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &, |
685 | 258 | const Environment &Env) { |
686 | 258 | return diagnoseUnwrapCall(E, E->getImplicitObjectArgument(), Env); |
687 | 258 | }) |
688 | | |
689 | | // optional::operator*, optional::operator-> |
690 | 243 | .CaseOf<CallExpr>( |
691 | 243 | valueOperatorCall(IgnorableOptional), |
692 | 243 | [](const CallExpr *E, const MatchFinder::MatchResult &, |
693 | 243 | const Environment &Env) { |
694 | 48 | return diagnoseUnwrapCall(E, E->getArg(0), Env); |
695 | 48 | }) |
696 | 243 | .Build(); |
697 | 243 | } |
698 | | |
699 | | } // namespace |
700 | | |
701 | | ast_matchers::DeclarationMatcher |
702 | 0 | UncheckedOptionalAccessModel::optionalClassDecl() { |
703 | 0 | return optionalClass(); |
704 | 0 | } |
705 | | |
706 | | UncheckedOptionalAccessModel::UncheckedOptionalAccessModel( |
707 | | ASTContext &Ctx, UncheckedOptionalAccessModelOptions Options) |
708 | | : DataflowAnalysis<UncheckedOptionalAccessModel, NoopLattice>(Ctx), |
709 | 243 | TransferMatchSwitch(buildTransferMatchSwitch(Options)) {} |
710 | | |
711 | | void UncheckedOptionalAccessModel::transfer(const Stmt *S, NoopLattice &L, |
712 | 6.91k | Environment &Env) { |
713 | 6.91k | LatticeTransferState State(L, Env); |
714 | 6.91k | TransferMatchSwitch(*S, getASTContext(), State); |
715 | 6.91k | } |
716 | | |
717 | | bool UncheckedOptionalAccessModel::compareEquivalent(QualType Type, |
718 | | const Value &Val1, |
719 | | const Environment &Env1, |
720 | | const Value &Val2, |
721 | 27 | const Environment &Env2) { |
722 | 27 | return isNonEmptyOptional(Val1, Env1) == isNonEmptyOptional(Val2, Env2); |
723 | 27 | } |
724 | | |
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 | 142 | Environment &MergedEnv) { |
731 | 142 | if (!IsOptionalType(Type)) |
732 | 46 | return true; |
733 | | |
734 | 96 | auto &HasValueVal = MergedEnv.makeAtomicBoolValue(); |
735 | 96 | if (isNonEmptyOptional(Val1, Env1) && isNonEmptyOptional(Val2, Env2)29 ) |
736 | 18 | MergedEnv.addToFlowCondition(HasValueVal); |
737 | 78 | else if (isEmptyOptional(Val1, Env1) && isEmptyOptional(Val2, Env2)28 ) |
738 | 14 | MergedEnv.addToFlowCondition(MergedEnv.makeNot(HasValueVal)); |
739 | 96 | setHasValue(MergedVal, HasValueVal); |
740 | 96 | return true; |
741 | 142 | } |
742 | | |
743 | | UncheckedOptionalAccessDiagnoser::UncheckedOptionalAccessDiagnoser( |
744 | | UncheckedOptionalAccessModelOptions Options) |
745 | 243 | : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {} |
746 | | |
747 | | std::vector<SourceLocation> UncheckedOptionalAccessDiagnoser::diagnose( |
748 | 3.39k | ASTContext &Context, const Stmt *Stmt, const Environment &Env) { |
749 | 3.39k | return DiagnoseMatchSwitch(*Stmt, Context, Env); |
750 | 3.39k | } |
751 | | |
752 | | } // namespace dataflow |
753 | | } // namespace clang |