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