clang 22.0.0git
UnsafeBufferUsage.cpp
Go to the documentation of this file.
1//===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
10#include "clang/AST/APValue.h"
13#include "clang/AST/Attr.h"
14#include "clang/AST/Decl.h"
15#include "clang/AST/DeclCXX.h"
17#include "clang/AST/Expr.h"
20#include "clang/AST/Stmt.h"
22#include "clang/AST/Type.h"
26#include "clang/Lex/Lexer.h"
28#include "llvm/ADT/APInt.h"
29#include "llvm/ADT/APSInt.h"
30#include "llvm/ADT/STLFunctionalExtras.h"
31#include "llvm/ADT/SmallSet.h"
32#include "llvm/ADT/SmallVector.h"
33#include "llvm/ADT/StringRef.h"
34#include <cstddef>
35#include <optional>
36#include <queue>
37#include <set>
38#include <sstream>
39
40using namespace clang;
41
42#ifndef NDEBUG
43namespace {
44class StmtDebugPrinter
45 : public ConstStmtVisitor<StmtDebugPrinter, std::string> {
46public:
47 std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); }
48
49 std::string VisitBinaryOperator(const BinaryOperator *BO) {
50 return "BinaryOperator(" + BO->getOpcodeStr().str() + ")";
51 }
52
53 std::string VisitUnaryOperator(const UnaryOperator *UO) {
54 return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")";
55 }
56
57 std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
58 return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")";
59 }
60};
61
62// Returns a string of ancestor `Stmt`s of the given `DRE` in such a form:
63// "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...".
64static std::string getDREAncestorString(const DeclRefExpr *DRE,
65 ASTContext &Ctx) {
66 std::stringstream SS;
67 const Stmt *St = DRE;
68 StmtDebugPrinter StmtPriner;
69
70 do {
71 SS << StmtPriner.Visit(St);
72
73 DynTypedNodeList StParents = Ctx.getParents(*St);
74
75 if (StParents.size() > 1)
76 return "unavailable due to multiple parents";
77 if (StParents.empty())
78 break;
79 St = StParents.begin()->get<Stmt>();
80 if (St)
81 SS << " ==> ";
82 } while (St);
83 return SS.str();
84}
85
86} // namespace
87#endif /* NDEBUG */
88
89namespace {
90// Using a custom `FastMatcher` instead of ASTMatchers to achieve better
91// performance. FastMatcher uses simple function `matches` to find if a node
92// is a match, avoiding the dependency on the ASTMatchers framework which
93// provide a nice abstraction, but incur big performance costs.
94class FastMatcher {
95public:
96 virtual bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
97 const UnsafeBufferUsageHandler &Handler) = 0;
98 virtual ~FastMatcher() = default;
99};
100
101class MatchResult {
102
103public:
104 template <typename T> const T *getNodeAs(StringRef ID) const {
105 auto It = Nodes.find(ID);
106 if (It == Nodes.end()) {
107 return nullptr;
108 }
109 return It->second.get<T>();
110 }
111
112 void addNode(StringRef ID, const DynTypedNode &Node) { Nodes[ID] = Node; }
113
114private:
115 llvm::StringMap<DynTypedNode> Nodes;
116};
117} // namespace
118
119#define SIZED_CONTAINER_OR_VIEW_LIST \
120 "span", "array", "vector", "basic_string_view", "basic_string", \
121 "initializer_list",
122
123// A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
124// except for those belonging to a different callable of "n".
126public:
127 // Creates an AST visitor that matches `Matcher` on all
128 // descendants of a given node "n" except for the ones
129 // belonging to a different callable of "n".
130 MatchDescendantVisitor(ASTContext &Context, FastMatcher &Matcher,
131 bool FindAll, bool ignoreUnevaluatedContext,
132 const UnsafeBufferUsageHandler &NewHandler)
133 : Matcher(&Matcher), FindAll(FindAll), Matches(false),
134 ignoreUnevaluatedContext(ignoreUnevaluatedContext),
135 ActiveASTContext(&Context), Handler(&NewHandler) {
137 ShouldVisitImplicitCode = false; // TODO: let's ignore implicit code for now
138 }
139
140 // Returns true if a match is found in a subtree of `DynNode`, which belongs
141 // to the same callable of `DynNode`.
142 bool findMatch(const DynTypedNode &DynNode) {
143 Matches = false;
144 if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
145 TraverseStmt(const_cast<Stmt *>(StmtNode));
146 return Matches;
147 }
148 return false;
149 }
150
151 // The following are overriding methods from the base visitor class.
152 // They are public only to allow CRTP to work. They are *not *part
153 // of the public API of this class.
154
155 // For the matchers so far used in safe buffers, we only need to match
156 // `Stmt`s. To override more as needed.
157
158 bool TraverseDecl(Decl *Node) override {
159 if (!Node)
160 return true;
161 if (!match(*Node))
162 return false;
163 // To skip callables:
164 if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))
165 return true;
166 // Traverse descendants
168 }
169
171 // These are unevaluated, except the result expression.
172 if (ignoreUnevaluatedContext)
173 return TraverseStmt(Node->getResultExpr());
174 return DynamicRecursiveASTVisitor::TraverseGenericSelectionExpr(Node);
175 }
176
177 bool
179 // Unevaluated context.
180 if (ignoreUnevaluatedContext)
181 return true;
182 return DynamicRecursiveASTVisitor::TraverseUnaryExprOrTypeTraitExpr(Node);
183 }
184
186 bool TraverseQualifier) override {
187 // Unevaluated context.
188 if (ignoreUnevaluatedContext)
189 return true;
190 return DynamicRecursiveASTVisitor::TraverseTypeOfExprTypeLoc(
191 Node, TraverseQualifier);
192 }
193
195 bool TraverseQualifier) override {
196 // Unevaluated context.
197 if (ignoreUnevaluatedContext)
198 return true;
199 return DynamicRecursiveASTVisitor::TraverseDecltypeTypeLoc(
200 Node, TraverseQualifier);
201 }
202
204 // Unevaluated context.
205 if (ignoreUnevaluatedContext)
206 return true;
207 return DynamicRecursiveASTVisitor::TraverseCXXNoexceptExpr(Node);
208 }
209
211 // Unevaluated context.
212 if (ignoreUnevaluatedContext)
213 return true;
214 return DynamicRecursiveASTVisitor::TraverseCXXTypeidExpr(Node);
215 }
216
218 if (!TraverseStmt(Node->getExpr()))
219 return false;
220 return DynamicRecursiveASTVisitor::TraverseCXXDefaultInitExpr(Node);
221 }
222
223 bool TraverseStmt(Stmt *Node) override {
224 if (!Node)
225 return true;
226 if (!match(*Node))
227 return false;
229 }
230
231private:
232 // Sets 'Matched' to true if 'Matcher' matches 'Node'
233 //
234 // Returns 'true' if traversal should continue after this function
235 // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
236 template <typename T> bool match(const T &Node) {
237 if (Matcher->matches(DynTypedNode::create(Node), *ActiveASTContext,
238 *Handler)) {
239 Matches = true;
240 if (!FindAll)
241 return false; // Abort as soon as a match is found.
242 }
243 return true;
244 }
245
246 FastMatcher *const Matcher;
247 // When true, finds all matches. When false, finds the first match and stops.
248 const bool FindAll;
249 bool Matches;
250 bool ignoreUnevaluatedContext;
251 ASTContext *ActiveASTContext;
252 const UnsafeBufferUsageHandler *Handler;
253};
254
255// Because we're dealing with raw pointers, let's define what we mean by that.
256static bool hasPointerType(const Expr &E) {
257 return isa<PointerType>(E.getType().getCanonicalType());
258}
259
260static bool hasArrayType(const Expr &E) {
261 return isa<ArrayType>(E.getType().getCanonicalType());
262}
263
264static void
266 const UnsafeBufferUsageHandler &Handler,
267 FastMatcher &Matcher) {
268 MatchDescendantVisitor Visitor(Ctx, Matcher, /*FindAll=*/true,
269 /*ignoreUnevaluatedContext=*/true, Handler);
270 Visitor.findMatch(DynTypedNode::create(*S));
271}
272
273static void forEachDescendantStmt(const Stmt *S, ASTContext &Ctx,
274 const UnsafeBufferUsageHandler &Handler,
275 FastMatcher &Matcher) {
276 MatchDescendantVisitor Visitor(Ctx, Matcher, /*FindAll=*/true,
277 /*ignoreUnevaluatedContext=*/false, Handler);
278 Visitor.findMatch(DynTypedNode::create(*S));
279}
280
281// Matches a `Stmt` node iff the node is in a safe-buffer opt-out region
282static bool notInSafeBufferOptOut(const Stmt &Node,
283 const UnsafeBufferUsageHandler *Handler) {
284 return !Handler->isSafeBufferOptOut(Node.getBeginLoc());
285}
286
287static bool
289 const UnsafeBufferUsageHandler *Handler) {
290 return Handler->ignoreUnsafeBufferInContainer(Node.getBeginLoc());
291}
292
293static bool ignoreUnsafeLibcCall(const ASTContext &Ctx, const Stmt &Node,
294 const UnsafeBufferUsageHandler *Handler) {
295 if (Ctx.getLangOpts().CPlusPlus)
296 return Handler->ignoreUnsafeBufferInLibcCall(Node.getBeginLoc());
297 return true; /* Only warn about libc calls for C++ */
298}
299
300// Finds any expression 'e' such that `OnResult`
301// matches 'e' and 'e' is in an Unspecified Lvalue Context.
303 const Stmt *S, const llvm::function_ref<void(const Expr *)> OnResult) {
304 if (const auto *CE = dyn_cast<ImplicitCastExpr>(S);
305 CE && CE->getCastKind() == CastKind::CK_LValueToRValue)
306 OnResult(CE->getSubExpr());
307 if (const auto *BO = dyn_cast<BinaryOperator>(S);
308 BO && BO->getOpcode() == BO_Assign)
309 OnResult(BO->getLHS());
310}
311
312// Finds any expression `e` such that `InnerMatcher` matches `e` and
313// `e` is in an Unspecified Pointer Context (UPC).
315 const Stmt *S, llvm::function_ref<void(const Stmt *)> InnerMatcher) {
316 // A UPC can be
317 // 1. an argument of a function call (except the callee has [[unsafe_...]]
318 // attribute), or
319 // 2. the operand of a pointer-to-(integer or bool) cast operation; or
320 // 3. the operand of a comparator operation; or
321 // 4. the operand of a pointer subtraction operation
322 // (i.e., computing the distance between two pointers); or ...
323
324 if (auto *CE = dyn_cast<CallExpr>(S)) {
325 if (const auto *FnDecl = CE->getDirectCallee();
326 FnDecl && FnDecl->hasAttr<UnsafeBufferUsageAttr>())
327 return;
329 *CE, [&InnerMatcher](QualType Type, const Expr *Arg) {
330 if (Type->isAnyPointerType())
331 InnerMatcher(Arg);
332 });
333 }
334
335 if (auto *CE = dyn_cast<CastExpr>(S)) {
336 if (CE->getCastKind() != CastKind::CK_PointerToIntegral &&
337 CE->getCastKind() != CastKind::CK_PointerToBoolean)
338 return;
339 if (!hasPointerType(*CE->getSubExpr()))
340 return;
341 InnerMatcher(CE->getSubExpr());
342 }
343
344 // Pointer comparison operator.
345 if (const auto *BO = dyn_cast<BinaryOperator>(S);
346 BO && (BO->getOpcode() == BO_EQ || BO->getOpcode() == BO_NE ||
347 BO->getOpcode() == BO_LT || BO->getOpcode() == BO_LE ||
348 BO->getOpcode() == BO_GT || BO->getOpcode() == BO_GE)) {
349 auto *LHS = BO->getLHS();
350 if (hasPointerType(*LHS))
351 InnerMatcher(LHS);
352
353 auto *RHS = BO->getRHS();
354 if (hasPointerType(*RHS))
355 InnerMatcher(RHS);
356 }
357
358 // Pointer subtractions.
359 if (const auto *BO = dyn_cast<BinaryOperator>(S);
360 BO && BO->getOpcode() == BO_Sub && hasPointerType(*BO->getLHS()) &&
361 hasPointerType(*BO->getRHS())) {
362 // Note that here we need both LHS and RHS to be
363 // pointer. Then the inner matcher can match any of
364 // them:
365 InnerMatcher(BO->getLHS());
366 InnerMatcher(BO->getRHS());
367 }
368 // FIXME: any more cases? (UPC excludes the RHS of an assignment. For now
369 // we don't have to check that.)
370}
371
372// Finds statements in unspecified untyped context i.e. any expression 'e' such
373// that `InnerMatcher` matches 'e' and 'e' is in an unspecified untyped context
374// (i.e the expression 'e' isn't evaluated to an RValue). For example, consider
375// the following code:
376// int *p = new int[4];
377// int *q = new int[4];
378// if ((p = q)) {}
379// p = q;
380// The expression `p = q` in the conditional of the `if` statement
381// `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;`
382// in the assignment statement is in an untyped context.
384 const Stmt *S, llvm::function_ref<void(const Stmt *)> InnerMatcher) {
385 // An unspecified context can be
386 // 1. A compound statement,
387 // 2. The body of an if statement
388 // 3. Body of a loop
389 if (auto *CS = dyn_cast<CompoundStmt>(S)) {
390 for (auto *Child : CS->body())
391 InnerMatcher(Child);
392 }
393 if (auto *IfS = dyn_cast<IfStmt>(S)) {
394 if (IfS->getThen())
395 InnerMatcher(IfS->getThen());
396 if (IfS->getElse())
397 InnerMatcher(IfS->getElse());
398 }
399 // FIXME: Handle loop bodies.
400}
401
402// Returns true iff integer E1 is equivalent to integer E2.
403//
404// For now we only support such expressions:
405// expr := DRE | const-value | expr BO expr
406// BO := '*' | '+'
407//
408// FIXME: We can reuse the expression comparator of the interop analysis after
409// it has been upstreamed.
410static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx);
412 const Expr *E2_LHS,
414 const Expr *E2_RHS,
415 ASTContext &Ctx) {
416 if (E1->getOpcode() == BOP) {
417 switch (BOP) {
418 // Commutative operators:
419 case BO_Mul:
420 case BO_Add:
421 return (areEqualIntegers(E1->getLHS(), E2_LHS, Ctx) &&
422 areEqualIntegers(E1->getRHS(), E2_RHS, Ctx)) ||
423 (areEqualIntegers(E1->getLHS(), E2_RHS, Ctx) &&
424 areEqualIntegers(E1->getRHS(), E2_LHS, Ctx));
425 default:
426 return false;
427 }
428 }
429 return false;
430}
431
432static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx) {
433 E1 = E1->IgnoreParenImpCasts();
434 E2 = E2->IgnoreParenImpCasts();
435 if (!E1->getType()->isIntegerType() || E1->getType() != E2->getType())
436 return false;
437
438 Expr::EvalResult ER1, ER2;
439
440 // If both are constants:
441 if (E1->EvaluateAsInt(ER1, Ctx) && E2->EvaluateAsInt(ER2, Ctx))
442 return ER1.Val.getInt() == ER2.Val.getInt();
443
444 // Otherwise, they should have identical stmt kind:
445 if (E1->getStmtClass() != E2->getStmtClass())
446 return false;
447 switch (E1->getStmtClass()) {
448 case Stmt::DeclRefExprClass:
449 return cast<DeclRefExpr>(E1)->getDecl() == cast<DeclRefExpr>(E2)->getDecl();
450 case Stmt::BinaryOperatorClass: {
451 auto BO2 = cast<BinaryOperator>(E2);
452 return areEqualIntegralBinaryOperators(cast<BinaryOperator>(E1),
453 BO2->getLHS(), BO2->getOpcode(),
454 BO2->getRHS(), Ctx);
455 }
456 default:
457 return false;
458 }
459}
460
461// Providing that `Ptr` is a pointer and `Size` is an unsigned-integral
462// expression, returns true iff they follow one of the following safe
463// patterns:
464// 1. Ptr is `DRE.data()` and Size is `DRE.size()`, where DRE is a hardened
465// container or view;
466//
467// 2. Ptr is `a` and Size is `n`, where `a` is of an array-of-T with constant
468// size `n`;
469//
470// 3. Ptr is `&var` and Size is `1`; or
471// Ptr is `std::addressof(...)` and Size is `1`;
472//
473// 4. Size is `0`;
474static bool isPtrBufferSafe(const Expr *Ptr, const Expr *Size,
475 ASTContext &Ctx) {
476 // Pattern 1:
477 if (auto *MCEPtr = dyn_cast<CXXMemberCallExpr>(Ptr->IgnoreParenImpCasts()))
478 if (auto *MCESize =
479 dyn_cast<CXXMemberCallExpr>(Size->IgnoreParenImpCasts())) {
480 auto *DREOfPtr = dyn_cast<DeclRefExpr>(
481 MCEPtr->getImplicitObjectArgument()->IgnoreParenImpCasts());
482 auto *DREOfSize = dyn_cast<DeclRefExpr>(
483 MCESize->getImplicitObjectArgument()->IgnoreParenImpCasts());
484
485 if (!DREOfPtr || !DREOfSize)
486 return false; // not in safe pattern
487 // We need to make sure 'a' is identical to 'b' for 'a.data()' and
488 // 'b.size()' otherwise we do not know they match:
489 if (DREOfPtr->getDecl() != DREOfSize->getDecl())
490 return false;
491 if (MCEPtr->getMethodDecl()->getName() != "data")
492 return false;
493 // `MCEPtr->getRecordDecl()` must be non-null as `DREOfPtr` is non-null:
494 if (!MCEPtr->getRecordDecl()->isInStdNamespace())
495 return false;
496
497 auto *ObjII = MCEPtr->getRecordDecl()->getIdentifier();
498
499 if (!ObjII)
500 return false;
501
502 bool AcceptSizeBytes = Ptr->getType()->getPointeeType()->isCharType();
503
504 if (!((AcceptSizeBytes &&
505 MCESize->getMethodDecl()->getName() == "size_bytes") ||
506 // Note here the pointer must be a pointer-to-char type unless there
507 // is explicit casting. If there is explicit casting, this branch
508 // is unreachable. Thus, at this branch "size" and "size_bytes" are
509 // equivalent as the pointer is a char pointer:
510 MCESize->getMethodDecl()->getName() == "size"))
511 return false;
512
513 return llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
514 ObjII->getName());
515 }
516
518
519 // Pattern 2-4:
520 if (Size->EvaluateAsInt(ER, Ctx)) {
521 // Pattern 2:
522 if (auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
523 if (auto *CAT = Ctx.getAsConstantArrayType(DRE->getType())) {
524 llvm::APSInt SizeInt = ER.Val.getInt();
525
526 return llvm::APSInt::compareValues(
527 SizeInt, llvm::APSInt(CAT->getSize(), true)) == 0;
528 }
529 return false;
530 }
531
532 // Pattern 3:
533 if (ER.Val.getInt().isOne()) {
534 if (auto *UO = dyn_cast<UnaryOperator>(Ptr->IgnoreParenImpCasts()))
535 return UO && UO->getOpcode() == UnaryOperator::Opcode::UO_AddrOf;
536 if (auto *CE = dyn_cast<CallExpr>(Ptr->IgnoreParenImpCasts())) {
537 auto *FnDecl = CE->getDirectCallee();
538
539 return FnDecl && FnDecl->getNameAsString() == "addressof" &&
540 FnDecl->isInStdNamespace();
541 }
542 return false;
543 }
544 // Pattern 4:
545 if (ER.Val.getInt().isZero())
546 return true;
547 }
548 return false;
549}
550
551// Given a two-param std::span construct call, matches iff the call has the
552// following forms:
553// 1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE
554// 2. `std::span<T>{new T, 1}`
555// 3. `std::span<T>{ (char *)f(args), args[N] * arg*[M]}`, where
556// `f` is a function with attribute `alloc_size(N, M)`;
557// `args` represents the list of arguments;
558// `N, M` are parameter indexes to the allocating element number and size.
559// Sometimes, there is only one parameter index representing the total
560// size.
561// 4. `std::span<T>{x.begin(), x.end()}` where `x` is an object in the
562// SIZED_CONTAINER_OR_VIEW_LIST.
563// 5. `isPtrBufferSafe` returns true for the two arguments of the span
564// constructor
566 ASTContext &Ctx) {
567 assert(Node.getNumArgs() == 2 &&
568 "expecting a two-parameter std::span constructor");
569 const Expr *Arg0 = Node.getArg(0)->IgnoreParenImpCasts();
570 const Expr *Arg1 = Node.getArg(1)->IgnoreParenImpCasts();
571 auto HaveEqualConstantValues = [&Ctx](const Expr *E0, const Expr *E1) {
572 if (auto E0CV = E0->getIntegerConstantExpr(Ctx))
573 if (auto E1CV = E1->getIntegerConstantExpr(Ctx)) {
574 return llvm::APSInt::compareValues(*E0CV, *E1CV) == 0;
575 }
576 return false;
577 };
578 auto AreSameDRE = [](const Expr *E0, const Expr *E1) {
579 if (auto *DRE0 = dyn_cast<DeclRefExpr>(E0))
580 if (auto *DRE1 = dyn_cast<DeclRefExpr>(E1)) {
581 return DRE0->getDecl() == DRE1->getDecl();
582 }
583 return false;
584 };
585 std::optional<llvm::APSInt> Arg1CV = Arg1->getIntegerConstantExpr(Ctx);
586
587 if (Arg1CV && Arg1CV->isZero())
588 // Check form 5:
589 return true;
590
591 // Check forms 1-2:
592 switch (Arg0->getStmtClass()) {
593 case Stmt::CXXNewExprClass:
594 if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize()) {
595 // Check form 1:
596 return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) ||
597 HaveEqualConstantValues(*Size, Arg1);
598 }
599 // TODO: what's placeholder type? avoid it for now.
600 if (!cast<CXXNewExpr>(Arg0)->hasPlaceholderType()) {
601 // Check form 2:
602 return Arg1CV && Arg1CV->isOne();
603 }
604 break;
605 default:
606 break;
607 }
608
609 // Check form 3:
610 if (auto CCast = dyn_cast<CStyleCastExpr>(Arg0)) {
611 if (!CCast->getType()->isPointerType())
612 return false;
613
614 QualType PteTy = CCast->getType()->getPointeeType();
615
616 if (!(PteTy->isConstantSizeType() && Ctx.getTypeSizeInChars(PteTy).isOne()))
617 return false;
618
619 if (const auto *Call = dyn_cast<CallExpr>(CCast->getSubExpr())) {
620 if (const FunctionDecl *FD = Call->getDirectCallee())
621 if (auto *AllocAttr = FD->getAttr<AllocSizeAttr>()) {
622 const Expr *EleSizeExpr =
623 Call->getArg(AllocAttr->getElemSizeParam().getASTIndex());
624 // NumElemIdx is invalid if AllocSizeAttr has 1 argument:
625 ParamIdx NumElemIdx = AllocAttr->getNumElemsParam();
626
627 if (!NumElemIdx.isValid())
628 return areEqualIntegers(Arg1, EleSizeExpr, Ctx);
629
630 const Expr *NumElesExpr = Call->getArg(NumElemIdx.getASTIndex());
631
632 if (auto BO = dyn_cast<BinaryOperator>(Arg1))
633 return areEqualIntegralBinaryOperators(BO, NumElesExpr, BO_Mul,
634 EleSizeExpr, Ctx);
635 }
636 }
637 }
638 // Check form 4:
639 auto IsMethodCallToSizedObject = [](const Stmt *Node, StringRef MethodName) {
640 if (const auto *MC = dyn_cast<CXXMemberCallExpr>(Node)) {
641 const auto *MD = MC->getMethodDecl();
642 const auto *RD = MC->getRecordDecl();
643
644 if (RD && MD)
645 if (auto *II = RD->getDeclName().getAsIdentifierInfo();
646 II && RD->isInStdNamespace())
647 return llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
648 II->getName()) &&
649 MD->getName() == MethodName;
650 }
651 return false;
652 };
653
654 if (IsMethodCallToSizedObject(Arg0, "begin") &&
655 IsMethodCallToSizedObject(Arg1, "end"))
656 return AreSameDRE(
657 // We know Arg0 and Arg1 are `CXXMemberCallExpr`s:
658 cast<CXXMemberCallExpr>(Arg0)
659 ->getImplicitObjectArgument()
660 ->IgnoreParenImpCasts(),
661 cast<CXXMemberCallExpr>(Arg1)
662 ->getImplicitObjectArgument()
663 ->IgnoreParenImpCasts());
664
665 // Check 5:
666 return isPtrBufferSafe(Arg0, Arg1, Ctx);
667}
668
670 const ASTContext &Ctx) {
671 // FIXME: Proper solution:
672 // - refactor Sema::CheckArrayAccess
673 // - split safe/OOB/unknown decision logic from diagnostics emitting code
674 // - e. g. "Try harder to find a NamedDecl to point at in the note."
675 // already duplicated
676 // - call both from Sema and from here
677
678 uint64_t limit;
679 if (const auto *CATy =
680 dyn_cast<ConstantArrayType>(Node.getBase()
681 ->IgnoreParenImpCasts()
682 ->getType()
683 ->getUnqualifiedDesugaredType())) {
684 limit = CATy->getLimitedSize();
685 } else if (const auto *SLiteral = dyn_cast<clang::StringLiteral>(
686 Node.getBase()->IgnoreParenImpCasts())) {
687 limit = SLiteral->getLength() + 1;
688 } else {
689 return false;
690 }
691
692 Expr::EvalResult EVResult;
693 const Expr *IndexExpr = Node.getIdx();
694 if (!IndexExpr->isValueDependent() &&
695 IndexExpr->EvaluateAsInt(EVResult, Ctx)) {
696 llvm::APSInt ArrIdx = EVResult.Val.getInt();
697 // FIXME: ArrIdx.isNegative() we could immediately emit an error as that's a
698 // bug
699 if (ArrIdx.isNonNegative() && ArrIdx.getLimitedValue() < limit)
700 return true;
701 } else if (const auto *BE = dyn_cast<BinaryOperator>(IndexExpr)) {
702 // For an integer expression `e` and an integer constant `n`, `e & n` and
703 // `n & e` are bounded by `n`:
704 if (BE->getOpcode() != BO_And && BE->getOpcode() != BO_Rem)
705 return false;
706
707 const Expr *LHS = BE->getLHS();
708 const Expr *RHS = BE->getRHS();
709
710 if (BE->getOpcode() == BO_Rem) {
711 // If n is a negative number, then n % const can be greater than const
712 if (!LHS->getType()->isUnsignedIntegerType()) {
713 return false;
714 }
715
716 if (!RHS->isValueDependent() && RHS->EvaluateAsInt(EVResult, Ctx)) {
717 llvm::APSInt result = EVResult.Val.getInt();
718 if (result.isNonNegative() && result.getLimitedValue() <= limit)
719 return true;
720 }
721
722 return false;
723 }
724
725 if ((!LHS->isValueDependent() &&
726 LHS->EvaluateAsInt(EVResult, Ctx)) || // case: `n & e`
727 (!RHS->isValueDependent() &&
728 RHS->EvaluateAsInt(EVResult, Ctx))) { // `e & n`
729 llvm::APSInt result = EVResult.Val.getInt();
730 if (result.isNonNegative() && result.getLimitedValue() < limit)
731 return true;
732 }
733 return false;
734 }
735 return false;
736}
737
739// Under `libc_func_matchers`, define a set of matchers that match unsafe
740// functions in libc and unsafe calls to them.
741
742// A tiny parser to strip off common prefix and suffix of libc function names
743// in real code.
744//
745// Given a function name, `matchName` returns `CoreName` according to the
746// following grammar:
747//
748// LibcName := CoreName | CoreName + "_s"
749// MatchingName := "__builtin_" + LibcName |
750// "__builtin___" + LibcName + "_chk" |
751// "__asan_" + LibcName
752//
754 StringRef matchName(StringRef FunName, bool isBuiltin) {
755 // Try to match __builtin_:
756 if (isBuiltin && FunName.starts_with("__builtin_"))
757 // Then either it is __builtin_LibcName or __builtin___LibcName_chk or
758 // no match:
760 FunName.drop_front(10 /* truncate "__builtin_" */));
761 // Try to match __asan_:
762 if (FunName.starts_with("__asan_"))
763 return matchLibcName(FunName.drop_front(7 /* truncate of "__asan_" */));
764 return matchLibcName(FunName);
765 }
766
767 // Parameter `Name` is the substring after stripping off the prefix
768 // "__builtin_".
769 StringRef matchLibcNameOrBuiltinChk(StringRef Name) {
770 if (Name.starts_with("__") && Name.ends_with("_chk"))
771 return matchLibcName(
772 Name.drop_front(2).drop_back(4) /* truncate "__" and "_chk" */);
773 return matchLibcName(Name);
774 }
775
776 StringRef matchLibcName(StringRef Name) {
777 if (Name.ends_with("_s"))
778 return Name.drop_back(2 /* truncate "_s" */);
779 return Name;
780 }
781};
782
783// A pointer type expression is known to be null-terminated, if it has the
784// form: E.c_str(), for any expression E of `std::string` type.
785static bool isNullTermPointer(const Expr *Ptr) {
786 if (isa<clang::StringLiteral>(Ptr->IgnoreParenImpCasts()))
787 return true;
788 if (isa<PredefinedExpr>(Ptr->IgnoreParenImpCasts()))
789 return true;
790 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Ptr->IgnoreParenImpCasts())) {
791 const CXXMethodDecl *MD = MCE->getMethodDecl();
792 const CXXRecordDecl *RD = MCE->getRecordDecl()->getCanonicalDecl();
793
794 if (MD && RD && RD->isInStdNamespace() && MD->getIdentifier())
795 if (MD->getName() == "c_str" && RD->getName() == "basic_string")
796 return true;
797 }
798 return false;
799}
800
801// Return true iff at least one of following cases holds:
802// 1. Format string is a literal and there is an unsafe pointer argument
803// corresponding to an `s` specifier;
804// 2. Format string is not a literal and there is least an unsafe pointer
805// argument (including the formatter argument).
806//
807// `UnsafeArg` is the output argument that will be set only if this function
808// returns true.
809static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg,
810 const unsigned FmtArgIdx, ASTContext &Ctx,
811 bool isKprintf = false) {
812 class StringFormatStringHandler
814 const CallExpr *Call;
815 unsigned FmtArgIdx;
816 const Expr *&UnsafeArg;
817 ASTContext &Ctx;
818
819 // Returns an `Expr` representing the precision if specified, null
820 // otherwise.
821 // The parameter `Call` is a printf call and the parameter `Precision` is
822 // the precision of a format specifier of the `Call`.
823 //
824 // For example, for the `printf("%d, %.10s", 10, p)` call
825 // `Precision` can be the precision of either "%d" or "%.10s". The former
826 // one will have `NotSpecified` kind.
827 const Expr *
828 getPrecisionAsExpr(const analyze_printf::OptionalAmount &Precision,
829 const CallExpr *Call) {
830 unsigned PArgIdx = -1;
831
832 if (Precision.hasDataArgument())
833 PArgIdx = Precision.getPositionalArgIndex() + FmtArgIdx;
834 if (0 < PArgIdx && PArgIdx < Call->getNumArgs()) {
835 const Expr *PArg = Call->getArg(PArgIdx);
836
837 // Strip the cast if `PArg` is a cast-to-int expression:
838 if (auto *CE = dyn_cast<CastExpr>(PArg);
839 CE && CE->getType()->isSignedIntegerType())
840 PArg = CE->getSubExpr();
841 return PArg;
842 }
843 if (Precision.getHowSpecified() ==
844 analyze_printf::OptionalAmount::HowSpecified::Constant) {
845 auto SizeTy = Ctx.getSizeType();
846 llvm::APSInt PArgVal = llvm::APSInt(
847 llvm::APInt(Ctx.getTypeSize(SizeTy), Precision.getConstantAmount()),
848 true);
849
850 return IntegerLiteral::Create(Ctx, PArgVal, Ctx.getSizeType(), {});
851 }
852 return nullptr;
853 }
854
855 public:
856 StringFormatStringHandler(const CallExpr *Call, unsigned FmtArgIdx,
857 const Expr *&UnsafeArg, ASTContext &Ctx)
858 : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg), Ctx(Ctx) {}
859
860 bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
861 const char *startSpecifier,
862 unsigned specifierLen,
863 const TargetInfo &Target) override {
864 if (FS.getConversionSpecifier().getKind() !=
865 analyze_printf::PrintfConversionSpecifier::sArg)
866 return true; // continue parsing
867
868 unsigned ArgIdx = FS.getPositionalArgIndex() + FmtArgIdx;
869
870 if (!(0 < ArgIdx && ArgIdx < Call->getNumArgs()))
871 // If the `ArgIdx` is invalid, give up.
872 return true; // continue parsing
873
874 const Expr *Arg = Call->getArg(ArgIdx);
875
876 if (isNullTermPointer(Arg))
877 // If Arg is a null-terminated pointer, it is safe anyway.
878 return true; // continue parsing
879
880 // Otherwise, check if the specifier has a precision and if the character
881 // pointer is safely bound by the precision:
882 auto LengthModifier = FS.getLengthModifier();
883 QualType ArgType = Arg->getType();
884 bool IsArgTypeValid = // Is ArgType a character pointer type?
885 ArgType->isPointerType() &&
886 (LengthModifier.getKind() == LengthModifier.AsWideChar
887 ? ArgType->getPointeeType()->isWideCharType()
888 : ArgType->getPointeeType()->isCharType());
889
890 if (auto *Precision = getPrecisionAsExpr(FS.getPrecision(), Call);
891 Precision && IsArgTypeValid)
892 if (isPtrBufferSafe(Arg, Precision, Ctx))
893 return true;
894 // Handle unsafe case:
895 UnsafeArg = Call->getArg(ArgIdx); // output
896 return false; // returning false stops parsing immediately
897 }
898 };
899
900 const Expr *Fmt = Call->getArg(FmtArgIdx);
901
902 if (auto *SL = dyn_cast<clang::StringLiteral>(Fmt->IgnoreParenImpCasts())) {
903 StringRef FmtStr;
904
905 if (SL->getCharByteWidth() == 1)
906 FmtStr = SL->getString();
907 else if (auto EvaledFmtStr = SL->tryEvaluateString(Ctx))
908 FmtStr = *EvaledFmtStr;
909 else
910 goto CHECK_UNSAFE_PTR;
911
912 StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg, Ctx);
913
915 Handler, FmtStr.begin(), FmtStr.end(), Ctx.getLangOpts(),
916 Ctx.getTargetInfo(), isKprintf);
917 }
918CHECK_UNSAFE_PTR:
919 // If format is not a string literal, we cannot analyze the format string.
920 // In this case, this call is considered unsafe if at least one argument
921 // (including the format argument) is unsafe pointer.
922 return llvm::any_of(
923 llvm::make_range(Call->arg_begin() + FmtArgIdx, Call->arg_end()),
924 [&UnsafeArg](const Expr *Arg) -> bool {
925 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg)) {
926 UnsafeArg = Arg;
927 return true;
928 }
929 return false;
930 });
931}
932
933// Matches a FunctionDecl node such that
934// 1. It's name, after stripping off predefined prefix and suffix, is
935// `CoreName`; and
936// 2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
937// is a set of libc function names.
938//
939// Note: For predefined prefix and suffix, see `LibcFunNamePrefixSuffixParser`.
940// The notation `CoreName[str/wcs]` means a new name obtained from replace
941// string "wcs" with "str" in `CoreName`.
943 static std::unique_ptr<std::set<StringRef>> PredefinedNames = nullptr;
944 if (!PredefinedNames)
945 PredefinedNames =
946 std::make_unique<std::set<StringRef>, std::set<StringRef>>({
947 // numeric conversion:
948 "atof",
949 "atoi",
950 "atol",
951 "atoll",
952 "strtol",
953 "strtoll",
954 "strtoul",
955 "strtoull",
956 "strtof",
957 "strtod",
958 "strtold",
959 "strtoimax",
960 "strtoumax",
961 // "strfromf", "strfromd", "strfroml", // C23?
962 // string manipulation:
963 "strcpy",
964 "strncpy",
965 "strlcpy",
966 "strcat",
967 "strncat",
968 "strlcat",
969 "strxfrm",
970 "strdup",
971 "strndup",
972 // string examination:
973 "strlen",
974 "strnlen",
975 "strcmp",
976 "strncmp",
977 "stricmp",
978 "strcasecmp",
979 "strcoll",
980 "strchr",
981 "strrchr",
982 "strspn",
983 "strcspn",
984 "strpbrk",
985 "strstr",
986 "strtok",
987 // "mem-" functions
988 "memchr",
989 "wmemchr",
990 "memcmp",
991 "wmemcmp",
992 "memcpy",
993 "memccpy",
994 "mempcpy",
995 "wmemcpy",
996 "memmove",
997 "wmemmove",
998 "memset",
999 "wmemset",
1000 // IO:
1001 "fread",
1002 "fwrite",
1003 "fgets",
1004 "fgetws",
1005 "gets",
1006 "fputs",
1007 "fputws",
1008 "puts",
1009 // others
1010 "strerror_s",
1011 "strerror_r",
1012 "bcopy",
1013 "bzero",
1014 "bsearch",
1015 "qsort",
1016 });
1017
1018 auto *II = Node.getIdentifier();
1019
1020 if (!II)
1021 return false;
1022
1023 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
1024 II->getName(), Node.getBuiltinID());
1025
1026 // Match predefined names:
1027 if (PredefinedNames->find(Name) != PredefinedNames->end())
1028 return true;
1029
1030 std::string NameWCS = Name.str();
1031 size_t WcsPos = NameWCS.find("wcs");
1032
1033 while (WcsPos != std::string::npos) {
1034 NameWCS[WcsPos++] = 's';
1035 NameWCS[WcsPos++] = 't';
1036 NameWCS[WcsPos++] = 'r';
1037 WcsPos = NameWCS.find("wcs", WcsPos);
1038 }
1039 if (PredefinedNames->find(NameWCS) != PredefinedNames->end())
1040 return true;
1041 // All `scanf` functions are unsafe (including `sscanf`, `vsscanf`, etc.. They
1042 // all should end with "scanf"):
1043 return Name.ends_with("scanf");
1044}
1045
1046// Match a call to one of the `v*printf` functions taking `va_list`. We cannot
1047// check safety for these functions so they should be changed to their
1048// non-va_list versions.
1050 auto *II = Node.getIdentifier();
1051
1052 if (!II)
1053 return false;
1054
1055 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
1056 II->getName(), Node.getBuiltinID());
1057
1058 if (!Name.ends_with("printf"))
1059 return false; // neither printf nor scanf
1060 return Name.starts_with("v");
1061}
1062
1063// Matches a call to one of the `sprintf` functions as they are always unsafe
1064// and should be changed to `snprintf`.
1066 auto *II = Node.getIdentifier();
1067
1068 if (!II)
1069 return false;
1070
1071 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
1072 II->getName(), Node.getBuiltinID());
1073
1074 if (!Name.ends_with("printf") ||
1075 // Let `isUnsafeVaListPrintfFunc` check for cases with va-list:
1076 Name.starts_with("v"))
1077 return false;
1078
1079 StringRef Prefix = Name.drop_back(6);
1080
1081 if (Prefix.ends_with("w"))
1082 Prefix = Prefix.drop_back(1);
1083 return Prefix == "s";
1084}
1085
1086// Match function declarations of `printf`, `fprintf`, `snprintf` and their wide
1087// character versions. Calls to these functions can be safe if their arguments
1088// are carefully made safe.
1090 auto *II = Node.getIdentifier();
1091
1092 if (!II)
1093 return false;
1094
1095 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
1096 II->getName(), Node.getBuiltinID());
1097
1098 if (!Name.ends_with("printf") || Name.starts_with("v"))
1099 return false;
1100
1101 StringRef Prefix = Name.drop_back(6);
1102
1103 if (Prefix.ends_with("w"))
1104 Prefix = Prefix.drop_back(1);
1105
1106 return Prefix.empty() || Prefix == "k" || Prefix == "f" || Prefix == "sn";
1107}
1108
1109// This matcher requires that it is known that the callee `isNormalPrintf`.
1110// Then if the format string is a string literal, this matcher matches when at
1111// least one string argument is unsafe. If the format is not a string literal,
1112// this matcher matches when at least one pointer type argument is unsafe.
1114 MatchResult &Result, llvm::StringRef Tag) {
1115 // Determine what printf it is by examining formal parameters:
1116 const FunctionDecl *FD = Node.getDirectCallee();
1117
1118 assert(FD && "It should have been checked that FD is non-null.");
1119
1120 unsigned NumParms = FD->getNumParams();
1121
1122 if (NumParms < 1)
1123 return false; // possibly some user-defined printf function
1124
1125 QualType FirstParmTy = FD->getParamDecl(0)->getType();
1126
1127 if (!FirstParmTy->isPointerType())
1128 return false; // possibly some user-defined printf function
1129
1130 QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
1131
1132 if (!Ctx.getFILEType()
1133 .isNull() && //`FILE *` must be in the context if it is fprintf
1134 FirstPteTy.getCanonicalType() == Ctx.getFILEType().getCanonicalType()) {
1135 // It is a fprintf:
1136 const Expr *UnsafeArg;
1137
1138 if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 1, Ctx, false)) {
1139 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1140 return true;
1141 }
1142 return false;
1143 }
1144
1145 if (FirstPteTy.isConstQualified()) {
1146 // If the first parameter is a `const char *`, it is a printf/kprintf:
1147 bool isKprintf = false;
1148 const Expr *UnsafeArg;
1149
1150 if (auto *II = FD->getIdentifier())
1151 isKprintf = II->getName() == "kprintf";
1152 if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 0, Ctx, isKprintf)) {
1153 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1154 return true;
1155 }
1156 return false;
1157 }
1158
1159 if (NumParms > 2) {
1160 QualType SecondParmTy = FD->getParamDecl(1)->getType();
1161
1162 if (!FirstPteTy.isConstQualified() && SecondParmTy->isIntegerType()) {
1163 // If the first parameter type is non-const qualified `char *` and the
1164 // second is an integer, it is a snprintf:
1165 const Expr *UnsafeArg;
1166
1167 if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 2, Ctx, false)) {
1168 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1169 return true;
1170 }
1171 return false;
1172 }
1173 }
1174 // We don't really recognize this "normal" printf, the only thing we
1175 // can do is to require all pointers to be null-terminated:
1176 for (const auto *Arg : Node.arguments())
1177 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg)) {
1178 Result.addNode(Tag, DynTypedNode::create(*Arg));
1179 return true;
1180 }
1181 return false;
1182}
1183
1184// This function requires that it is known that the callee `isNormalPrintf`.
1185// It returns true iff the first two arguments of the call is a pointer
1186// `Ptr` and an unsigned integer `Size` and they are NOT safe, i.e.,
1187// `!isPtrBufferSafe(Ptr, Size)`.
1189 const FunctionDecl *FD = Node.getDirectCallee();
1190
1191 assert(FD && "It should have been checked that FD is non-null.");
1192
1193 if (FD->getNumParams() < 3)
1194 return false; // Not an snprint
1195
1196 QualType FirstParmTy = FD->getParamDecl(0)->getType();
1197
1198 if (!FirstParmTy->isPointerType())
1199 return false; // Not an snprint
1200
1201 QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
1202 const Expr *Buf = Node.getArg(0), *Size = Node.getArg(1);
1203
1204 if (FirstPteTy.isConstQualified() || !FirstPteTy->isAnyCharacterType() ||
1205 !Buf->getType()->isPointerType() ||
1206 !Size->getType()->isUnsignedIntegerType())
1207 return false; // not an snprintf call
1208
1209 return !isPtrBufferSafe(Buf, Size, Ctx);
1210}
1211} // namespace libc_func_matchers
1212
1213namespace {
1214// Because the analysis revolves around variables and their types, we'll need to
1215// track uses of variables (aka DeclRefExprs).
1216using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
1217
1218// Convenience typedef.
1219using FixItList = SmallVector<FixItHint, 4>;
1220} // namespace
1221
1222namespace {
1223/// Gadget is an individual operation in the code that may be of interest to
1224/// this analysis. Each (non-abstract) subclass corresponds to a specific
1225/// rigid AST structure that constitutes an operation on a pointer-type object.
1226/// Discovery of a gadget in the code corresponds to claiming that we understand
1227/// what this part of code is doing well enough to potentially improve it.
1228/// Gadgets can be warning (immediately deserving a warning) or fixable (not
1229/// always deserving a warning per se, but requires our attention to identify
1230/// it warrants a fixit).
1231class Gadget {
1232public:
1233 enum class Kind {
1234#define GADGET(x) x,
1235#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1236 };
1237
1238 Gadget(Kind K) : K(K) {}
1239
1240 Kind getKind() const { return K; }
1241
1242#ifndef NDEBUG
1243 StringRef getDebugName() const {
1244 switch (K) {
1245#define GADGET(x) \
1246 case Kind::x: \
1247 return #x;
1248#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1249 }
1250 llvm_unreachable("Unhandled Gadget::Kind enum");
1251 }
1252#endif
1253
1254 virtual bool isWarningGadget() const = 0;
1255 // TODO remove this method from WarningGadget interface. It's only used for
1256 // debug prints in FixableGadget.
1257 virtual SourceLocation getSourceLoc() const = 0;
1258
1259 /// Returns the list of pointer-type variables on which this gadget performs
1260 /// its operation. Typically, there's only one variable. This isn't a list
1261 /// of all DeclRefExprs in the gadget's AST!
1262 virtual DeclUseList getClaimedVarUseSites() const = 0;
1263
1264 virtual ~Gadget() = default;
1265
1266private:
1267 Kind K;
1268};
1269
1270/// Warning gadgets correspond to unsafe code patterns that warrants
1271/// an immediate warning.
1272class WarningGadget : public Gadget {
1273public:
1274 WarningGadget(Kind K) : Gadget(K) {}
1275
1276 static bool classof(const Gadget *G) { return G->isWarningGadget(); }
1277 bool isWarningGadget() const final { return true; }
1278
1279 virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1280 bool IsRelatedToDecl,
1281 ASTContext &Ctx) const = 0;
1282
1283 virtual SmallVector<const Expr *, 1> getUnsafePtrs() const = 0;
1284};
1285
1286/// Fixable gadgets correspond to code patterns that aren't always unsafe but
1287/// need to be properly recognized in order to emit fixes. For example, if a raw
1288/// pointer-type variable is replaced by a safe C++ container, every use of such
1289/// variable must be carefully considered and possibly updated.
1290class FixableGadget : public Gadget {
1291public:
1292 FixableGadget(Kind K) : Gadget(K) {}
1293
1294 static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
1295 bool isWarningGadget() const final { return false; }
1296
1297 /// Returns a fixit that would fix the current gadget according to
1298 /// the current strategy. Returns std::nullopt if the fix cannot be produced;
1299 /// returns an empty list if no fixes are necessary.
1300 virtual std::optional<FixItList> getFixits(const FixitStrategy &) const {
1301 return std::nullopt;
1302 }
1303
1304 /// Returns a list of two elements where the first element is the LHS of a
1305 /// pointer assignment statement and the second element is the RHS. This
1306 /// two-element list represents the fact that the LHS buffer gets its bounds
1307 /// information from the RHS buffer. This information will be used later to
1308 /// group all those variables whose types must be modified together to prevent
1309 /// type mismatches.
1310 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1311 getStrategyImplications() const {
1312 return std::nullopt;
1313 }
1314};
1315
1316static bool isSupportedVariable(const DeclRefExpr &Node) {
1317 const Decl *D = Node.getDecl();
1318 return D != nullptr && isa<VarDecl>(D);
1319}
1320
1321using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
1322using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
1323
1324/// An increment of a pointer-type value is unsafe as it may run the pointer
1325/// out of bounds.
1326class IncrementGadget : public WarningGadget {
1327 static constexpr const char *const OpTag = "op";
1328 const UnaryOperator *Op;
1329
1330public:
1331 IncrementGadget(const MatchResult &Result)
1332 : WarningGadget(Kind::Increment),
1333 Op(Result.getNodeAs<UnaryOperator>(OpTag)) {}
1334
1335 static bool classof(const Gadget *G) {
1336 return G->getKind() == Kind::Increment;
1337 }
1338
1339 static bool matches(const Stmt *S, const ASTContext &Ctx,
1340 MatchResult &Result) {
1341 const auto *UO = dyn_cast<UnaryOperator>(S);
1342 if (!UO || !UO->isIncrementOp())
1343 return false;
1345 return false;
1346 Result.addNode(OpTag, DynTypedNode::create(*UO));
1347 return true;
1348 }
1349
1350 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1351 bool IsRelatedToDecl,
1352 ASTContext &Ctx) const override {
1353 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1354 }
1355 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1356
1357 DeclUseList getClaimedVarUseSites() const override {
1359 if (const auto *DRE =
1360 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1361 Uses.push_back(DRE);
1362 }
1363
1364 return std::move(Uses);
1365 }
1366
1367 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1368 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1369 }
1370};
1371
1372/// A decrement of a pointer-type value is unsafe as it may run the pointer
1373/// out of bounds.
1374class DecrementGadget : public WarningGadget {
1375 static constexpr const char *const OpTag = "op";
1376 const UnaryOperator *Op;
1377
1378public:
1379 DecrementGadget(const MatchResult &Result)
1380 : WarningGadget(Kind::Decrement),
1381 Op(Result.getNodeAs<UnaryOperator>(OpTag)) {}
1382
1383 static bool classof(const Gadget *G) {
1384 return G->getKind() == Kind::Decrement;
1385 }
1386
1387 static bool matches(const Stmt *S, const ASTContext &Ctx,
1388 MatchResult &Result) {
1389 const auto *UO = dyn_cast<UnaryOperator>(S);
1390 if (!UO || !UO->isDecrementOp())
1391 return false;
1393 return false;
1394 Result.addNode(OpTag, DynTypedNode::create(*UO));
1395 return true;
1396 }
1397
1398 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1399 bool IsRelatedToDecl,
1400 ASTContext &Ctx) const override {
1401 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1402 }
1403 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1404
1405 DeclUseList getClaimedVarUseSites() const override {
1406 if (const auto *DRE =
1407 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1408 return {DRE};
1409 }
1410
1411 return {};
1412 }
1413
1414 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1415 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1416 }
1417};
1418
1419/// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
1420/// it doesn't have any bounds checks for the array.
1421class ArraySubscriptGadget : public WarningGadget {
1422 static constexpr const char *const ArraySubscrTag = "ArraySubscript";
1423 const ArraySubscriptExpr *ASE;
1424
1425public:
1426 ArraySubscriptGadget(const MatchResult &Result)
1427 : WarningGadget(Kind::ArraySubscript),
1428 ASE(Result.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
1429
1430 static bool classof(const Gadget *G) {
1431 return G->getKind() == Kind::ArraySubscript;
1432 }
1433
1434 static bool matches(const Stmt *S, const ASTContext &Ctx,
1435 MatchResult &Result) {
1436 const auto *ASE = dyn_cast<ArraySubscriptExpr>(S);
1437 if (!ASE)
1438 return false;
1439 const auto *const Base = ASE->getBase()->IgnoreParenImpCasts();
1440 if (!hasPointerType(*Base) && !hasArrayType(*Base))
1441 return false;
1442 const auto *Idx = dyn_cast<IntegerLiteral>(ASE->getIdx());
1443 bool IsSafeIndex = (Idx && Idx->getValue().isZero()) ||
1444 isa<ArrayInitIndexExpr>(ASE->getIdx());
1445 if (IsSafeIndex || isSafeArraySubscript(*ASE, Ctx))
1446 return false;
1447 Result.addNode(ArraySubscrTag, DynTypedNode::create(*ASE));
1448 return true;
1449 }
1450
1451 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1452 bool IsRelatedToDecl,
1453 ASTContext &Ctx) const override {
1454 Handler.handleUnsafeOperation(ASE, IsRelatedToDecl, Ctx);
1455 }
1456 SourceLocation getSourceLoc() const override { return ASE->getBeginLoc(); }
1457
1458 DeclUseList getClaimedVarUseSites() const override {
1459 if (const auto *DRE =
1460 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
1461 return {DRE};
1462 }
1463
1464 return {};
1465 }
1466
1467 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1468 return {ASE->getBase()->IgnoreParenImpCasts()};
1469 }
1470};
1471
1472/// A pointer arithmetic expression of one of the forms:
1473/// \code
1474/// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
1475/// \endcode
1476class PointerArithmeticGadget : public WarningGadget {
1477 static constexpr const char *const PointerArithmeticTag = "ptrAdd";
1478 static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
1479 const BinaryOperator *PA; // pointer arithmetic expression
1480 const Expr *Ptr; // the pointer expression in `PA`
1481
1482public:
1483 PointerArithmeticGadget(const MatchResult &Result)
1484 : WarningGadget(Kind::PointerArithmetic),
1485 PA(Result.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
1486 Ptr(Result.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
1487
1488 static bool classof(const Gadget *G) {
1489 return G->getKind() == Kind::PointerArithmetic;
1490 }
1491
1492 static bool matches(const Stmt *S, const ASTContext &Ctx,
1493 MatchResult &Result) {
1494 const auto *BO = dyn_cast<BinaryOperator>(S);
1495 if (!BO)
1496 return false;
1497 const auto *LHS = BO->getLHS();
1498 const auto *RHS = BO->getRHS();
1499 // ptr at left
1500 if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub ||
1501 BO->getOpcode() == BO_AddAssign || BO->getOpcode() == BO_SubAssign) {
1502 if (hasPointerType(*LHS) && (RHS->getType()->isIntegerType() ||
1503 RHS->getType()->isEnumeralType())) {
1504 Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*LHS));
1505 Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
1506 return true;
1507 }
1508 }
1509 // ptr at right
1510 if (BO->getOpcode() == BO_Add && hasPointerType(*RHS) &&
1511 (LHS->getType()->isIntegerType() || LHS->getType()->isEnumeralType())) {
1512 Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*RHS));
1513 Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
1514 return true;
1515 }
1516 return false;
1517 }
1518
1519 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1520 bool IsRelatedToDecl,
1521 ASTContext &Ctx) const override {
1522 Handler.handleUnsafeOperation(PA, IsRelatedToDecl, Ctx);
1523 }
1524 SourceLocation getSourceLoc() const override { return PA->getBeginLoc(); }
1525
1526 DeclUseList getClaimedVarUseSites() const override {
1527 if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
1528 return {DRE};
1529 }
1530
1531 return {};
1532 }
1533
1534 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1535 return {Ptr->IgnoreParenImpCasts()};
1536 }
1537
1538 // FIXME: pointer adding zero should be fine
1539 // FIXME: this gadge will need a fix-it
1540};
1541
1542class SpanTwoParamConstructorGadget : public WarningGadget {
1543 static constexpr const char *const SpanTwoParamConstructorTag =
1544 "spanTwoParamConstructor";
1545 const CXXConstructExpr *Ctor; // the span constructor expression
1546
1547public:
1548 SpanTwoParamConstructorGadget(const MatchResult &Result)
1549 : WarningGadget(Kind::SpanTwoParamConstructor),
1550 Ctor(Result.getNodeAs<CXXConstructExpr>(SpanTwoParamConstructorTag)) {}
1551
1552 static bool classof(const Gadget *G) {
1553 return G->getKind() == Kind::SpanTwoParamConstructor;
1554 }
1555
1556 static bool matches(const Stmt *S, ASTContext &Ctx, MatchResult &Result) {
1557 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1558 if (!CE)
1559 return false;
1560 const auto *CDecl = CE->getConstructor();
1561 const auto *CRecordDecl = CDecl->getParent();
1562 auto HasTwoParamSpanCtorDecl =
1563 CRecordDecl->isInStdNamespace() &&
1564 CDecl->getDeclName().getAsString() == "span" && CE->getNumArgs() == 2;
1565 if (!HasTwoParamSpanCtorDecl || isSafeSpanTwoParamConstruct(*CE, Ctx))
1566 return false;
1567 Result.addNode(SpanTwoParamConstructorTag, DynTypedNode::create(*CE));
1568 return true;
1569 }
1570
1571 static bool matches(const Stmt *S, ASTContext &Ctx,
1572 const UnsafeBufferUsageHandler *Handler,
1573 MatchResult &Result) {
1574 if (ignoreUnsafeBufferInContainer(*S, Handler))
1575 return false;
1576 return matches(S, Ctx, Result);
1577 }
1578
1579 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1580 bool IsRelatedToDecl,
1581 ASTContext &Ctx) const override {
1582 Handler.handleUnsafeOperationInContainer(Ctor, IsRelatedToDecl, Ctx);
1583 }
1584 SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); }
1585
1586 DeclUseList getClaimedVarUseSites() const override {
1587 // If the constructor call is of the form `std::span{var, n}`, `var` is
1588 // considered an unsafe variable.
1589 if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1590 if (isa<VarDecl>(DRE->getDecl()))
1591 return {DRE};
1592 }
1593 return {};
1594 }
1595
1596 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1597};
1598
1599/// A pointer initialization expression of the form:
1600/// \code
1601/// int *p = q;
1602/// \endcode
1603class PointerInitGadget : public FixableGadget {
1604private:
1605 static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
1606 static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
1607 const VarDecl *PtrInitLHS; // the LHS pointer expression in `PI`
1608 const DeclRefExpr *PtrInitRHS; // the RHS pointer expression in `PI`
1609
1610public:
1611 PointerInitGadget(const MatchResult &Result)
1612 : FixableGadget(Kind::PointerInit),
1613 PtrInitLHS(Result.getNodeAs<VarDecl>(PointerInitLHSTag)),
1614 PtrInitRHS(Result.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
1615
1616 static bool classof(const Gadget *G) {
1617 return G->getKind() == Kind::PointerInit;
1618 }
1619
1620 static bool matches(const Stmt *S,
1622 const DeclStmt *DS = dyn_cast<DeclStmt>(S);
1623 if (!DS || !DS->isSingleDecl())
1624 return false;
1625 const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
1626 if (!VD)
1627 return false;
1628 const Expr *Init = VD->getAnyInitializer();
1629 if (!Init)
1630 return false;
1631 const auto *DRE = dyn_cast<DeclRefExpr>(Init->IgnoreImpCasts());
1632 if (!DRE || !hasPointerType(*DRE) || !isSupportedVariable(*DRE)) {
1633 return false;
1634 }
1635 MatchResult R;
1636 R.addNode(PointerInitLHSTag, DynTypedNode::create(*VD));
1637 R.addNode(PointerInitRHSTag, DynTypedNode::create(*DRE));
1638 Results.emplace_back(std::move(R));
1639 return true;
1640 }
1641
1642 virtual std::optional<FixItList>
1643 getFixits(const FixitStrategy &S) const override;
1644 SourceLocation getSourceLoc() const override {
1645 return PtrInitRHS->getBeginLoc();
1646 }
1647
1648 virtual DeclUseList getClaimedVarUseSites() const override {
1649 return DeclUseList{PtrInitRHS};
1650 }
1651
1652 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1653 getStrategyImplications() const override {
1654 return std::make_pair(PtrInitLHS, cast<VarDecl>(PtrInitRHS->getDecl()));
1655 }
1656};
1657
1658/// A pointer assignment expression of the form:
1659/// \code
1660/// p = q;
1661/// \endcode
1662/// where both `p` and `q` are pointers.
1663class PtrToPtrAssignmentGadget : public FixableGadget {
1664private:
1665 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
1666 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
1667 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
1668 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
1669
1670public:
1671 PtrToPtrAssignmentGadget(const MatchResult &Result)
1672 : FixableGadget(Kind::PtrToPtrAssignment),
1673 PtrLHS(Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
1674 PtrRHS(Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
1675
1676 static bool classof(const Gadget *G) {
1677 return G->getKind() == Kind::PtrToPtrAssignment;
1678 }
1679
1680 static bool matches(const Stmt *S,
1682 size_t SizeBefore = Results.size();
1683 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
1684 const auto *BO = dyn_cast<BinaryOperator>(S);
1685 if (!BO || BO->getOpcode() != BO_Assign)
1686 return;
1687 const auto *RHS = BO->getRHS()->IgnoreParenImpCasts();
1688 if (const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
1689 !RHSRef || !hasPointerType(*RHSRef) ||
1690 !isSupportedVariable(*RHSRef)) {
1691 return;
1692 }
1693 const auto *LHS = BO->getLHS();
1694 if (const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
1695 !LHSRef || !hasPointerType(*LHSRef) ||
1696 !isSupportedVariable(*LHSRef)) {
1697 return;
1698 }
1699 MatchResult R;
1700 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
1701 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
1702 Results.emplace_back(std::move(R));
1703 });
1704 return SizeBefore != Results.size();
1705 }
1706
1707 virtual std::optional<FixItList>
1708 getFixits(const FixitStrategy &S) const override;
1709 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
1710
1711 virtual DeclUseList getClaimedVarUseSites() const override {
1712 return DeclUseList{PtrLHS, PtrRHS};
1713 }
1714
1715 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1716 getStrategyImplications() const override {
1717 return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
1718 cast<VarDecl>(PtrRHS->getDecl()));
1719 }
1720};
1721
1722/// An assignment expression of the form:
1723/// \code
1724/// ptr = array;
1725/// \endcode
1726/// where `p` is a pointer and `array` is a constant size array.
1727class CArrayToPtrAssignmentGadget : public FixableGadget {
1728private:
1729 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
1730 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
1731 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
1732 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
1733
1734public:
1735 CArrayToPtrAssignmentGadget(const MatchResult &Result)
1736 : FixableGadget(Kind::CArrayToPtrAssignment),
1737 PtrLHS(Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
1738 PtrRHS(Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
1739
1740 static bool classof(const Gadget *G) {
1741 return G->getKind() == Kind::CArrayToPtrAssignment;
1742 }
1743
1744 static bool matches(const Stmt *S,
1746 size_t SizeBefore = Results.size();
1747 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
1748 const auto *BO = dyn_cast<BinaryOperator>(S);
1749 if (!BO || BO->getOpcode() != BO_Assign)
1750 return;
1751 const auto *RHS = BO->getRHS()->IgnoreParenImpCasts();
1752 if (const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
1753 !RHSRef ||
1754 !isa<ConstantArrayType>(RHSRef->getType().getCanonicalType()) ||
1755 !isSupportedVariable(*RHSRef)) {
1756 return;
1757 }
1758 const auto *LHS = BO->getLHS();
1759 if (const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
1760 !LHSRef || !hasPointerType(*LHSRef) ||
1761 !isSupportedVariable(*LHSRef)) {
1762 return;
1763 }
1764 MatchResult R;
1765 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
1766 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
1767 Results.emplace_back(std::move(R));
1768 });
1769 return SizeBefore != Results.size();
1770 }
1771
1772 virtual std::optional<FixItList>
1773 getFixits(const FixitStrategy &S) const override;
1774 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
1775
1776 virtual DeclUseList getClaimedVarUseSites() const override {
1777 return DeclUseList{PtrLHS, PtrRHS};
1778 }
1779
1780 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1781 getStrategyImplications() const override {
1782 return {};
1783 }
1784};
1785
1786/// A call of a function or method that performs unchecked buffer operations
1787/// over one of its pointer parameters.
1788class UnsafeBufferUsageAttrGadget : public WarningGadget {
1789 constexpr static const char *const OpTag = "attr_expr";
1790 const Expr *Op;
1791
1792public:
1793 UnsafeBufferUsageAttrGadget(const MatchResult &Result)
1794 : WarningGadget(Kind::UnsafeBufferUsageAttr),
1795 Op(Result.getNodeAs<Expr>(OpTag)) {}
1796
1797 static bool classof(const Gadget *G) {
1798 return G->getKind() == Kind::UnsafeBufferUsageAttr;
1799 }
1800
1801 static bool matches(const Stmt *S, const ASTContext &Ctx,
1802 MatchResult &Result) {
1803 if (auto *CE = dyn_cast<CallExpr>(S)) {
1804 if (CE->getDirectCallee() &&
1805 CE->getDirectCallee()->hasAttr<UnsafeBufferUsageAttr>()) {
1806 Result.addNode(OpTag, DynTypedNode::create(*CE));
1807 return true;
1808 }
1809 }
1810 if (auto *ME = dyn_cast<MemberExpr>(S)) {
1811 if (!isa<FieldDecl>(ME->getMemberDecl()))
1812 return false;
1813 if (ME->getMemberDecl()->hasAttr<UnsafeBufferUsageAttr>()) {
1814 Result.addNode(OpTag, DynTypedNode::create(*ME));
1815 return true;
1816 }
1817 }
1818 return false;
1819 }
1820
1821 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1822 bool IsRelatedToDecl,
1823 ASTContext &Ctx) const override {
1824 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1825 }
1826 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1827
1828 DeclUseList getClaimedVarUseSites() const override { return {}; }
1829
1830 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1831};
1832
1833/// A call of a constructor that performs unchecked buffer operations
1834/// over one of its pointer parameters, or constructs a class object that will
1835/// perform buffer operations that depend on the correctness of the parameters.
1836class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
1837 constexpr static const char *const OpTag = "cxx_construct_expr";
1838 const CXXConstructExpr *Op;
1839
1840public:
1841 UnsafeBufferUsageCtorAttrGadget(const MatchResult &Result)
1842 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
1843 Op(Result.getNodeAs<CXXConstructExpr>(OpTag)) {}
1844
1845 static bool classof(const Gadget *G) {
1846 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
1847 }
1848
1849 static bool matches(const Stmt *S, ASTContext &Ctx, MatchResult &Result) {
1850 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1851 if (!CE || !CE->getConstructor()->hasAttr<UnsafeBufferUsageAttr>())
1852 return false;
1853 // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget.
1854 MatchResult Tmp;
1855 if (SpanTwoParamConstructorGadget::matches(CE, Ctx, Tmp))
1856 return false;
1857 Result.addNode(OpTag, DynTypedNode::create(*CE));
1858 return true;
1859 }
1860
1861 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1862 bool IsRelatedToDecl,
1863 ASTContext &Ctx) const override {
1864 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1865 }
1866 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1867
1868 DeclUseList getClaimedVarUseSites() const override { return {}; }
1869
1870 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1871};
1872
1873// Warning gadget for unsafe invocation of span::data method.
1874// Triggers when the pointer returned by the invocation is immediately
1875// cast to a larger type.
1876
1877class DataInvocationGadget : public WarningGadget {
1878 constexpr static const char *const OpTag = "data_invocation_expr";
1879 const ExplicitCastExpr *Op;
1880
1881public:
1882 DataInvocationGadget(const MatchResult &Result)
1883 : WarningGadget(Kind::DataInvocation),
1884 Op(Result.getNodeAs<ExplicitCastExpr>(OpTag)) {}
1885
1886 static bool classof(const Gadget *G) {
1887 return G->getKind() == Kind::DataInvocation;
1888 }
1889
1890 static bool matches(const Stmt *S, const ASTContext &Ctx,
1891 MatchResult &Result) {
1892 auto *CE = dyn_cast<ExplicitCastExpr>(S);
1893 if (!CE)
1894 return false;
1895 for (auto *Child : CE->children()) {
1896 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Child);
1897 MCE && isDataFunction(MCE)) {
1898 Result.addNode(OpTag, DynTypedNode::create(*CE));
1899 return true;
1900 }
1901 if (auto *Paren = dyn_cast<ParenExpr>(Child)) {
1902 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Paren->getSubExpr());
1903 MCE && isDataFunction(MCE)) {
1904 Result.addNode(OpTag, DynTypedNode::create(*CE));
1905 return true;
1906 }
1907 }
1908 }
1909 return false;
1910 }
1911
1912 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1913 bool IsRelatedToDecl,
1914 ASTContext &Ctx) const override {
1915 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1916 }
1917 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1918
1919 DeclUseList getClaimedVarUseSites() const override { return {}; }
1920
1921private:
1922 static bool isDataFunction(const CXXMemberCallExpr *call) {
1923 if (!call)
1924 return false;
1925 auto *callee = call->getDirectCallee();
1926 if (!callee || !isa<CXXMethodDecl>(callee))
1927 return false;
1928 auto *method = cast<CXXMethodDecl>(callee);
1929 if (method->getNameAsString() == "data" &&
1930 method->getParent()->isInStdNamespace() &&
1931 llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
1932 method->getParent()->getName()))
1933 return true;
1934 return false;
1935 }
1936
1937 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1938};
1939
1940class UnsafeLibcFunctionCallGadget : public WarningGadget {
1941 const CallExpr *const Call;
1942 const Expr *UnsafeArg = nullptr;
1943 constexpr static const char *const Tag = "UnsafeLibcFunctionCall";
1944 // Extra tags for additional information:
1945 constexpr static const char *const UnsafeSprintfTag =
1946 "UnsafeLibcFunctionCall_sprintf";
1947 constexpr static const char *const UnsafeSizedByTag =
1948 "UnsafeLibcFunctionCall_sized_by";
1949 constexpr static const char *const UnsafeStringTag =
1950 "UnsafeLibcFunctionCall_string";
1951 constexpr static const char *const UnsafeVaListTag =
1952 "UnsafeLibcFunctionCall_va_list";
1953
1954 enum UnsafeKind {
1955 OTHERS = 0, // no specific information, the callee function is unsafe
1956 SPRINTF = 1, // never call `-sprintf`s, call `-snprintf`s instead.
1957 SIZED_BY =
1958 2, // the first two arguments of `snprintf` function have
1959 // "__sized_by" relation but they do not conform to safe patterns
1960 STRING = 3, // an argument is a pointer-to-char-as-string but does not
1961 // guarantee null-termination
1962 VA_LIST = 4, // one of the `-printf`s function that take va_list, which is
1963 // considered unsafe as it is not compile-time check
1964 } WarnedFunKind = OTHERS;
1965
1966public:
1967 UnsafeLibcFunctionCallGadget(const MatchResult &Result)
1968 : WarningGadget(Kind::UnsafeLibcFunctionCall),
1969 Call(Result.getNodeAs<CallExpr>(Tag)) {
1970 if (Result.getNodeAs<Decl>(UnsafeSprintfTag))
1971 WarnedFunKind = SPRINTF;
1972 else if (auto *E = Result.getNodeAs<Expr>(UnsafeStringTag)) {
1973 WarnedFunKind = STRING;
1974 UnsafeArg = E;
1975 } else if (Result.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
1976 WarnedFunKind = SIZED_BY;
1977 UnsafeArg = Call->getArg(0);
1978 } else if (Result.getNodeAs<Decl>(UnsafeVaListTag))
1979 WarnedFunKind = VA_LIST;
1980 }
1981
1982 static bool matches(const Stmt *S, ASTContext &Ctx,
1983 const UnsafeBufferUsageHandler *Handler,
1984 MatchResult &Result) {
1985 if (ignoreUnsafeLibcCall(Ctx, *S, Handler))
1986 return false;
1987 auto *CE = dyn_cast<CallExpr>(S);
1988 if (!CE || !CE->getDirectCallee())
1989 return false;
1990 const auto *FD = dyn_cast<FunctionDecl>(CE->getDirectCallee());
1991 if (!FD)
1992 return false;
1993
1994 bool IsGlobalAndNotInAnyNamespace =
1995 FD->isGlobal() && !FD->getEnclosingNamespaceContext()->isNamespace();
1996
1997 // A libc function must either be in the std:: namespace or a global
1998 // function that is not in any namespace:
1999 if (!FD->isInStdNamespace() && !IsGlobalAndNotInAnyNamespace)
2000 return false;
2001 auto isSingleStringLiteralArg = false;
2002 if (CE->getNumArgs() == 1) {
2003 isSingleStringLiteralArg =
2004 isa<clang::StringLiteral>(CE->getArg(0)->IgnoreParenImpCasts());
2005 }
2006 if (!isSingleStringLiteralArg) {
2007 // (unless the call has a sole string literal argument):
2009 Result.addNode(Tag, DynTypedNode::create(*CE));
2010 return true;
2011 }
2013 Result.addNode(Tag, DynTypedNode::create(*CE));
2014 Result.addNode(UnsafeVaListTag, DynTypedNode::create(*FD));
2015 return true;
2016 }
2018 Result.addNode(Tag, DynTypedNode::create(*CE));
2019 Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*FD));
2020 return true;
2021 }
2022 }
2025 Result.addNode(Tag, DynTypedNode::create(*CE));
2026 Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*CE));
2027 return true;
2028 }
2030 UnsafeStringTag)) {
2031 Result.addNode(Tag, DynTypedNode::create(*CE));
2032 return true;
2033 }
2034 }
2035 return false;
2036 }
2037
2038 const Stmt *getBaseStmt() const { return Call; }
2039
2040 SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
2041
2042 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2043 bool IsRelatedToDecl,
2044 ASTContext &Ctx) const override {
2045 Handler.handleUnsafeLibcCall(Call, WarnedFunKind, Ctx, UnsafeArg);
2046 }
2047
2048 DeclUseList getClaimedVarUseSites() const override { return {}; }
2049
2050 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2051};
2052
2053// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
2054// Context (see `findStmtsInUnspecifiedLvalueContext`).
2055// Note here `[]` is the built-in subscript operator.
2056class ULCArraySubscriptGadget : public FixableGadget {
2057private:
2058 static constexpr const char *const ULCArraySubscriptTag =
2059 "ArraySubscriptUnderULC";
2060 const ArraySubscriptExpr *Node;
2061
2062public:
2063 ULCArraySubscriptGadget(const MatchResult &Result)
2064 : FixableGadget(Kind::ULCArraySubscript),
2065 Node(Result.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
2066 assert(Node != nullptr && "Expecting a non-null matching result");
2067 }
2068
2069 static bool classof(const Gadget *G) {
2070 return G->getKind() == Kind::ULCArraySubscript;
2071 }
2072
2073 static bool matches(const Stmt *S,
2075 size_t SizeBefore = Results.size();
2076 findStmtsInUnspecifiedLvalueContext(S, [&Results](const Expr *E) {
2077 const auto *ASE = dyn_cast<ArraySubscriptExpr>(E);
2078 if (!ASE)
2079 return;
2080 const auto *DRE =
2081 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts());
2082 if (!DRE || !(hasPointerType(*DRE) || hasArrayType(*DRE)) ||
2083 !isSupportedVariable(*DRE))
2084 return;
2085 MatchResult R;
2086 R.addNode(ULCArraySubscriptTag, DynTypedNode::create(*ASE));
2087 Results.emplace_back(std::move(R));
2088 });
2089 return SizeBefore != Results.size();
2090 }
2091
2092 virtual std::optional<FixItList>
2093 getFixits(const FixitStrategy &S) const override;
2094 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2095
2096 virtual DeclUseList getClaimedVarUseSites() const override {
2097 if (const auto *DRE =
2098 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
2099 return {DRE};
2100 }
2101 return {};
2102 }
2103};
2104
2105// Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
2106// unspecified pointer context (findStmtsInUnspecifiedPointerContext). The
2107// gadget emits fixit of the form `UPC(DRE.data())`.
2108class UPCStandalonePointerGadget : public FixableGadget {
2109private:
2110 static constexpr const char *const DeclRefExprTag = "StandalonePointer";
2111 const DeclRefExpr *Node;
2112
2113public:
2114 UPCStandalonePointerGadget(const MatchResult &Result)
2115 : FixableGadget(Kind::UPCStandalonePointer),
2116 Node(Result.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
2117 assert(Node != nullptr && "Expecting a non-null matching result");
2118 }
2119
2120 static bool classof(const Gadget *G) {
2121 return G->getKind() == Kind::UPCStandalonePointer;
2122 }
2123
2124 static bool matches(const Stmt *S,
2126 size_t SizeBefore = Results.size();
2127 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2128 auto *E = dyn_cast<Expr>(S);
2129 if (!E)
2130 return;
2131 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
2132 if (!DRE || (!hasPointerType(*DRE) && !hasArrayType(*DRE)) ||
2133 !isSupportedVariable(*DRE))
2134 return;
2135 MatchResult R;
2136 R.addNode(DeclRefExprTag, DynTypedNode::create(*DRE));
2137 Results.emplace_back(std::move(R));
2138 });
2139 return SizeBefore != Results.size();
2140 }
2141
2142 virtual std::optional<FixItList>
2143 getFixits(const FixitStrategy &S) const override;
2144 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2145
2146 virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }
2147};
2148
2149class PointerDereferenceGadget : public FixableGadget {
2150 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
2151 static constexpr const char *const OperatorTag = "op";
2152
2153 const DeclRefExpr *BaseDeclRefExpr = nullptr;
2154 const UnaryOperator *Op = nullptr;
2155
2156public:
2157 PointerDereferenceGadget(const MatchResult &Result)
2158 : FixableGadget(Kind::PointerDereference),
2159 BaseDeclRefExpr(Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2160 Op(Result.getNodeAs<UnaryOperator>(OperatorTag)) {}
2161
2162 static bool classof(const Gadget *G) {
2163 return G->getKind() == Kind::PointerDereference;
2164 }
2165
2166 static bool matches(const Stmt *S,
2168 size_t SizeBefore = Results.size();
2169 findStmtsInUnspecifiedLvalueContext(S, [&Results](const Stmt *S) {
2170 const auto *UO = dyn_cast<UnaryOperator>(S);
2171 if (!UO || UO->getOpcode() != UO_Deref)
2172 return;
2173 const auto *CE = dyn_cast<Expr>(UO->getSubExpr());
2174 if (!CE)
2175 return;
2176 CE = CE->IgnoreParenImpCasts();
2177 const auto *DRE = dyn_cast<DeclRefExpr>(CE);
2178 if (!DRE || !isSupportedVariable(*DRE))
2179 return;
2180 MatchResult R;
2181 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2182 R.addNode(OperatorTag, DynTypedNode::create(*UO));
2183 Results.emplace_back(std::move(R));
2184 });
2185 return SizeBefore != Results.size();
2186 }
2187
2188 DeclUseList getClaimedVarUseSites() const override {
2189 return {BaseDeclRefExpr};
2190 }
2191
2192 virtual std::optional<FixItList>
2193 getFixits(const FixitStrategy &S) const override;
2194 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2195};
2196
2197// Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
2198// Context (see `findStmtsInUnspecifiedPointerContext`).
2199// Note here `[]` is the built-in subscript operator.
2200class UPCAddressofArraySubscriptGadget : public FixableGadget {
2201private:
2202 static constexpr const char *const UPCAddressofArraySubscriptTag =
2203 "AddressofArraySubscriptUnderUPC";
2204 const UnaryOperator *Node; // the `&DRE[any]` node
2205
2206public:
2207 UPCAddressofArraySubscriptGadget(const MatchResult &Result)
2208 : FixableGadget(Kind::ULCArraySubscript),
2209 Node(Result.getNodeAs<UnaryOperator>(UPCAddressofArraySubscriptTag)) {
2210 assert(Node != nullptr && "Expecting a non-null matching result");
2211 }
2212
2213 static bool classof(const Gadget *G) {
2214 return G->getKind() == Kind::UPCAddressofArraySubscript;
2215 }
2216
2217 static bool matches(const Stmt *S,
2219 size_t SizeBefore = Results.size();
2220 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2221 auto *E = dyn_cast<Expr>(S);
2222 if (!E)
2223 return;
2224 const auto *UO = dyn_cast<UnaryOperator>(E->IgnoreImpCasts());
2225 if (!UO || UO->getOpcode() != UO_AddrOf)
2226 return;
2227 const auto *ASE = dyn_cast<ArraySubscriptExpr>(UO->getSubExpr());
2228 if (!ASE)
2229 return;
2230 const auto *DRE =
2231 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts());
2232 if (!DRE || !isSupportedVariable(*DRE))
2233 return;
2234 MatchResult R;
2235 R.addNode(UPCAddressofArraySubscriptTag, DynTypedNode::create(*UO));
2236 Results.emplace_back(std::move(R));
2237 });
2238 return SizeBefore != Results.size();
2239 }
2240
2241 virtual std::optional<FixItList>
2242 getFixits(const FixitStrategy &) const override;
2243 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2244
2245 virtual DeclUseList getClaimedVarUseSites() const override {
2246 const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
2247 const auto *DRE =
2248 cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts());
2249 return {DRE};
2250 }
2251};
2252} // namespace
2253
2254namespace {
2255// An auxiliary tracking facility for the fixit analysis. It helps connect
2256// declarations to its uses and make sure we've covered all uses with our
2257// analysis before we try to fix the declaration.
2258class DeclUseTracker {
2260 using DefMapTy = llvm::DenseMap<const VarDecl *, const DeclStmt *>;
2261
2262 // Allocate on the heap for easier move.
2263 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
2264 DefMapTy Defs{};
2265
2266public:
2267 DeclUseTracker() = default;
2268 DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
2269 DeclUseTracker &operator=(const DeclUseTracker &) = delete;
2270 DeclUseTracker(DeclUseTracker &&) = default;
2271 DeclUseTracker &operator=(DeclUseTracker &&) = default;
2272
2273 // Start tracking a freshly discovered DRE.
2274 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
2275
2276 // Stop tracking the DRE as it's been fully figured out.
2277 void claimUse(const DeclRefExpr *DRE) {
2278 assert(Uses->count(DRE) &&
2279 "DRE not found or claimed by multiple matchers!");
2280 Uses->erase(DRE);
2281 }
2282
2283 // A variable is unclaimed if at least one use is unclaimed.
2284 bool hasUnclaimedUses(const VarDecl *VD) const {
2285 // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
2286 return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
2287 return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
2288 });
2289 }
2290
2291 UseSetTy getUnclaimedUses(const VarDecl *VD) const {
2292 UseSetTy ReturnSet;
2293 for (auto use : *Uses) {
2294 if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
2295 ReturnSet.insert(use);
2296 }
2297 }
2298 return ReturnSet;
2299 }
2300
2301 void discoverDecl(const DeclStmt *DS) {
2302 for (const Decl *D : DS->decls()) {
2303 if (const auto *VD = dyn_cast<VarDecl>(D)) {
2304 // FIXME: Assertion temporarily disabled due to a bug in
2305 // ASTMatcher internal behavior in presence of GNU
2306 // statement-expressions. We need to properly investigate this
2307 // because it can screw up our algorithm in other ways.
2308 // assert(Defs.count(VD) == 0 && "Definition already discovered!");
2309 Defs[VD] = DS;
2310 }
2311 }
2312 }
2313
2314 const DeclStmt *lookupDecl(const VarDecl *VD) const {
2315 return Defs.lookup(VD);
2316 }
2317};
2318} // namespace
2319
2320// Representing a pointer type expression of the form `++Ptr` in an Unspecified
2321// Pointer Context (UPC):
2322class UPCPreIncrementGadget : public FixableGadget {
2323private:
2324 static constexpr const char *const UPCPreIncrementTag =
2325 "PointerPreIncrementUnderUPC";
2326 const UnaryOperator *Node; // the `++Ptr` node
2327
2328public:
2329 UPCPreIncrementGadget(const MatchResult &Result)
2330 : FixableGadget(Kind::UPCPreIncrement),
2331 Node(Result.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
2332 assert(Node != nullptr && "Expecting a non-null matching result");
2333 }
2334
2335 static bool classof(const Gadget *G) {
2336 return G->getKind() == Kind::UPCPreIncrement;
2337 }
2338
2339 static bool matches(const Stmt *S,
2341 // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
2342 // Although currently we can only provide fix-its when `Ptr` is a DRE, we
2343 // can have the matcher be general, so long as `getClaimedVarUseSites` does
2344 // things right.
2345 size_t SizeBefore = Results.size();
2346 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2347 auto *E = dyn_cast<Expr>(S);
2348 if (!E)
2349 return;
2350 const auto *UO = dyn_cast<UnaryOperator>(E->IgnoreImpCasts());
2351 if (!UO || UO->getOpcode() != UO_PreInc)
2352 return;
2353 const auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr());
2354 if (!DRE || !isSupportedVariable(*DRE))
2355 return;
2356 MatchResult R;
2357 R.addNode(UPCPreIncrementTag, DynTypedNode::create(*UO));
2358 Results.emplace_back(std::move(R));
2359 });
2360 return SizeBefore != Results.size();
2361 }
2362
2363 virtual std::optional<FixItList>
2364 getFixits(const FixitStrategy &S) const override;
2365 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2366
2367 virtual DeclUseList getClaimedVarUseSites() const override {
2368 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
2369 }
2370};
2371
2372// Representing a pointer type expression of the form `Ptr += n` in an
2373// Unspecified Untyped Context (UUC):
2374class UUCAddAssignGadget : public FixableGadget {
2375private:
2376 static constexpr const char *const UUCAddAssignTag =
2377 "PointerAddAssignUnderUUC";
2378 static constexpr const char *const OffsetTag = "Offset";
2379
2380 const BinaryOperator *Node; // the `Ptr += n` node
2381 const Expr *Offset = nullptr;
2382
2383public:
2384 UUCAddAssignGadget(const MatchResult &Result)
2385 : FixableGadget(Kind::UUCAddAssign),
2386 Node(Result.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
2387 Offset(Result.getNodeAs<Expr>(OffsetTag)) {
2388 assert(Node != nullptr && "Expecting a non-null matching result");
2389 }
2390
2391 static bool classof(const Gadget *G) {
2392 return G->getKind() == Kind::UUCAddAssign;
2393 }
2394
2395 static bool matches(const Stmt *S,
2397 size_t SizeBefore = Results.size();
2398 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
2399 const auto *E = dyn_cast<Expr>(S);
2400 if (!E)
2401 return;
2402 const auto *BO = dyn_cast<BinaryOperator>(E->IgnoreImpCasts());
2403 if (!BO || BO->getOpcode() != BO_AddAssign)
2404 return;
2405 const auto *DRE = dyn_cast<DeclRefExpr>(BO->getLHS());
2406 if (!DRE || !hasPointerType(*DRE) || !isSupportedVariable(*DRE))
2407 return;
2408 MatchResult R;
2409 R.addNode(UUCAddAssignTag, DynTypedNode::create(*BO));
2410 R.addNode(OffsetTag, DynTypedNode::create(*BO->getRHS()));
2411 Results.emplace_back(std::move(R));
2412 });
2413 return SizeBefore != Results.size();
2414 }
2415
2416 virtual std::optional<FixItList>
2417 getFixits(const FixitStrategy &S) const override;
2418 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2419
2420 virtual DeclUseList getClaimedVarUseSites() const override {
2421 return {dyn_cast<DeclRefExpr>(Node->getLHS())};
2422 }
2423};
2424
2425// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
2426// ptr)`:
2427class DerefSimplePtrArithFixableGadget : public FixableGadget {
2428 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
2429 static constexpr const char *const DerefOpTag = "DerefOp";
2430 static constexpr const char *const AddOpTag = "AddOp";
2431 static constexpr const char *const OffsetTag = "Offset";
2432
2433 const DeclRefExpr *BaseDeclRefExpr = nullptr;
2434 const UnaryOperator *DerefOp = nullptr;
2435 const BinaryOperator *AddOp = nullptr;
2436 const IntegerLiteral *Offset = nullptr;
2437
2438public:
2439 DerefSimplePtrArithFixableGadget(const MatchResult &Result)
2440 : FixableGadget(Kind::DerefSimplePtrArithFixable),
2441 BaseDeclRefExpr(Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2442 DerefOp(Result.getNodeAs<UnaryOperator>(DerefOpTag)),
2443 AddOp(Result.getNodeAs<BinaryOperator>(AddOpTag)),
2444 Offset(Result.getNodeAs<IntegerLiteral>(OffsetTag)) {}
2445
2446 static bool matches(const Stmt *S,
2448 auto IsPtr = [](const Expr *E, MatchResult &R) {
2449 if (!E || !hasPointerType(*E))
2450 return false;
2451 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImpCasts());
2452 if (!DRE || !isSupportedVariable(*DRE))
2453 return false;
2454 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2455 return true;
2456 };
2457 const auto IsPlusOverPtrAndInteger = [&IsPtr](const Expr *E,
2458 MatchResult &R) {
2459 const auto *BO = dyn_cast<BinaryOperator>(E);
2460 if (!BO || BO->getOpcode() != BO_Add)
2461 return false;
2462
2463 const auto *LHS = BO->getLHS();
2464 const auto *RHS = BO->getRHS();
2465 if (isa<IntegerLiteral>(RHS) && IsPtr(LHS, R)) {
2466 R.addNode(OffsetTag, DynTypedNode::create(*RHS));
2467 R.addNode(AddOpTag, DynTypedNode::create(*BO));
2468 return true;
2469 }
2470 if (isa<IntegerLiteral>(LHS) && IsPtr(RHS, R)) {
2471 R.addNode(OffsetTag, DynTypedNode::create(*LHS));
2472 R.addNode(AddOpTag, DynTypedNode::create(*BO));
2473 return true;
2474 }
2475 return false;
2476 };
2477 size_t SizeBefore = Results.size();
2478 const auto InnerMatcher = [&IsPlusOverPtrAndInteger,
2479 &Results](const Expr *E) {
2480 const auto *UO = dyn_cast<UnaryOperator>(E);
2481 if (!UO || UO->getOpcode() != UO_Deref)
2482 return;
2483
2484 const auto *Operand = UO->getSubExpr()->IgnoreParens();
2485 MatchResult R;
2486 if (IsPlusOverPtrAndInteger(Operand, R)) {
2487 R.addNode(DerefOpTag, DynTypedNode::create(*UO));
2488 Results.emplace_back(std::move(R));
2489 }
2490 };
2491 findStmtsInUnspecifiedLvalueContext(S, InnerMatcher);
2492 return SizeBefore != Results.size();
2493 }
2494
2495 virtual std::optional<FixItList>
2496 getFixits(const FixitStrategy &s) const final;
2497 SourceLocation getSourceLoc() const override {
2498 return DerefOp->getBeginLoc();
2499 }
2500
2501 virtual DeclUseList getClaimedVarUseSites() const final {
2502 return {BaseDeclRefExpr};
2503 }
2504};
2505
2506class WarningGadgetMatcher : public FastMatcher {
2507
2508public:
2509 WarningGadgetMatcher(WarningGadgetList &WarningGadgets)
2510 : WarningGadgets(WarningGadgets) {}
2511
2512 bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
2513 const UnsafeBufferUsageHandler &Handler) override {
2514 const Stmt *S = DynNode.get<Stmt>();
2515 if (!S)
2516 return false;
2517
2518 MatchResult Result;
2519#define WARNING_GADGET(name) \
2520 if (name##Gadget::matches(S, Ctx, Result) && \
2521 notInSafeBufferOptOut(*S, &Handler)) { \
2522 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
2523 return true; \
2524 }
2525#define WARNING_OPTIONAL_GADGET(name) \
2526 if (name##Gadget::matches(S, Ctx, &Handler, Result) && \
2527 notInSafeBufferOptOut(*S, &Handler)) { \
2528 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
2529 return true; \
2530 }
2531#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2532 return false;
2533 }
2534
2535private:
2536 WarningGadgetList &WarningGadgets;
2537};
2538
2539class FixableGadgetMatcher : public FastMatcher {
2540
2541public:
2542 FixableGadgetMatcher(FixableGadgetList &FixableGadgets,
2543 DeclUseTracker &Tracker)
2544 : FixableGadgets(FixableGadgets), Tracker(Tracker) {}
2545
2546 bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
2547 const UnsafeBufferUsageHandler &Handler) override {
2548 bool matchFound = false;
2549 const Stmt *S = DynNode.get<Stmt>();
2550 if (!S) {
2551 return matchFound;
2552 }
2553
2555#define FIXABLE_GADGET(name) \
2556 if (name##Gadget::matches(S, Results)) { \
2557 for (const auto &R : Results) { \
2558 FixableGadgets.push_back(std::make_unique<name##Gadget>(R)); \
2559 matchFound = true; \
2560 } \
2561 Results = {}; \
2562 }
2563#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2564 // In parallel, match all DeclRefExprs so that to find out
2565 // whether there are any uncovered by gadgets.
2566 if (auto *DRE = findDeclRefExpr(S); DRE) {
2567 Tracker.discoverUse(DRE);
2568 matchFound = true;
2569 }
2570 // Also match DeclStmts because we'll need them when fixing
2571 // their underlying VarDecls that otherwise don't have
2572 // any backreferences to DeclStmts.
2573 if (auto *DS = findDeclStmt(S); DS) {
2574 Tracker.discoverDecl(DS);
2575 matchFound = true;
2576 }
2577 return matchFound;
2578 }
2579
2580private:
2581 const DeclRefExpr *findDeclRefExpr(const Stmt *S) {
2582 const auto *DRE = dyn_cast<DeclRefExpr>(S);
2583 if (!DRE || (!hasPointerType(*DRE) && !hasArrayType(*DRE)))
2584 return nullptr;
2585 const Decl *D = DRE->getDecl();
2586 if (!D || (!isa<VarDecl>(D) && !isa<BindingDecl>(D)))
2587 return nullptr;
2588 return DRE;
2589 }
2590 const DeclStmt *findDeclStmt(const Stmt *S) {
2591 const auto *DS = dyn_cast<DeclStmt>(S);
2592 if (!DS)
2593 return nullptr;
2594 return DS;
2595 }
2596 FixableGadgetList &FixableGadgets;
2597 DeclUseTracker &Tracker;
2598};
2599
2600// Scan the function and return a list of gadgets found with provided kits.
2601static void findGadgets(const Stmt *S, ASTContext &Ctx,
2602 const UnsafeBufferUsageHandler &Handler,
2603 bool EmitSuggestions, FixableGadgetList &FixableGadgets,
2604 WarningGadgetList &WarningGadgets,
2605 DeclUseTracker &Tracker) {
2606 WarningGadgetMatcher WMatcher{WarningGadgets};
2607 forEachDescendantEvaluatedStmt(S, Ctx, Handler, WMatcher);
2608 if (EmitSuggestions) {
2609 FixableGadgetMatcher FMatcher{FixableGadgets, Tracker};
2610 forEachDescendantStmt(S, Ctx, Handler, FMatcher);
2611 }
2612}
2613
2614// Compares AST nodes by source locations.
2615template <typename NodeTy> struct CompareNode {
2616 bool operator()(const NodeTy *N1, const NodeTy *N2) const {
2617 return N1->getBeginLoc().getRawEncoding() <
2618 N2->getBeginLoc().getRawEncoding();
2619 }
2620};
2621
2622std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
2623 class MockReporter : public UnsafeBufferUsageHandler {
2624 public:
2625 MockReporter() {}
2626 void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {}
2627 void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &,
2628 const Expr *UnsafeArg = nullptr) override {}
2629 void handleUnsafeOperationInContainer(const Stmt *, bool,
2630 ASTContext &) override {}
2632 const VariableGroupsManager &, FixItList &&,
2633 const Decl *,
2634 const FixitStrategy &) override {}
2635 bool isSafeBufferOptOut(const SourceLocation &) const override {
2636 return false;
2637 }
2638 bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
2639 return false;
2640 }
2641 bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
2642 return false;
2643 }
2645 SourceLocation, StringRef WSSuffix = "") const override {
2646 return "";
2647 }
2648 };
2649
2650 FixableGadgetList FixableGadgets;
2651 WarningGadgetList WarningGadgets;
2652 DeclUseTracker Tracker;
2653 MockReporter IgnoreHandler;
2654
2655 findGadgets(FD->getBody(), FD->getASTContext(), IgnoreHandler, false,
2656 FixableGadgets, WarningGadgets, Tracker);
2657
2658 std::set<const Expr *> Result;
2659 for (auto &G : WarningGadgets) {
2660 for (const Expr *E : G->getUnsafePtrs()) {
2661 Result.insert(E);
2662 }
2663 }
2664
2665 return Result;
2666}
2667
2669 std::map<const VarDecl *, std::set<const WarningGadget *>,
2670 // To keep keys sorted by their locations in the map so that the
2671 // order is deterministic:
2674 // These Gadgets are not related to pointer variables (e. g. temporaries).
2676};
2677
2678static WarningGadgetSets
2679groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
2680 WarningGadgetSets result;
2681 // If some gadgets cover more than one
2682 // variable, they'll appear more than once in the map.
2683 for (auto &G : AllUnsafeOperations) {
2684 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
2685
2686 bool AssociatedWithVarDecl = false;
2687 for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
2688 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2689 result.byVar[VD].insert(G.get());
2690 AssociatedWithVarDecl = true;
2691 }
2692 }
2693
2694 if (!AssociatedWithVarDecl) {
2695 result.noVar.push_back(G.get());
2696 continue;
2697 }
2698 }
2699 return result;
2700}
2701
2703 std::map<const VarDecl *, std::set<const FixableGadget *>,
2704 // To keep keys sorted by their locations in the map so that the
2705 // order is deterministic:
2708};
2709
2710static FixableGadgetSets
2711groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
2712 FixableGadgetSets FixablesForUnsafeVars;
2713 for (auto &F : AllFixableOperations) {
2714 DeclUseList DREs = F->getClaimedVarUseSites();
2715
2716 for (const DeclRefExpr *DRE : DREs) {
2717 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2718 FixablesForUnsafeVars.byVar[VD].insert(F.get());
2719 }
2720 }
2721 }
2722 return FixablesForUnsafeVars;
2723}
2724
2726 const SourceManager &SM) {
2727 // A simple interval overlap detection algorithm. Sorts all ranges by their
2728 // begin location then finds the first overlap in one pass.
2729 std::vector<const FixItHint *> All; // a copy of `FixIts`
2730
2731 for (const FixItHint &H : FixIts)
2732 All.push_back(&H);
2733 std::sort(All.begin(), All.end(),
2734 [&SM](const FixItHint *H1, const FixItHint *H2) {
2735 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
2736 H2->RemoveRange.getBegin());
2737 });
2738
2739 const FixItHint *CurrHint = nullptr;
2740
2741 for (const FixItHint *Hint : All) {
2742 if (!CurrHint ||
2743 SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
2744 Hint->RemoveRange.getBegin())) {
2745 // Either to initialize `CurrHint` or `CurrHint` does not
2746 // overlap with `Hint`:
2747 CurrHint = Hint;
2748 } else
2749 // In case `Hint` overlaps the `CurrHint`, we found at least one
2750 // conflict:
2751 return true;
2752 }
2753 return false;
2754}
2755
2756std::optional<FixItList>
2757PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
2758 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
2759 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
2760 switch (S.lookup(LeftVD)) {
2761 case FixitStrategy::Kind::Span:
2762 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
2763 return FixItList{};
2764 return std::nullopt;
2765 case FixitStrategy::Kind::Wontfix:
2766 return std::nullopt;
2767 case FixitStrategy::Kind::Iterator:
2768 case FixitStrategy::Kind::Array:
2769 return std::nullopt;
2770 case FixitStrategy::Kind::Vector:
2771 llvm_unreachable("unsupported strategies for FixableGadgets");
2772 }
2773 return std::nullopt;
2774}
2775
2776/// \returns fixit that adds .data() call after \DRE.
2777static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
2778 const DeclRefExpr *DRE);
2779
2780std::optional<FixItList>
2781CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
2782 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
2783 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
2784 // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
2785 // non-trivial.
2786 //
2787 // CArrayToPtrAssignmentGadget doesn't have strategy implications because
2788 // constant size array propagates its bounds. Because of that LHS and RHS are
2789 // addressed by two different fixits.
2790 //
2791 // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
2792 // to and can't be generally relied on in multi-variable Fixables!
2793 //
2794 // E. g. If an instance of this gadget is fixing variable on LHS then the
2795 // variable on RHS is fixed by a different fixit and its strategy for LHS
2796 // fixit is as if Wontfix.
2797 //
2798 // The only exception is Wontfix strategy for a given variable as that is
2799 // valid for any fixit produced for the given input source code.
2800 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
2801 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
2802 return FixItList{};
2803 }
2804 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
2805 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
2806 return createDataFixit(RightVD->getASTContext(), PtrRHS);
2807 }
2808 }
2809 return std::nullopt;
2810}
2811
2812std::optional<FixItList>
2813PointerInitGadget::getFixits(const FixitStrategy &S) const {
2814 const auto *LeftVD = PtrInitLHS;
2815 const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
2816 switch (S.lookup(LeftVD)) {
2817 case FixitStrategy::Kind::Span:
2818 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
2819 return FixItList{};
2820 return std::nullopt;
2821 case FixitStrategy::Kind::Wontfix:
2822 return std::nullopt;
2823 case FixitStrategy::Kind::Iterator:
2824 case FixitStrategy::Kind::Array:
2825 return std::nullopt;
2826 case FixitStrategy::Kind::Vector:
2827 llvm_unreachable("unsupported strategies for FixableGadgets");
2828 }
2829 return std::nullopt;
2830}
2831
2832static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
2833 const ASTContext &Ctx) {
2834 if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
2835 if (ConstVal->isNegative())
2836 return false;
2837 } else if (!Expr->getType()->isUnsignedIntegerType())
2838 return false;
2839 return true;
2840}
2841
2842std::optional<FixItList>
2843ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2844 if (const auto *DRE =
2845 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
2846 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2847 switch (S.lookup(VD)) {
2848 case FixitStrategy::Kind::Span: {
2849
2850 // If the index has a negative constant value, we give up as no valid
2851 // fix-it can be generated:
2852 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
2853 VD->getASTContext();
2854 if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
2855 return std::nullopt;
2856 // no-op is a good fix-it, otherwise
2857 return FixItList{};
2858 }
2859 case FixitStrategy::Kind::Array:
2860 return FixItList{};
2861 case FixitStrategy::Kind::Wontfix:
2862 case FixitStrategy::Kind::Iterator:
2863 case FixitStrategy::Kind::Vector:
2864 llvm_unreachable("unsupported strategies for FixableGadgets");
2865 }
2866 }
2867 return std::nullopt;
2868}
2869
2870static std::optional<FixItList> // forward declaration
2872
2873std::optional<FixItList>
2874UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2875 auto DREs = getClaimedVarUseSites();
2876 const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
2877
2878 switch (S.lookup(VD)) {
2879 case FixitStrategy::Kind::Span:
2881 case FixitStrategy::Kind::Wontfix:
2882 case FixitStrategy::Kind::Iterator:
2883 case FixitStrategy::Kind::Array:
2884 return std::nullopt;
2885 case FixitStrategy::Kind::Vector:
2886 llvm_unreachable("unsupported strategies for FixableGadgets");
2887 }
2888 return std::nullopt; // something went wrong, no fix-it
2889}
2890
2891// FIXME: this function should be customizable through format
2892static StringRef getEndOfLine() {
2893 static const char *const EOL = "\n";
2894 return EOL;
2895}
2896
2897// Returns the text indicating that the user needs to provide input there:
2898static std::string
2899getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
2900 std::string s = std::string("<# ");
2901 s += HintTextToUser;
2902 s += " #>";
2903 return s;
2904}
2905
2906// Return the source location of the last character of the AST `Node`.
2907template <typename NodeTy>
2908static std::optional<SourceLocation>
2909getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
2910 const LangOptions &LangOpts) {
2911 if (unsigned TkLen =
2912 Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts)) {
2913 SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
2914
2915 if (Loc.isValid())
2916 return Loc;
2917 }
2918 return std::nullopt;
2919}
2920
2921// We cannot fix a variable declaration if it has some other specifiers than the
2922// type specifier. Because the source ranges of those specifiers could overlap
2923// with the source range that is being replaced using fix-its. Especially when
2924// we often cannot obtain accurate source ranges of cv-qualified type
2925// specifiers.
2926// FIXME: also deal with type attributes
2927static bool hasUnsupportedSpecifiers(const VarDecl *VD,
2928 const SourceManager &SM) {
2929 // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
2930 // source range of `VD`:
2931 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
2932 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
2933 VD->getBeginLoc())) &&
2934 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
2935 At->getRange().getBegin()));
2936 });
2937 return VD->isInlineSpecified() || VD->isConstexpr() ||
2939 AttrRangeOverlapping;
2940}
2941
2942// Returns the `SourceRange` of `D`. The reason why this function exists is
2943// that `D->getSourceRange()` may return a range where the end location is the
2944// starting location of the last token. The end location of the source range
2945// returned by this function is the last location of the last token.
2947 const SourceManager &SM,
2948 const LangOptions &LangOpts) {
2951 End = // `D->getEndLoc` should always return the starting location of the
2952 // last token, so we should get the end of the token
2953 Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
2954
2955 return SourceRange(Begin, End);
2956}
2957
2958// Returns the text of the name (with qualifiers) of a `FunctionDecl`.
2959static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
2960 const SourceManager &SM,
2961 const LangOptions &LangOpts) {
2962 SourceLocation BeginLoc = FD->getQualifier()
2964 : FD->getNameInfo().getBeginLoc();
2965 // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
2966 // last token:
2968 FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
2969 SourceRange NameRange{BeginLoc, EndLoc};
2970
2971 return getRangeText(NameRange, SM, LangOpts);
2972}
2973
2974// Returns the text representing a `std::span` type where the element type is
2975// represented by `EltTyText`.
2976//
2977// Note the optional parameter `Qualifiers`: one needs to pass qualifiers
2978// explicitly if the element type needs to be qualified.
2979static std::string
2980getSpanTypeText(StringRef EltTyText,
2981 std::optional<Qualifiers> Quals = std::nullopt) {
2982 const char *const SpanOpen = "std::span<";
2983
2984 if (Quals)
2985 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
2986 return SpanOpen + EltTyText.str() + '>';
2987}
2988
2989std::optional<FixItList>
2991 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
2992
2993 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
2994 ASTContext &Ctx = VD->getASTContext();
2995 // std::span can't represent elements before its begin()
2996 if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
2997 if (ConstVal->isNegative())
2998 return std::nullopt;
2999
3000 // note that the expr may (oddly) has multiple layers of parens
3001 // example:
3002 // *((..(pointer + 123)..))
3003 // goal:
3004 // pointer[123]
3005 // Fix-It:
3006 // remove '*('
3007 // replace ' + ' with '['
3008 // replace ')' with ']'
3009
3010 // example:
3011 // *((..(123 + pointer)..))
3012 // goal:
3013 // 123[pointer]
3014 // Fix-It:
3015 // remove '*('
3016 // replace ' + ' with '['
3017 // replace ')' with ']'
3018
3019 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
3020 const SourceManager &SM = Ctx.getSourceManager();
3021 const LangOptions &LangOpts = Ctx.getLangOpts();
3022 CharSourceRange StarWithTrailWhitespace =
3024 LHS->getBeginLoc());
3025
3026 std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
3027 if (!LHSLocation)
3028 return std::nullopt;
3029
3030 CharSourceRange PlusWithSurroundingWhitespace =
3031 clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
3032
3033 std::optional<SourceLocation> AddOpLocation =
3034 getPastLoc(AddOp, SM, LangOpts);
3035 std::optional<SourceLocation> DerefOpLocation =
3036 getPastLoc(DerefOp, SM, LangOpts);
3037
3038 if (!AddOpLocation || !DerefOpLocation)
3039 return std::nullopt;
3040
3041 CharSourceRange ClosingParenWithPrecWhitespace =
3042 clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
3043
3044 return FixItList{
3045 {FixItHint::CreateRemoval(StarWithTrailWhitespace),
3046 FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
3047 FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
3048 }
3049 return std::nullopt; // something wrong or unsupported, give up
3050}
3051
3052std::optional<FixItList>
3053PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
3054 const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
3055 switch (S.lookup(VD)) {
3056 case FixitStrategy::Kind::Span: {
3057 ASTContext &Ctx = VD->getASTContext();
3059 // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
3060 // Deletes the *operand
3062 Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
3063 // Inserts the [0]
3064 if (auto LocPastOperand =
3065 getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
3066 return FixItList{{FixItHint::CreateRemoval(derefRange),
3067 FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
3068 }
3069 break;
3070 }
3071 case FixitStrategy::Kind::Iterator:
3072 case FixitStrategy::Kind::Array:
3073 return std::nullopt;
3074 case FixitStrategy::Kind::Vector:
3075 llvm_unreachable("FixitStrategy not implemented yet!");
3076 case FixitStrategy::Kind::Wontfix:
3077 llvm_unreachable("Invalid strategy!");
3078 }
3079
3080 return std::nullopt;
3081}
3082
3083static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
3084 const DeclRefExpr *DRE) {
3085 const SourceManager &SM = Ctx.getSourceManager();
3086 // Inserts the .data() after the DRE
3087 std::optional<SourceLocation> EndOfOperand =
3088 getPastLoc(DRE, SM, Ctx.getLangOpts());
3089
3090 if (EndOfOperand)
3091 return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
3092
3093 return std::nullopt;
3094}
3095
3096// Generates fix-its replacing an expression of the form UPC(DRE) with
3097// `DRE.data()`
3098std::optional<FixItList>
3099UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
3100 const auto VD = cast<VarDecl>(Node->getDecl());
3101 switch (S.lookup(VD)) {
3102 case FixitStrategy::Kind::Array:
3103 case FixitStrategy::Kind::Span: {
3104 return createDataFixit(VD->getASTContext(), Node);
3105 // FIXME: Points inside a macro expansion.
3106 break;
3107 }
3108 case FixitStrategy::Kind::Wontfix:
3109 case FixitStrategy::Kind::Iterator:
3110 return std::nullopt;
3111 case FixitStrategy::Kind::Vector:
3112 llvm_unreachable("unsupported strategies for FixableGadgets");
3113 }
3114
3115 return std::nullopt;
3116}
3117
3118// Generates fix-its replacing an expression of the form `&DRE[e]` with
3119// `&DRE.data()[e]`:
3120static std::optional<FixItList>
3122 const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
3123 const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
3124 // FIXME: this `getASTContext` call is costly, we should pass the
3125 // ASTContext in:
3126 const ASTContext &Ctx = DRE->getDecl()->getASTContext();
3127 const Expr *Idx = ArraySub->getIdx();
3128 const SourceManager &SM = Ctx.getSourceManager();
3129 const LangOptions &LangOpts = Ctx.getLangOpts();
3130 std::stringstream SS;
3131 bool IdxIsLitZero = false;
3132
3133 if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
3134 if ((*ICE).isZero())
3135 IdxIsLitZero = true;
3136 std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
3137 if (!DreString)
3138 return std::nullopt;
3139
3140 if (IdxIsLitZero) {
3141 // If the index is literal zero, we produce the most concise fix-it:
3142 SS << (*DreString).str() << ".data()";
3143 } else {
3144 std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
3145 if (!IndexString)
3146 return std::nullopt;
3147
3148 SS << "&" << (*DreString).str() << ".data()"
3149 << "[" << (*IndexString).str() << "]";
3150 }
3151 return FixItList{
3153}
3154
3155std::optional<FixItList>
3157 DeclUseList DREs = getClaimedVarUseSites();
3158
3159 if (DREs.size() != 1)
3160 return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
3161 // give up
3162 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3163 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3164 FixItList Fixes;
3165
3166 const Stmt *AddAssignNode = Node;
3167 StringRef varName = VD->getName();
3168 const ASTContext &Ctx = VD->getASTContext();
3169
3170 if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
3171 return std::nullopt;
3172
3173 // To transform UUC(p += n) to UUC(p = p.subspan(..)):
3174 bool NotParenExpr =
3175 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
3176 std::string SS = varName.str() + " = " + varName.str() + ".subspan";
3177 if (NotParenExpr)
3178 SS += "(";
3179
3180 std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
3181 AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
3182 if (!AddAssignLocation)
3183 return std::nullopt;
3184
3185 Fixes.push_back(FixItHint::CreateReplacement(
3186 SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
3187 SS));
3188 if (NotParenExpr)
3189 Fixes.push_back(FixItHint::CreateInsertion(
3190 Offset->getEndLoc().getLocWithOffset(1), ")"));
3191 return Fixes;
3192 }
3193 }
3194 return std::nullopt; // Not in the cases that we can handle for now, give up.
3195}
3196
3197std::optional<FixItList>
3199 DeclUseList DREs = getClaimedVarUseSites();
3200
3201 if (DREs.size() != 1)
3202 return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
3203 // give up
3204 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3205 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3206 FixItList Fixes;
3207 std::stringstream SS;
3208 StringRef varName = VD->getName();
3209 const ASTContext &Ctx = VD->getASTContext();
3210
3211 // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
3212 SS << "(" << varName.data() << " = " << varName.data()
3213 << ".subspan(1)).data()";
3214 std::optional<SourceLocation> PreIncLocation =
3216 if (!PreIncLocation)
3217 return std::nullopt;
3218
3219 Fixes.push_back(FixItHint::CreateReplacement(
3220 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
3221 return Fixes;
3222 }
3223 }
3224 return std::nullopt; // Not in the cases that we can handle for now, give up.
3225}
3226
3227// For a non-null initializer `Init` of `T *` type, this function returns
3228// `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
3229// to output stream.
3230// In many cases, this function cannot figure out the actual extent `S`. It
3231// then will use a place holder to replace `S` to ask users to fill `S` in. The
3232// initializer shall be used to initialize a variable of type `std::span<T>`.
3233// In some cases (e. g. constant size array) the initializer should remain
3234// unchanged and the function returns empty list. In case the function can't
3235// provide the right fixit it will return nullopt.
3236//
3237// FIXME: Support multi-level pointers
3238//
3239// Parameters:
3240// `Init` a pointer to the initializer expression
3241// `Ctx` a reference to the ASTContext
3242static std::optional<FixItList>
3244 const StringRef UserFillPlaceHolder) {
3245 const SourceManager &SM = Ctx.getSourceManager();
3246 const LangOptions &LangOpts = Ctx.getLangOpts();
3247
3248 // If `Init` has a constant value that is (or equivalent to) a
3249 // NULL pointer, we use the default constructor to initialize the span
3250 // object, i.e., a `std:span` variable declaration with no initializer.
3251 // So the fix-it is just to remove the initializer.
3252 if (Init->isNullPointerConstant(
3253 Ctx,
3254 // FIXME: Why does this function not ask for `const ASTContext
3255 // &`? It should. Maybe worth an NFC patch later.
3257 NPC_ValueDependentIsNotNull)) {
3258 std::optional<SourceLocation> InitLocation =
3259 getEndCharLoc(Init, SM, LangOpts);
3260 if (!InitLocation)
3261 return std::nullopt;
3262
3263 SourceRange SR(Init->getBeginLoc(), *InitLocation);
3264
3265 return FixItList{FixItHint::CreateRemoval(SR)};
3266 }
3267
3268 FixItList FixIts{};
3269 std::string ExtentText = UserFillPlaceHolder.data();
3270 StringRef One = "1";
3271
3272 // Insert `{` before `Init`:
3273 FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
3274 // Try to get the data extent. Break into different cases:
3275 if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
3276 // In cases `Init` is `new T[n]` and there is no explicit cast over
3277 // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
3278 // of `T`. So the extent is `n` unless `n` has side effects. Similar but
3279 // simpler for the case where `Init` is `new T`.
3280 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
3281 if (!Ext->HasSideEffects(Ctx)) {
3282 std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
3283 if (!ExtentString)
3284 return std::nullopt;
3285 ExtentText = *ExtentString;
3286 }
3287 } else if (!CxxNew->isArray())
3288 // Although the initializer is not allocating a buffer, the pointer
3289 // variable could still be used in buffer access operations.
3290 ExtentText = One;
3291 } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) {
3292 // std::span has a single parameter constructor for initialization with
3293 // constant size array. The size is auto-deduced as the constructor is a
3294 // function template. The correct fixit is empty - no changes should happen.
3295 return FixItList{};
3296 } else {
3297 // In cases `Init` is of the form `&Var` after stripping of implicit
3298 // casts, where `&` is the built-in operator, the extent is 1.
3299 if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
3300 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
3301 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
3302 ExtentText = One;
3303 // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
3304 // and explicit casting, etc. etc.
3305 }
3306
3307 SmallString<32> StrBuffer{};
3308 std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
3309
3310 if (!LocPassInit)
3311 return std::nullopt;
3312
3313 StrBuffer.append(", ");
3314 StrBuffer.append(ExtentText);
3315 StrBuffer.append("}");
3316 FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
3317 return FixIts;
3318}
3319
3320#ifndef NDEBUG
3321#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
3322 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
3323 "failed to produce fixit for declaration '" + \
3324 (D)->getNameAsString() + "'" + (Msg))
3325#else
3326#define DEBUG_NOTE_DECL_FAIL(D, Msg)
3327#endif
3328
3329// For the given variable declaration with a pointer-to-T type, returns the text
3330// `std::span<T>`. If it is unable to generate the text, returns
3331// `std::nullopt`.
3332static std::optional<std::string>
3334 assert(VD->getType()->isPointerType());
3335
3336 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3337 std::optional<std::string> PteTyText = getPointeeTypeText(
3338 VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
3339
3340 if (!PteTyText)
3341 return std::nullopt;
3342
3343 std::string SpanTyText = "std::span<";
3344
3345 SpanTyText.append(*PteTyText);
3346 // Append qualifiers to span element type if any:
3347 if (PteTyQualifiers) {
3348 SpanTyText.append(" ");
3349 SpanTyText.append(PteTyQualifiers->getAsString());
3350 }
3351 SpanTyText.append(">");
3352 return SpanTyText;
3353}
3354
3355// For a `VarDecl` of the form `T * var (= Init)?`, this
3356// function generates fix-its that
3357// 1) replace `T * var` with `std::span<T> var`; and
3358// 2) change `Init` accordingly to a span constructor, if it exists.
3359//
3360// FIXME: support Multi-level pointers
3361//
3362// Parameters:
3363// `D` a pointer the variable declaration node
3364// `Ctx` a reference to the ASTContext
3365// `UserFillPlaceHolder` the user-input placeholder text
3366// Returns:
3367// the non-empty fix-it list, if fix-its are successfuly generated; empty
3368// list otherwise.
3369static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
3370 const StringRef UserFillPlaceHolder,
3371 UnsafeBufferUsageHandler &Handler) {
3373 return {};
3374
3375 FixItList FixIts{};
3376 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
3377
3378 if (!SpanTyText) {
3379 DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
3380 return {};
3381 }
3382
3383 // Will hold the text for `std::span<T> Ident`:
3384 std::stringstream SS;
3385
3386 SS << *SpanTyText;
3387 // Fix the initializer if it exists:
3388 if (const Expr *Init = D->getInit()) {
3389 std::optional<FixItList> InitFixIts =
3390 FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
3391 if (!InitFixIts)
3392 return {};
3393 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
3394 std::make_move_iterator(InitFixIts->end()));
3395 }
3396 // For declaration of the form `T * ident = init;`, we want to replace
3397 // `T * ` with `std::span<T>`.
3398 // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
3399 // just `T *` with `std::span<T>`.
3400 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
3401 if (!EndLocForReplacement.isValid()) {
3402 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
3403 return {};
3404 }
3405 // The only exception is that for `T *ident` we'll add a single space between
3406 // "std::span<T>" and "ident".
3407 // FIXME: The condition is false for identifiers expended from macros.
3408 if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
3409 SS << " ";
3410
3411 FixIts.push_back(FixItHint::CreateReplacement(
3412 SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
3413 return FixIts;
3414}
3415
3416static bool hasConflictingOverload(const FunctionDecl *FD) {
3417 return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
3418}
3419
3420// For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
3421// types, this function produces fix-its to make the change self-contained. Let
3422// 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
3423// entity defined by the `FunctionDecl` after the change to the parameters.
3424// Fix-its produced by this function are
3425// 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
3426// of 'F';
3427// 2. Create a declaration of "NewF" next to each declaration of `F`;
3428// 3. Create a definition of "F" (as its' original definition is now belongs
3429// to "NewF") next to its original definition. The body of the creating
3430// definition calls to "NewF".
3431//
3432// Example:
3433//
3434// void f(int *p); // original declaration
3435// void f(int *p) { // original definition
3436// p[5];
3437// }
3438//
3439// To change the parameter `p` to be of `std::span<int>` type, we
3440// also add overloads:
3441//
3442// [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
3443// void f(std::span<int> p); // added overload decl
3444// void f(std::span<int> p) { // original def where param is changed
3445// p[5];
3446// }
3447// [[clang::unsafe_buffer_usage]] void f(int *p) { // added def
3448// return f(std::span(p, <# size #>));
3449// }
3450//
3451static std::optional<FixItList>
3453 const ASTContext &Ctx,
3454 UnsafeBufferUsageHandler &Handler) {
3455 // FIXME: need to make this conflict checking better:
3456 if (hasConflictingOverload(FD))
3457 return std::nullopt;
3458
3459 const SourceManager &SM = Ctx.getSourceManager();
3460 const LangOptions &LangOpts = Ctx.getLangOpts();
3461 const unsigned NumParms = FD->getNumParams();
3462 std::vector<std::string> NewTysTexts(NumParms);
3463 std::vector<bool> ParmsMask(NumParms, false);
3464 bool AtLeastOneParmToFix = false;
3465
3466 for (unsigned i = 0; i < NumParms; i++) {
3467 const ParmVarDecl *PVD = FD->getParamDecl(i);
3468
3469 if (S.lookup(PVD) == FixitStrategy::Kind::Wontfix)
3470 continue;
3471 if (S.lookup(PVD) != FixitStrategy::Kind::Span)
3472 // Not supported, not suppose to happen:
3473 return std::nullopt;
3474
3475 std::optional<Qualifiers> PteTyQuals = std::nullopt;
3476 std::optional<std::string> PteTyText =
3477 getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
3478
3479 if (!PteTyText)
3480 // something wrong in obtaining the text of the pointee type, give up
3481 return std::nullopt;
3482 // FIXME: whether we should create std::span type depends on the
3483 // FixitStrategy.
3484 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
3485 ParmsMask[i] = true;
3486 AtLeastOneParmToFix = true;
3487 }
3488 if (!AtLeastOneParmToFix)
3489 // No need to create function overloads:
3490 return {};
3491 // FIXME Respect indentation of the original code.
3492
3493 // A lambda that creates the text representation of a function declaration
3494 // with the new type signatures:
3495 const auto NewOverloadSignatureCreator =
3496 [&SM, &LangOpts, &NewTysTexts,
3497 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3498 std::stringstream SS;
3499
3500 SS << ";";
3501 SS << getEndOfLine().str();
3502 // Append: ret-type func-name "("
3503 if (auto Prefix = getRangeText(
3504 SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
3505 SM, LangOpts))
3506 SS << Prefix->str();
3507 else
3508 return std::nullopt; // give up
3509 // Append: parameter-type-list
3510 const unsigned NumParms = FD->getNumParams();
3511
3512 for (unsigned i = 0; i < NumParms; i++) {
3513 const ParmVarDecl *Parm = FD->getParamDecl(i);
3514
3515 if (Parm->isImplicit())
3516 continue;
3517 if (ParmsMask[i]) {
3518 // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
3519 // new type:
3520 SS << NewTysTexts[i];
3521 // print parameter name if provided:
3522 if (IdentifierInfo *II = Parm->getIdentifier())
3523 SS << ' ' << II->getName().str();
3524 } else if (auto ParmTypeText =
3525 getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts),
3526 SM, LangOpts)) {
3527 // print the whole `Parm` without modification:
3528 SS << ParmTypeText->str();
3529 } else
3530 return std::nullopt; // something wrong, give up
3531 if (i != NumParms - 1)
3532 SS << ", ";
3533 }
3534 SS << ")";
3535 return SS.str();
3536 };
3537
3538 // A lambda that creates the text representation of a function definition with
3539 // the original signature:
3540 const auto OldOverloadDefCreator =
3541 [&Handler, &SM, &LangOpts, &NewTysTexts,
3542 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3543 std::stringstream SS;
3544
3545 SS << getEndOfLine().str();
3546 // Append: attr-name ret-type func-name "(" param-list ")" "{"
3547 if (auto FDPrefix = getRangeText(
3548 SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
3549 LangOpts))
3550 SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
3551 << FDPrefix->str() << "{";
3552 else
3553 return std::nullopt;
3554 // Append: "return" func-name "("
3555 if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
3556 SS << "return " << FunQualName->str() << "(";
3557 else
3558 return std::nullopt;
3559
3560 // Append: arg-list
3561 const unsigned NumParms = FD->getNumParams();
3562 for (unsigned i = 0; i < NumParms; i++) {
3563 const ParmVarDecl *Parm = FD->getParamDecl(i);
3564
3565 if (Parm->isImplicit())
3566 continue;
3567 // FIXME: If a parameter has no name, it is unused in the
3568 // definition. So we could just leave it as it is.
3569 if (!Parm->getIdentifier())
3570 // If a parameter of a function definition has no name:
3571 return std::nullopt;
3572 if (ParmsMask[i])
3573 // This is our spanified paramter!
3574 SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
3575 << ", " << getUserFillPlaceHolder("size") << ")";
3576 else
3577 SS << Parm->getIdentifier()->getName().str();
3578 if (i != NumParms - 1)
3579 SS << ", ";
3580 }
3581 // finish call and the body
3582 SS << ");}" << getEndOfLine().str();
3583 // FIXME: 80-char line formatting?
3584 return SS.str();
3585 };
3586
3587 FixItList FixIts{};
3588 for (FunctionDecl *FReDecl : FD->redecls()) {
3589 std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
3590
3591 if (!Loc)
3592 return {};
3593 if (FReDecl->isThisDeclarationADefinition()) {
3594 assert(FReDecl == FD && "inconsistent function definition");
3595 // Inserts a definition with the old signature to the end of
3596 // `FReDecl`:
3597 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
3598 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
3599 else
3600 return {}; // give up
3601 } else {
3602 // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
3603 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
3604 FixIts.emplace_back(FixItHint::CreateInsertion(
3605 FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
3606 FReDecl->getBeginLoc(), " ")));
3607 }
3608 // Inserts a declaration with the new signature to the end of `FReDecl`:
3609 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
3610 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
3611 else
3612 return {};
3613 }
3614 }
3615 return FixIts;
3616}
3617
3618// To fix a `ParmVarDecl` to be of `std::span` type.
3619static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
3620 UnsafeBufferUsageHandler &Handler) {
3622 DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
3623 return {};
3624 }
3625 if (PVD->hasDefaultArg()) {
3626 // FIXME: generate fix-its for default values:
3627 DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
3628 return {};
3629 }
3630
3631 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3632 std::optional<std::string> PteTyText = getPointeeTypeText(
3633 PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
3634
3635 if (!PteTyText) {
3636 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
3637 return {};
3638 }
3639
3640 std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
3641
3642 if (!PVDNameText) {
3643 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
3644 return {};
3645 }
3646
3647 std::stringstream SS;
3648 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
3649
3650 if (PteTyQualifiers)
3651 // Append qualifiers if they exist:
3652 SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
3653 else
3654 SS << getSpanTypeText(*PteTyText);
3655 // Append qualifiers to the type of the parameter:
3656 if (PVD->getType().hasQualifiers())
3657 SS << ' ' << PVD->getType().getQualifiers().getAsString();
3658 // Append parameter's name:
3659 SS << ' ' << PVDNameText->str();
3660 // Add replacement fix-it:
3661 return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
3662}
3663
3664static FixItList fixVariableWithSpan(const VarDecl *VD,
3665 const DeclUseTracker &Tracker,
3666 ASTContext &Ctx,
3667 UnsafeBufferUsageHandler &Handler) {
3668 const DeclStmt *DS = Tracker.lookupDecl(VD);
3669 if (!DS) {
3671 " : variables declared this way not implemented yet");
3672 return {};
3673 }
3674 if (!DS->isSingleDecl()) {
3675 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
3676 DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
3677 return {};
3678 }
3679 // Currently DS is an unused variable but we'll need it when
3680 // non-single decls are implemented, where the pointee type name
3681 // and the '*' are spread around the place.
3682 (void)DS;
3683
3684 // FIXME: handle cases where DS has multiple declarations
3685 return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
3686}
3687
3688static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
3689 UnsafeBufferUsageHandler &Handler) {
3690 FixItList FixIts{};
3691
3692 // Note: the code below expects the declaration to not use any type sugar like
3693 // typedef.
3694 if (auto CAT = Ctx.getAsConstantArrayType(D->getType())) {
3695 const QualType &ArrayEltT = CAT->getElementType();
3696 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
3697 // FIXME: support multi-dimensional arrays
3698 if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType()))
3699 return {};
3700
3702
3703 // Get the spelling of the element type as written in the source file
3704 // (including macros, etc.).
3705 auto MaybeElemTypeTxt =
3707 Ctx.getLangOpts());
3708 if (!MaybeElemTypeTxt)
3709 return {};
3710 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
3711
3712 // Find the '[' token.
3713 std::optional<Token> NextTok = Lexer::findNextToken(
3715 while (NextTok && !NextTok->is(tok::l_square) &&
3716 NextTok->getLocation() <= D->getSourceRange().getEnd())
3717 NextTok = Lexer::findNextToken(NextTok->getLocation(),
3718 Ctx.getSourceManager(), Ctx.getLangOpts());
3719 if (!NextTok)
3720 return {};
3721 const SourceLocation LSqBracketLoc = NextTok->getLocation();
3722
3723 // Get the spelling of the array size as written in the source file
3724 // (including macros, etc.).
3725 auto MaybeArraySizeTxt = getRangeText(
3726 {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
3727 Ctx.getSourceManager(), Ctx.getLangOpts());
3728 if (!MaybeArraySizeTxt)
3729 return {};
3730 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
3731 if (ArraySizeTxt.empty()) {
3732 // FIXME: Support array size getting determined from the initializer.
3733 // Examples:
3734 // int arr1[] = {0, 1, 2};
3735 // int arr2{3, 4, 5};
3736 // We might be able to preserve the non-specified size with `auto` and
3737 // `std::to_array`:
3738 // auto arr1 = std::to_array<int>({0, 1, 2});
3739 return {};
3740 }
3741
3742 std::optional<StringRef> IdentText =
3744
3745 if (!IdentText) {
3746 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
3747 return {};
3748 }
3749
3750 SmallString<32> Replacement;
3751 llvm::raw_svector_ostream OS(Replacement);
3752 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
3753 << IdentText->str();
3754
3755 FixIts.push_back(FixItHint::CreateReplacement(
3756 SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));
3757 }
3758
3759 return FixIts;
3760}
3761
3762static FixItList fixVariableWithArray(const VarDecl *VD,
3763 const DeclUseTracker &Tracker,
3764 const ASTContext &Ctx,
3765 UnsafeBufferUsageHandler &Handler) {
3766 const DeclStmt *DS = Tracker.lookupDecl(VD);
3767 assert(DS && "Fixing non-local variables not implemented yet!");
3768 if (!DS->isSingleDecl()) {
3769 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
3770 return {};
3771 }
3772 // Currently DS is an unused variable but we'll need it when
3773 // non-single decls are implemented, where the pointee type name
3774 // and the '*' are spread around the place.
3775 (void)DS;
3776
3777 // FIXME: handle cases where DS has multiple declarations
3778 return fixVarDeclWithArray(VD, Ctx, Handler);
3779}
3780
3781// TODO: we should be consistent to use `std::nullopt` to represent no-fix due
3782// to any unexpected problem.
3783static FixItList
3785 /* The function decl under analysis */ const Decl *D,
3786 const DeclUseTracker &Tracker, ASTContext &Ctx,
3787 UnsafeBufferUsageHandler &Handler) {
3788 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
3789 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
3790 if (!FD || FD != D) {
3791 // `FD != D` means that `PVD` belongs to a function that is not being
3792 // analyzed currently. Thus `FD` may not be complete.
3793 DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
3794 return {};
3795 }
3796
3797 // TODO If function has a try block we can't change params unless we check
3798 // also its catch block for their use.
3799 // FIXME We might support static class methods, some select methods,
3800 // operators and possibly lamdas.
3801 if (FD->isMain() || FD->isConstexpr() ||
3802 FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
3803 FD->isVariadic() ||
3804 // also covers call-operator of lamdas
3805 isa<CXXMethodDecl>(FD) ||
3806 // skip when the function body is a try-block
3807 (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
3808 FD->isOverloadedOperator()) {
3809 DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
3810 return {}; // TODO test all these cases
3811 }
3812 }
3813
3814 switch (K) {
3815 case FixitStrategy::Kind::Span: {
3816 if (VD->getType()->isPointerType()) {
3817 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
3818 return fixParamWithSpan(PVD, Ctx, Handler);
3819
3820 if (VD->isLocalVarDecl())
3821 return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
3822 }
3823 DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
3824 return {};
3825 }
3826 case FixitStrategy::Kind::Array: {
3827 if (VD->isLocalVarDecl() && Ctx.getAsConstantArrayType(VD->getType()))
3828 return fixVariableWithArray(VD, Tracker, Ctx, Handler);
3829
3830 DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
3831 return {};
3832 }
3833 case FixitStrategy::Kind::Iterator:
3834 case FixitStrategy::Kind::Vector:
3835 llvm_unreachable("FixitStrategy not implemented yet!");
3836 case FixitStrategy::Kind::Wontfix:
3837 llvm_unreachable("Invalid strategy!");
3838 }
3839 llvm_unreachable("Unknown strategy!");
3840}
3841
3842// Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
3843// `RemoveRange` of 'h' overlaps with a macro use.
3844static bool overlapWithMacro(const FixItList &FixIts) {
3845 // FIXME: For now we only check if the range (or the first token) is (part of)
3846 // a macro expansion. Ideally, we want to check for all tokens in the range.
3847 return llvm::any_of(FixIts, [](const FixItHint &Hint) {
3848 auto Range = Hint.RemoveRange;
3850 // If the range (or the first token) is (part of) a macro expansion:
3851 return true;
3852 return false;
3853 });
3854}
3855
3856// Returns true iff `VD` is a parameter of the declaration `D`:
3857static bool isParameterOf(const VarDecl *VD, const Decl *D) {
3858 return isa<ParmVarDecl>(VD) &&
3859 VD->getDeclContext() == dyn_cast<DeclContext>(D);
3860}
3861
3862// Erases variables in `FixItsForVariable`, if such a variable has an unfixable
3863// group mate. A variable `v` is unfixable iff `FixItsForVariable` does not
3864// contain `v`.
3866 std::map<const VarDecl *, FixItList> &FixItsForVariable,
3867 const VariableGroupsManager &VarGrpMgr) {
3868 // Variables will be removed from `FixItsForVariable`:
3870
3871 for (const auto &[VD, Ignore] : FixItsForVariable) {
3872 VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
3873 if (llvm::any_of(Grp,
3874 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
3875 return !FixItsForVariable.count(GrpMember);
3876 })) {
3877 // At least one group member cannot be fixed, so we have to erase the
3878 // whole group:
3879 for (const VarDecl *Member : Grp)
3880 ToErase.push_back(Member);
3881 }
3882 }
3883 for (auto *VarToErase : ToErase)
3884 FixItsForVariable.erase(VarToErase);
3885}
3886
3887// Returns the fix-its that create bounds-safe function overloads for the
3888// function `D`, if `D`'s parameters will be changed to safe-types through
3889// fix-its in `FixItsForVariable`.
3890//
3891// NOTE: In case `D`'s parameters will be changed but bounds-safe function
3892// overloads cannot created, the whole group that contains the parameters will
3893// be erased from `FixItsForVariable`.
3895 std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
3896 const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
3897 const FixitStrategy &S, ASTContext &Ctx,
3898 UnsafeBufferUsageHandler &Handler) {
3899 FixItList FixItsSharedByParms{};
3900
3901 std::optional<FixItList> OverloadFixes =
3902 createOverloadsForFixedParams(S, FD, Ctx, Handler);
3903
3904 if (OverloadFixes) {
3905 FixItsSharedByParms.append(*OverloadFixes);
3906 } else {
3907 // Something wrong in generating `OverloadFixes`, need to remove the
3908 // whole group, where parameters are in, from `FixItsForVariable` (Note
3909 // that all parameters should be in the same group):
3910 for (auto *Member : VarGrpMgr.getGroupOfParms())
3911 FixItsForVariable.erase(Member);
3912 }
3913 return FixItsSharedByParms;
3914}
3915
3916// Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
3917static std::map<const VarDecl *, FixItList>
3918getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S,
3919 ASTContext &Ctx,
3920 /* The function decl under analysis */ const Decl *D,
3921 const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
3922 const VariableGroupsManager &VarGrpMgr) {
3923 // `FixItsForVariable` will map each variable to a set of fix-its directly
3924 // associated to the variable itself. Fix-its of distinct variables in
3925 // `FixItsForVariable` are disjoint.
3926 std::map<const VarDecl *, FixItList> FixItsForVariable;
3927
3928 // Populate `FixItsForVariable` with fix-its directly associated with each
3929 // variable. Fix-its directly associated to a variable 'v' are the ones
3930 // produced by the `FixableGadget`s whose claimed variable is 'v'.
3931 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
3932 FixItsForVariable[VD] =
3933 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
3934 // If we fail to produce Fix-It for the declaration we have to skip the
3935 // variable entirely.
3936 if (FixItsForVariable[VD].empty()) {
3937 FixItsForVariable.erase(VD);
3938 continue;
3939 }
3940 for (const auto &F : Fixables) {
3941 std::optional<FixItList> Fixits = F->getFixits(S);
3942
3943 if (Fixits) {
3944 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
3945 Fixits->begin(), Fixits->end());
3946 continue;
3947 }
3948#ifndef NDEBUG
3949 Handler.addDebugNoteForVar(
3950 VD, F->getSourceLoc(),
3951 ("gadget '" + F->getDebugName() + "' refused to produce a fix")
3952 .str());
3953#endif
3954 FixItsForVariable.erase(VD);
3955 break;
3956 }
3957 }
3958
3959 // `FixItsForVariable` now contains only variables that can be
3960 // fixed. A variable can be fixed if its' declaration and all Fixables
3961 // associated to it can all be fixed.
3962
3963 // To further remove from `FixItsForVariable` variables whose group mates
3964 // cannot be fixed...
3965 eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
3966 // Now `FixItsForVariable` gets further reduced: a variable is in
3967 // `FixItsForVariable` iff it can be fixed and all its group mates can be
3968 // fixed.
3969
3970 // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
3971 // That is, when fixing multiple parameters in one step, these fix-its will
3972 // be applied only once (instead of being applied per parameter).
3973 FixItList FixItsSharedByParms{};
3974
3975 if (auto *FD = dyn_cast<FunctionDecl>(D))
3976 FixItsSharedByParms = createFunctionOverloadsForParms(
3977 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
3978
3979 // The map that maps each variable `v` to fix-its for the whole group where
3980 // `v` is in:
3981 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
3982 FixItsForVariable};
3983
3984 for (auto &[Var, Ignore] : FixItsForVariable) {
3985 bool AnyParm = false;
3986 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
3987
3988 for (const VarDecl *GrpMate : VarGroupForVD) {
3989 if (Var == GrpMate)
3990 continue;
3991 if (FixItsForVariable.count(GrpMate))
3992 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
3993 }
3994 if (AnyParm) {
3995 // This assertion should never fail. Otherwise we have a bug.
3996 assert(!FixItsSharedByParms.empty() &&
3997 "Should not try to fix a parameter that does not belong to a "
3998 "FunctionDecl");
3999 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
4000 }
4001 }
4002 // Fix-its that will be applied in one step shall NOT:
4003 // 1. overlap with macros or/and templates; or
4004 // 2. conflict with each other.
4005 // Otherwise, the fix-its will be dropped.
4006 for (auto Iter = FinalFixItsForVariable.begin();
4007 Iter != FinalFixItsForVariable.end();)
4008 if (overlapWithMacro(Iter->second) ||
4010 Iter = FinalFixItsForVariable.erase(Iter);
4011 } else
4012 Iter++;
4013 return FinalFixItsForVariable;
4014}
4015
4016template <typename VarDeclIterTy>
4017static FixitStrategy
4018getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
4019 FixitStrategy S;
4020 for (const VarDecl *VD : UnsafeVars) {
4021 if (isa<ConstantArrayType>(VD->getType().getCanonicalType()))
4022 S.set(VD, FixitStrategy::Kind::Array);
4023 else
4024 S.set(VD, FixitStrategy::Kind::Span);
4025 }
4026 return S;
4027}
4028
4029// Manages variable groups:
4031 const std::vector<VarGrpTy> Groups;
4032 const std::map<const VarDecl *, unsigned> &VarGrpMap;
4033 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
4034
4035public:
4037 const std::vector<VarGrpTy> &Groups,
4038 const std::map<const VarDecl *, unsigned> &VarGrpMap,
4039 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
4040 : Groups(Groups), VarGrpMap(VarGrpMap),
4041 GrpsUnionForParms(GrpsUnionForParms) {}
4042
4043 VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
4044 if (GrpsUnionForParms.contains(Var)) {
4045 if (HasParm)
4046 *HasParm = true;
4047 return GrpsUnionForParms.getArrayRef();
4048 }
4049 if (HasParm)
4050 *HasParm = false;
4051
4052 auto It = VarGrpMap.find(Var);
4053
4054 if (It == VarGrpMap.end())
4055 return {};
4056 return Groups[It->second];
4057 }
4058
4059 VarGrpRef getGroupOfParms() const override {
4060 return GrpsUnionForParms.getArrayRef();
4061 }
4062};
4063
4064static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
4065 WarningGadgetList WarningGadgets,
4066 DeclUseTracker Tracker,
4067 UnsafeBufferUsageHandler &Handler,
4068 bool EmitSuggestions) {
4069 if (!EmitSuggestions) {
4070 // Our job is very easy without suggestions. Just warn about
4071 // every problematic operation and consider it done. No need to deal
4072 // with fixable gadgets, no need to group operations by variable.
4073 for (const auto &G : WarningGadgets) {
4074 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4075 D->getASTContext());
4076 }
4077
4078 // This return guarantees that most of the machine doesn't run when
4079 // suggestions aren't requested.
4080 assert(FixableGadgets.empty() &&
4081 "Fixable gadgets found but suggestions not requested!");
4082 return;
4083 }
4084
4085 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4086 // function under the analysis. No need to fix any Fixables.
4087 if (!WarningGadgets.empty()) {
4088 // Gadgets "claim" variables they're responsible for. Once this loop
4089 // finishes, the tracker will only track DREs that weren't claimed by any
4090 // gadgets, i.e. not understood by the analysis.
4091 for (const auto &G : FixableGadgets) {
4092 for (const auto *DRE : G->getClaimedVarUseSites()) {
4093 Tracker.claimUse(DRE);
4094 }
4095 }
4096 }
4097
4098 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4099 // function under the analysis. Thus, it early returns here as there is
4100 // nothing needs to be fixed.
4101 //
4102 // Note this claim is based on the assumption that there is no unsafe
4103 // variable whose declaration is invisible from the analyzing function.
4104 // Otherwise, we need to consider if the uses of those unsafe varuables needs
4105 // fix.
4106 // So far, we are not fixing any global variables or class members. And,
4107 // lambdas will be analyzed along with the enclosing function. So this early
4108 // return is correct for now.
4109 if (WarningGadgets.empty())
4110 return;
4111
4112 WarningGadgetSets UnsafeOps =
4113 groupWarningGadgetsByVar(std::move(WarningGadgets));
4114 FixableGadgetSets FixablesForAllVars =
4115 groupFixablesByVar(std::move(FixableGadgets));
4116
4117 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
4118
4119 // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
4120 for (auto it = FixablesForAllVars.byVar.cbegin();
4121 it != FixablesForAllVars.byVar.cend();) {
4122 // FIXME: need to deal with global variables later
4123 if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
4124#ifndef NDEBUG
4125 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4126 ("failed to produce fixit for '" +
4127 it->first->getNameAsString() +
4128 "' : neither local nor a parameter"));
4129#endif
4130 it = FixablesForAllVars.byVar.erase(it);
4131 } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
4132#ifndef NDEBUG
4133 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4134 ("failed to produce fixit for '" +
4135 it->first->getNameAsString() +
4136 "' : has a reference type"));
4137#endif
4138 it = FixablesForAllVars.byVar.erase(it);
4139 } else if (Tracker.hasUnclaimedUses(it->first)) {
4140 it = FixablesForAllVars.byVar.erase(it);
4141 } else if (it->first->isInitCapture()) {
4142#ifndef NDEBUG
4143 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4144 ("failed to produce fixit for '" +
4145 it->first->getNameAsString() +
4146 "' : init capture"));
4147#endif
4148 it = FixablesForAllVars.byVar.erase(it);
4149 } else {
4150 ++it;
4151 }
4152 }
4153
4154#ifndef NDEBUG
4155 for (const auto &it : UnsafeOps.byVar) {
4156 const VarDecl *const UnsafeVD = it.first;
4157 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
4158 if (UnclaimedDREs.empty())
4159 continue;
4160 const auto UnfixedVDName = UnsafeVD->getNameAsString();
4161 for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
4162 std::string UnclaimedUseTrace =
4163 getDREAncestorString(UnclaimedDRE, D->getASTContext());
4164
4165 Handler.addDebugNoteForVar(
4166 UnsafeVD, UnclaimedDRE->getBeginLoc(),
4167 ("failed to produce fixit for '" + UnfixedVDName +
4168 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
4169 UnclaimedUseTrace));
4170 }
4171 }
4172#endif
4173
4174 // Fixpoint iteration for pointer assignments
4175 using DepMapTy =
4176 llvm::DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
4177 DepMapTy DependenciesMap{};
4178 DepMapTy PtrAssignmentGraph{};
4179
4180 for (const auto &it : FixablesForAllVars.byVar) {
4181 for (const FixableGadget *fixable : it.second) {
4182 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
4183 fixable->getStrategyImplications();
4184 if (ImplPair) {
4185 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
4186 PtrAssignmentGraph[Impl.first].insert(Impl.second);
4187 }
4188 }
4189 }
4190
4191 /*
4192 The following code does a BFS traversal of the `PtrAssignmentGraph`
4193 considering all unsafe vars as starting nodes and constructs an undirected
4194 graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
4195 elimiates all variables that are unreachable from any unsafe var. In other
4196 words, this removes all dependencies that don't include any unsafe variable
4197 and consequently don't need any fixit generation.
4198 Note: A careful reader would observe that the code traverses
4199 `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
4200 `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
4201 achieve the same result but the one used here dramatically cuts the
4202 amount of hoops the second part of the algorithm needs to jump, given that
4203 a lot of these connections become "direct". The reader is advised not to
4204 imagine how the graph is transformed because of using `Var` instead of
4205 `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
4206 and think about why it's equivalent later.
4207 */
4208 std::set<const VarDecl *> VisitedVarsDirected{};
4209 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4210 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
4211
4212 std::queue<const VarDecl *> QueueDirected{};
4213 QueueDirected.push(Var);
4214 while (!QueueDirected.empty()) {
4215 const VarDecl *CurrentVar = QueueDirected.front();
4216 QueueDirected.pop();
4217 VisitedVarsDirected.insert(CurrentVar);
4218 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
4219 for (const VarDecl *Adj : AdjacentNodes) {
4220 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
4221 QueueDirected.push(Adj);
4222 }
4223 DependenciesMap[Var].insert(Adj);
4224 DependenciesMap[Adj].insert(Var);
4225 }
4226 }
4227 }
4228 }
4229
4230 // `Groups` stores the set of Connected Components in the graph.
4231 std::vector<VarGrpTy> Groups;
4232 // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
4233 // variables belong to. Group indexes refer to the elements in `Groups`.
4234 // `VarGrpMap` is complete in that every variable that needs fix is in it.
4235 std::map<const VarDecl *, unsigned> VarGrpMap;
4236 // The union group over the ones in "Groups" that contain parameters of `D`:
4237 llvm::SetVector<const VarDecl *>
4238 GrpsUnionForParms; // these variables need to be fixed in one step
4239
4240 // Group Connected Components for Unsafe Vars
4241 // (Dependencies based on pointer assignments)
4242 std::set<const VarDecl *> VisitedVars{};
4243 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4244 if (VisitedVars.find(Var) == VisitedVars.end()) {
4245 VarGrpTy &VarGroup = Groups.emplace_back();
4246 std::queue<const VarDecl *> Queue{};
4247
4248 Queue.push(Var);
4249 while (!Queue.empty()) {
4250 const VarDecl *CurrentVar = Queue.front();
4251 Queue.pop();
4252 VisitedVars.insert(CurrentVar);
4253 VarGroup.push_back(CurrentVar);
4254 auto AdjacentNodes = DependenciesMap[CurrentVar];
4255 for (const VarDecl *Adj : AdjacentNodes) {
4256 if (VisitedVars.find(Adj) == VisitedVars.end()) {
4257 Queue.push(Adj);
4258 }
4259 }
4260 }
4261
4262 bool HasParm = false;
4263 unsigned GrpIdx = Groups.size() - 1;
4264
4265 for (const VarDecl *V : VarGroup) {
4266 VarGrpMap[V] = GrpIdx;
4267 if (!HasParm && isParameterOf(V, D))
4268 HasParm = true;
4269 }
4270 if (HasParm)
4271 GrpsUnionForParms.insert_range(VarGroup);
4272 }
4273 }
4274
4275 // Remove a `FixableGadget` if the associated variable is not in the graph
4276 // computed above. We do not want to generate fix-its for such variables,
4277 // since they are neither warned nor reachable from a warned one.
4278 //
4279 // Note a variable is not warned if it is not directly used in any unsafe
4280 // operation. A variable `v` is NOT reachable from an unsafe variable, if it
4281 // does not exist another variable `u` such that `u` is warned and fixing `u`
4282 // (transitively) implicates fixing `v`.
4283 //
4284 // For example,
4285 // ```
4286 // void f(int * p) {
4287 // int * a = p; *p = 0;
4288 // }
4289 // ```
4290 // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
4291 // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of
4292 // the function above, `p` becomes reachable from a warned variable.
4293 for (auto I = FixablesForAllVars.byVar.begin();
4294 I != FixablesForAllVars.byVar.end();) {
4295 // Note `VisitedVars` contain all the variables in the graph:
4296 if (!VisitedVars.count((*I).first)) {
4297 // no such var in graph:
4298 I = FixablesForAllVars.byVar.erase(I);
4299 } else
4300 ++I;
4301 }
4302
4303 // We assign strategies to variables that are 1) in the graph and 2) can be
4304 // fixed. Other variables have the default "Won't fix" strategy.
4305 FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
4306 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
4307 // If a warned variable has no "Fixable", it is considered unfixable:
4308 return FixablesForAllVars.byVar.count(V);
4309 }));
4310 VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
4311
4312 if (isa<NamedDecl>(D))
4313 // The only case where `D` is not a `NamedDecl` is when `D` is a
4314 // `BlockDecl`. Let's not fix variables in blocks for now
4315 FixItsForVariableGroup =
4316 getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
4317 Tracker, Handler, VarGrpMgr);
4318
4319 for (const auto &G : UnsafeOps.noVar) {
4320 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4321 D->getASTContext());
4322 }
4323
4324 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
4325 auto FixItsIt = FixItsForVariableGroup.find(VD);
4326 Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
4327 FixItsIt != FixItsForVariableGroup.end()
4328 ? std::move(FixItsIt->second)
4329 : FixItList{},
4330 D, NaiveStrategy);
4331 for (const auto &G : WarningGadgets) {
4332 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
4333 D->getASTContext());
4334 }
4335 }
4336}
4337
4339 UnsafeBufferUsageHandler &Handler,
4340 bool EmitSuggestions) {
4341#ifndef NDEBUG
4342 Handler.clearDebugNotes();
4343#endif
4344
4345 assert(D);
4346
4347 SmallVector<Stmt *> Stmts;
4348
4349 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
4350 // We do not want to visit a Lambda expression defined inside a method
4351 // independently. Instead, it should be visited along with the outer method.
4352 // FIXME: do we want to do the same thing for `BlockDecl`s?
4353 if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
4354 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
4355 return;
4356 }
4357
4358 for (FunctionDecl *FReDecl : FD->redecls()) {
4359 if (FReDecl->isExternC()) {
4360 // Do not emit fixit suggestions for functions declared in an
4361 // extern "C" block.
4362 EmitSuggestions = false;
4363 break;
4364 }
4365 }
4366
4367 Stmts.push_back(FD->getBody());
4368
4369 if (const auto *ID = dyn_cast<CXXConstructorDecl>(D)) {
4370 for (const CXXCtorInitializer *CI : ID->inits()) {
4371 Stmts.push_back(CI->getInit());
4372 }
4373 }
4374 } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
4375 Stmts.push_back(D->getBody());
4376 }
4377
4378 assert(!Stmts.empty());
4379
4380 FixableGadgetList FixableGadgets;
4381 WarningGadgetList WarningGadgets;
4382 DeclUseTracker Tracker;
4383 for (Stmt *S : Stmts) {
4384 findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
4385 WarningGadgets, Tracker);
4386 }
4387 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
4388 std::move(Tracker), Handler, EmitSuggestions);
4389}
Defines the clang::ASTContext interface.
#define V(N, I)
Definition: ASTContext.h:3597
BoundNodesTreeBuilder Nodes
DynTypedNode Node
const Decl * D
Expr * E
static Decl::Kind getKind(const Decl *D)
Definition: DeclBase.cpp:1192
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
unsigned Iter
Definition: HTMLLogger.cpp:153
static const Decl * getCanonicalDecl(const Decl *D)
llvm::MachO::Target Target
Definition: MachO.h:51
#define SM(sm)
Definition: OffloadArch.cpp:16
Defines the clang::Preprocessor interface.
MatchFinder::MatchResult MatchResult
SourceRange Range
Definition: SemaObjC.cpp:753
SourceLocation Loc
Definition: SemaObjC.cpp:754
Defines the clang::SourceLocation class and associated facilities.
static QualType getPointeeType(const MemRegion *R)
C Language Family Type Representation.
SourceLocation Begin
static bool ignoreUnsafeLibcCall(const ASTContext &Ctx, const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
static void findStmtsInUnspecifiedLvalueContext(const Stmt *S, const llvm::function_ref< void(const Expr *)> OnResult)
static bool isSafeArraySubscript(const ArraySubscriptExpr &Node, const ASTContext &Ctx)
static std::string getUserFillPlaceHolder(StringRef HintTextToUser="placeholder")
static FixItList fixVariableWithSpan(const VarDecl *VD, const DeclUseTracker &Tracker, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static std::optional< FixItList > fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node)
static bool ignoreUnsafeBufferInContainer(const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
static WarningGadgetSets groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations)
static bool hasArrayType(const Expr &E)
static StringRef getEndOfLine()
static bool notInSafeBufferOptOut(const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
static std::optional< FixItList > FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx, const StringRef UserFillPlaceHolder)
static std::optional< SourceLocation > getEndCharLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)
static FixItList fixVariableWithArray(const VarDecl *VD, const DeclUseTracker &Tracker, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static bool areEqualIntegralBinaryOperators(const BinaryOperator *E1, const Expr *E2_LHS, BinaryOperatorKind BOP, const Expr *E2_RHS, ASTContext &Ctx)
static bool hasPointerType(const Expr &E)
static std::string getSpanTypeText(StringRef EltTyText, std::optional< Qualifiers > Quals=std::nullopt)
static SourceRange getSourceRangeToTokenEnd(const Decl *D, const SourceManager &SM, const LangOptions &LangOpts)
static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx, const StringRef UserFillPlaceHolder, UnsafeBufferUsageHandler &Handler)
static std::optional< FixItList > createDataFixit(const ASTContext &Ctx, const DeclRefExpr *DRE)
static FixItList createFunctionOverloadsForParms(std::map< const VarDecl *, FixItList > &FixItsForVariable, const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD, const FixitStrategy &S, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node, ASTContext &Ctx)
static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static FixItList fixVariable(const VarDecl *VD, FixitStrategy::Kind K, const Decl *D, const DeclUseTracker &Tracker, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static FixitStrategy getNaiveStrategy(llvm::iterator_range< VarDeclIterTy > UnsafeVars)
static std::optional< std::string > createSpanTypeForVarDecl(const VarDecl *VD, const ASTContext &Ctx)
static bool hasConflictingOverload(const FunctionDecl *FD)
static void findStmtsInUnspecifiedPointerContext(const Stmt *S, llvm::function_ref< void(const Stmt *)> InnerMatcher)
static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD, const ASTContext &Ctx)
static bool overlapWithMacro(const FixItList &FixIts)
static void forEachDescendantStmt(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, FastMatcher &Matcher)
static bool hasUnsupportedSpecifiers(const VarDecl *VD, const SourceManager &SM)
#define DEBUG_NOTE_DECL_FAIL(D, Msg)
static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets, WarningGadgetList WarningGadgets, DeclUseTracker Tracker, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx)
static void findGadgets(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, bool EmitSuggestions, FixableGadgetList &FixableGadgets, WarningGadgetList &WarningGadgets, DeclUseTracker &Tracker)
static std::map< const VarDecl *, FixItList > getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S, ASTContext &Ctx, const Decl *D, const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler, const VariableGroupsManager &VarGrpMgr)
static bool isPtrBufferSafe(const Expr *Ptr, const Expr *Size, ASTContext &Ctx)
static void forEachDescendantEvaluatedStmt(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, FastMatcher &Matcher)
static void findStmtsInUnspecifiedUntypedContext(const Stmt *S, llvm::function_ref< void(const Stmt *)> InnerMatcher)
static std::optional< FixItList > createOverloadsForFixedParams(const FixitStrategy &S, const FunctionDecl *FD, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static void eraseVarsForUnfixableGroupMates(std::map< const VarDecl *, FixItList > &FixItsForVariable, const VariableGroupsManager &VarGrpMgr)
static FixableGadgetSets groupFixablesByVar(FixableGadgetList &&AllFixableOperations)
static bool isParameterOf(const VarDecl *VD, const Decl *D)
#define SIZED_CONTAINER_OR_VIEW_LIST
static std::optional< StringRef > getFunNameText(const FunctionDecl *FD, const SourceManager &SM, const LangOptions &LangOpts)
__device__ __2f16 float __ockl_bool s
virtual std::optional< FixItList > getFixits(const FixitStrategy &s) const final
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
DerefSimplePtrArithFixableGadget(const MatchResult &Result)
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const final
FixableGadgetMatcher(FixableGadgetList &FixableGadgets, DeclUseTracker &Tracker)
bool matches(const DynTypedNode &DynNode, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler) override
bool TraverseCXXTypeidExpr(CXXTypeidExpr *Node) override
MatchDescendantVisitor(ASTContext &Context, FastMatcher &Matcher, bool FindAll, bool ignoreUnevaluatedContext, const UnsafeBufferUsageHandler &NewHandler)
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node, bool TraverseQualifier) override
bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node, bool TraverseQualifier) override
bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node) override
bool TraverseDecl(Decl *Node) override
bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node) override
bool findMatch(const DynTypedNode &DynNode)
bool TraverseCXXDefaultInitExpr(CXXDefaultInitExpr *Node) override
bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node) override
bool TraverseStmt(Stmt *Node) override
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const override
UPCPreIncrementGadget(const MatchResult &Result)
static bool classof(const Gadget *G)
static bool classof(const Gadget *G)
UUCAddAssignGadget(const MatchResult &Result)
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
static bool matches(const Stmt *S, llvm::SmallVectorImpl< MatchResult > &Results)
virtual DeclUseList getClaimedVarUseSites() const override
SourceLocation getSourceLoc() const override
VariableGroupsManagerImpl(const std::vector< VarGrpTy > &Groups, const std::map< const VarDecl *, unsigned > &VarGrpMap, const llvm::SetVector< const VarDecl * > &GrpsUnionForParms)
VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override
Returns the set of variables (including Var) that need to be fixed together in one step.
VarGrpRef getGroupOfParms() const override
Returns the non-empty group of variables that include parameters of the analyzing function,...
bool matches(const DynTypedNode &DynNode, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler) override
WarningGadgetMatcher(WarningGadgetList &WarningGadgets)
APSInt & getInt()
Definition: APValue.h:489
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:188
SourceManager & getSourceManager()
Definition: ASTContext.h:801
const ConstantArrayType * getAsConstantArrayType(QualType T) const
Definition: ASTContext.h:3056
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
QualType getFILEType() const
Retrieve the C FILE type.
Definition: ASTContext.h:2222
const LangOptions & getLangOpts() const
Definition: ASTContext.h:894
uint64_t getTypeSize(QualType T) const
Return the size of the specified (complete) type T, in bits.
Definition: ASTContext.h:2625
CharUnits getTypeSizeInChars(QualType T) const
Return the size of the specified (complete) type T, in characters.
QualType getSizeType() const
Return the unique type for "size_t" (C99 7.17), defined in <stddef.h>.
const TargetInfo & getTargetInfo() const
Definition: ASTContext.h:859
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition: Expr.h:2723
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:2766
Attr - This represents one attribute.
Definition: Attr.h:44
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3974
Expr * getLHS() const
Definition: Expr.h:4024
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:4029
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
Definition: Expr.cpp:2128
Expr * getRHS() const
Definition: Expr.h:4026
Opcode getOpcode() const
Definition: Expr.h:4019
Represents a call to a C++ constructor.
Definition: ExprCXX.h:1549
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition: ExprCXX.h:1692
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: ExprCXX.cpp:575
Represents a C++ base or member initializer.
Definition: DeclCXX.h:2369
A use of a default initializer in a constructor or in aggregate initialization.
Definition: ExprCXX.h:1378
Represents a call to a member function that may be written either with member call syntax (e....
Definition: ExprCXX.h:179
Represents a static or instance method of a struct/union/class.
Definition: DeclCXX.h:2129
Represents a C++11 noexcept expression (C++ [expr.unary.noexcept]).
Definition: ExprCXX.h:4303
Represents a C++ struct/union/class.
Definition: DeclCXX.h:258
CXXRecordDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition: DeclCXX.h:522
A C++ typeid expression (C++ [expr.typeid]), which gets the type_info that corresponds to the supplie...
Definition: ExprCXX.h:848
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2879
FunctionDecl * getDirectCallee()
If the callee is a FunctionDecl, return it. Otherwise return null.
Definition: Expr.h:3062
static const char * getCastKindName(CastKind CK)
Definition: Expr.cpp:1946
Represents a character-granular source range.
static CharSourceRange getCharRange(SourceRange R)
SourceLocation getEnd() const
bool isOne() const
isOne - Test whether the quantity equals one.
Definition: CharUnits.h:125
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:196
lookup_result lookup(DeclarationName Name) const
lookup - Find the declarations (if any) with the given Name in this context.
Definition: DeclBase.cpp:1879
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1272
ValueDecl * getDecl()
Definition: Expr.h:1340
SourceLocation getBeginLoc() const
Definition: Expr.h:1351
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition: Stmt.h:1611
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Definition: Stmt.h:1624
decl_range decls()
Definition: Stmt.h:1659
const Decl * getSingleDecl() const
Definition: Stmt.h:1626
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
bool isInStdNamespace() const
Definition: DeclBase.cpp:427
SourceLocation getEndLoc() const LLVM_READONLY
Definition: DeclBase.h:435
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:524
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition: DeclBase.h:593
virtual Stmt * getBody() const
getBody - If this Decl represents a declaration for a body of code, such as a function or method defi...
Definition: DeclBase.h:1087
DeclContext * getDeclContext()
Definition: DeclBase.h:448
attr_range attrs() const
Definition: DeclBase.h:535
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: DeclBase.h:431
virtual SourceRange getSourceRange() const LLVM_READONLY
Source range that this declaration covers.
Definition: DeclBase.h:427
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Decl.h:830
NestedNameSpecifierLoc getQualifierLoc() const
Retrieve the nested-name-specifier (with source-location information) that qualifies the name of this...
Definition: Decl.h:844
NestedNameSpecifier getQualifier() const
Retrieve the nested-name-specifier that qualifies the name of this declaration, if it was present in ...
Definition: Decl.h:836
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
const DynTypedNode * begin() const
A dynamically typed AST node container.
const T * get() const
Retrieve the stored node as type T.
static DynTypedNode create(const T &Node)
Creates a DynTypedNode from Node.
SourceRange getSourceRange(bool IncludeQualifier=false) const
For nodes which represent textual entities in the source code, return their SourceRange.
Recursive AST visitor that supports extension via dynamic dispatch.
virtual bool TraverseDecl(MaybeConst< Decl > *D)
Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...
bool ShouldVisitTemplateInstantiations
Whether this visitor should recurse into template instantiations.
bool ShouldVisitImplicitCode
Whether this visitor should recurse into implicit code, e.g.
virtual bool TraverseStmt(MaybeConst< Stmt > *S)
Recursively visit a statement or expression, by dispatching to Traverse*() based on the argument's dy...
ExplicitCastExpr - An explicit cast written in the source code.
Definition: Expr.h:3864
This represents one expression.
Definition: Expr.h:112
bool EvaluateAsInt(EvalResult &Result, const ASTContext &Ctx, SideEffectsKind AllowSideEffects=SE_NoSideEffects, bool InConstantContext=false) const
EvaluateAsInt - Return true if this is a constant which we can fold and convert to an integer,...
bool isValueDependent() const
Determines whether the value of this expression depends on.
Definition: Expr.h:177
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition: Expr.cpp:3073
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition: Expr.cpp:3069
std::optional< llvm::APSInt > getIntegerConstantExpr(const ASTContext &Ctx) const
isIntegerConstantExpr - Return the value if this expression is a valid integer constant expression.
NullPointerConstantValueDependence
Enumeration used to describe how isNullPointerConstant() should cope with value-dependent expressions...
Definition: Expr.h:827
Expr * IgnoreImpCasts() LLVM_READONLY
Skip past any implicit casts which might surround this expression until reaching a fixed point.
Definition: Expr.cpp:3053
QualType getType() const
Definition: Expr.h:144
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition: Diagnostic.h:78
CharSourceRange RemoveRange
Code that should be replaced to correct the error.
Definition: Diagnostic.h:82
static FixItHint CreateReplacement(CharSourceRange RemoveRange, StringRef Code)
Create a code modification hint that replaces the given source range with the given code string.
Definition: Diagnostic.h:139
static FixItHint CreateRemoval(CharSourceRange RemoveRange)
Create a code modification hint that removes the given source range.
Definition: Diagnostic.h:128
static FixItHint CreateInsertion(SourceLocation InsertionLoc, StringRef Code, bool BeforePreviousInsertions=false)
Create a code modification hint that inserts the given code string at a specific location.
Definition: Diagnostic.h:102
Represents a function declaration or definition.
Definition: Decl.h:1999
const ParmVarDecl * getParamDecl(unsigned i) const
Definition: Decl.h:2794
Stmt * getBody(const FunctionDecl *&Definition) const
Retrieve the body (definition) of the function.
Definition: Decl.cpp:3271
param_iterator param_begin()
Definition: Decl.h:2783
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition: Decl.cpp:3767
DeclarationNameInfo getNameInfo() const
Definition: Decl.h:2210
Represents a C11 generic selection.
Definition: Expr.h:6114
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
A simple pair of identifier info and location.
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:3789
static IntegerLiteral * Create(const ASTContext &C, const llvm::APInt &V, QualType type, SourceLocation l)
Returns a new integer literal with value 'V' and type 'type'.
Definition: Expr.cpp:971
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:434
static std::optional< Token > findNextToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts, bool IncludeComments=false)
Finds the token that comes right after the given location.
Definition: Lexer.cpp:1321
static unsigned MeasureTokenLength(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
MeasureTokenLength - Relex the token at the specified location and return its length in bytes in the ...
Definition: Lexer.cpp:498
static SourceLocation getLocForEndOfToken(SourceLocation Loc, unsigned Offset, const SourceManager &SM, const LangOptions &LangOpts)
Computes the source location just past the end of the token at this source location.
Definition: Lexer.cpp:848
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:294
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:300
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:339
std::string getNameAsString() const
Get a human-readable name for the declaration, even if it is one of the special kinds of names (C++ c...
Definition: Decl.h:316
SourceLocation getBeginLoc() const
Retrieve the location of the beginning of this nested-name-specifier.
A single parameter index whose accessors require each use to make explicit the parameter index encodi...
Definition: Attr.h:256
bool isValid() const
Is this parameter index valid?
Definition: Attr.h:316
unsigned getASTIndex() const
Get the parameter index as it would normally be encoded at the AST level of representation: zero-orig...
Definition: Attr.h:335
Represents a parameter to a function.
Definition: Decl.h:1789
bool hasDefaultArg() const
Determines whether this parameter has a default argument, either parsed or not.
Definition: Decl.cpp:3050
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition: Decl.cpp:2969
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition: TypeBase.h:3346
A (possibly-)qualified type.
Definition: TypeBase.h:937
bool hasQualifiers() const
Determine whether this type has any qualifiers.
Definition: TypeBase.h:8432
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition: TypeBase.h:1004
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition: TypeBase.h:8383
QualType getCanonicalType() const
Definition: TypeBase.h:8395
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition: TypeBase.h:8416
std::string getAsString() const
redecl_range redecls() const
Returns an iterator range for all the redeclarations of the same decl.
Definition: Redeclarable.h:293
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
SourceLocation getBegin() const
Stmt - This represents one statement.
Definition: Stmt.h:85
SourceLocation getEndLoc() const LLVM_READONLY
Definition: Stmt.cpp:358
StmtClass getStmtClass() const
Definition: Stmt.h:1472
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Stmt.cpp:346
Exposes information about the current target.
Definition: TargetInfo.h:226
The base class of the type hierarchy.
Definition: TypeBase.h:1833
bool isConstantSizeType() const
Return true if this is not a variable sized type, according to the rules of C99 6....
Definition: Type.cpp:2430
bool isCharType() const
Definition: Type.cpp:2136
bool isPointerType() const
Definition: TypeBase.h:8580
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition: TypeBase.h:8980
const T * castAs() const
Member-template castAs<specific type>.
Definition: TypeBase.h:9226
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:752
bool isAnyCharacterType() const
Determine whether this type is any of the built-in character types.
Definition: Type.cpp:2172
bool isWideCharType() const
Definition: Type.cpp:2145
bool isUnsignedIntegerType() const
Return true if this is an integer type that is unsigned, according to C99 6.2.5p6 [which returns true...
Definition: Type.cpp:2257
bool isAnyPointerType() const
Definition: TypeBase.h:8588
UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand.
Definition: Expr.h:2627
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition: Expr.h:2246
SourceLocation getOperatorLoc() const
getOperatorLoc - Return the location of the operator.
Definition: Expr.h:2291
Expr * getSubExpr() const
Definition: Expr.h:2287
Opcode getOpcode() const
Definition: Expr.h:2282
static bool isIncrementOp(Opcode Op)
Definition: Expr.h:2328
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:2364
static bool isDecrementOp(Opcode Op)
Definition: Expr.h:2335
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
Definition: Expr.cpp:1402
The interface that lets the caller handle unsafe buffer usage analysis results by overriding this cla...
void addDebugNoteForVar(const VarDecl *VD, SourceLocation Loc, std::string Text)
virtual std::string getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc, StringRef WSSuffix="") const =0
virtual bool isSafeBufferOptOut(const SourceLocation &Loc) const =0
virtual bool ignoreUnsafeBufferInContainer(const SourceLocation &Loc) const =0
virtual void handleUnsafeOperation(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0
Invoked when an unsafe operation over raw pointers is found.
virtual void handleUnsafeVariableGroup(const VarDecl *Variable, const VariableGroupsManager &VarGrpMgr, FixItList &&Fixes, const Decl *D, const FixitStrategy &VarTargetTypes)=0
Invoked when a fix is suggested against a variable.
virtual void handleUnsafeOperationInContainer(const Stmt *Operation, bool IsRelatedToDecl, ASTContext &Ctx)=0
Invoked when an unsafe operation with a std container is found.
virtual bool ignoreUnsafeBufferInLibcCall(const SourceLocation &Loc) const =0
virtual void handleUnsafeLibcCall(const CallExpr *Call, unsigned PrintfInfo, ASTContext &Ctx, const Expr *UnsafeArg=nullptr)=0
Invoked when a call to an unsafe libc function is found.
QualType getType() const
Definition: Decl.h:722
Represents a variable declaration or definition.
Definition: Decl.h:925
bool isConstexpr() const
Whether this variable is (C++11) constexpr.
Definition: Decl.h:1568
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition: Decl.cpp:2257
bool isInlineSpecified() const
Definition: Decl.h:1553
bool hasConstantInitialization() const
Determine whether this variable has constant initialization.
Definition: Decl.cpp:2648
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
Definition: Decl.h:1183
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition: Decl.h:1252
const Expr * getAnyInitializer() const
Get the initializer for this variable, no matter which declaration it is attached to.
Definition: Decl.h:1357
virtual VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm=nullptr) const =0
Returns the set of variables (including Var) that need to be fixed together in one step.
virtual VarGrpRef getGroupOfParms() const =0
Returns the non-empty group of variables that include parameters of the analyzing function,...
bool ParsePrintfString(FormatStringHandler &H, const char *beg, const char *end, const LangOptions &LO, const TargetInfo &Target, bool isFreeBSDKPrintf)
void matchEachArgumentWithParamType(const CallExpr &Node, llvm::function_ref< void(QualType, const Expr *)> OnParamAndArg)
bool anyConflict(const llvm::SmallVectorImpl< FixItHint > &FixIts, const SourceManager &SM)
bool Call(InterpState &S, CodePtr OpPC, const Function *Func, uint32_t VarArgSize)
Definition: Interp.cpp:1551
bool matches(const til::SExpr *E1, const til::SExpr *E2)
The JSON file list parser is used to communicate input to InstallAPI.
void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
SourceLocation getVarDeclIdentifierLoc(const DeclaratorDecl *VD)
Definition: FixitUtil.cpp:232
BinaryOperatorKind
std::vector< const VarDecl * > VarGrpTy
std::optional< StringRef > getExprText(const Expr *E, const SourceManager &SM, const LangOptions &LangOpts)
Definition: FixitUtil.cpp:217
std::optional< std::string > getPointeeTypeText(const DeclaratorDecl *VD, const SourceManager &SM, const LangOptions &LangOpts, std::optional< Qualifiers > *QualifiersToAppend)
Definition: FixitUtil.cpp:22
std::optional< StringRef > getRangeText(SourceRange SR, const SourceManager &SM, const LangOptions &LangOpts)
Definition: FixitUtil.cpp:185
std::optional< StringRef > getVarDeclIdentifierText(const DeclaratorDecl *VD, const SourceManager &SM, const LangOptions &LangOpts)
Definition: FixitUtil.cpp:199
const FunctionProtoType * T
std::optional< SourceLocation > getPastLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)
Definition: FixitUtil.h:54
std::set< const Expr * > findUnsafePointers(const FunctionDecl *FD)
static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx, MatchResult &Result, llvm::StringRef Tag)
static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg, const unsigned FmtArgIdx, ASTContext &Ctx, bool isKprintf=false)
static bool isPredefinedUnsafeLibcFunc(const FunctionDecl &Node)
static bool hasUnsafeSnprintfBuffer(const CallExpr &Node, ASTContext &Ctx)
static bool isUnsafeVaListPrintfFunc(const FunctionDecl &Node)
static bool isNullTermPointer(const Expr *Ptr)
static bool isUnsafeSprintfFunc(const FunctionDecl &Node)
static bool isNormalPrintfFunc(const FunctionDecl &Node)
#define false
Definition: stdbool.h:26
bool operator()(const NodeTy *N1, const NodeTy *N2) const
std::map< const VarDecl *, std::set< const FixableGadget * >, CompareNode< VarDecl > > byVar
std::map< const VarDecl *, std::set< const WarningGadget * >, CompareNode< VarDecl > > byVar
llvm::SmallVector< const WarningGadget *, 16 > noVar
SourceLocation getBeginLoc() const
getBeginLoc - Retrieve the location of the first token.
SourceLocation getEndLoc() const LLVM_READONLY
EvalResult is a struct with detailed info about an evaluated expression.
Definition: Expr.h:645
APValue Val
Val - This is the value the expression can be folded to.
Definition: Expr.h:647
StringRef matchName(StringRef FunName, bool isBuiltin)