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
367buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options) {
368 return CFGMatchSwitchBuilder<const Environment,
369 llvm::SmallVector<SourceLocation>>()
370 // StatusOr::value, StatusOr::ValueOrDie
371 .CaseOfCFGStmt<CXXMemberCallExpr>(
372 M: valueCall(),
373 A: [](const CXXMemberCallExpr *E,
374 const ast_matchers::MatchFinder::MatchResult &,
375 const Environment &Env) {
376 if (!isSafeUnwrap(StatusOrLoc: getImplicitObjectLocation(MCE: *E, Env), Env))
377 return llvm::SmallVector<SourceLocation>({E->getExprLoc()});
378 return llvm::SmallVector<SourceLocation>();
379 })
380
381 // StatusOr::operator*, StatusOr::operator->
382 .CaseOfCFGStmt<CXXOperatorCallExpr>(
383 M: valueOperatorCall(),
384 A: [](const CXXOperatorCallExpr *E,
385 const ast_matchers::MatchFinder::MatchResult &,
386 const Environment &Env) {
387 RecordStorageLocation *StatusOrLoc =
388 Env.get<RecordStorageLocation>(E: *E->getArg(Arg: 0));
389 if (!isSafeUnwrap(StatusOrLoc, Env))
390 return llvm::SmallVector<SourceLocation>({E->getOperatorLoc()});
391 return llvm::SmallVector<SourceLocation>();
392 })
393 .Build();
394}
395
396UncheckedStatusOrAccessDiagnoser::UncheckedStatusOrAccessDiagnoser(
397 UncheckedStatusOrAccessModelOptions Options)
398 : DiagnoseMatchSwitch(buildDiagnoseMatchSwitch(Options)) {}
399
400llvm::SmallVector<SourceLocation> UncheckedStatusOrAccessDiagnoser::operator()(
401 const CFGElement &Elt, ASTContext &Ctx,
402 const TransferStateForDiagnostics<UncheckedStatusOrAccessModel::Lattice>
403 &State) {
404 return DiagnoseMatchSwitch(Elt, Ctx, State.Env);
405}
406
407BoolValue &initializeStatus(RecordStorageLocation &StatusLoc,
408 Environment &Env) {
409 auto &OkVal = Env.makeAtomicBoolValue();
410 Env.setValue(Loc: locForOk(StatusLoc), Val&: OkVal);
411 return OkVal;
412}
413
414BoolValue &initializeStatusOr(RecordStorageLocation &StatusOrLoc,
415 Environment &Env) {
416 return initializeStatus(StatusLoc&: locForStatus(StatusOrLoc), Env);
417}
418
419clang::ast_matchers::DeclarationMatcher statusOrClass() {
420 using namespace ::clang::ast_matchers;
421 return classTemplateSpecializationDecl(
422 hasName(Name: "absl::StatusOr"),
423 hasTemplateArgument(N: 0, InnerMatcher: refersToType(InnerMatcher: type().bind(ID: "T"))));
424}
425
426clang::ast_matchers::DeclarationMatcher statusClass() {
427 using namespace ::clang::ast_matchers;
428 return cxxRecordDecl(hasName(Name: "absl::Status"));
429}
430
431clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass() {
432 using namespace ::clang::ast_matchers;
433 return classTemplateSpecializationDecl(
434 hasName(Name: "absl::internal_statusor::OperatorBase"));
435}
436
437clang::ast_matchers::TypeMatcher statusOrType() {
438 using namespace ::clang::ast_matchers;
439 return hasCanonicalType(InnerMatcher: qualType(hasDeclaration(InnerMatcher: statusOrClass())));
440}
441
442clang::ast_matchers::TypeMatcher statusType() {
443 using namespace ::clang::ast_matchers;
444 return hasCanonicalType(InnerMatcher: qualType(hasDeclaration(InnerMatcher: statusClass())));
445}
446
447bool isStatusOrType(QualType Type) {
448 return isTypeNamed(Type, NS: {"absl"}, Name: "StatusOr");
449}
450
451bool isStatusType(QualType Type) {
452 return isTypeNamed(Type, NS: {"absl"}, Name: "Status");
453}
454
455static bool isPredicateFormatterFromMatcherType(QualType Type) {
456 return isTypeNamed(Type, NS: {"testing", "internal"},
457 Name: "PredicateFormatterFromMatcher");
458}
459
460static bool isAssertionResultType(QualType Type) {
461 return isTypeNamed(Type, NS: {"testing"}, Name: "AssertionResult");
462}
463
464static bool isStatusIsMatcherType(QualType Type) {
465 return isTypeNamed(Type, NS: {"testing", "status", "internal_status"},
466 Name: "StatusIsMatcher") ||
467 isTypeNamed(Type, NS: {"testing", "status", "internal_status"},
468 Name: "CanonicalStatusIsMatcher") ||
469 isTypeNamed(Type, NS: {"absl_testing", "status_internal"},
470 Name: "StatusIsMatcher") ||
471 isTypeNamed(Type, NS: {"absl_testing", "status_internal"},
472 Name: "CanonicalStatusIsMatcher");
473}
474
475llvm::StringMap<QualType> getSyntheticFields(QualType Ty, QualType StatusType,
476 const CXXRecordDecl &RD) {
477 if (auto *TRD = getStatusOrBaseClass(Ty))
478 return {{"status", StatusType}, {"value", getStatusOrValueType(TRD)}};
479 if (isStatusType(Type: Ty) || (RD.hasDefinition() &&
480 RD.isDerivedFrom(Base: StatusType->getAsCXXRecordDecl())))
481 return {{"ok", RD.getASTContext().BoolTy}};
482 if (isAssertionResultType(Type: Ty))
483 return {{"ok", RD.getASTContext().BoolTy}};
484 if (isPredicateFormatterFromMatcherType(Type: Ty))
485 return {{"ok_predicate", RD.getASTContext().BoolTy}};
486 if (isStatusIsMatcherType(Type: Ty))
487 return {{"ok_matcher", RD.getASTContext().BoolTy}};
488 return {};
489}
490
491RecordStorageLocation &locForStatus(RecordStorageLocation &StatusOrLoc) {
492 return cast<RecordStorageLocation>(Val&: StatusOrLoc.getSyntheticField(Name: "status"));
493}
494
495StorageLocation &locForOk(RecordStorageLocation &StatusLoc) {
496 return StatusLoc.getSyntheticField(Name: "ok");
497}
498
499BoolValue &valForOk(RecordStorageLocation &StatusLoc, Environment &Env) {
500 if (auto *Val = Env.get<BoolValue>(Loc: locForOk(StatusLoc)))
501 return *Val;
502 return initializeStatus(StatusLoc, Env);
503}
504static StorageLocation &locForOkPredicate(RecordStorageLocation &StatusLoc) {
505 return StatusLoc.getSyntheticField(Name: "ok_predicate");
506}
507
508static StorageLocation &locForOkMatcher(RecordStorageLocation &StatusLoc) {
509 return StatusLoc.getSyntheticField(Name: "ok_matcher");
510}
511
512static void transferStatusOrOkCall(const CXXMemberCallExpr *Expr,
513 const MatchFinder::MatchResult &,
514 LatticeTransferState &State) {
515 RecordStorageLocation *StatusOrLoc =
516 getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
517 if (StatusOrLoc == nullptr)
518 return;
519
520 auto &OkVal = valForOk(StatusLoc&: locForStatus(StatusOrLoc&: *StatusOrLoc), Env&: State.Env);
521 State.Env.setValue(E: *Expr, Val&: OkVal);
522}
523
524static void transferStatusCall(const CXXMemberCallExpr *Expr,
525 const MatchFinder::MatchResult &,
526 LatticeTransferState &State) {
527 RecordStorageLocation *StatusOrLoc =
528 getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
529 if (StatusOrLoc == nullptr)
530 return;
531
532 RecordStorageLocation &StatusLoc = locForStatus(StatusOrLoc&: *StatusOrLoc);
533
534 if (State.Env.getValue(Loc: locForOk(StatusLoc)) == nullptr)
535 initializeStatusOr(StatusOrLoc&: *StatusOrLoc, Env&: State.Env);
536
537 if (Expr->isPRValue())
538 copyRecord(Src&: StatusLoc, Dst&: State.Env.getResultObjectLocation(RecordPRValue: *Expr), Env&: State.Env);
539 else
540 State.Env.setStorageLocation(E: *Expr, Loc&: StatusLoc);
541}
542
543static void transferStatusOkCall(const CXXMemberCallExpr *Expr,
544 const MatchFinder::MatchResult &,
545 LatticeTransferState &State) {
546 RecordStorageLocation *StatusLoc =
547 getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
548 if (StatusLoc == nullptr)
549 return;
550
551 if (Value *Val = State.Env.getValue(Loc: locForOk(StatusLoc&: *StatusLoc)))
552 State.Env.setValue(E: *Expr, Val&: *Val);
553}
554
555static void transferStatusUpdateCall(const CXXMemberCallExpr *Expr,
556 const MatchFinder::MatchResult &,
557 LatticeTransferState &State) {
558 // S.Update(OtherS) sets S to the error code of OtherS if it is OK,
559 // otherwise does nothing.
560 assert(Expr->getNumArgs() == 1);
561 auto *Arg = Expr->getArg(Arg: 0);
562 RecordStorageLocation *ArgRecord =
563 Arg->isPRValue() ? &State.Env.getResultObjectLocation(RecordPRValue: *Arg)
564 : State.Env.get<RecordStorageLocation>(E: *Arg);
565 RecordStorageLocation *ThisLoc = getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
566 if (ThisLoc == nullptr || ArgRecord == nullptr)
567 return;
568
569 auto &ThisOkVal = valForOk(StatusLoc&: *ThisLoc, Env&: State.Env);
570 auto &ArgOkVal = valForOk(StatusLoc&: *ArgRecord, Env&: State.Env);
571 auto &A = State.Env.arena();
572 auto &NewVal = State.Env.makeAtomicBoolValue();
573 State.Env.assume(A.makeImplies(LHS: A.makeNot(Val: ThisOkVal.formula()),
574 RHS: A.makeNot(Val: NewVal.formula())));
575 State.Env.assume(A.makeImplies(LHS: NewVal.formula(), RHS: ArgOkVal.formula()));
576 State.Env.setValue(Loc: locForOk(StatusLoc&: *ThisLoc), Val&: NewVal);
577}
578
579static BoolValue *evaluateStatusEquality(RecordStorageLocation &LhsStatusLoc,
580 RecordStorageLocation &RhsStatusLoc,
581 Environment &Env) {
582 auto &A = Env.arena();
583 // Logically, a Status object is composed of an error code that could take one
584 // of multiple possible values, including the "ok" value. We track whether a
585 // Status object has an "ok" value and represent this as an `ok` bit. Equality
586 // of Status objects compares their error codes. Therefore, merely comparing
587 // the `ok` bits isn't sufficient: when two Status objects are assigned non-ok
588 // error codes the equality of their respective error codes matters. Since we
589 // only track the `ok` bits, we can't make any conclusions about equality when
590 // we know that two Status objects have non-ok values.
591
592 auto &LhsOkVal = valForOk(StatusLoc&: LhsStatusLoc, Env);
593 auto &RhsOkVal = valForOk(StatusLoc&: RhsStatusLoc, Env);
594
595 auto &Res = Env.makeAtomicBoolValue();
596
597 // lhs && rhs => res (a.k.a. !res => !lhs || !rhs)
598 Env.assume(A.makeImplies(LHS: A.makeAnd(LHS: LhsOkVal.formula(), RHS: RhsOkVal.formula()),
599 RHS: Res.formula()));
600 // res => (lhs == rhs)
601 Env.assume(A.makeImplies(
602 LHS: Res.formula(), RHS: A.makeEquals(LHS: LhsOkVal.formula(), RHS: RhsOkVal.formula())));
603
604 return &Res;
605}
606
607static BoolValue *
608evaluateStatusOrEquality(RecordStorageLocation &LhsStatusOrLoc,
609 RecordStorageLocation &RhsStatusOrLoc,
610 Environment &Env) {
611 auto &A = Env.arena();
612 // Logically, a StatusOr<T> object is composed of two values - a Status and a
613 // value of type T. Equality of StatusOr objects compares both values.
614 // Therefore, merely comparing the `ok` bits of the Status values isn't
615 // sufficient. When two StatusOr objects are engaged, the equality of their
616 // respective values of type T matters. Similarly, when two StatusOr objects
617 // have Status values that have non-ok error codes, the equality of the error
618 // codes matters. Since we only track the `ok` bits of the Status values, we
619 // can't make any conclusions about equality when we know that two StatusOr
620 // objects are engaged or when their Status values contain non-ok error codes.
621 auto &LhsOkVal = valForOk(StatusLoc&: locForStatus(StatusOrLoc&: LhsStatusOrLoc), Env);
622 auto &RhsOkVal = valForOk(StatusLoc&: locForStatus(StatusOrLoc&: RhsStatusOrLoc), Env);
623 auto &res = Env.makeAtomicBoolValue();
624
625 // res => (lhs == rhs)
626 Env.assume(A.makeImplies(
627 LHS: res.formula(), RHS: A.makeEquals(LHS: LhsOkVal.formula(), RHS: RhsOkVal.formula())));
628 return &res;
629}
630
631static BoolValue *evaluateEquality(const Expr *LhsExpr, const Expr *RhsExpr,
632 Environment &Env) {
633 // Check the type of both sides in case an operator== is added that admits
634 // different types.
635 if (isStatusOrType(Type: LhsExpr->getType()) &&
636 isStatusOrType(Type: RhsExpr->getType())) {
637 auto *LhsStatusOrLoc = Env.get<RecordStorageLocation>(E: *LhsExpr);
638 if (LhsStatusOrLoc == nullptr)
639 return nullptr;
640 auto *RhsStatusOrLoc = Env.get<RecordStorageLocation>(E: *RhsExpr);
641 if (RhsStatusOrLoc == nullptr)
642 return nullptr;
643
644 return evaluateStatusOrEquality(LhsStatusOrLoc&: *LhsStatusOrLoc, RhsStatusOrLoc&: *RhsStatusOrLoc, Env);
645 }
646 if (isStatusType(Type: LhsExpr->getType()) && isStatusType(Type: RhsExpr->getType())) {
647 auto *LhsStatusLoc = Env.get<RecordStorageLocation>(E: *LhsExpr);
648 if (LhsStatusLoc == nullptr)
649 return nullptr;
650
651 auto *RhsStatusLoc = Env.get<RecordStorageLocation>(E: *RhsExpr);
652 if (RhsStatusLoc == nullptr)
653 return nullptr;
654
655 return evaluateStatusEquality(LhsStatusLoc&: *LhsStatusLoc, RhsStatusLoc&: *RhsStatusLoc, Env);
656 }
657 return nullptr;
658}
659
660static void transferComparisonOperator(const CXXOperatorCallExpr *Expr,
661 LatticeTransferState &State,
662 bool IsNegative) {
663 auto *LhsAndRhsVal =
664 evaluateEquality(LhsExpr: Expr->getArg(Arg: 0), RhsExpr: Expr->getArg(Arg: 1), Env&: State.Env);
665 if (LhsAndRhsVal == nullptr)
666 return;
667
668 if (IsNegative)
669 State.Env.setValue(E: *Expr, Val&: State.Env.makeNot(Val&: *LhsAndRhsVal));
670 else
671 State.Env.setValue(E: *Expr, Val&: *LhsAndRhsVal);
672}
673
674static RecordStorageLocation *getPointeeLocation(const Expr &Expr,
675 Environment &Env) {
676 if (auto *PointerVal = Env.get<PointerValue>(E: Expr))
677 return dyn_cast<RecordStorageLocation>(Val: &PointerVal->getPointeeLoc());
678 return nullptr;
679}
680
681static BoolValue *evaluatePointerEquality(const Expr *LhsExpr,
682 const Expr *RhsExpr,
683 Environment &Env) {
684 assert(LhsExpr->getType()->isPointerType());
685 assert(RhsExpr->getType()->isPointerType());
686 RecordStorageLocation *LhsStatusLoc = nullptr;
687 RecordStorageLocation *RhsStatusLoc = nullptr;
688 if (isStatusOrType(Type: LhsExpr->getType()->getPointeeType()) &&
689 isStatusOrType(Type: RhsExpr->getType()->getPointeeType())) {
690 auto *LhsStatusOrLoc = getPointeeLocation(Expr: *LhsExpr, Env);
691 auto *RhsStatusOrLoc = getPointeeLocation(Expr: *RhsExpr, Env);
692 if (LhsStatusOrLoc == nullptr || RhsStatusOrLoc == nullptr)
693 return nullptr;
694 LhsStatusLoc = &locForStatus(StatusOrLoc&: *LhsStatusOrLoc);
695 RhsStatusLoc = &locForStatus(StatusOrLoc&: *RhsStatusOrLoc);
696 } else if (isStatusType(Type: LhsExpr->getType()->getPointeeType()) &&
697 isStatusType(Type: RhsExpr->getType()->getPointeeType())) {
698 LhsStatusLoc = getPointeeLocation(Expr: *LhsExpr, Env);
699 RhsStatusLoc = getPointeeLocation(Expr: *RhsExpr, Env);
700 }
701 if (LhsStatusLoc == nullptr || RhsStatusLoc == nullptr)
702 return nullptr;
703 auto &LhsOkVal = valForOk(StatusLoc&: *LhsStatusLoc, Env);
704 auto &RhsOkVal = valForOk(StatusLoc&: *RhsStatusLoc, Env);
705 auto &Res = Env.makeAtomicBoolValue();
706 auto &A = Env.arena();
707 Env.assume(A.makeImplies(
708 LHS: Res.formula(), RHS: A.makeEquals(LHS: LhsOkVal.formula(), RHS: RhsOkVal.formula())));
709 return &Res;
710}
711
712static void transferPointerComparisonOperator(const BinaryOperator *Expr,
713 LatticeTransferState &State,
714 bool IsNegative) {
715 auto *LhsAndRhsVal =
716 evaluatePointerEquality(LhsExpr: Expr->getLHS(), RhsExpr: Expr->getRHS(), Env&: State.Env);
717 if (LhsAndRhsVal == nullptr)
718 return;
719
720 if (IsNegative)
721 State.Env.setValue(E: *Expr, Val&: State.Env.makeNot(Val&: *LhsAndRhsVal));
722 else
723 State.Env.setValue(E: *Expr, Val&: *LhsAndRhsVal);
724}
725
726static void transferOkStatusCall(const CallExpr *Expr,
727 const MatchFinder::MatchResult &,
728 LatticeTransferState &State) {
729 auto &OkVal =
730 initializeStatus(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr), Env&: State.Env);
731 State.Env.assume(OkVal.formula());
732}
733
734static void transferNotOkStatusCall(const CallExpr *Expr,
735 const MatchFinder::MatchResult &,
736 LatticeTransferState &State) {
737 auto &OkVal =
738 initializeStatus(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr), Env&: State.Env);
739 auto &A = State.Env.arena();
740 State.Env.assume(A.makeNot(Val: OkVal.formula()));
741}
742
743static void transferEmplaceCall(const CXXMemberCallExpr *Expr,
744 const MatchFinder::MatchResult &,
745 LatticeTransferState &State) {
746 RecordStorageLocation *StatusOrLoc =
747 getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
748 if (StatusOrLoc == nullptr)
749 return;
750
751 auto &OkVal = valForOk(StatusLoc&: locForStatus(StatusOrLoc&: *StatusOrLoc), Env&: State.Env);
752 State.Env.assume(OkVal.formula());
753}
754
755static void transferValueAssignmentCall(const CXXOperatorCallExpr *Expr,
756 const MatchFinder::MatchResult &,
757 LatticeTransferState &State) {
758 assert(Expr->getNumArgs() > 1);
759
760 auto *StatusOrLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
761 if (StatusOrLoc == nullptr)
762 return;
763
764 auto &OkVal = initializeStatusOr(StatusOrLoc&: *StatusOrLoc, Env&: State.Env);
765 State.Env.assume(OkVal.formula());
766}
767
768static void transferValueConstructor(const CXXConstructExpr *Expr,
769 const MatchFinder::MatchResult &,
770 LatticeTransferState &State) {
771 auto &OkVal =
772 initializeStatusOr(StatusOrLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr), Env&: State.Env);
773 State.Env.assume(OkVal.formula());
774}
775
776static void transferStatusOrConstructor(const CXXConstructExpr *Expr,
777 const MatchFinder::MatchResult &,
778 LatticeTransferState &State) {
779 RecordStorageLocation &StatusOrLoc = State.Env.getResultObjectLocation(RecordPRValue: *Expr);
780 RecordStorageLocation &StatusLoc = locForStatus(StatusOrLoc);
781
782 if (State.Env.getValue(Loc: locForOk(StatusLoc)) == nullptr)
783 initializeStatusOr(StatusOrLoc, Env&: State.Env);
784}
785
786static void transferStatusConstructor(const CXXConstructExpr *Expr,
787 const MatchFinder::MatchResult &,
788 LatticeTransferState &State) {
789 RecordStorageLocation &StatusLoc = State.Env.getResultObjectLocation(RecordPRValue: *Expr);
790
791 if (State.Env.getValue(Loc: locForOk(StatusLoc)) == nullptr)
792 initializeStatus(StatusLoc, Env&: State.Env);
793}
794static void
795transferLoggingGetReferenceableValueCall(const CallExpr *Expr,
796 const MatchFinder::MatchResult &,
797 LatticeTransferState &State) {
798 assert(Expr->getNumArgs() == 1);
799 if (Expr->getArg(Arg: 0)->isPRValue())
800 return;
801 auto *ArgLoc = State.Env.getStorageLocation(E: *Expr->getArg(Arg: 0));
802 if (ArgLoc == nullptr)
803 return;
804
805 State.Env.setStorageLocation(E: *Expr, Loc&: *ArgLoc);
806}
807
808static void transferLoggingCheckEqImpl(const CallExpr *Expr,
809 const MatchFinder::MatchResult &,
810 LatticeTransferState &State) {
811 assert(Expr->getNumArgs() > 2);
812
813 auto *EqVal = evaluateEquality(LhsExpr: Expr->getArg(Arg: 0), RhsExpr: Expr->getArg(Arg: 1), Env&: State.Env);
814 if (EqVal == nullptr)
815 return;
816
817 // Consider modelling this more accurately instead of assigning BoolValue
818 // as the value of an expression of pointer type.
819 // For now, this is being handled in transferPointerToBoolean.
820 State.Env.setValue(E: *Expr, Val&: State.Env.makeNot(Val&: *EqVal));
821}
822
823static void transferAsStatusCallWithStatus(const CallExpr *Expr,
824 const MatchFinder::MatchResult &,
825 LatticeTransferState &State) {
826 assert(Expr->getNumArgs() == 1);
827
828 auto *ArgLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
829 if (ArgLoc == nullptr)
830 return;
831
832 if (State.Env.getValue(Loc: locForOk(StatusLoc&: *ArgLoc)) == nullptr)
833 initializeStatus(StatusLoc&: *ArgLoc, Env&: State.Env);
834
835 auto &ExprVal = State.Env.create<PointerValue>(args&: *ArgLoc);
836 State.Env.setValue(E: *Expr, Val&: ExprVal);
837}
838
839static void transferAsStatusCallWithStatusOr(const CallExpr *Expr,
840 const MatchFinder::MatchResult &,
841 LatticeTransferState &State) {
842 assert(Expr->getNumArgs() == 1);
843
844 auto *ArgLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
845 if (ArgLoc == nullptr)
846 return;
847
848 RecordStorageLocation &StatusLoc = locForStatus(StatusOrLoc&: *ArgLoc);
849
850 if (State.Env.getValue(Loc: locForOk(StatusLoc)) == nullptr)
851 initializeStatusOr(StatusOrLoc&: *ArgLoc, Env&: State.Env);
852
853 auto &ExprVal = State.Env.create<PointerValue>(args&: StatusLoc);
854 State.Env.setValue(E: *Expr, Val&: ExprVal);
855}
856
857static void transferPointerToBoolean(const ImplicitCastExpr *Expr,
858 const MatchFinder::MatchResult &,
859 LatticeTransferState &State) {
860 if (auto *SubExprVal =
861 dyn_cast_or_null<BoolValue>(Val: State.Env.getValue(E: *Expr->getSubExpr())))
862 State.Env.setValue(E: *Expr, Val&: *SubExprVal);
863}
864
865static void transferStatusOrReturningCall(const CallExpr *Expr,
866 LatticeTransferState &State) {
867 RecordStorageLocation *StatusOrLoc =
868 Expr->isPRValue() ? &State.Env.getResultObjectLocation(RecordPRValue: *Expr)
869 : State.Env.get<RecordStorageLocation>(E: *Expr);
870 if (StatusOrLoc != nullptr &&
871 State.Env.getValue(Loc: locForOk(StatusLoc&: locForStatus(StatusOrLoc&: *StatusOrLoc))) == nullptr)
872 initializeStatusOr(StatusOrLoc&: *StatusOrLoc, Env&: State.Env);
873}
874
875static bool doHandleConstAccessorMemberCall(
876 const CallExpr *Expr, RecordStorageLocation *RecordLoc,
877 const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
878 if (RecordLoc == nullptr)
879 return false;
880 const FunctionDecl *DirectCallee = Expr->getDirectCallee();
881 if (DirectCallee == nullptr)
882 return false;
883 StorageLocation &Loc =
884 State.Lattice.getOrCreateConstMethodReturnStorageLocation(
885 RecordLoc: *RecordLoc, Callee: DirectCallee, Env&: State.Env, Initialize: [&](StorageLocation &Loc) {
886 if (isStatusOrType(Type: Expr->getType()))
887 initializeStatusOr(StatusOrLoc&: cast<RecordStorageLocation>(Val&: Loc), Env&: State.Env);
888 });
889 if (Expr->isPRValue()) {
890 auto &ResultLoc = State.Env.getResultObjectLocation(RecordPRValue: *Expr);
891 copyRecord(Src&: cast<RecordStorageLocation>(Val&: Loc), Dst&: ResultLoc, Env&: State.Env);
892 } else {
893 State.Env.setStorageLocation(E: *Expr, Loc);
894 }
895 return true;
896}
897
898static void handleConstAccessorMemberCall(
899 const CallExpr *Expr, RecordStorageLocation *RecordLoc,
900 const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
901 if (!doHandleConstAccessorMemberCall(Expr, RecordLoc, Result, State) &&
902 isStatusOrType(Type: Expr->getType()))
903 transferStatusOrReturningCall(Expr, State);
904}
905static void handleConstPointerAccessorMemberCall(
906 const CallExpr *Expr, RecordStorageLocation *RecordLoc,
907 const MatchFinder::MatchResult &Result, LatticeTransferState &State) {
908 if (RecordLoc == nullptr)
909 return;
910 auto *Val = State.Lattice.getOrCreateConstMethodReturnValue(RecordLoc: *RecordLoc, CE: Expr,
911 Env&: State.Env);
912 State.Env.setValue(E: *Expr, Val&: *Val);
913}
914
915static void
916transferConstAccessorMemberCall(const CXXMemberCallExpr *Expr,
917 const MatchFinder::MatchResult &Result,
918 LatticeTransferState &State) {
919 auto Type = Expr->getType();
920 if (!Type->isRecordType() && !Type->isReferenceType())
921 return;
922 handleConstAccessorMemberCall(
923 Expr, RecordLoc: getImplicitObjectLocation(MCE: *Expr, Env: State.Env), Result, State);
924}
925
926static void
927transferConstAccessorMemberOperatorCall(const CXXOperatorCallExpr *Expr,
928 const MatchFinder::MatchResult &Result,
929 LatticeTransferState &State) {
930 auto Type = Expr->getArg(Arg: 0)->getType();
931 if (!Type->isRecordType() && !Type->isReferenceType())
932 return;
933 auto *RecordLoc = cast_or_null<RecordStorageLocation>(
934 Val: State.Env.getStorageLocation(E: *Expr->getArg(Arg: 0)));
935 handleConstAccessorMemberCall(Expr, RecordLoc, Result, State);
936}
937
938static void
939transferConstPointerAccessorMemberCall(const CXXMemberCallExpr *Expr,
940 const MatchFinder::MatchResult &Result,
941 LatticeTransferState &State) {
942 handleConstPointerAccessorMemberCall(
943 Expr, RecordLoc: getImplicitObjectLocation(MCE: *Expr, Env: State.Env), Result, State);
944}
945
946static void transferConstPointerAccessorMemberOperatorCall(
947 const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &Result,
948 LatticeTransferState &State) {
949 auto *RecordLoc = cast_or_null<RecordStorageLocation>(
950 Val: State.Env.getStorageLocation(E: *Expr->getArg(Arg: 0)));
951 handleConstPointerAccessorMemberCall(Expr, RecordLoc, Result, State);
952}
953
954static void handleNonConstMemberCall(const CallExpr *Expr,
955 RecordStorageLocation *RecordLoc,
956 const MatchFinder::MatchResult &Result,
957 LatticeTransferState &State) {
958 if (RecordLoc) {
959 State.Lattice.clearConstMethodReturnValues(RecordLoc: *RecordLoc);
960 State.Lattice.clearConstMethodReturnStorageLocations(RecordLoc: *RecordLoc);
961 }
962 if (isStatusOrType(Type: Expr->getType()))
963 transferStatusOrReturningCall(Expr, State);
964}
965
966static void transferNonConstMemberCall(const CXXMemberCallExpr *Expr,
967 const MatchFinder::MatchResult &Result,
968 LatticeTransferState &State) {
969 handleNonConstMemberCall(Expr, RecordLoc: getImplicitObjectLocation(MCE: *Expr, Env: State.Env),
970 Result, State);
971}
972
973static void
974transferNonConstMemberOperatorCall(const CXXOperatorCallExpr *Expr,
975 const MatchFinder::MatchResult &Result,
976 LatticeTransferState &State) {
977 auto *RecordLoc = cast_or_null<RecordStorageLocation>(
978 Val: State.Env.getStorageLocation(E: *Expr->getArg(Arg: 0)));
979 handleNonConstMemberCall(Expr, RecordLoc, Result, State);
980}
981
982static void transferMakePredicateFormatterFromIsOkMatcherCall(
983 const CallExpr *Expr, const MatchFinder::MatchResult &,
984 LatticeTransferState &State) {
985 State.Env.setValue(
986 Loc: locForOkPredicate(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr)),
987 Val&: State.Env.getBoolLiteralValue(Value: true));
988}
989
990static void transferStatusIsOkMatcherCall(const CallExpr *Expr,
991 const MatchFinder::MatchResult &,
992 LatticeTransferState &State) {
993 BoolValue &OkMatcherVal = State.Env.getBoolLiteralValue(Value: true);
994 State.Env.setValue(Loc: locForOkMatcher(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr)),
995 Val&: OkMatcherVal);
996}
997
998static void transferMakePredicateFormatterFromStatusIsMatcherCall(
999 const CallExpr *Expr, const MatchFinder::MatchResult &,
1000 LatticeTransferState &State) {
1001 assert(Expr->isPRValue());
1002 auto &Loc = State.Env.getResultObjectLocation(RecordPRValue: *Expr->getArg(Arg: 0));
1003 auto &OkMatcherLoc = locForOkMatcher(StatusLoc&: Loc);
1004 BoolValue *OkMatcherVal = State.Env.get<BoolValue>(Loc: OkMatcherLoc);
1005 if (OkMatcherVal == nullptr)
1006 return;
1007 State.Env.setValue(
1008 Loc: locForOkPredicate(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr)),
1009 Val&: *OkMatcherVal);
1010}
1011
1012static void
1013transferPredicateFormatterMatcherCall(const CXXOperatorCallExpr *Expr,
1014 LatticeTransferState &State,
1015 bool IsStatusOr) {
1016 auto *Loc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
1017 if (Loc == nullptr)
1018 return;
1019
1020 auto *ObjectLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 2));
1021 if (ObjectLoc == nullptr)
1022 return;
1023
1024 auto &OkPredicateLoc = locForOkPredicate(StatusLoc&: *Loc);
1025 BoolValue *OkPredicateVal = State.Env.get<BoolValue>(Loc: OkPredicateLoc);
1026 if (OkPredicateVal == nullptr)
1027 return;
1028
1029 if (IsStatusOr)
1030 ObjectLoc = &locForStatus(StatusOrLoc&: *ObjectLoc);
1031 auto &StatusOk = valForOk(StatusLoc&: *ObjectLoc, Env&: State.Env);
1032
1033 auto &A = State.Env.arena();
1034 auto &Res = State.Env.makeAtomicBoolValue();
1035 State.Env.assume(
1036 A.makeImplies(LHS: OkPredicateVal->formula(),
1037 RHS: A.makeEquals(LHS: StatusOk.formula(), RHS: Res.formula())));
1038 State.Env.setValue(Loc: locForOk(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr)), Val&: Res);
1039}
1040
1041static void
1042transferAssertionResultConstructFromBoolCall(const CXXConstructExpr *Expr,
1043 const MatchFinder::MatchResult &,
1044 LatticeTransferState &State) {
1045 assert(Expr->getNumArgs() > 0);
1046
1047 auto *StatusAdaptorLoc = State.Env.get<StorageLocation>(E: *Expr->getArg(Arg: 0));
1048 if (StatusAdaptorLoc == nullptr)
1049 return;
1050 BoolValue *OkVal = State.Env.get<BoolValue>(Loc: *StatusAdaptorLoc);
1051 if (OkVal == nullptr)
1052 return;
1053 State.Env.setValue(Loc: locForOk(StatusLoc&: State.Env.getResultObjectLocation(RecordPRValue: *Expr)),
1054 Val&: *OkVal);
1055}
1056
1057static void
1058transferAssertionResultOperatorBoolCall(const CXXMemberCallExpr *Expr,
1059 const MatchFinder::MatchResult &,
1060 LatticeTransferState &State) {
1061 auto *RecordLoc = getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
1062 if (RecordLoc == nullptr)
1063 return;
1064 BoolValue *OkVal = State.Env.get<BoolValue>(Loc: locForOk(StatusLoc&: *RecordLoc));
1065 if (OkVal == nullptr)
1066 return;
1067 auto &A = State.Env.arena();
1068 auto &Res = State.Env.makeAtomicBoolValue();
1069 State.Env.assume(A.makeEquals(LHS: OkVal->formula(), RHS: Res.formula()));
1070 State.Env.setValue(E: *Expr, Val&: Res);
1071}
1072
1073static void transferDerefCall(const CXXOperatorCallExpr *Expr,
1074 const MatchFinder::MatchResult &,
1075 LatticeTransferState &State) {
1076 auto *StatusOrLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
1077
1078 if (StatusOrLoc && State.Env.getStorageLocation(E: *Expr) == nullptr)
1079 State.Env.setStorageLocation(E: *Expr,
1080 Loc&: StatusOrLoc->getSyntheticField(Name: "value"));
1081}
1082
1083static void transferArrowCall(const CXXOperatorCallExpr *Expr,
1084 const MatchFinder::MatchResult &,
1085 LatticeTransferState &State) {
1086 auto *StatusOrLoc = State.Env.get<RecordStorageLocation>(E: *Expr->getArg(Arg: 0));
1087 if (!StatusOrLoc)
1088 return;
1089 State.Env.setValue(E: *Expr, Val&: State.Env.create<PointerValue>(
1090 args&: StatusOrLoc->getSyntheticField(Name: "value")));
1091}
1092
1093static void transferValueCall(const CXXMemberCallExpr *Expr,
1094 const MatchFinder::MatchResult &,
1095 LatticeTransferState &State) {
1096 auto *StatusOrLoc = getImplicitObjectLocation(MCE: *Expr, Env: State.Env);
1097
1098 if (StatusOrLoc && State.Env.getStorageLocation(E: *Expr) == nullptr)
1099 State.Env.setStorageLocation(E: *Expr,
1100 Loc&: StatusOrLoc->getSyntheticField(Name: "value"));
1101}
1102
1103static void transferStatusOrPtrReturningCall(const CallExpr *Expr,
1104 const MatchFinder::MatchResult &,
1105 LatticeTransferState &State) {
1106 PointerValue *PointerVal =
1107 dyn_cast_or_null<PointerValue>(Val: State.Env.getValue(E: *Expr));
1108 if (!PointerVal) {
1109 PointerVal = cast<PointerValue>(Val: State.Env.createValue(Type: Expr->getType()));
1110 State.Env.setValue(E: *Expr, Val&: *PointerVal);
1111 }
1112
1113 auto *RecordLoc =
1114 dyn_cast_or_null<RecordStorageLocation>(Val: &PointerVal->getPointeeLoc());
1115 if (RecordLoc != nullptr &&
1116 State.Env.getValue(Loc: locForOk(StatusLoc&: locForStatus(StatusOrLoc&: *RecordLoc))) == nullptr)
1117 initializeStatusOr(StatusOrLoc&: *RecordLoc, Env&: State.Env);
1118}
1119
1120static void transferStatusPtrReturningCall(const CallExpr *Expr,
1121 const MatchFinder::MatchResult &,
1122 LatticeTransferState &State) {
1123 PointerValue *PointerVal =
1124 dyn_cast_or_null<PointerValue>(Val: State.Env.getValue(E: *Expr));
1125 if (!PointerVal) {
1126 PointerVal = cast<PointerValue>(Val: State.Env.createValue(Type: Expr->getType()));
1127 State.Env.setValue(E: *Expr, Val&: *PointerVal);
1128 }
1129
1130 auto *RecordLoc =
1131 dyn_cast_or_null<RecordStorageLocation>(Val: &PointerVal->getPointeeLoc());
1132 if (RecordLoc != nullptr &&
1133 State.Env.getValue(Loc: locForOk(StatusLoc&: *RecordLoc)) == nullptr)
1134 initializeStatus(StatusLoc&: *RecordLoc, Env&: State.Env);
1135}
1136
1137static RecordStorageLocation *
1138getSmartPtrLikeStorageLocation(const Expr &E, const Environment &Env) {
1139 if (!E.isPRValue())
1140 return dyn_cast_or_null<RecordStorageLocation>(Val: Env.getStorageLocation(E));
1141 if (auto *PointerVal = dyn_cast_or_null<PointerValue>(Val: Env.getValue(E)))
1142 return dyn_cast_or_null<RecordStorageLocation>(
1143 Val: &PointerVal->getPointeeLoc());
1144 return nullptr;
1145}
1146
1147CFGMatchSwitch<LatticeTransferState>
1148buildTransferMatchSwitch(ASTContext &Ctx,
1149 CFGMatchSwitchBuilder<LatticeTransferState> Builder) {
1150 using namespace ::clang::ast_matchers;
1151 return std::move(Builder)
1152 .CaseOfCFGStmt<CallExpr>(
1153 M: isMakePredicateFormatterFromIsOkMatcherCall(),
1154 A: transferMakePredicateFormatterFromIsOkMatcherCall)
1155 .CaseOfCFGStmt<CallExpr>(M: isStatusIsOkMatcherCall(),
1156 A: transferStatusIsOkMatcherCall)
1157 .CaseOfCFGStmt<CallExpr>(
1158 M: isMakePredicateFormatterFromStatusIsMatcherCall(),
1159 A: transferMakePredicateFormatterFromStatusIsMatcherCall)
1160 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1161 M: isPredicateFormatterFromStatusOrMatcherCall(),
1162 A: [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
1163 LatticeTransferState &State) {
1164 transferPredicateFormatterMatcherCall(Expr, State,
1165 /*IsStatusOr=*/true);
1166 })
1167 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1168 M: isPredicateFormatterFromStatusMatcherCall(),
1169 A: [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
1170 LatticeTransferState &State) {
1171 transferPredicateFormatterMatcherCall(Expr, State,
1172 /*IsStatusOr=*/false);
1173 })
1174 .CaseOfCFGStmt<CXXConstructExpr>(
1175 M: isAssertionResultConstructFromBoolCall(),
1176 A: transferAssertionResultConstructFromBoolCall)
1177 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isAssertionResultOperatorBoolCall(),
1178 A: transferAssertionResultOperatorBoolCall)
1179 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusOrMemberCallWithName(member_name: "ok"),
1180 A: transferStatusOrOkCall)
1181 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusOrMemberCallWithName(member_name: "status"),
1182 A: transferStatusCall)
1183 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusMemberCallWithName(member_name: "ok"),
1184 A: transferStatusOkCall)
1185 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusMemberCallWithName(member_name: "Update"),
1186 A: transferStatusUpdateCall)
1187 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1188 M: isComparisonOperatorCall(operator_name: "=="),
1189 A: [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
1190 LatticeTransferState &State) {
1191 transferComparisonOperator(Expr, State,
1192 /*IsNegative=*/false);
1193 })
1194 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1195 M: isComparisonOperatorCall(operator_name: "!="),
1196 A: [](const CXXOperatorCallExpr *Expr, const MatchFinder::MatchResult &,
1197 LatticeTransferState &State) {
1198 transferComparisonOperator(Expr, State,
1199 /*IsNegative=*/true);
1200 })
1201 .CaseOfCFGStmt<BinaryOperator>(
1202 M: isPointerComparisonOperatorCall(operator_name: "=="),
1203 A: [](const BinaryOperator *Expr, const MatchFinder::MatchResult &,
1204 LatticeTransferState &State) {
1205 transferPointerComparisonOperator(Expr, State,
1206 /*IsNegative=*/false);
1207 })
1208 .CaseOfCFGStmt<BinaryOperator>(
1209 M: isPointerComparisonOperatorCall(operator_name: "!="),
1210 A: [](const BinaryOperator *Expr, const MatchFinder::MatchResult &,
1211 LatticeTransferState &State) {
1212 transferPointerComparisonOperator(Expr, State,
1213 /*IsNegative=*/true);
1214 })
1215 .CaseOfCFGStmt<CallExpr>(M: isOkStatusCall(), A: transferOkStatusCall)
1216 .CaseOfCFGStmt<CallExpr>(M: isNotOkStatusCall(), A: transferNotOkStatusCall)
1217 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusOrMemberCallWithName(member_name: "emplace"),
1218 A: transferEmplaceCall)
1219 .CaseOfCFGStmt<CXXOperatorCallExpr>(M: isStatusOrValueAssignmentCall(),
1220 A: transferValueAssignmentCall)
1221 .CaseOfCFGStmt<CXXConstructExpr>(M: isStatusOrValueConstructor(),
1222 A: transferValueConstructor)
1223 .CaseOfCFGStmt<CXXOperatorCallExpr>(M: isStatusOrOperatorCallWithName(operator_name: "->"),
1224 A: transferArrowCall)
1225 .CaseOfCFGStmt<CXXOperatorCallExpr>(M: isStatusOrOperatorCallWithName(operator_name: "*"),
1226 A: transferDerefCall)
1227 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isStatusOrMemberCallWithName(member_name: "value"),
1228 A: transferValueCall)
1229 .CaseOfCFGStmt<CallExpr>(M: isAsStatusCallWithStatus(),
1230 A: transferAsStatusCallWithStatus)
1231 .CaseOfCFGStmt<CallExpr>(M: isAsStatusCallWithStatusOr(),
1232 A: transferAsStatusCallWithStatusOr)
1233 .CaseOfCFGStmt<CallExpr>(M: isLoggingGetReferenceableValueCall(),
1234 A: transferLoggingGetReferenceableValueCall)
1235 .CaseOfCFGStmt<CallExpr>(M: isLoggingCheckEqImpl(),
1236 A: transferLoggingCheckEqImpl)
1237 // This needs to go before the const accessor call matcher, because these
1238 // look like them, but we model `operator`* and `get` to return the same
1239 // object. Also, we model them for non-const cases.
1240 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1241 M: isPointerLikeOperatorStar(),
1242 A: [](const CXXOperatorCallExpr *E,
1243 const MatchFinder::MatchResult &Result,
1244 LatticeTransferState &State) {
1245 transferSmartPointerLikeCachedDeref(
1246 DerefExpr: E, SmartPointerLoc: getSmartPtrLikeStorageLocation(E: *E->getArg(Arg: 0), Env: State.Env),
1247 State, InitializeLoc: [](StorageLocation &Loc) {});
1248 })
1249 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1250 M: isPointerLikeOperatorArrow(),
1251 A: [](const CXXOperatorCallExpr *E,
1252 const MatchFinder::MatchResult &Result,
1253 LatticeTransferState &State) {
1254 transferSmartPointerLikeCachedGet(
1255 GetExpr: E, SmartPointerLoc: getSmartPtrLikeStorageLocation(E: *E->getArg(Arg: 0), Env: State.Env),
1256 State, InitializeLoc: [](StorageLocation &Loc) {});
1257 })
1258 .CaseOfCFGStmt<CXXMemberCallExpr>(
1259 M: isSmartPointerLikeValueMethodCall(),
1260 A: [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
1261 LatticeTransferState &State) {
1262 transferSmartPointerLikeCachedDeref(
1263 DerefExpr: E, SmartPointerLoc: getImplicitObjectLocation(MCE: *E, Env: State.Env), State,
1264 InitializeLoc: [](StorageLocation &Loc) {});
1265 })
1266 .CaseOfCFGStmt<CXXMemberCallExpr>(
1267 M: isSmartPointerLikeGetMethodCall(),
1268 A: [](const CXXMemberCallExpr *E, const MatchFinder::MatchResult &Result,
1269 LatticeTransferState &State) {
1270 transferSmartPointerLikeCachedGet(
1271 GetExpr: E, SmartPointerLoc: getImplicitObjectLocation(MCE: *E, Env: State.Env), State,
1272 InitializeLoc: [](StorageLocation &Loc) {});
1273 })
1274 // const accessor calls
1275 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isConstAccessorMemberCall(),
1276 A: transferConstAccessorMemberCall)
1277 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1278 M: isConstAccessorMemberOperatorCall(),
1279 A: transferConstAccessorMemberOperatorCall)
1280 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isConstPointerAccessorMemberCall(),
1281 A: transferConstPointerAccessorMemberCall)
1282 .CaseOfCFGStmt<CXXOperatorCallExpr>(
1283 M: isConstPointerAccessorMemberOperatorCall(),
1284 A: transferConstPointerAccessorMemberOperatorCall)
1285 // non-const member calls that may modify the state of an object.
1286 .CaseOfCFGStmt<CXXMemberCallExpr>(M: isNonConstMemberCall(),
1287 A: transferNonConstMemberCall)
1288 .CaseOfCFGStmt<CXXOperatorCallExpr>(M: isNonConstMemberOperatorCall(),
1289 A: transferNonConstMemberOperatorCall)
1290 // N.B. this has to be after transferConstMemberCall, otherwise we would
1291 // always return a fresh RecordStorageLocation for the StatusOr.
1292 .CaseOfCFGStmt<CallExpr>(M: isStatusOrReturningCall(),
1293 A: [](const CallExpr *Expr,
1294 const MatchFinder::MatchResult &,
1295 LatticeTransferState &State) {
1296 transferStatusOrReturningCall(Expr, State);
1297 })
1298 .CaseOfCFGStmt<CallExpr>(M: isStatusOrPtrReturningCall(),
1299 A: transferStatusOrPtrReturningCall)
1300 .CaseOfCFGStmt<CallExpr>(M: isStatusPtrReturningCall(),
1301 A: transferStatusPtrReturningCall)
1302 // N.B. These need to come after all other CXXConstructExpr.
1303 // These are there to make sure that every Status and StatusOr object
1304 // have their ok boolean initialized when constructed. If we were to
1305 // lazily initialize them when we first access them, we can produce
1306 // false positives if that first access is in a control flow statement.
1307 // You can comment out these two constructors and see tests fail.
1308 .CaseOfCFGStmt<CXXConstructExpr>(M: isStatusOrConstructor(),
1309 A: transferStatusOrConstructor)
1310 .CaseOfCFGStmt<CXXConstructExpr>(M: isStatusConstructor(),
1311 A: transferStatusConstructor)
1312 .CaseOfCFGStmt<ImplicitCastExpr>(
1313 M: implicitCastExpr(hasCastKind(Kind: CK_PointerToBoolean)),
1314 A: transferPointerToBoolean)
1315 .Build();
1316}
1317
1318QualType findStatusType(const ASTContext &Ctx) {
1319 for (Type *Ty : Ctx.getTypes())
1320 if (isStatusType(Type: QualType(Ty, 0)))
1321 return QualType(Ty, 0);
1322
1323 return QualType();
1324}
1325
1326UncheckedStatusOrAccessModel::UncheckedStatusOrAccessModel(ASTContext &Ctx,
1327 Environment &Env)
1328 : DataflowAnalysis<UncheckedStatusOrAccessModel,
1329 UncheckedStatusOrAccessModel::Lattice>(Ctx),
1330 TransferMatchSwitch(buildTransferMatchSwitch(Ctx, Builder: {})) {
1331 QualType StatusType = findStatusType(Ctx);
1332 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
1333 [StatusType](QualType Ty) -> llvm::StringMap<QualType> {
1334 CXXRecordDecl *RD = Ty->getAsCXXRecordDecl();
1335 if (RD == nullptr)
1336 return {};
1337
1338 if (auto Fields = getSyntheticFields(Ty, StatusType, RD: *RD);
1339 !Fields.empty())
1340 return Fields;
1341 return {};
1342 });
1343}
1344
1345void UncheckedStatusOrAccessModel::transfer(const CFGElement &Elt, Lattice &L,
1346 Environment &Env) {
1347 LatticeTransferState State(L, Env);
1348 TransferMatchSwitch(Elt, getASTContext(), State);
1349}
1350
1351} // namespace clang::dataflow::statusor_model
1352