1//===- UncheckedStatusOrAccessModel.cpp -----------------------------------===//
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#include "clang/Analysis/FlowSensitive/Models/UncheckedStatusOrAccessModel.h"
10
11#include <cassert>
12#include <utility>
13
14#include "clang/AST/DeclCXX.h"
15#include "clang/AST/DeclTemplate.h"
16#include "clang/AST/Expr.h"
17#include "clang/AST/ExprCXX.h"
18#include "clang/AST/TypeBase.h"
19#include "clang/ASTMatchers/ASTMatchFinder.h"
20#include "clang/ASTMatchers/ASTMatchers.h"
21#include "clang/ASTMatchers/ASTMatchersInternal.h"
22#include "clang/Analysis/CFG.h"
23#include "clang/Analysis/FlowSensitive/CFGMatchSwitch.h"
24#include "clang/Analysis/FlowSensitive/DataflowAnalysis.h"
25#include "clang/Analysis/FlowSensitive/DataflowEnvironment.h"
26#include "clang/Analysis/FlowSensitive/MatchSwitch.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/LLVM.h"
32#include "clang/Basic/SourceLocation.h"
33#include "llvm/ADT/StringMap.h"
34
35namespace clang::dataflow::statusor_model {
36namespace {
37
38using ::clang::ast_matchers::MatchFinder;
39using ::clang::ast_matchers::StatementMatcher;
40
41} // namespace
42
43static bool namespaceEquals(const NamespaceDecl *NS,
44 clang::ArrayRef<clang::StringRef> NamespaceNames) {
45 while (!NamespaceNames.empty() && NS) {
46 if (NS->getName() != NamespaceNames.consume_back())
47 return false;
48 NS = dyn_cast_or_null<NamespaceDecl>(Val: NS->getParent());
49 }
50 return NamespaceNames.empty() && !NS;
51}
52
53// TODO: move this to a proper place to share with the rest of clang
54static bool isTypeNamed(QualType Type, clang::ArrayRef<clang::StringRef> NS,
55 StringRef Name) {
56 if (Type.isNull())
57 return false;
58 if (auto *RD = Type->getAsRecordDecl())
59 if (RD->getName() == Name)
60 if (const auto *N = dyn_cast_or_null<NamespaceDecl>(Val: RD->getDeclContext()))
61 return namespaceEquals(NS: N, NamespaceNames: NS);
62 return false;
63}
64
65static bool isStatusOrOperatorBaseType(QualType Type) {
66 return isTypeNamed(Type, NS: {"absl", "internal_statusor"}, Name: "OperatorBase");
67}
68
69static bool isSafeUnwrap(RecordStorageLocation *StatusOrLoc,
70 const Environment &Env) {
71 if (!StatusOrLoc)
72 return false;
73 auto &StatusLoc = locForStatus(StatusOrLoc&: *StatusOrLoc);
74 auto *OkVal = Env.get<BoolValue>(Loc: locForOk(StatusLoc));
75 return OkVal != nullptr && Env.proves(OkVal->formula());
76}
77
78static ClassTemplateSpecializationDecl *
79getStatusOrBaseClass(const QualType &Ty) {
80 auto *RD = Ty->getAsCXXRecordDecl();
81 if (RD == nullptr)
82 return nullptr;
83 if (isStatusOrType(Type: Ty) ||
84 // In case we are analyzing code under OperatorBase itself that uses
85 // operator* (e.g. to implement operator->).
86 isStatusOrOperatorBaseType(Type: Ty))
87 return cast<ClassTemplateSpecializationDecl>(Val: RD);
88 if (!RD->hasDefinition())
89 return nullptr;
90 for (const auto &Base : RD->bases())
91 if (auto *QT = getStatusOrBaseClass(Ty: Base.getType()))
92 return QT;
93 return nullptr;
94}
95
96static QualType getStatusOrValueType(ClassTemplateSpecializationDecl *TRD) {
97 return TRD->getTemplateArgs().get(Idx: 0).getAsType();
98}
99
100static auto ofClassStatus() {
101 using namespace ::clang::ast_matchers;
102 return ofClass(InnerMatcher: hasName(Name: "::absl::Status"));
103}
104
105static auto isStatusMemberCallWithName(llvm::StringRef member_name) {
106 using namespace ::clang::ast_matchers;
107 return cxxMemberCallExpr(
108 on(InnerMatcher: expr(unless(cxxThisExpr()))),
109 callee(InnerMatcher: cxxMethodDecl(hasName(Name: member_name), ofClassStatus())));
110}
111
112static auto isStatusOrMemberCallWithName(llvm::StringRef member_name) {
113 using namespace ::clang::ast_matchers;
114 return cxxMemberCallExpr(
115 on(InnerMatcher: expr(unless(cxxThisExpr()))),
116 callee(InnerMatcher: cxxMethodDecl(
117 hasName(Name: member_name),
118 ofClass(InnerMatcher: anyOf(statusOrClass(), statusOrOperatorBaseClass())))));
119}
120
121static auto isStatusOrOperatorCallWithName(llvm::StringRef operator_name) {
122 using namespace ::clang::ast_matchers;
123 return cxxOperatorCallExpr(
124 hasOverloadedOperatorName(Name: operator_name),
125 callee(InnerMatcher: cxxMethodDecl(
126 ofClass(InnerMatcher: anyOf(statusOrClass(), statusOrOperatorBaseClass())))));
127}
128
129static auto valueCall() {
130 using namespace ::clang::ast_matchers;
131 return anyOf(isStatusOrMemberCallWithName(member_name: "value"),
132 isStatusOrMemberCallWithName(member_name: "ValueOrDie"));
133}
134
135static auto valueOperatorCall() {
136 using namespace ::clang::ast_matchers;
137 return expr(anyOf(isStatusOrOperatorCallWithName(operator_name: "*"),
138 isStatusOrOperatorCallWithName(operator_name: "->")));
139}
140
141static auto isComparisonOperatorCall(llvm::StringRef operator_name) {
142 using namespace ::clang::ast_matchers;
143 return cxxOperatorCallExpr(
144 hasOverloadedOperatorName(Name: operator_name), argumentCountIs(N: 2),
145 hasArgument(N: 0, InnerMatcher: anyOf(hasType(InnerMatcher: statusType()), hasType(InnerMatcher: statusOrType()))),
146 hasArgument(N: 1, InnerMatcher: anyOf(hasType(InnerMatcher: statusType()), hasType(InnerMatcher: statusOrType()))));
147}
148
149static auto isOkStatusCall() {
150 using namespace ::clang::ast_matchers;
151 return callExpr(callee(InnerMatcher: functionDecl(hasName(Name: "::absl::OkStatus"))));
152}
153
154static auto isNotOkStatusCall() {
155 using namespace ::clang::ast_matchers;
156 return callExpr(callee(InnerMatcher: functionDecl(hasAnyName(
157 "::absl::AbortedError", "::absl::AlreadyExistsError",
158 "::absl::CancelledError", "::absl::DataLossError",
159 "::absl::DeadlineExceededError", "::absl::FailedPreconditionError",
160 "::absl::InternalError", "::absl::InvalidArgumentError",
161 "::absl::NotFoundError", "::absl::OutOfRangeError",
162 "::absl::PermissionDeniedError", "::absl::ResourceExhaustedError",
163 "::absl::UnauthenticatedError", "::absl::UnavailableError",
164 "::absl::UnimplementedError", "::absl::UnknownError"))));
165}
166
167static auto isPointerComparisonOperatorCall(std::string operator_name) {
168 using namespace ::clang::ast_matchers;
169 return binaryOperator(hasOperatorName(Name: operator_name),
170 hasLHS(InnerMatcher: hasType(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType(
171 pointee(anyOf(statusOrType(), statusType())))))),
172 hasRHS(InnerMatcher: hasType(InnerMatcher: hasCanonicalType(InnerMatcher: pointerType(
173 pointee(anyOf(statusOrType(), statusType())))))));
174}
175
176// The nullPointerConstant in the two matchers below is to support
177// absl::StatusOr<void*> X = nullptr.
178// nullptr does not match the bound type.
179// TODO: be less restrictive around convertible types in general.
180static auto isStatusOrValueAssignmentCall() {
181 using namespace ::clang::ast_matchers;
182 return cxxOperatorCallExpr(
183 hasOverloadedOperatorName(Name: "="),
184 callee(InnerMatcher: cxxMethodDecl(ofClass(InnerMatcher: statusOrClass()))),
185 hasArgument(N: 1, InnerMatcher: anyOf(hasType(InnerMatcher: hasUnqualifiedDesugaredType(
186 InnerMatcher: type(equalsBoundNode(ID: "T")))),
187 nullPointerConstant())));
188}
189
190static auto isStatusOrValueConstructor() {
191 using namespace ::clang::ast_matchers;
192 return cxxConstructExpr(
193 hasType(InnerMatcher: statusOrType()),
194 hasArgument(N: 0,
195 InnerMatcher: anyOf(hasType(InnerMatcher: hasCanonicalType(InnerMatcher: type(equalsBoundNode(ID: "T")))),
196 nullPointerConstant(),
197 hasType(InnerMatcher: namedDecl(hasAnyName("absl::in_place_t",
198 "std::in_place_t"))))));
199}
200
201static auto isStatusOrConstructor() {
202 using namespace ::clang::ast_matchers;
203 return cxxConstructExpr(hasType(InnerMatcher: statusOrType()));
204}
205
206static auto isStatusConstructor() {
207 using namespace ::clang::ast_matchers;
208 return cxxConstructExpr(hasType(InnerMatcher: statusType()));
209}
210static auto isLoggingGetReferenceableValueCall() {
211 using namespace ::clang::ast_matchers;
212 return callExpr(callee(
213 InnerMatcher: functionDecl(hasName(Name: "::absl::log_internal::GetReferenceableValue"))));
214}
215
216static auto isLoggingCheckEqImpl() {
217 using namespace ::clang::ast_matchers;
218 return callExpr(
219 callee(InnerMatcher: functionDecl(hasName(Name: "::absl::log_internal::Check_EQImpl"))));
220}
221
222static auto isAsStatusCallWithStatus() {
223 using namespace ::clang::ast_matchers;
224 return callExpr(
225 callee(InnerMatcher: functionDecl(hasName(Name: "::absl::log_internal::AsStatus"))),
226 hasArgument(N: 0, InnerMatcher: hasType(InnerMatcher: statusClass())));
227}
228
229static auto isAsStatusCallWithStatusOr() {
230 using namespace ::clang::ast_matchers;
231 return callExpr(
232 callee(InnerMatcher: functionDecl(hasName(Name: "::absl::log_internal::AsStatus"))),
233 hasArgument(N: 0, InnerMatcher: hasType(InnerMatcher: statusOrType())));
234}
235
236static auto possiblyReferencedStatusOrType() {
237 using namespace ::clang::ast_matchers;
238 return anyOf(statusOrType(), referenceType(pointee(statusOrType())));
239}
240
241static auto isConstAccessorMemberCall() {
242 using namespace ::clang::ast_matchers;
243 return cxxMemberCallExpr(callee(InnerMatcher: cxxMethodDecl(
244 parameterCountIs(N: 0), isConst(),
245 returns(InnerMatcher: hasCanonicalType(InnerMatcher: anyOf(referenceType(), recordType()))))));
246}
247
248static auto isConstAccessorMemberOperatorCall() {
249 using namespace ::clang::ast_matchers;
250 return cxxOperatorCallExpr(callee(InnerMatcher: cxxMethodDecl(
251 parameterCountIs(N: 0), isConst(),
252 returns(InnerMatcher: hasCanonicalType(InnerMatcher: anyOf(referenceType(), recordType()))))));
253}
254
255static auto isConstPointerAccessorMemberCall() {
256 using namespace ::clang::ast_matchers;
257 return cxxMemberCallExpr(callee(
258 InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0), isConst(), returns(InnerMatcher: pointerType()))));
259}
260
261static auto isConstPointerAccessorMemberOperatorCall() {
262 using namespace ::clang::ast_matchers;
263 return cxxOperatorCallExpr(callee(
264 InnerMatcher: cxxMethodDecl(parameterCountIs(N: 0), isConst(), returns(InnerMatcher: pointerType()))));
265}
266
267static auto isNonConstMemberCall() {
268 using namespace ::clang::ast_matchers;
269 return cxxMemberCallExpr(callee(InnerMatcher: cxxMethodDecl(unless(isConst()))));
270}
271
272static auto isNonConstMemberOperatorCall() {
273 using namespace ::clang::ast_matchers;
274 return cxxOperatorCallExpr(callee(InnerMatcher: cxxMethodDecl(unless(isConst()))));
275}
276
277static auto isMakePredicateFormatterFromIsOkMatcherCall() {
278 using namespace ::clang::ast_matchers;
279 return callExpr(
280 callee(InnerMatcher: functionDecl(
281 hasName(Name: "::testing::internal::MakePredicateFormatterFromMatcher"))),
282 hasArgument(
283 N: 0, InnerMatcher: hasType(InnerMatcher: cxxRecordDecl(hasAnyName(
284 "::testing::status::internal_status::IsOkMatcher",
285 "::absl_testing::status_internal::IsOkMatcher",
286 "::testing::status::internal_status::IsOkAndHoldsMatcher",
287 "::absl_testing::status_internal::IsOkAndHoldsMatcher")))));
288}
289
290static auto isStatusIsOkMatcherCall() {
291 using namespace ::clang::ast_matchers;
292 return callExpr(callee(InnerMatcher: functionDecl(hasAnyName(
293 "::testing::status::StatusIs", "absl_testing::StatusIs",
294 "::testing::status::CanonicalStatusIs",
295 "::absl_testing::CanonicalStatusIs"))),
296 hasArgument(N: 0, InnerMatcher: declRefExpr(to(InnerMatcher: enumConstantDecl(hasAnyName(
297 "::absl::StatusCode::kOk", "OK"))))));
298}
299
300static auto isMakePredicateFormatterFromStatusIsMatcherCall() {
301 using namespace ::clang::ast_matchers;
302 return callExpr(
303 callee(InnerMatcher: functionDecl(
304 hasName(Name: "::testing::internal::MakePredicateFormatterFromMatcher"))),
305 hasArgument(N: 0, InnerMatcher: hasType(InnerMatcher: cxxRecordDecl(hasAnyName(
306 "::testing::status::internal_status::StatusIsMatcher",
307 "::testing::status::internal_status::"
308 "CanonicalStatusIsMatcher",
309 "::absl_testing::status_internal::StatusIsMatcher",
310 "::absl_testing::status_internal::"
311 "CanonicalStatusIsMatcher")))));
312}
313
314static auto isPredicateFormatterFromStatusMatcherCall() {
315 using namespace ::clang::ast_matchers;
316 return cxxOperatorCallExpr(
317 hasOverloadedOperatorName(Name: "()"),
318 callee(InnerMatcher: cxxMethodDecl(ofClass(
319 InnerMatcher: hasName(Name: "testing::internal::PredicateFormatterFromMatcher")))),
320 hasArgument(N: 2, InnerMatcher: hasType(InnerMatcher: statusType())));
321}
322
323static auto isPredicateFormatterFromStatusOrMatcherCall() {
324 using namespace ::clang::ast_matchers;
325 return cxxOperatorCallExpr(
326 hasOverloadedOperatorName(Name: "()"),
327 callee(InnerMatcher: cxxMethodDecl(ofClass(
328 InnerMatcher: hasName(Name: "testing::internal::PredicateFormatterFromMatcher")))),
329 hasArgument(N: 2, InnerMatcher: hasType(InnerMatcher: statusOrType())));
330}
331
332static auto isAssertionResultOperatorBoolCall() {
333 using namespace ::clang::ast_matchers;
334 return cxxMemberCallExpr(
335 on(InnerMatcher: expr(unless(cxxThisExpr()))),
336 callee(InnerMatcher: cxxMethodDecl(hasName(Name: "operator bool"),
337 ofClass(InnerMatcher: hasName(Name: "testing::AssertionResult")))));
338}
339
340static auto isAssertionResultConstructFromBoolCall() {
341 using namespace ::clang::ast_matchers;
342 return cxxConstructExpr(
343 hasType(InnerMatcher: recordDecl(hasName(Name: "testing::AssertionResult"))),
344 hasArgument(N: 0, InnerMatcher: hasType(InnerMatcher: booleanType())));
345}
346
347static auto isStatusOrReturningCall() {
348 using namespace ::clang::ast_matchers;
349 return callExpr(
350 callee(InnerMatcher: functionDecl(returns(InnerMatcher: possiblyReferencedStatusOrType()))));
351}
352
353static auto isStatusOrPtrReturningCall() {
354 using namespace ::clang::ast_matchers;
355 return callExpr(callee(InnerMatcher: functionDecl(returns(InnerMatcher: hasUnqualifiedDesugaredType(
356 InnerMatcher: pointerType(pointee(possiblyReferencedStatusOrType())))))));
357}
358
359static auto isStatusPtrReturningCall() {
360 using namespace ::clang::ast_matchers;
361 return callExpr(callee(InnerMatcher: functionDecl(returns(InnerMatcher: hasUnqualifiedDesugaredType(
362 InnerMatcher: pointerType(pointee(hasUnqualifiedDesugaredType(
363 InnerMatcher: recordType(hasDeclaration(InnerMatcher: statusClass()))))))))));
364}
365
366static auto ofClassReturnIfErrorAdaptor() {
367 using namespace ::clang::ast_matchers;
368 return ofClass(
369 InnerMatcher: hasName(Name: "::absl::status_macro_internal::ReturnIfErrorAdaptor"));
370}
371
372static auto returnIfErrorAdaptorClass() {
373 using namespace ::clang::ast_matchers;
374 return cxxRecordDecl(
375 hasName(Name: "::absl::status_macro_internal::ReturnIfErrorAdaptor"));
376}
377
378static auto returnIfErrorAdaptorType() {
379 using namespace ::clang::ast_matchers;
380 return hasUnqualifiedDesugaredType(
381 InnerMatcher: recordType(hasDeclaration(InnerMatcher: returnIfErrorAdaptorClass())));
382}
383
384static auto isReturnIfErrorAdaptorOperatorBoolCall() {
385 using namespace ::clang::ast_matchers;
386 return cxxMemberCallExpr(
387 on(InnerMatcher: expr(unless(cxxThisExpr()))),
388 callee(InnerMatcher: cxxMethodDecl(hasName(Name: "operator bool"),
389 ofClassReturnIfErrorAdaptor())));
390}
391
392static auto isMacroAdaptorCall() {
393 using namespace ::clang::ast_matchers;
394 return callExpr(callee(
395 InnerMatcher: functionDecl(hasName(Name: "::absl::status_macro_internal::MacroAdaptor"),
396 returns(InnerMatcher: returnIfErrorAdaptorType()))));
397}
398
399static auto
400buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
401 return CFGMatchSwitchBuilder<const Environment,
402 llvm::SmallVector<SourceLocation>>()
403 // StatusOr::value, StatusOr::ValueOrDie
404 .CaseOfCFGStmt<CXXMemberCallExpr>(
405 M: valueCall(),
406 A: [](const CXXMemberCallExpr *E,
407 const ast_matchers::MatchFinder::MatchResult &,
408 const Environment &Env) {
409 if (!isSafeUnwrap(StatusOrLoc: getImplicitObjectLocation(MCE: *E, Env), Env))
410 return llvm::SmallVector<SourceLocation>({E->getExprLoc()});
411 return llvm::SmallVector<SourceLocation>();
412 })
413
414 // StatusOr::operator*, StatusOr::operator->
415 .CaseOfCFGStmt<CXXOperatorCallExpr>(
416 M: valueOperatorCall(),
417 A: [](const CXXOperatorCallExpr *E,
418 const ast_matchers::MatchFinder::MatchResult &,
419 const Environment &Env) {
420 RecordStorageLocation *StatusOrLoc =
421 Env.get<RecordStorageLocation>(E: *E->getArg(Arg: 0));
422 if (!isSafeUnwrap(StatusOrLoc, Env))
423 return llvm::SmallVector<SourceLocation>({E->getOperatorLoc()});
424 return llvm::SmallVector<SourceLocation>();
425 })
426 .Build();
427}
428
429UncheckedStatusOrAccessDiagnoser::UncheckedStatusOrAccessDiagnoser(
430 UncheckedStatusOrAccessModelOptions Options)
431 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
432
433llvm::SmallVector<SourceLocation> UncheckedStatusOrAccessDiagnoser::operator()(
434 const CFGElement &Elt, ASTContext &Ctx,
435 const TransferStateForDiagnostics<UncheckedStatusOrAccessModel::Lattice>
436 &State) {
437 return DiagnoseMatchSwitch(Elt, Ctx, State.Env);
438}
439
440BoolValue &initializeStatus(RecordStorageLocation &StatusLoc,
441 Environment &Env) {
442 auto &OkVal = Env.makeAtomicBoolValue();
443 Env.setValue(Loc: locForOk(StatusLoc), Val&: OkVal);
444 return OkVal;
445}
446
447BoolValue &initializeStatusOr(RecordStorageLocation &StatusOrLoc,
448 Environment &Env) {
449 return initializeStatus(StatusLoc&: locForStatus(StatusOrLoc), Env);
450}
451
452clang::ast_matchers::DeclarationMatcher statusOrClass() {
453 using namespace ::clang::ast_matchers;
454 return classTemplateSpecializationDecl(
455 hasName(Name: "absl::StatusOr"),
456 hasTemplateArgument(N: 0, InnerMatcher: refersToType(InnerMatcher: type().bind(ID: "T"))));
457}
458
459clang::ast_matchers::DeclarationMatcher statusClass() {
460 using namespace ::clang::ast_matchers;
461 return cxxRecordDecl(hasName(Name: "absl::Status"));
462}
463
464clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass() {
465 using namespace ::clang::ast_matchers;
466 return classTemplateSpecializationDecl(
467 hasName(Name: "absl::internal_statusor::OperatorBase"));
468}
469
470clang::ast_matchers::TypeMatcher statusOrType() {
471 using namespace ::clang::ast_matchers;
472 return hasCanonicalType(InnerMatcher: qualType(hasDeclaration(InnerMatcher: statusOrClass())));
473}
474
475clang::ast_matchers::TypeMatcher statusType() {
476 using namespace ::clang::ast_matchers;
477 return hasCanonicalType(InnerMatcher: qualType(hasDeclaration(InnerMatcher: statusClass())));
478}
479
480bool isStatusOrType(QualType Type) {
481 return isTypeNamed(Type, NS: {"absl"}, Name: "StatusOr");
482}
483
484bool isStatusType(QualType Type) {
485 return isTypeNamed(Type, NS: {"absl"}, Name: "Status");
486}
487
488static bool isPredicateFormatterFromMatcherType(QualType Type) {
489 return isTypeNamed(Type, NS: {"testing", "internal"},
490 Name: "PredicateFormatterFromMatcher");
491}
492
493static bool isAssertionResultType(QualType Type) {
494 return isTypeNamed(Type, NS: {"testing"}, Name: "AssertionResult");
495}
496
497static bool isStatusIsMatcherType(QualType Type) {
498 return isTypeNamed(Type, NS: {"testing", "status", "internal_status"},
499 Name: "StatusIsMatcher") ||
500 isTypeNamed(Type, NS: {"testing", "status", "internal_status"},
501 Name: "CanonicalStatusIsMatcher") ||
502 isTypeNamed(Type, NS: {"absl_testing", "status_internal"},
503 Name: "StatusIsMatcher") ||
504 isTypeNamed(Type, NS: {"absl_testing", "status_internal"},
505 Name: "CanonicalStatusIsMatcher");
506}
507
508static bool IsMacroAdaptorType(clang::QualType type) {
509 return isTypeNamed(Type: type, NS: {"absl", "status_macro_internal"},
510 Name: "ReturnIfErrorAdaptor");
511}
512
513llvm::StringMap<QualType> getSyntheticFields(QualType Ty, QualType StatusType,
514 const CXXRecordDecl &RD) {
515 if (auto *TRD = getStatusOrBaseClass(Ty))
516 return {{"status", StatusType}, {"value", getStatusOrValueType(TRD)}};
517 if (isStatusType(Type: Ty) || (RD.hasDefinition() &&
518 RD.isDerivedFrom(Base: StatusType->getAsCXXRecordDecl())))
519 return {{"ok", RD.getASTContext().BoolTy}};
520 if (isAssertionResultType(Type: Ty))
521 return {{"ok", RD.getASTContext().BoolTy}};
522 if (isPredicateFormatterFromMatcherType(Type: Ty))
523 return {{"ok_predicate", RD.getASTContext().BoolTy}};
524 if (isStatusIsMatcherType(Type: Ty))
525 return {{"ok_matcher", RD.getASTContext().BoolTy}};
526 if (IsMacroAdaptorType(type: Ty))
527 return {{"status", StatusType}};
528 return {};
529}
530
531RecordStorageLocation &locForStatus(RecordStorageLocation &StatusOrLoc) {
532 return cast<RecordStorageLocation>(Val&: StatusOrLoc.getSyntheticField(Name: "status"));
533}
534
535StorageLocation &locForOk(RecordStorageLocation &StatusLoc) {
536 return StatusLoc.getSyntheticField(Name: "ok");
537}
538
539BoolValue &valForOk(RecordStorageLocation &StatusLoc, Environment &Env) {
540 if (auto *Val = Env.get<BoolValue>(Loc: locForOk(StatusLoc)))
541 return *Val;
542 return initializeStatus(StatusLoc, Env);
543}
544static StorageLocation &locForOkPredicate(RecordStorageLocation &StatusLoc) {
545 return StatusLoc.getSyntheticField(Name: "ok_predicate");
546}
547
548static StorageLocation &locForOkMatcher(RecordStorageLocation &StatusLoc) {
549 return StatusLoc.getSyntheticField(Name: "ok_matcher");
550}
551
552static void transferStatusOrOkCall(const CXXMemberCallExpr *Expr,
553 const MatchFinder::MatchResult &,
554 LatticeTransferState &State) {
555 RecordStorageLocation *StatusOrLoc =
556 getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
557 if (StatusOrLoc == nullptr)
558 return;
559
560 auto &OkVal = valForOk(StatusLoc&: locForStatus(StatusOrLoc&: *StatusOrLoc), Env&: State.Env);
561 State.Env.setValue(E: *Expr, Val&: OkVal);
562}
563
564static void transferStatusCall(const CXXMemberCallExpr *Expr,
565 const MatchFinder::MatchResult &,
566 LatticeTransferState &State) {
567 RecordStorageLocation *StatusOrLoc =
568 getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
569 if (StatusOrLoc == nullptr)
570 return;
571
572 RecordStorageLocation &StatusLoc = locForStatus(StatusOrLoc&: *StatusOrLoc);
573
574 if (State.Env.getValue(Loc: locForOk(StatusLoc)) == nullptr)
575 initializeStatusOr(StatusOrLoc&: *StatusOrLoc, Env&: State.Env);
576
577 if (Expr->isPRValue())
578 copyRecord(Src&: StatusLoc, Dst&: State.Env.getResultObjectLocation(RecordPRValue: *Expr), Env&: State.Env);
579 else
580 State.Env.setStorageLocation(E: *Expr, Loc&: StatusLoc);
581}
582
583static void transferStatusOkCall(const CXXMemberCallExpr *Expr,
584 const MatchFinder::MatchResult &,
585 LatticeTransferState &State) {
586 RecordStorageLocation *StatusLoc =
587 getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
588 if (StatusLoc == nullptr)
589 return;
590
591 if (Value *Val = State.Env.getValue(Loc: locForOk(StatusLoc&: *StatusLoc)))
592 State.Env.setValue(E: *Expr, Val&: *Val);
593}
594
595static void transferStatusUpdateCall(const CXXMemberCallExpr *Expr,
596 const MatchFinder::MatchResult &,
597 LatticeTransferState &State) {
598 // S.Update(OtherS) sets S to the error code of OtherS if it is OK,
599 // otherwise does nothing.
600 assert(Expr->getNumArgs() == 1);
601 auto *Arg = Expr->getArg(Arg: 0);
602 RecordStorageLocation *ArgRecord =
603 Arg->isPRValue() ? &State.Env.getResultObjectLocation(RecordPRValue: *Arg)
604 : State.Env.get<RecordStorageLocation>(E: *Arg);
605 RecordStorageLocation *ThisLoc = getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
606 if (ThisLoc == nullptr || ArgRecord == nullptr)
607 return;
608
609 auto &ThisOkVal = valForOk(StatusLoc&: *ThisLoc, Env&: State.Env);
610 auto &ArgOkVal = valForOk(StatusLoc&: *ArgRecord, Env&: State.Env);
611 auto &A = State.Env.arena();
612 auto &NewVal = State.Env.makeAtomicBoolValue();
613 State.Env.assume(A.makeImplies(LHS: A.makeNot(Val: ThisOkVal.formula()),
614 RHS: A.makeNot(Val: NewVal.formula())));
615 State.Env.assume(A.makeImplies(LHS: NewVal.formula(), RHS: ArgOkVal.formula()));
616 State.Env.setValue(Loc: locForOk(StatusLoc&: *ThisLoc), Val&: NewVal);
617}
618
619static BoolValue *evaluateStatusEquality(RecordStorageLocation &LhsStatusLoc,
620 RecordStorageLocation &RhsStatusLoc,
621 Environment &Env) {
622 auto &A = Env.arena();
623 // Logically, a Status object is composed of an error code that could take one
624 // of multiple possible values, including the "ok" value. We track whether a
625 // Status object has an "ok" value and represent this as an `ok` bit. Equality
626 // of Status objects compares their error codes. Therefore, merely comparing
627 // the `ok` bits isn't sufficient: when two Status objects are assigned non-ok
628 // error codes the equality of their respective error codes matters. Since we
629 // only track the `ok` bits, we can't make any conclusions about equality when
630 // we know that two Status objects have non-ok values.
631
632 auto &LhsOkVal = valForOk(StatusLoc&: LhsStatusLoc, Env);
633 auto &RhsOkVal = valForOk(StatusLoc&: RhsStatusLoc, Env);
634
635 auto &Res = Env.makeAtomicBoolValue();
636
637 // lhs && rhs => res (a.k.a. !res => !lhs || !rhs)
638 Env.assume(A.makeImplies(LHS: A.makeAnd(LHS: LhsOkVal.formula(), RHS: RhsOkVal.formula()),
639 RHS: Res.formula()));
640 // res => (lhs == rhs)
641 Env.assume(A.makeImplies(
642 LHS: Res.formula(), RHS: A.makeEquals(LHS: LhsOkVal.formula(), RHS: RhsOkVal.formula())));
643
644 return &Res;
645}
646
647static BoolValue *
648evaluateStatusOrEquality(RecordStorageLocation &LhsStatusOrLoc,
649 RecordStorageLocation &RhsStatusOrLoc,
650 Environment &Env) {
651 auto &A = Env.arena();
652 // Logically, a StatusOr<T> object is composed of two values - a Status and a
653 // value of type T. Equality of StatusOr objects compares both values.
654 // Therefore, merely comparing the `ok` bits of the Status values isn't
655 // sufficient. When two StatusOr objects are engaged, the equality of their
656 // respective values of type T matters. Similarly, when two StatusOr objects
657 // have Status values that have non-ok error codes, the equality of the error
658 // codes matters. Since we only track the `ok` bits of the Status values, we
659 // can't make any conclusions about equality when we know that two StatusOr
660 // objects are engaged or when their Status values contain non-ok error codes.
661 auto &LhsOkVal = valForOk(StatusLoc&: locForStatus(StatusOrLoc&: LhsStatusOrLoc), Env);
662 auto &RhsOkVal = valForOk(StatusLoc&: locForStatus(StatusOrLoc&: RhsStatusOrLoc), Env);
663 auto &res = Env.makeAtomicBoolValue();
664
665 // res => (lhs == rhs)
666 Env.assume(A.makeImplies(
667 LHS: res.formula(), RHS: A.makeEquals(LHS: LhsOkVal.formula(), RHS: RhsOkVal.formula())));
668 return &res;
669}
670
671static BoolValue *evaluateEquality(const Expr *LhsExpr, const Expr *RhsExpr,
672 Environment &Env) {
673 // Check the type of both sides in case an operator== is added that admits
674 // different types.
675 if (isStatusOrType(Type: LhsExpr->getType()) &&
676 isStatusOrType(Type: RhsExpr->getType())) {
677 auto *LhsStatusOrLoc = Env.get<RecordStorageLocation>(E: *LhsExpr);
678 if (LhsStatusOrLoc == nullptr)
679 return nullptr;
680 auto *RhsStatusOrLoc = Env.get<RecordStorageLocation>(E: *RhsExpr);
681 if (RhsStatusOrLoc == nullptr)
682 return nullptr;
683
684 return evaluateStatusOrEquality(LhsStatusOrLoc&: *LhsStatusOrLoc, RhsStatusOrLoc&: *RhsStatusOrLoc, Env);
685 }
686 if (isStatusType(Type: LhsExpr->getType()) && isStatusType(Type: RhsExpr->getType())) {
687 auto *LhsStatusLoc = Env.get<RecordStorageLocation>(E: *LhsExpr);
688 if (LhsStatusLoc == nullptr)
689 return nullptr;
690
691 auto *RhsStatusLoc = Env.get<RecordStorageLocation>(E: *RhsExpr);
692 if (RhsStatusLoc == nullptr)
693 return nullptr;
694
695 return evaluateStatusEquality(LhsStatusLoc&: *LhsStatusLoc, RhsStatusLoc&: *RhsStatusLoc, Env);
696 }
697 return nullptr;
698}
699
700static void transferComparisonOperator(const CXXOperatorCallExpr *Expr,
701 LatticeTransferState &State,
702 bool IsNegative) {
703 auto *LhsAndRhsVal =
704 evaluateEquality(LhsExpr: Expr->getArg(Arg: 0), RhsExpr: Expr->getArg(Arg: 1), Env&: State.Env);
705 if (LhsAndRhsVal == nullptr)
706 return;
707
708 if (IsNegative)
709 State.Env.setValue(E: *Expr, Val&: State.Env.makeNot(Val&: *LhsAndRhsVal));
710 else
711 State.Env.setValue(E: *Expr, Val&: *LhsAndRhsVal);
712}
713
714static RecordStorageLocation *getPointeeLocation(const Expr &Expr,
715 Environment &Env) {
716 if (auto *PointerVal = Env.get<PointerValue>(E: Expr))
717 return dyn_cast<RecordStorageLocation>(Val: &PointerVal->getPointeeLoc());
718 return nullptr;
719}
720
721static BoolValue *evaluatePointerEquality(const Expr *LhsExpr,
722 const Expr *RhsExpr,
723 Environment &Env) {
724 assert(LhsExpr->getType()->isPointerType());
725 assert(RhsExpr->getType()->isPointerType());
726 RecordStorageLocation *LhsStatusLoc = nullptr;
727 RecordStorageLocation *RhsStatusLoc = nullptr;
728 if (isStatusOrType(Type: LhsExpr->getType()->getPointeeType()) &&
729 isStatusOrType(Type: RhsExpr->getType()->getPointeeType())) {
730 auto *LhsStatusOrLoc = getPointeeLocation(Expr: *LhsExpr, Env);
731 auto *RhsStatusOrLoc = getPointeeLocation(Expr: *RhsExpr, Env);
732 if (LhsStatusOrLoc == nullptr || RhsStatusOrLoc == nullptr)
733 return nullptr;
734 LhsStatusLoc = &locForStatus(StatusOrLoc&: *LhsStatusOrLoc);
735 RhsStatusLoc = &locForStatus(StatusOrLoc&: *RhsStatusOrLoc);
736 } else if (isStatusType(Type: LhsExpr->getType()->getPointeeType()) &&
737 isStatusType(Type: RhsExpr->getType()->getPointeeType())) {
738 LhsStatusLoc = getPointeeLocation(Expr: *LhsExpr, Env);
739 RhsStatusLoc = getPointeeLocation(Expr: *RhsExpr, Env);
740 }
741 if (LhsStatusLoc == nullptr || RhsStatusLoc == nullptr)
742 return nullptr;
743 auto &LhsOkVal = valForOk(StatusLoc&: *LhsStatusLoc, Env);
744 auto &RhsOkVal = valForOk(StatusLoc&: *RhsStatusLoc, Env);
745 auto &Res = Env.makeAtomicBoolValue();
746 auto &A = Env.arena();
747 Env.assume(A.makeImplies(
748 LHS: Res.formula(), RHS: A.makeEquals(LHS: LhsOkVal.formula(), RHS: RhsOkVal.formula())));
749 return &Res;
750}
751
752static void transferPointerComparisonOperator(const BinaryOperator *Expr,
753 LatticeTransferState &State,
754 bool IsNegative) {
755 auto *LhsAndRhsVal =
756 evaluatePointerEquality(LhsExpr: Expr->getLHS(), RhsExpr: Expr->getRHS(), Env&: State.Env);
757 if (LhsAndRhsVal == nullptr)
758 return;
759
760 if (IsNegative)
761 State.Env.setValue(E: *Expr, Val&: State.Env.makeNot(Val&: *LhsAndRhsVal));
762 else
763 State.Env.setValue(E: *Expr, Val&: *LhsAndRhsVal);
764}
765
766static void transferOkStatusCall(const CallExpr *Expr,
767 const MatchFinder::MatchResult &,
768 LatticeTransferState &State) {
769 auto &OkVal =
770 initializeStatus(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr), Env&: State.Env);
771 State.Env.assume(OkVal.formula());
772}
773
774static void transferNotOkStatusCall(const CallExpr *Expr,
775 const MatchFinder::MatchResult &,
776 LatticeTransferState &State) {
777 auto &OkVal =
778 initializeStatus(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr), Env&: State.Env);
779 auto &A = State.Env.arena();
780 State.Env.assume(A.makeNot(Val: OkVal.formula()));
781}
782
783static void transferEmplaceCall(const CXXMemberCallExpr *Expr,
784 const MatchFinder::MatchResult &,
785 LatticeTransferState &State) {
786 RecordStorageLocation *StatusOrLoc =
787 getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
788 if (StatusOrLoc == nullptr)
789 return;
790
791 auto &OkVal = valForOk(StatusLoc&: locForStatus(StatusOrLoc&: *StatusOrLoc), Env&: State.Env);
792 State.Env.assume(OkVal.formula());
793}
794
795static void transferValueAssignmentCall(const CXXOperatorCallExpr *Expr,
796 const MatchFinder::MatchResult &,
797 LatticeTransferState &State) {
798 assert(Expr->getNumArgs() > 1);
799
800 auto *StatusOrLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
801 if (StatusOrLoc == nullptr)
802 return;
803
804 auto &OkVal = initializeStatusOr(StatusOrLoc&: *StatusOrLoc, Env&: State.Env);
805 State.Env.assume(OkVal.formula());
806}
807
808static void transferValueConstructor(const CXXConstructExpr *Expr,
809 const MatchFinder::MatchResult &,
810 LatticeTransferState &State) {
811 auto &OkVal =
812 initializeStatusOr(StatusOrLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr), Env&: State.Env);
813 State.Env.assume(OkVal.formula());
814}
815
816static void transferStatusOrConstructor(const CXXConstructExpr *Expr,
817 const MatchFinder::MatchResult &,
818 LatticeTransferState &State) {
819 RecordStorageLocation &StatusOrLoc = State.Env.getResultObjectLocation(RecordPRValue: *Expr);
820 RecordStorageLocation &StatusLoc = locForStatus(StatusOrLoc);
821
822 if (State.Env.getValue(Loc: locForOk(StatusLoc)) == nullptr)
823 initializeStatusOr(StatusOrLoc, Env&: State.Env);
824}
825
826static void transferStatusConstructor(const CXXConstructExpr *Expr,
827 const MatchFinder::MatchResult &,
828 LatticeTransferState &State) {
829 RecordStorageLocation &StatusLoc = State.Env.getResultObjectLocation(RecordPRValue: *Expr);
830
831 if (State.Env.getValue(Loc: locForOk(StatusLoc)) == nullptr)
832 initializeStatus(StatusLoc, Env&: State.Env);
833}
834static void
835transferLoggingGetReferenceableValueCall(const CallExpr *Expr,
836 const MatchFinder::MatchResult &,
837 LatticeTransferState &State) {
838 assert(Expr->getNumArgs() == 1);
839 if (Expr->getArg(Arg: 0)->isPRValue())
840 return;
841 auto *ArgLoc = State.Env.getStorageLocation(E: *Expr->getArg(Arg: 0));
842 if (ArgLoc == nullptr)
843 return;
844
845 State.Env.setStorageLocation(E: *Expr, Loc&: *ArgLoc);
846}
847
848static void transferLoggingCheckEqImpl(const CallExpr *Expr,
849 const MatchFinder::MatchResult &,
850 LatticeTransferState &State) {
851 assert(Expr->getNumArgs() > 2);
852
853 auto *EqVal = evaluateEquality(LhsExpr: Expr->getArg(Arg: 0), RhsExpr: Expr->getArg(Arg: 1), Env&: State.Env);
854 if (EqVal == nullptr)
855 return;
856
857 // Consider modelling this more accurately instead of assigning BoolValue
858 // as the value of an expression of pointer type.
859 // For now, this is being handled in transferPointerToBoolean.
860 State.Env.setValue(E: *Expr, Val&: State.Env.makeNot(Val&: *EqVal));
861}
862
863static void transferAsStatusCallWithStatus(const CallExpr *Expr,
864 const MatchFinder::MatchResult &,
865 LatticeTransferState &State) {
866 assert(Expr->getNumArgs() == 1);
867
868 auto *ArgLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
869 if (ArgLoc == nullptr)
870 return;
871
872 if (State.Env.getValue(Loc: locForOk(StatusLoc&: *ArgLoc)) == nullptr)
873 initializeStatus(StatusLoc&: *ArgLoc, Env&: State.Env);
874
875 auto &ExprVal = State.Env.create<PointerValue>(args&: *ArgLoc);
876 State.Env.setValue(E: *Expr, Val&: ExprVal);
877}
878
879static void transferAsStatusCallWithStatusOr(const CallExpr *Expr,
880 const MatchFinder::MatchResult &,
881 LatticeTransferState &State) {
882 assert(Expr->getNumArgs() == 1);
883
884 auto *ArgLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
885 if (ArgLoc == nullptr)
886 return;
887
888 RecordStorageLocation &StatusLoc = locForStatus(StatusOrLoc&: *ArgLoc);
889
890 if (State.Env.getValue(Loc: locForOk(StatusLoc)) == nullptr)
891 initializeStatusOr(StatusOrLoc&: *ArgLoc, Env&: State.Env);
892
893 auto &ExprVal = State.Env.create<PointerValue>(args&: StatusLoc);
894 State.Env.setValue(E: *Expr, Val&: ExprVal);
895}
896
897static void transferPointerToBoolean(const ImplicitCastExpr *Expr,
898 const MatchFinder::MatchResult &,
899 LatticeTransferState &State) {
900 if (auto *SubExprVal =
901 dyn_cast_or_null<BoolValue>(Val: State.Env.getValue(E: *Expr->getSubExpr())))
902 State.Env.setValue(E: *Expr, Val&: *SubExprVal);
903}
904
905static void transferStatusOrReturningCall(const CallExpr *Expr,
906 LatticeTransferState &State) {
907 RecordStorageLocation *StatusOrLoc =
908 Expr->isPRValue() ? &State.Env.getResultObjectLocation(RecordPRValue: *Expr)
909 : State.Env.get<RecordStorageLocation>(E: *Expr);
910 if (StatusOrLoc != nullptr &&
911 State.Env.getValue(Loc: locForOk(StatusLoc&: locForStatus(StatusOrLoc&: *StatusOrLoc))) == nullptr)
912 initializeStatusOr(StatusOrLoc&: *StatusOrLoc, Env&: State.Env);
913}
914
915static bool doHandleConstAccessorMemberCall(
916 const CallExpr *Expr, RecordStorageLocation *RecordLoc,
917 const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
918 if (RecordLoc == nullptr)
919 return false;
920 const FunctionDecl *DirectCallee = Expr->getDirectCallee();
921 if (DirectCallee == nullptr)
922 return false;
923 StorageLocation &Loc =
924 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
925 RecordLoc: *RecordLoc, Callee: DirectCallee, Env&: State.Env, Initialize: [&](StorageLocation &Loc) {
926 if (isStatusOrType(Type: Expr->getType()))
927 initializeStatusOr(StatusOrLoc&: cast<RecordStorageLocation>(Val&: Loc), Env&: State.Env);
928 });
929 if (Expr->isPRValue()) {
930 auto &ResultLoc = State.Env.getResultObjectLocation(RecordPRValue: *Expr);
931 copyRecord(Src&: cast<RecordStorageLocation>(Val&: Loc), Dst&: ResultLoc, Env&: State.Env);
932 } else {
933 State.Env.setStorageLocation(E: *Expr, Loc);
934 }
935 return true;
936}
937
938static void handleConstAccessorMemberCall(
939 const CallExpr *Expr, RecordStorageLocation *RecordLoc,
940 const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
941 if (!doHandleConstAccessorMemberCall(Expr, RecordLoc, Result, State) &&
942 isStatusOrType(Type: Expr->getType()))
943 transferStatusOrReturningCall(Expr, State);
944}
945static void handleConstPointerAccessorMemberCall(
946 const CallExpr *Expr, RecordStorageLocation *RecordLoc,
947 const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
948 if (RecordLoc == nullptr)
949 return;
950 auto *Val = State.Lattice.getOrCreateConstMethodReturnValue(RecordLoc: *RecordLoc, CE: Expr,
951 Env&: State.Env);
952 State.Env.setValue(E: *Expr, Val&: *Val);
953}
954
955static void
956transferConstAccessorMemberCall(const CXXMemberCallExpr *Expr,
957 const MatchFinder::MatchResult &Result,
958 LatticeTransferState &State) {
959 auto Type = Expr->getType();
960 if (!Type->isRecordType() && !Type->isReferenceType())
961 return;
962 handleConstAccessorMemberCall(
963 Expr, RecordLoc: getImplicitObjectLocation(MCE: *Expr, Env: State.Env), Result, State);
964}
965
966static void
967transferConstAccessorMemberOperatorCall(const CXXOperatorCallExpr *Expr,
968 const MatchFinder::MatchResult &Result,
969 LatticeTransferState &State) {
970 auto Type = Expr->getArg(Arg: 0)->getType();
971 if (!Type->isRecordType() && !Type->isReferenceType())
972 return;
973 auto *RecordLoc = cast_or_null<RecordStorageLocation>(
974 Val: State.Env.getStorageLocation(E: *Expr->getArg(Arg: 0)));
975 handleConstAccessorMemberCall(Expr, RecordLoc, Result, State);
976}
977
978static void
979transferConstPointerAccessorMemberCall(const CXXMemberCallExpr *Expr,
980 const MatchFinder::MatchResult &Result,
981 LatticeTransferState &State) {
982 handleConstPointerAccessorMemberCall(
983 Expr, RecordLoc: getImplicitObjectLocation(MCE: *Expr, Env: State.Env), Result, State);
984}
985
986static void transferConstPointerAccessorMemberOperatorCall(
987 const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &Result,
988 LatticeTransferState &State) {
989 auto *RecordLoc = cast_or_null<RecordStorageLocation>(
990 Val: State.Env.getStorageLocation(E: *Expr->getArg(Arg: 0)));
991 handleConstPointerAccessorMemberCall(Expr, RecordLoc, Result, State);
992}
993
994static void handleNonConstMemberCall(const CallExpr *Expr,
995 RecordStorageLocation *RecordLoc,
996 const MatchFinder::MatchResult &Result,
997 LatticeTransferState &State) {
998 if (RecordLoc) {
999 State.Lattice.clearConstMethodReturnValues(RecordLoc: *RecordLoc);
1000 State.Lattice.clearConstMethodReturnStorageLocations(RecordLoc: *RecordLoc);
1001 }
1002 if (isStatusOrType(Type: Expr->getType()))
1003 transferStatusOrReturningCall(Expr, State);
1004}
1005
1006static void transferNonConstMemberCall(const CXXMemberCallExpr *Expr,
1007 const MatchFinder::MatchResult &Result,
1008 LatticeTransferState &State) {
1009 handleNonConstMemberCall(Expr, RecordLoc: getImplicitObjectLocation(MCE: *Expr, Env: State.Env),
1010 Result, State);
1011}
1012
1013static void
1014transferNonConstMemberOperatorCall(const CXXOperatorCallExpr *Expr,
1015 const MatchFinder::MatchResult &Result,
1016 LatticeTransferState &State) {
1017 auto *RecordLoc = cast_or_null<RecordStorageLocation>(
1018 Val: State.Env.getStorageLocation(E: *Expr->getArg(Arg: 0)));
1019 handleNonConstMemberCall(Expr, RecordLoc, Result, State);
1020}
1021
1022static void transferMakePredicateFormatterFromIsOkMatcherCall(
1023 const CallExpr *Expr, const MatchFinder::MatchResult &,
1024 LatticeTransferState &State) {
1025 State.Env.setValue(
1026 Loc: locForOkPredicate(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr)),
1027 Val&: State.Env.getBoolLiteralValue(Value: true));
1028}
1029
1030static void transferStatusIsOkMatcherCall(const CallExpr *Expr,
1031 const MatchFinder::MatchResult &,
1032 LatticeTransferState &State) {
1033 BoolValue &OkMatcherVal = State.Env.getBoolLiteralValue(Value: true);
1034 State.Env.setValue(Loc: locForOkMatcher(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr)),
1035 Val&: OkMatcherVal);
1036}
1037
1038static void transferMakePredicateFormatterFromStatusIsMatcherCall(
1039 const CallExpr *Expr, const MatchFinder::MatchResult &,
1040 LatticeTransferState &State) {
1041 assert(Expr->isPRValue());
1042 auto &Loc = State.Env.getResultObjectLocation(RecordPRValue: *Expr->getArg(Arg: 0));
1043 auto &OkMatcherLoc = locForOkMatcher(StatusLoc&: Loc);
1044 BoolValue *OkMatcherVal = State.Env.get<BoolValue>(Loc: OkMatcherLoc);
1045 if (OkMatcherVal == nullptr)
1046 return;
1047 State.Env.setValue(
1048 Loc: locForOkPredicate(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr)),
1049 Val&: *OkMatcherVal);
1050}
1051
1052static void
1053transferPredicateFormatterMatcherCall(const CXXOperatorCallExpr *Expr,
1054 LatticeTransferState &State,
1055 bool IsStatusOr) {
1056 auto *Loc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
1057 if (Loc == nullptr)
1058 return;
1059
1060 auto *ObjectLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 2));
1061 if (ObjectLoc == nullptr)
1062 return;
1063
1064 auto &OkPredicateLoc = locForOkPredicate(StatusLoc&: *Loc);
1065 BoolValue *OkPredicateVal = State.Env.get<BoolValue>(Loc: OkPredicateLoc);
1066 if (OkPredicateVal == nullptr)
1067 return;
1068
1069 if (IsStatusOr)
1070 ObjectLoc = &locForStatus(StatusOrLoc&: *ObjectLoc);
1071 auto &StatusOk = valForOk(StatusLoc&: *ObjectLoc, Env&: State.Env);
1072
1073 auto &A = State.Env.arena();
1074 auto &Res = State.Env.makeAtomicBoolValue();
1075 State.Env.assume(
1076 A.makeImplies(LHS: OkPredicateVal->formula(),
1077 RHS: A.makeEquals(LHS: StatusOk.formula(), RHS: Res.formula())));
1078 State.Env.setValue(Loc: locForOk(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr)), Val&: Res);
1079}
1080
1081static void
1082transferAssertionResultConstructFromBoolCall(const CXXConstructExpr *Expr,
1083 const MatchFinder::MatchResult &,
1084 LatticeTransferState &State) {
1085 assert(Expr->getNumArgs() > 0);
1086
1087 auto *StatusAdaptorLoc = State.Env.get<StorageLocation>(E: *Expr->getArg(Arg: 0));
1088 if (StatusAdaptorLoc == nullptr)
1089 return;
1090 BoolValue *OkVal = State.Env.get<BoolValue>(Loc: *StatusAdaptorLoc);
1091 if (OkVal == nullptr)
1092 return;
1093 State.Env.setValue(Loc: locForOk(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr)),
1094 Val&: *OkVal);
1095}
1096
1097static void
1098transferAssertionResultOperatorBoolCall(const CXXMemberCallExpr *Expr,
1099 const MatchFinder::MatchResult &,
1100 LatticeTransferState &State) {
1101 auto *RecordLoc = getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
1102 if (RecordLoc == nullptr)
1103 return;
1104 BoolValue *OkVal = State.Env.get<BoolValue>(Loc: locForOk(StatusLoc&: *RecordLoc));
1105 if (OkVal == nullptr)
1106 return;
1107 auto &A = State.Env.arena();
1108 auto &Res = State.Env.makeAtomicBoolValue();
1109 State.Env.assume(A.makeEquals(LHS: OkVal->formula(), RHS: Res.formula()));
1110 State.Env.setValue(E: *Expr, Val&: Res);
1111}
1112
1113static void transferDerefCall(const CXXOperatorCallExpr *Expr,
1114 const MatchFinder::MatchResult &,
1115 LatticeTransferState &State) {
1116 auto *StatusOrLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
1117
1118 if (StatusOrLoc && State.Env.getStorageLocation(E: *Expr) == nullptr)
1119 State.Env.setStorageLocation(E: *Expr,
1120 Loc&: StatusOrLoc->getSyntheticField(Name: "value"));
1121}
1122
1123static void transferArrowCall(const CXXOperatorCallExpr *Expr,
1124 const MatchFinder::MatchResult &,
1125 LatticeTransferState &State) {
1126 auto *StatusOrLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
1127 if (!StatusOrLoc)
1128 return;
1129 State.Env.setValue(E: *Expr, Val&: State.Env.create<PointerValue>(
1130 args&: StatusOrLoc->getSyntheticField(Name: "value")));
1131}
1132
1133static void transferValueCall(const CXXMemberCallExpr *Expr,
1134 const MatchFinder::MatchResult &,
1135 LatticeTransferState &State) {
1136 auto *StatusOrLoc = getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
1137
1138 if (StatusOrLoc && State.Env.getStorageLocation(E: *Expr) == nullptr)
1139 State.Env.setStorageLocation(E: *Expr,
1140 Loc&: StatusOrLoc->getSyntheticField(Name: "value"));
1141}
1142
1143static void transferStatusOrPtrReturningCall(const CallExpr *Expr,
1144 const MatchFinder::MatchResult &,
1145 LatticeTransferState &State) {
1146 PointerValue *PointerVal =
1147 dyn_cast_or_null<PointerValue>(Val: State.Env.getValue(E: *Expr));
1148 if (!PointerVal) {
1149 PointerVal = cast<PointerValue>(Val: State.Env.createValue(Type: Expr->getType()));
1150 State.Env.setValue(E: *Expr, Val&: *PointerVal);
1151 }
1152
1153 auto *RecordLoc =
1154 dyn_cast_or_null<RecordStorageLocation>(Val: &PointerVal->getPointeeLoc());
1155 if (RecordLoc != nullptr &&
1156 State.Env.getValue(Loc: locForOk(StatusLoc&: locForStatus(StatusOrLoc&: *RecordLoc))) == nullptr)
1157 initializeStatusOr(StatusOrLoc&: *RecordLoc, Env&: State.Env);
1158}
1159
1160static void transferStatusPtrReturningCall(const CallExpr *Expr,
1161 const MatchFinder::MatchResult &,
1162 LatticeTransferState &State) {
1163 PointerValue *PointerVal =
1164 dyn_cast_or_null<PointerValue>(Val: State.Env.getValue(E: *Expr));
1165 if (!PointerVal) {
1166 PointerVal = cast<PointerValue>(Val: State.Env.createValue(Type: Expr->getType()));
1167 State.Env.setValue(E: *Expr, Val&: *PointerVal);
1168 }
1169
1170 auto *RecordLoc =
1171 dyn_cast_or_null<RecordStorageLocation>(Val: &PointerVal->getPointeeLoc());
1172 if (RecordLoc != nullptr &&
1173 State.Env.getValue(Loc: locForOk(StatusLoc&: *RecordLoc)) == nullptr)
1174 initializeStatus(StatusLoc&: *RecordLoc, Env&: State.Env);
1175}
1176
1177static RecordStorageLocation *
1178getSmartPtrLikeStorageLocation(const Expr &E, const Environment &Env) {
1179 if (!E.isPRValue())
1180 return dyn_cast_or_null<RecordStorageLocation>(Val: Env.getStorageLocation(E));
1181 if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Val: Env.getValue(E)))
1182 return dyn_cast_or_null<RecordStorageLocation>(
1183 Val: &PointerVal->getPointeeLoc());
1184 return nullptr;
1185}
1186static void transferMacroAdaptorCall(const clang::CallExpr *Expr,
1187 const MatchFinder::MatchResult &,
1188 LatticeTransferState &State) {
1189 assert(Expr->getNumArgs() > 0);
1190
1191 auto *StatusAdaptorLoc =
1192 State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
1193 if (StatusAdaptorLoc == nullptr)
1194 return;
1195
1196 copyRecord(Src&: *StatusAdaptorLoc,
1197 Dst&: locForStatus(StatusOrLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr)), Env&: State.Env);
1198}
1199
1200static void transferReturnIfErrorAdaptorOperatorBoolCall(
1201 const clang::CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &,
1202 LatticeTransferState &State) {
1203 RecordStorageLocation *StatusAdaptorLoc =
1204 getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
1205 if (StatusAdaptorLoc == nullptr)
1206 return;
1207
1208 auto &OkVal = valForOk(StatusLoc&: locForStatus(StatusOrLoc&: *StatusAdaptorLoc), Env&: State.Env);
1209 State.Env.setValue(E: *Expr, Val&: OkVal);
1210}
1211
1212CFGMatchSwitch<LatticeTransferState>
1213buildTransferMatchSwitch(ASTContext &Ctx,
1214 CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
1215 using namespace ::clang::ast_matchers;
1216 return std::move(Builder)
1217 .CaseOfCFGStmt<CallExpr>(
1218 M: isMakePredicateFormatterFromIsOkMatcherCall(),
1219 A: transferMakePredicateFormatterFromIsOkMatcherCall)
1220 .CaseOfCFGStmt<CallExpr>(M: isStatusIsOkMatcherCall(),
1221 A: transferStatusIsOkMatcherCall)
1222 .CaseOfCFGStmt<CallExpr>(
1223 M: isMakePredicateFormatterFromStatusIsMatcherCall(),
1224 A: transferMakePredicateFormatterFromStatusIsMatcherCall)
1225 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1226 M: isPredicateFormatterFromStatusOrMatcherCall(),
1227 A: [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
1228 LatticeTransferState &State) {
1229 transferPredicateFormatterMatcherCall(Expr, State,
1230 /*IsStatusOr=*/true);
1231 })
1232 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1233 M: isPredicateFormatterFromStatusMatcherCall(),
1234 A: [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
1235 LatticeTransferState &State) {
1236 transferPredicateFormatterMatcherCall(Expr, State,
1237 /*IsStatusOr=*/false);
1238 })
1239 .CaseOfCFGStmt<CXXConstructExpr>(
1240 M: isAssertionResultConstructFromBoolCall(),
1241 A: transferAssertionResultConstructFromBoolCall)
1242 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isAssertionResultOperatorBoolCall(),
1243 A: transferAssertionResultOperatorBoolCall)
1244 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusOrMemberCallWithName(member_name: "ok"),
1245 A: transferStatusOrOkCall)
1246 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusOrMemberCallWithName(member_name: "status"),
1247 A: transferStatusCall)
1248 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusMemberCallWithName(member_name: "ok"),
1249 A: transferStatusOkCall)
1250 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusMemberCallWithName(member_name: "Update"),
1251 A: transferStatusUpdateCall)
1252 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1253 M: isComparisonOperatorCall(operator_name: "=="),
1254 A: [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
1255 LatticeTransferState &State) {
1256 transferComparisonOperator(Expr, State,
1257 /*IsNegative=*/false);
1258 })
1259 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1260 M: isComparisonOperatorCall(operator_name: "!="),
1261 A: [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
1262 LatticeTransferState &State) {
1263 transferComparisonOperator(Expr, State,
1264 /*IsNegative=*/true);
1265 })
1266 .CaseOfCFGStmt<BinaryOperator>(
1267 M: isPointerComparisonOperatorCall(operator_name: "=="),
1268 A: [](const BinaryOperator *Expr, const MatchFinder::MatchResult &,
1269 LatticeTransferState &State) {
1270 transferPointerComparisonOperator(Expr, State,
1271 /*IsNegative=*/false);
1272 })
1273 .CaseOfCFGStmt<BinaryOperator>(
1274 M: isPointerComparisonOperatorCall(operator_name: "!="),
1275 A: [](const BinaryOperator *Expr, const MatchFinder::MatchResult &,
1276 LatticeTransferState &State) {
1277 transferPointerComparisonOperator(Expr, State,
1278 /*IsNegative=*/true);
1279 })
1280 .CaseOfCFGStmt<CallExpr>(M: isOkStatusCall(), A: transferOkStatusCall)
1281 .CaseOfCFGStmt<CallExpr>(M: isNotOkStatusCall(), A: transferNotOkStatusCall)
1282 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusOrMemberCallWithName(member_name: "emplace"),
1283 A: transferEmplaceCall)
1284 .CaseOfCFGStmt<CXXOperatorCallExpr>(M: isStatusOrValueAssignmentCall(),
1285 A: transferValueAssignmentCall)
1286 .CaseOfCFGStmt<CXXConstructExpr>(M: isStatusOrValueConstructor(),
1287 A: transferValueConstructor)
1288 .CaseOfCFGStmt<CXXOperatorCallExpr>(M: isStatusOrOperatorCallWithName(operator_name: "->"),
1289 A: transferArrowCall)
1290 .CaseOfCFGStmt<CXXOperatorCallExpr>(M: isStatusOrOperatorCallWithName(operator_name: "*"),
1291 A: transferDerefCall)
1292 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusOrMemberCallWithName(member_name: "value"),
1293 A: transferValueCall)
1294 .CaseOfCFGStmt<CallExpr>(M: isAsStatusCallWithStatus(),
1295 A: transferAsStatusCallWithStatus)
1296 .CaseOfCFGStmt<CallExpr>(M: isAsStatusCallWithStatusOr(),
1297 A: transferAsStatusCallWithStatusOr)
1298 .CaseOfCFGStmt<CallExpr>(M: isLoggingGetReferenceableValueCall(),
1299 A: transferLoggingGetReferenceableValueCall)
1300 .CaseOfCFGStmt<CallExpr>(M: isLoggingCheckEqImpl(),
1301 A: transferLoggingCheckEqImpl)
1302 // This needs to go before the const accessor call matcher, because these
1303 // look like them, but we model `operator`* and `get` to return the same
1304 // object. Also, we model them for non-const cases.
1305 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1306 M: isPointerLikeOperatorStar(),
1307 A: [](const CXXOperatorCallExpr *E,
1308 const MatchFinder::MatchResult &Result,
1309 LatticeTransferState &State) {
1310 transferSmartPointerLikeCachedDeref(
1311 DerefExpr: E, SmartPointerLoc: getSmartPtrLikeStorageLocation(E: *E->getArg(Arg: 0), Env: State.Env),
1312 State, InitializeLoc: [](StorageLocation &Loc) {});
1313 })
1314 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1315 M: isPointerLikeOperatorArrow(),
1316 A: [](const CXXOperatorCallExpr *E,
1317 const MatchFinder::MatchResult &Result,
1318 LatticeTransferState &State) {
1319 transferSmartPointerLikeCachedGet(
1320 GetExpr: E, SmartPointerLoc: getSmartPtrLikeStorageLocation(E: *E->getArg(Arg: 0), Env: State.Env),
1321 State, InitializeLoc: [](StorageLocation &Loc) {});
1322 })
1323 .CaseOfCFGStmt<CXXMemberCallExpr>(
1324 M: isSmartPointerLikeValueMethodCall(),
1325 A: [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
1326 LatticeTransferState &State) {
1327 transferSmartPointerLikeCachedDeref(
1328 DerefExpr: E, SmartPointerLoc: getImplicitObjectLocation(MCE: *E, Env: State.Env), State,
1329 InitializeLoc: [](StorageLocation &Loc) {});
1330 })
1331 .CaseOfCFGStmt<CXXMemberCallExpr>(
1332 M: isSmartPointerLikeGetMethodCall(),
1333 A: [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
1334 LatticeTransferState &State) {
1335 transferSmartPointerLikeCachedGet(
1336 GetExpr: E, SmartPointerLoc: getImplicitObjectLocation(MCE: *E, Env: State.Env), State,
1337 InitializeLoc: [](StorageLocation &Loc) {});
1338 })
1339 // const accessor calls
1340 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isConstAccessorMemberCall(),
1341 A: transferConstAccessorMemberCall)
1342 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1343 M: isConstAccessorMemberOperatorCall(),
1344 A: transferConstAccessorMemberOperatorCall)
1345 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isConstPointerAccessorMemberCall(),
1346 A: transferConstPointerAccessorMemberCall)
1347 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1348 M: isConstPointerAccessorMemberOperatorCall(),
1349 A: transferConstPointerAccessorMemberOperatorCall)
1350 // non-const member calls that may modify the state of an object.
1351 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isNonConstMemberCall(),
1352 A: transferNonConstMemberCall)
1353 .CaseOfCFGStmt<CXXOperatorCallExpr>(M: isNonConstMemberOperatorCall(),
1354 A: transferNonConstMemberOperatorCall)
1355 // N.B. this has to be after transferConstMemberCall, otherwise we would
1356 // always return a fresh RecordStorageLocation for the StatusOr.
1357 .CaseOfCFGStmt<CallExpr>(M: isStatusOrReturningCall(),
1358 A: [](const CallExpr *Expr,
1359 const MatchFinder::MatchResult &,
1360 LatticeTransferState &State) {
1361 transferStatusOrReturningCall(Expr, State);
1362 })
1363 .CaseOfCFGStmt<CallExpr>(M: isStatusOrPtrReturningCall(),
1364 A: transferStatusOrPtrReturningCall)
1365 .CaseOfCFGStmt<CallExpr>(M: isStatusPtrReturningCall(),
1366 A: transferStatusPtrReturningCall)
1367 // N.B. These need to come after all other CXXConstructExpr.
1368 // These are there to make sure that every Status and StatusOr object
1369 // have their ok boolean initialized when constructed. If we were to
1370 // lazily initialize them when we first access them, we can produce
1371 // false positives if that first access is in a control flow statement.
1372 // You can comment out these two constructors and see tests fail.
1373 .CaseOfCFGStmt<CXXConstructExpr>(M: isStatusOrConstructor(),
1374 A: transferStatusOrConstructor)
1375 .CaseOfCFGStmt<CXXConstructExpr>(M: isStatusConstructor(),
1376 A: transferStatusConstructor)
1377 .CaseOfCFGStmt<ImplicitCastExpr>(
1378 M: implicitCastExpr(hasCastKind(Kind: CK_PointerToBoolean)),
1379 A: transferPointerToBoolean)
1380 .CaseOfCFGStmt<clang::CXXMemberCallExpr>(
1381 M: isReturnIfErrorAdaptorOperatorBoolCall(),
1382 A: transferReturnIfErrorAdaptorOperatorBoolCall)
1383 .CaseOfCFGStmt<clang::CallExpr>(M: isMacroAdaptorCall(),
1384 A: transferMacroAdaptorCall)
1385 .Build();
1386}
1387
1388QualType findStatusType(const ASTContext &Ctx) {
1389 for (Type *Ty : Ctx.getTypes())
1390 if (isStatusType(Type: QualType(Ty, 0)))
1391 return QualType(Ty, 0);
1392
1393 return QualType();
1394}
1395
1396UncheckedStatusOrAccessModel::UncheckedStatusOrAccessModel(ASTContext &Ctx,
1397 Environment &Env)
1398 : DataflowAnalysis<UncheckedStatusOrAccessModel,
1399 UncheckedStatusOrAccessModel::Lattice>(Ctx),
1400 TransferMatchSwitch(buildTransferMatchSwitch(Ctx, Builder: {})) {
1401 QualType StatusType = findStatusType(Ctx);
1402 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
1403 [StatusType](QualType Ty) -> llvm::StringMap<QualType> {
1404 CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
1405 if (RD == nullptr)
1406 return {};
1407
1408 if (auto Fields = getSyntheticFields(Ty, StatusType, RD: *RD);
1409 !Fields.empty())
1410 return Fields;
1411 return {};
1412 });
1413}
1414
1415void UncheckedStatusOrAccessModel::transfer(const CFGElement &Elt, Lattice &L,
1416 Environment &Env) {
1417 LatticeTransferState State(L, Env);
1418 TransferMatchSwitch(Elt, getASTContext(), State);
1419}
1420
1421} // namespace clang::dataflow::statusor_model
1422