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