clang 22.0.0git
UncheckedStatusOrAccessModel.cpp
Go to the documentation of this file.
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
10
11#include <cassert>
12#include <utility>
13
14#include "clang/AST/DeclCXX.h"
16#include "clang/AST/Expr.h"
17#include "clang/AST/ExprCXX.h"
18#include "clang/AST/TypeBase.h"
22#include "clang/Analysis/CFG.h"
29#include "clang/Basic/LLVM.h"
31#include "llvm/ADT/StringMap.h"
32
34namespace {
35
36using ::clang::ast_matchers::MatchFinder;
38
39} // namespace
40
41static bool namespaceEquals(const NamespaceDecl *NS,
42 clang::ArrayRef<clang::StringRef> NamespaceNames) {
43 while (!NamespaceNames.empty() && NS) {
44 if (NS->getName() != NamespaceNames.consume_back())
45 return false;
46 NS = dyn_cast_or_null<NamespaceDecl>(NS->getParent());
47 }
48 return NamespaceNames.empty() && !NS;
49}
50
51// TODO: move this to a proper place to share with the rest of clang
53 StringRef Name) {
54 if (Type.isNull())
55 return false;
56 if (auto *RD = Type->getAsRecordDecl())
57 if (RD->getName() == Name)
58 if (const auto *N = dyn_cast_or_null<NamespaceDecl>(RD->getDeclContext()))
59 return namespaceEquals(N, NS);
60 return false;
61}
62
64 return isTypeNamed(Type, {"absl", "internal_statusor"}, "OperatorBase");
65}
66
67static bool isSafeUnwrap(RecordStorageLocation *StatusOrLoc,
68 const Environment &Env) {
69 if (!StatusOrLoc)
70 return false;
71 auto &StatusLoc = locForStatus(*StatusOrLoc);
72 auto *OkVal = Env.get<BoolValue>(locForOk(StatusLoc));
73 return OkVal != nullptr && Env.proves(OkVal->formula());
74}
75
78 auto *RD = Ty->getAsCXXRecordDecl();
79 if (RD == nullptr)
80 return nullptr;
81 if (isStatusOrType(Ty) ||
82 // In case we are analyzing code under OperatorBase itself that uses
83 // operator* (e.g. to implement operator->).
86 if (!RD->hasDefinition())
87 return nullptr;
88 for (const auto &Base : RD->bases())
89 if (auto *QT = getStatusOrBaseClass(Base.getType()))
90 return QT;
91 return nullptr;
92}
93
97
98static auto isStatusOrMemberCallWithName(llvm::StringRef member_name) {
99 using namespace ::clang::ast_matchers; // NOLINT: Too many names
100 return cxxMemberCallExpr(
101 on(expr(unless(cxxThisExpr()))),
102 callee(cxxMethodDecl(
103 hasName(member_name),
105}
106
107static auto isStatusOrOperatorCallWithName(llvm::StringRef operator_name) {
108 using namespace ::clang::ast_matchers; // NOLINT: Too many names
109 return cxxOperatorCallExpr(
110 hasOverloadedOperatorName(operator_name),
111 callee(cxxMethodDecl(
113}
114
115static auto valueCall() {
116 using namespace ::clang::ast_matchers; // NOLINT: Too many names
117 return anyOf(isStatusOrMemberCallWithName("value"),
118 isStatusOrMemberCallWithName("ValueOrDie"));
119}
120
121static auto valueOperatorCall() {
122 using namespace ::clang::ast_matchers; // NOLINT: Too many names
125}
126
127static auto
131 // StatusOr::value, StatusOr::ValueOrDie
132 .CaseOfCFGStmt<CXXMemberCallExpr>(
133 valueCall(),
134 [](const CXXMemberCallExpr *E,
136 const Environment &Env) {
137 if (!isSafeUnwrap(getImplicitObjectLocation(*E, Env), Env))
140 })
141
142 // StatusOr::operator*, StatusOr::operator->
143 .CaseOfCFGStmt<CXXOperatorCallExpr>(
145 [](const CXXOperatorCallExpr *E,
147 const Environment &Env) {
148 RecordStorageLocation *StatusOrLoc =
149 Env.get<RecordStorageLocation>(*E->getArg(0));
150 if (!isSafeUnwrap(StatusOrLoc, Env))
151 return llvm::SmallVector<SourceLocation>({E->getOperatorLoc()});
153 })
154 .Build();
155}
156
160
167
169 Environment &Env) {
170 auto &OkVal = Env.makeAtomicBoolValue();
171 Env.setValue(locForOk(StatusLoc), OkVal);
172 return OkVal;
173}
174
176 Environment &Env) {
177 return initializeStatus(locForStatus(StatusOrLoc), Env);
178}
179
181 using namespace ::clang::ast_matchers; // NOLINT: Too many names
183 hasName("absl::StatusOr"),
184 hasTemplateArgument(0, refersToType(type().bind("T"))));
185}
186
188 using namespace ::clang::ast_matchers; // NOLINT: Too many names
189 return cxxRecordDecl(hasName("absl::Status"));
190}
191
193 using namespace ::clang::ast_matchers; // NOLINT: Too many names
195 hasName("absl::internal_statusor::OperatorBase"));
196}
197
199 using namespace ::clang::ast_matchers; // NOLINT: Too many names
200 return hasCanonicalType(qualType(hasDeclaration(statusOrClass())));
201}
202
204 return isTypeNamed(Type, {"absl"}, "StatusOr");
205}
206
208 return isTypeNamed(Type, {"absl"}, "Status");
209}
210
211llvm::StringMap<QualType> getSyntheticFields(QualType Ty, QualType StatusType,
212 const CXXRecordDecl &RD) {
213 if (auto *TRD = getStatusOrBaseClass(Ty))
214 return {{"status", StatusType}, {"value", getStatusOrValueType(TRD)}};
215 if (isStatusType(Ty) || (RD.hasDefinition() &&
216 RD.isDerivedFrom(StatusType->getAsCXXRecordDecl())))
217 return {{"ok", RD.getASTContext().BoolTy}};
218 return {};
219}
220
224
226 return StatusLoc.getSyntheticField("ok");
227}
228
230 if (auto *Val = Env.get<BoolValue>(locForOk(StatusLoc)))
231 return *Val;
232 return initializeStatus(StatusLoc, Env);
233}
234
237 LatticeTransferState &State) {
238 RecordStorageLocation *StatusOrLoc =
239 getImplicitObjectLocation(*Expr, State.Env);
240 if (StatusOrLoc == nullptr)
241 return;
242
243 auto &OkVal = valForOk(locForStatus(*StatusOrLoc), State.Env);
244 State.Env.setValue(*Expr, OkVal);
245}
246
250 using namespace ::clang::ast_matchers; // NOLINT: Too many names
251 return std::move(Builder)
254 .Build();
255}
256
258 for (Type *Ty : Ctx.getTypes())
259 if (isStatusType(QualType(Ty, 0)))
260 return QualType(Ty, 0);
261
262 return QualType();
263}
264
266 Environment &Env)
269 TransferMatchSwitch(buildTransferMatchSwitch(Ctx, {})) {
270 QualType StatusType = findStatusType(Ctx);
271 Env.getDataflowAnalysisContext().setSyntheticFieldCallback(
272 [StatusType](QualType Ty) -> llvm::StringMap<QualType> {
274 if (RD == nullptr)
275 return {};
276
277 if (auto Fields = getSyntheticFields(Ty, StatusType, *RD);
278 !Fields.empty())
279 return Fields;
280 return {};
281 });
282}
283
285 Environment &Env) {
286 LatticeTransferState State(L, Env);
287 TransferMatchSwitch(Elt, getASTContext(), State);
288}
289
290} // namespace clang::dataflow::statusor_model
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the C++ template declaration subclasses.
Defines the clang::Expr interface and subclasses for C++ expressions.
Forward-declares and imports various common LLVM datatypes that clang wants to use unqualified.
Defines the clang::SourceLocation class and associated facilities.
C Language Family Type Representation.
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition ASTContext.h:220
const SmallVectorImpl< Type * > & getTypes() const
CanQualType BoolTy
Represents a top-level expression in a basic block.
Definition CFG.h:55
Represents a call to a member function that may be written either with member call syntax (e....
Definition ExprCXX.h:179
SourceLocation getExprLoc() const LLVM_READONLY
Definition ExprCXX.h:220
A call to an overloaded operator written using operator syntax.
Definition ExprCXX.h:84
Represents a C++ struct/union/class.
Definition DeclCXX.h:258
bool hasDefinition() const
Definition DeclCXX.h:561
bool isDerivedFrom(const CXXRecordDecl *Base) const
Determine whether this class is derived from the class Base.
Expr * getArg(unsigned Arg)
getArg - Return the specified argument.
Definition Expr.h:3081
Represents a class template specialization, which refers to a class template with a given set of temp...
const TemplateArgumentList & getTemplateArgs() const
Retrieve the template arguments of the class template specialization.
DeclContext * getParent()
getParent - Returns the containing DeclContext.
Definition DeclBase.h:2109
ASTContext & getASTContext() const LLVM_READONLY
Definition DeclBase.cpp:546
This represents one expression.
Definition Expr.h:112
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition Decl.h:301
Represent a C++ namespace.
Definition Decl.h:592
A (possibly-)qualified type.
Definition TypeBase.h:937
const TemplateArgument & get(unsigned Idx) const
Retrieve the template argument at a given index.
QualType getAsType() const
Retrieve the type for a type template argument.
The base class of the type hierarchy.
Definition TypeBase.h:1833
CXXRecordDecl * getAsCXXRecordDecl() const
Retrieves the CXXRecordDecl that this type refers to, either because the type is a RecordType or beca...
Definition Type.h:26
RecordDecl * getAsRecordDecl() const
Retrieves the RecordDecl this type refers to.
Definition Type.h:41
Models a boolean.
Definition Value.h:94
Collects cases of a "match switch": a collection of matchers paired with callbacks,...
Holds the state of the program (store and heap) at a given program point.
BoolValue & makeAtomicBoolValue() const
Returns an atomic boolean value.
bool proves(const Formula &) const
Returns true if the formula is always true when this point is reached.
void setValue(const StorageLocation &Loc, Value &Val)
Assigns Val as the value of Loc in the environment.
std::enable_if_t< std::is_base_of_v< StorageLocation, T >, T * > get(const ValueDecl &D) const
Returns the result of casting getStorageLocation(...) to a subclass of StorageLocation (using cast_or...
A storage location for a record (struct, class, or union).
StorageLocation & getSyntheticField(llvm::StringRef Name) const
Returns the storage location for the synthetic field Name.
Base class for elements of the local variable store and of the heap.
UncheckedStatusOrAccessDiagnoser(UncheckedStatusOrAccessModelOptions Options={})
llvm::SmallVector< SourceLocation > operator()(const CFGElement &Elt, ASTContext &Ctx, const TransferStateForDiagnostics< UncheckedStatusOrAccessModel::Lattice > &State)
void transfer(const CFGElement &Elt, Lattice &L, Environment &Env)
internal::Matcher< QualType > TypeMatcher
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
internal::Matcher< Decl > DeclarationMatcher
Types of matchers for the top-level classes in the AST class hierarchy.
const internal::VariadicAllOfMatcher< Type > type
Matches Types in the clang AST.
internal::Matcher< Stmt > StatementMatcher
const internal::VariadicDynCastAllOfMatcher< Decl, ClassTemplateSpecializationDecl > classTemplateSpecializationDecl
Matches C++ class template specializations.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXRecordDecl > cxxRecordDecl
Matches C++ class declarations.
internal::PolymorphicMatcher< internal::HasDeclarationMatcher, void(internal::HasDeclarationSupportedTypes), internal::Matcher< Decl > > hasDeclaration(const internal::Matcher< Decl > &InnerMatcher)
Matches a node if the declaration associated with that node matches the given matcher.
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf
Matches if any of the given matchers matches.
const internal::VariadicAllOfMatcher< QualType > qualType
Matches QualTypes in the clang AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXThisExpr > cxxThisExpr
Matches implicit and explicit this expressions.
TransferState< UncheckedStatusOrAccessModel::Lattice > LatticeTransferState
static ClassTemplateSpecializationDecl * getStatusOrBaseClass(const QualType &Ty)
static bool isStatusOrOperatorBaseType(QualType Type)
static auto buildDiagnoseMatchSwitch(const UncheckedStatusOrAccessModelOptions &Options)
RecordStorageLocation & locForStatus(RecordStorageLocation &StatusOrLoc)
static auto isStatusOrOperatorCallWithName(llvm::StringRef operator_name)
clang::ast_matchers::DeclarationMatcher statusOrClass()
clang::ast_matchers::TypeMatcher statusOrType()
static bool isSafeUnwrap(RecordStorageLocation *StatusOrLoc, const Environment &Env)
static void transferStatusOrOkCall(const CXXMemberCallExpr *Expr, const MatchFinder::MatchResult &, LatticeTransferState &State)
static bool namespaceEquals(const NamespaceDecl *NS, clang::ArrayRef< clang::StringRef > NamespaceNames)
static bool isTypeNamed(QualType Type, clang::ArrayRef< clang::StringRef > NS, StringRef Name)
clang::ast_matchers::DeclarationMatcher statusClass()
BoolValue & initializeStatusOr(RecordStorageLocation &StatusOrLoc, Environment &Env)
QualType findStatusType(const ASTContext &Ctx)
BoolValue & initializeStatus(RecordStorageLocation &StatusLoc, Environment &Env)
llvm::StringMap< QualType > getSyntheticFields(QualType Ty, QualType StatusType, const CXXRecordDecl &RD)
StorageLocation & locForOk(RecordStorageLocation &StatusLoc)
static auto isStatusOrMemberCallWithName(llvm::StringRef member_name)
static QualType getStatusOrValueType(ClassTemplateSpecializationDecl *TRD)
BoolValue & valForOk(RecordStorageLocation &StatusLoc, Environment &Env)
CFGMatchSwitch< LatticeTransferState > buildTransferMatchSwitch(ASTContext &Ctx, CFGMatchSwitchBuilder< LatticeTransferState > Builder)
clang::ast_matchers::DeclarationMatcher statusOrOperatorBaseClass()
internal::Matcher< NamedDecl > hasName(StringRef Name)
Matches NamedDecl nodes that have the specified name.
std::function< Result(const CFGElement &, ASTContext &, State &)> CFGMatchSwitch
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXMemberCallExpr > cxxMemberCallExpr
Matches member call expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXOperatorCallExpr > cxxOperatorCallExpr
Matches overloaded operator calls.
internal::PolymorphicMatcher< internal::HasOverloadedOperatorNameMatcher, AST_POLYMORPHIC_SUPPORTED_TYPES(CXXOperatorCallExpr, FunctionDecl), std::vector< std::string > > hasOverloadedOperatorName(StringRef Name)
Matches overloaded operator names.
RecordStorageLocation * getImplicitObjectLocation(const CXXMemberCallExpr &MCE, const Environment &Env)
Returns the storage location for the implicit object of a CXXMemberCallExpr, or null if none is defin...
const internal::VariadicDynCastAllOfMatcher< Decl, CXXMethodDecl > cxxMethodDecl
Matches method declarations.
U cast(CodeGen::Address addr)
Definition Address.h:327
Contains all information for a given match.
A read-only version of TransferState.
Definition MatchSwitch.h:55
Contains all information for a given match.