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.
828static bool hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg,
829 const unsigned FmtArgIdx, ASTContext &Ctx,
830 bool isKprintf = false) {
831 class StringFormatStringHandler
833 const CallExpr *Call;
834 unsigned FmtArgIdx;
835 const Expr *&UnsafeArg;
836 ASTContext &Ctx;
837 bool UnsafeArgSet;
838
839 // Returns an `Expr` representing the precision if specified, null
840 // otherwise.
841 // The parameter `Call` is a printf call and the parameter `Precision` is
842 // the precision of a format specifier of the `Call`.
843 //
844 // For example, for the `printf("%d, %.10s", 10, p)` call
845 // `Precision` can be the precision of either "%d" or "%.10s". The former
846 // one will have `NotSpecified` kind.
847 const Expr *
848 getPrecisionAsExpr(const analyze_printf::OptionalAmount &Precision,
849 const CallExpr *Call) {
850 unsigned PArgIdx = -1;
851
852 if (Precision.hasDataArgument())
853 PArgIdx = Precision.getPositionalArgIndex() + FmtArgIdx;
854 if (0 < PArgIdx && PArgIdx < Call->getNumArgs()) {
855 const Expr *PArg = Call->getArg(PArgIdx);
856
857 // Strip the cast if `PArg` is a cast-to-int expression:
858 if (auto *CE = dyn_cast<CastExpr>(PArg);
859 CE && CE->getType()->isSignedIntegerType())
860 PArg = CE->getSubExpr();
861 return PArg;
862 }
863 if (Precision.getHowSpecified() ==
864 analyze_printf::OptionalAmount::HowSpecified::Constant) {
865 auto SizeTy = Ctx.getSizeType();
866 llvm::APSInt PArgVal = llvm::APSInt(
867 llvm::APInt(Ctx.getTypeSize(SizeTy), Precision.getConstantAmount()),
868 true);
869
870 return IntegerLiteral::Create(Ctx, PArgVal, Ctx.getSizeType(), {});
871 }
872 return nullptr;
873 }
874
875 public:
876 StringFormatStringHandler(const CallExpr *Call, unsigned FmtArgIdx,
877 const Expr *&UnsafeArg, ASTContext &Ctx)
878 : Call(Call), FmtArgIdx(FmtArgIdx), UnsafeArg(UnsafeArg), Ctx(Ctx),
879 UnsafeArgSet(false) {}
880
881 bool HandlePrintfSpecifier(const analyze_printf::PrintfSpecifier &FS,
882 const char *startSpecifier,
883 unsigned specifierLen,
884 const TargetInfo &Target) override {
885 if (FS.getConversionSpecifier().getKind() !=
887 return true; // continue parsing
888
889 unsigned ArgIdx = FS.getPositionalArgIndex() + FmtArgIdx;
890
891 if (!(0 < ArgIdx && ArgIdx < Call->getNumArgs()))
892 // If the `ArgIdx` is invalid, give up.
893 return true; // continue parsing
894
895 const Expr *Arg = Call->getArg(ArgIdx);
896
897 if (isNullTermPointer(Arg, Ctx))
898 // If Arg is a null-terminated pointer, it is safe anyway.
899 return true; // continue parsing
900
901 // Otherwise, check if the specifier has a precision and if the character
902 // pointer is safely bound by the precision:
904 QualType ArgType = Arg->getType();
905 bool IsArgTypeValid = // Is ArgType a character pointer type?
906 ArgType->isPointerType() &&
908 ? ArgType->getPointeeType()->isWideCharType()
909 : ArgType->getPointeeType()->isCharType());
910
911 if (auto *Precision = getPrecisionAsExpr(FS.getPrecision(), Call);
912 Precision && IsArgTypeValid)
913 if (isPtrBufferSafe(Arg, Precision, Ctx))
914 return true;
915 // Handle unsafe case:
916 UnsafeArg = Call->getArg(ArgIdx); // output
917 UnsafeArgSet = true;
918 return false; // returning false stops parsing immediately
919 }
920
921 bool isUnsafeArgSet() { return UnsafeArgSet; }
922 };
923
924 const Expr *Fmt = Call->getArg(FmtArgIdx);
925
926 if (auto *SL = dyn_cast<clang::StringLiteral>(Fmt->IgnoreParenImpCasts())) {
927 if (SL->getCharByteWidth() == 1) {
928 StringRef FmtStr = SL->getString();
929 StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg, Ctx);
930
932 Handler, FmtStr.begin(), FmtStr.end(), Ctx.getLangOpts(),
933 Ctx.getTargetInfo(), isKprintf) &&
934 Handler.isUnsafeArgSet();
935 }
936
937 if (auto FmtStr = SL->tryEvaluateString(Ctx)) {
938 StringFormatStringHandler Handler(Call, FmtArgIdx, UnsafeArg, Ctx);
940 Handler, FmtStr->data(), FmtStr->data() + FmtStr->size(),
941 Ctx.getLangOpts(), Ctx.getTargetInfo(), isKprintf) &&
942 Handler.isUnsafeArgSet();
943 }
944 }
945 // If format is not a string literal, we cannot analyze the format string.
946 // In this case, this call is considered unsafe if at least one argument
947 // (including the format argument) is unsafe pointer.
948 return llvm::any_of(
949 llvm::make_range(Call->arg_begin() + FmtArgIdx, Call->arg_end()),
950 [&UnsafeArg, &Ctx](const Expr *Arg) -> bool {
951 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg, Ctx)) {
952 UnsafeArg = Arg;
953 return true;
954 }
955 return false;
956 });
957}
958
959// Matches a FunctionDecl node such that
960// 1. It's name, after stripping off predefined prefix and suffix, is
961// `CoreName`; and
962// 2. `CoreName` or `CoreName[str/wcs]` is one of the `PredefinedNames`, which
963// is a set of libc function names.
964//
965// Note: For predefined prefix and suffix, see `LibcFunNamePrefixSuffixParser`.
966// The notation `CoreName[str/wcs]` means a new name obtained from replace
967// string "wcs" with "str" in `CoreName`.
968static bool isPredefinedUnsafeLibcFunc(const FunctionDecl &Node) {
969 static std::unique_ptr<std::set<StringRef>> PredefinedNames = nullptr;
970 if (!PredefinedNames)
971 PredefinedNames =
972 std::make_unique<std::set<StringRef>, std::set<StringRef>>({
973 // numeric conversion:
974 "atof",
975 "atoi",
976 "atol",
977 "atoll",
978 "strtol",
979 "strtoll",
980 "strtoul",
981 "strtoull",
982 "strtof",
983 "strtod",
984 "strtold",
985 "strtoimax",
986 "strtoumax",
987 // "strfromf", "strfromd", "strfroml", // C23?
988 // string manipulation:
989 "strcpy",
990 "strncpy",
991 "strlcpy",
992 "strcat",
993 "strncat",
994 "strlcat",
995 "strxfrm",
996 "strdup",
997 "strndup",
998 // string examination:
999 "strlen",
1000 "strnlen",
1001 "strcmp",
1002 "strncmp",
1003 "stricmp",
1004 "strcasecmp",
1005 "strcoll",
1006 "strchr",
1007 "strrchr",
1008 "strspn",
1009 "strcspn",
1010 "strpbrk",
1011 "strstr",
1012 "strtok",
1013 // "mem-" functions
1014 "memchr",
1015 "wmemchr",
1016 "memcmp",
1017 "wmemcmp",
1018 "memcpy",
1019 "memccpy",
1020 "mempcpy",
1021 "wmemcpy",
1022 "memmove",
1023 "wmemmove",
1024 "memset",
1025 "wmemset",
1026 // IO:
1027 "fread",
1028 "fwrite",
1029 "fgets",
1030 "fgetws",
1031 "gets",
1032 "fputs",
1033 "fputws",
1034 "puts",
1035 // others
1036 "strerror_s",
1037 "strerror_r",
1038 "bcopy",
1039 "bzero",
1040 "bsearch",
1041 "qsort",
1042 });
1043
1044 auto *II = Node.getIdentifier();
1045
1046 if (!II)
1047 return false;
1048
1049 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
1050 II->getName(), Node.getBuiltinID());
1051
1052 // Match predefined names:
1053 if (PredefinedNames->find(Name) != PredefinedNames->end())
1054 return true;
1055
1056 std::string NameWCS = Name.str();
1057 size_t WcsPos = NameWCS.find("wcs");
1058
1059 while (WcsPos != std::string::npos) {
1060 NameWCS[WcsPos++] = 's';
1061 NameWCS[WcsPos++] = 't';
1062 NameWCS[WcsPos++] = 'r';
1063 WcsPos = NameWCS.find("wcs", WcsPos);
1064 }
1065 if (PredefinedNames->find(NameWCS) != PredefinedNames->end())
1066 return true;
1067 // All `scanf` functions are unsafe (including `sscanf`, `vsscanf`, etc.. They
1068 // all should end with "scanf"):
1069 return Name.ends_with("scanf");
1070}
1071
1072// Match a call to one of the `v*printf` functions taking `va_list`. We cannot
1073// check safety for these functions so they should be changed to their
1074// non-va_list versions.
1075static bool isUnsafeVaListPrintfFunc(const FunctionDecl &Node) {
1076 auto *II = Node.getIdentifier();
1077
1078 if (!II)
1079 return false;
1080
1081 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
1082 II->getName(), Node.getBuiltinID());
1083
1084 if (!Name.ends_with("printf"))
1085 return false; // neither printf nor scanf
1086 return Name.starts_with("v");
1087}
1088
1089// Matches a call to one of the `sprintf` functions as they are always unsafe
1090// and should be changed to `snprintf`.
1091static bool isUnsafeSprintfFunc(const FunctionDecl &Node) {
1092 auto *II = Node.getIdentifier();
1093
1094 if (!II)
1095 return false;
1096
1097 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
1098 II->getName(), Node.getBuiltinID());
1099
1100 if (!Name.ends_with("printf") ||
1101 // Let `isUnsafeVaListPrintfFunc` check for cases with va-list:
1102 Name.starts_with("v"))
1103 return false;
1104
1105 StringRef Prefix = Name.drop_back(6);
1106
1107 if (Prefix.ends_with("w"))
1108 Prefix = Prefix.drop_back(1);
1109 return Prefix == "s";
1110}
1111
1112// Match function declarations of `printf`, `fprintf`, `snprintf` and their wide
1113// character versions. Calls to these functions can be safe if their arguments
1114// are carefully made safe.
1115static bool isNormalPrintfFunc(const FunctionDecl &Node) {
1116 auto *II = Node.getIdentifier();
1117
1118 if (!II)
1119 return false;
1120
1121 StringRef Name = LibcFunNamePrefixSuffixParser().matchName(
1122 II->getName(), Node.getBuiltinID());
1123
1124 if (!Name.ends_with("printf") || Name.starts_with("v"))
1125 return false;
1126
1127 StringRef Prefix = Name.drop_back(6);
1128
1129 if (Prefix.ends_with("w"))
1130 Prefix = Prefix.drop_back(1);
1131
1132 return Prefix.empty() || Prefix == "k" || Prefix == "f" || Prefix == "sn";
1133}
1134
1135// This matcher requires that it is known that the callee `isNormalPrintf`.
1136// Then if the format string is a string literal, this matcher matches when at
1137// least one string argument is unsafe. If the format is not a string literal,
1138// this matcher matches when at least one pointer type argument is unsafe.
1139static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx,
1140 MatchResult &Result, llvm::StringRef Tag) {
1141 // Determine what printf it is by examining formal parameters:
1142 const FunctionDecl *FD = Node.getDirectCallee();
1143
1144 assert(FD && "It should have been checked that FD is non-null.");
1145
1146 unsigned NumParms = FD->getNumParams();
1147
1148 if (NumParms < 1)
1149 return false; // possibly some user-defined printf function
1150
1151 QualType FirstParmTy = FD->getParamDecl(0)->getType();
1152
1153 if (!FirstParmTy->isPointerType())
1154 return false; // possibly some user-defined printf function
1155
1156 QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
1157
1158 if (!Ctx.getFILEType()
1159 .isNull() && //`FILE *` must be in the context if it is fprintf
1160 FirstPteTy.getCanonicalType() == Ctx.getFILEType().getCanonicalType()) {
1161 // It is a fprintf:
1162 const Expr *UnsafeArg;
1163
1164 if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 1, Ctx, false)) {
1165 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1166 return true;
1167 }
1168 return false;
1169 }
1170
1171 if (FirstPteTy.isConstQualified()) {
1172 // If the first parameter is a `const char *`, it is a printf/kprintf:
1173 bool isKprintf = false;
1174 const Expr *UnsafeArg;
1175
1176 if (auto *II = FD->getIdentifier())
1177 isKprintf = II->getName() == "kprintf";
1178 if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 0, Ctx, isKprintf)) {
1179 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1180 return true;
1181 }
1182 return false;
1183 }
1184
1185 if (NumParms > 2) {
1186 QualType SecondParmTy = FD->getParamDecl(1)->getType();
1187
1188 if (!FirstPteTy.isConstQualified() && SecondParmTy->isIntegerType()) {
1189 // If the first parameter type is non-const qualified `char *` and the
1190 // second is an integer, it is a snprintf:
1191 const Expr *UnsafeArg;
1192
1193 if (hasUnsafeFormatOrSArg(&Node, UnsafeArg, 2, Ctx, false)) {
1194 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1195 return true;
1196 }
1197 return false;
1198 }
1199 }
1200 // We don't really recognize this "normal" printf, the only thing we
1201 // can do is to require all pointers to be null-terminated:
1202 for (const auto *Arg : Node.arguments())
1203 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg, Ctx)) {
1204 Result.addNode(Tag, DynTypedNode::create(*Arg));
1205 return true;
1206 }
1207 return false;
1208}
1209
1210// This function requires that it is known that the callee `isNormalPrintf`.
1211// It returns true iff the first two arguments of the call is a pointer
1212// `Ptr` and an unsigned integer `Size` and they are NOT safe, i.e.,
1213// `!isPtrBufferSafe(Ptr, Size)`.
1214static bool hasUnsafeSnprintfBuffer(const CallExpr &Node, ASTContext &Ctx) {
1215 const FunctionDecl *FD = Node.getDirectCallee();
1216
1217 assert(FD && "It should have been checked that FD is non-null.");
1218
1219 if (FD->getNumParams() < 3)
1220 return false; // Not an snprint
1221
1222 QualType FirstParmTy = FD->getParamDecl(0)->getType();
1223
1224 if (!FirstParmTy->isPointerType())
1225 return false; // Not an snprint
1226
1227 QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
1228 const Expr *Buf = Node.getArg(0), *Size = Node.getArg(1);
1229
1230 if (FirstPteTy.isConstQualified() || !FirstPteTy->isAnyCharacterType() ||
1231 !Buf->getType()->isPointerType() ||
1232 !Size->getType()->isUnsignedIntegerType())
1233 return false; // not an snprintf call
1234
1235 return !isPtrBufferSafe(Buf, Size, Ctx);
1236}
1237} // namespace libc_func_matchers
1238
1239namespace {
1240// Because the analysis revolves around variables and their types, we'll need to
1241// track uses of variables (aka DeclRefExprs).
1242using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
1243
1244// Convenience typedef.
1245using FixItList = SmallVector<FixItHint, 4>;
1246} // namespace
1247
1248namespace {
1249/// Gadget is an individual operation in the code that may be of interest to
1250/// this analysis. Each (non-abstract) subclass corresponds to a specific
1251/// rigid AST structure that constitutes an operation on a pointer-type object.
1252/// Discovery of a gadget in the code corresponds to claiming that we understand
1253/// what this part of code is doing well enough to potentially improve it.
1254/// Gadgets can be warning (immediately deserving a warning) or fixable (not
1255/// always deserving a warning per se, but requires our attention to identify
1256/// it warrants a fixit).
1257class Gadget {
1258public:
1259 enum class Kind {
1260#define GADGET(x) x,
1261#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1262 };
1263
1264 Gadget(Kind K) : K(K) {}
1265
1266 Kind getKind() const { return K; }
1267
1268#ifndef NDEBUG
1269 StringRef getDebugName() const {
1270 switch (K) {
1271#define GADGET(x) \
1272 case Kind::x: \
1273 return #x;
1274#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1275 }
1276 llvm_unreachable("Unhandled Gadget::Kind enum");
1277 }
1278#endif
1279
1280 virtual bool isWarningGadget() const = 0;
1281 // TODO remove this method from WarningGadget interface. It's only used for
1282 // debug prints in FixableGadget.
1283 virtual SourceLocation getSourceLoc() const = 0;
1284
1285 /// Returns the list of pointer-type variables on which this gadget performs
1286 /// its operation. Typically, there's only one variable. This isn't a list
1287 /// of all DeclRefExprs in the gadget's AST!
1288 virtual DeclUseList getClaimedVarUseSites() const = 0;
1289
1290 virtual ~Gadget() = default;
1291
1292private:
1293 Kind K;
1294};
1295
1296/// Warning gadgets correspond to unsafe code patterns that warrants
1297/// an immediate warning.
1298class WarningGadget : public Gadget {
1299public:
1300 WarningGadget(Kind K) : Gadget(K) {}
1301
1302 static bool classof(const Gadget *G) { return G->isWarningGadget(); }
1303 bool isWarningGadget() const final { return true; }
1304
1305 virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1306 bool IsRelatedToDecl,
1307 ASTContext &Ctx) const = 0;
1308
1309 virtual SmallVector<const Expr *, 1> getUnsafePtrs() const = 0;
1310};
1311
1312/// Fixable gadgets correspond to code patterns that aren't always unsafe but
1313/// need to be properly recognized in order to emit fixes. For example, if a raw
1314/// pointer-type variable is replaced by a safe C++ container, every use of such
1315/// variable must be carefully considered and possibly updated.
1316class FixableGadget : public Gadget {
1317public:
1318 FixableGadget(Kind K) : Gadget(K) {}
1319
1320 static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
1321 bool isWarningGadget() const final { return false; }
1322
1323 /// Returns a fixit that would fix the current gadget according to
1324 /// the current strategy. Returns std::nullopt if the fix cannot be produced;
1325 /// returns an empty list if no fixes are necessary.
1326 virtual std::optional<FixItList> getFixits(const FixitStrategy &) const {
1327 return std::nullopt;
1328 }
1329
1330 /// Returns a list of two elements where the first element is the LHS of a
1331 /// pointer assignment statement and the second element is the RHS. This
1332 /// two-element list represents the fact that the LHS buffer gets its bounds
1333 /// information from the RHS buffer. This information will be used later to
1334 /// group all those variables whose types must be modified together to prevent
1335 /// type mismatches.
1336 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1337 getStrategyImplications() const {
1338 return std::nullopt;
1339 }
1340};
1341
1342static bool isSupportedVariable(const DeclRefExpr &Node) {
1343 const Decl *D = Node.getDecl();
1344 return D != nullptr && isa<VarDecl>(D);
1345}
1346
1347// Returns true for RecordDecl of type std::unique_ptr<T[]>
1348static bool isUniquePtrArray(const CXXRecordDecl *RecordDecl) {
1350 RecordDecl->getNameAsString() != "unique_ptr")
1351 return false;
1352
1353 const ClassTemplateSpecializationDecl *class_template_specialization_decl =
1354 dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl);
1355 if (!class_template_specialization_decl)
1356 return false;
1357
1358 const TemplateArgumentList &template_args =
1359 class_template_specialization_decl->getTemplateArgs();
1360 if (template_args.size() == 0)
1361 return false;
1362
1363 const TemplateArgument &first_arg = template_args[0];
1364 if (first_arg.getKind() != TemplateArgument::Type)
1365 return false;
1366
1367 QualType referred_type = first_arg.getAsType();
1368 return referred_type->isArrayType();
1369}
1370
1371class UniquePtrArrayAccessGadget : public WarningGadget {
1372private:
1373 static constexpr const char *const AccessorTag = "unique_ptr_array_access";
1374 const CXXOperatorCallExpr *AccessorExpr;
1375
1376public:
1377 UniquePtrArrayAccessGadget(const MatchResult &Result)
1378 : WarningGadget(Kind::UniquePtrArrayAccess),
1379 AccessorExpr(Result.getNodeAs<CXXOperatorCallExpr>(AccessorTag)) {
1380 assert(AccessorExpr &&
1381 "UniquePtrArrayAccessGadget requires a matched CXXOperatorCallExpr");
1382 }
1383
1384 static bool classof(const Gadget *G) {
1385 return G->getKind() == Kind::UniquePtrArrayAccess;
1386 }
1387
1388 static bool matches(const Stmt *S, const ASTContext &Ctx,
1390
1391 const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(S);
1392 if (!OpCall || OpCall->getOperator() != OO_Subscript)
1393 return false;
1394
1395 const Expr *Callee = OpCall->getCallee()->IgnoreParenImpCasts();
1396 if (!Callee)
1397 return false;
1398
1399 const CXXMethodDecl *Method =
1400 dyn_cast_or_null<CXXMethodDecl>(OpCall->getDirectCallee());
1401 if (!Method)
1402 return false;
1403
1404 if (Method->getOverloadedOperator() != OO_Subscript)
1405 return false;
1406
1407 const CXXRecordDecl *RecordDecl = Method->getParent();
1408 if (!isUniquePtrArray(RecordDecl))
1409 return false;
1410
1411 const Expr *IndexExpr = OpCall->getArg(1);
1412 clang::Expr::EvalResult Eval;
1413
1414 // Allow [0]
1415 if (IndexExpr->EvaluateAsInt(Eval, Ctx) && Eval.Val.getInt().isZero())
1416 return false;
1417
1418 Result.addNode(AccessorTag, DynTypedNode::create(*OpCall));
1419 return true;
1420 }
1421 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1422 bool IsRelatedToDecl,
1423 ASTContext &Ctx) const override {
1425 DynTypedNode::create(*AccessorExpr), IsRelatedToDecl, Ctx);
1426 }
1427
1428 SourceLocation getSourceLoc() const override {
1429 if (AccessorExpr)
1430 return AccessorExpr->getOperatorLoc();
1431 return SourceLocation();
1432 }
1433
1434 DeclUseList getClaimedVarUseSites() const override { return {}; }
1435 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1436};
1437
1438using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
1439using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
1440
1441/// An increment of a pointer-type value is unsafe as it may run the pointer
1442/// out of bounds.
1443class IncrementGadget : public WarningGadget {
1444 static constexpr const char *const OpTag = "op";
1445 const UnaryOperator *Op;
1446
1447public:
1448 IncrementGadget(const MatchResult &Result)
1449 : WarningGadget(Kind::Increment),
1450 Op(Result.getNodeAs<UnaryOperator>(OpTag)) {}
1451
1452 static bool classof(const Gadget *G) {
1453 return G->getKind() == Kind::Increment;
1454 }
1455
1456 static bool matches(const Stmt *S, const ASTContext &Ctx,
1458 const auto *UO = dyn_cast<UnaryOperator>(S);
1459 if (!UO || !UO->isIncrementOp())
1460 return false;
1462 return false;
1463 Result.addNode(OpTag, DynTypedNode::create(*UO));
1464 return true;
1465 }
1466
1467 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1468 bool IsRelatedToDecl,
1469 ASTContext &Ctx) const override {
1470 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1471 }
1472 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1473
1474 DeclUseList getClaimedVarUseSites() const override {
1475 SmallVector<const DeclRefExpr *, 2> Uses;
1476 if (const auto *DRE =
1477 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1478 Uses.push_back(DRE);
1479 }
1480
1481 return std::move(Uses);
1482 }
1483
1484 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1485 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1486 }
1487};
1488
1489/// A decrement of a pointer-type value is unsafe as it may run the pointer
1490/// out of bounds.
1491class DecrementGadget : public WarningGadget {
1492 static constexpr const char *const OpTag = "op";
1493 const UnaryOperator *Op;
1494
1495public:
1496 DecrementGadget(const MatchResult &Result)
1497 : WarningGadget(Kind::Decrement),
1498 Op(Result.getNodeAs<UnaryOperator>(OpTag)) {}
1499
1500 static bool classof(const Gadget *G) {
1501 return G->getKind() == Kind::Decrement;
1502 }
1503
1504 static bool matches(const Stmt *S, const ASTContext &Ctx,
1506 const auto *UO = dyn_cast<UnaryOperator>(S);
1507 if (!UO || !UO->isDecrementOp())
1508 return false;
1510 return false;
1511 Result.addNode(OpTag, DynTypedNode::create(*UO));
1512 return true;
1513 }
1514
1515 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1516 bool IsRelatedToDecl,
1517 ASTContext &Ctx) const override {
1518 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1519 }
1520 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1521
1522 DeclUseList getClaimedVarUseSites() const override {
1523 if (const auto *DRE =
1524 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1525 return {DRE};
1526 }
1527
1528 return {};
1529 }
1530
1531 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1532 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1533 }
1534};
1535
1536/// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
1537/// it doesn't have any bounds checks for the array.
1538class ArraySubscriptGadget : public WarningGadget {
1539 static constexpr const char *const ArraySubscrTag = "ArraySubscript";
1540 const ArraySubscriptExpr *ASE;
1541
1542public:
1543 ArraySubscriptGadget(const MatchResult &Result)
1544 : WarningGadget(Kind::ArraySubscript),
1545 ASE(Result.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
1546
1547 static bool classof(const Gadget *G) {
1548 return G->getKind() == Kind::ArraySubscript;
1549 }
1550
1551 static bool matches(const Stmt *S, const ASTContext &Ctx,
1553 const auto *ASE = dyn_cast<ArraySubscriptExpr>(S);
1554 if (!ASE)
1555 return false;
1556 const auto *const Base = ASE->getBase()->IgnoreParenImpCasts();
1557 if (!hasPointerType(*Base) && !hasArrayType(*Base))
1558 return false;
1559 const auto *Idx = dyn_cast<IntegerLiteral>(ASE->getIdx());
1560 bool IsSafeIndex = (Idx && Idx->getValue().isZero()) ||
1561 isa<ArrayInitIndexExpr>(ASE->getIdx());
1562 if (IsSafeIndex || isSafeArraySubscript(*ASE, Ctx))
1563 return false;
1564 Result.addNode(ArraySubscrTag, DynTypedNode::create(*ASE));
1565 return true;
1566 }
1567
1568 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1569 bool IsRelatedToDecl,
1570 ASTContext &Ctx) const override {
1571 Handler.handleUnsafeOperation(ASE, IsRelatedToDecl, Ctx);
1572 }
1573 SourceLocation getSourceLoc() const override { return ASE->getBeginLoc(); }
1574
1575 DeclUseList getClaimedVarUseSites() const override {
1576 if (const auto *DRE =
1577 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
1578 return {DRE};
1579 }
1580
1581 return {};
1582 }
1583
1584 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1585 return {ASE->getBase()->IgnoreParenImpCasts()};
1586 }
1587};
1588
1589/// A pointer arithmetic expression of one of the forms:
1590/// \code
1591/// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
1592/// \endcode
1593class PointerArithmeticGadget : public WarningGadget {
1594 static constexpr const char *const PointerArithmeticTag = "ptrAdd";
1595 static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
1596 const BinaryOperator *PA; // pointer arithmetic expression
1597 const Expr *Ptr; // the pointer expression in `PA`
1598
1599public:
1600 PointerArithmeticGadget(const MatchResult &Result)
1601 : WarningGadget(Kind::PointerArithmetic),
1602 PA(Result.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
1603 Ptr(Result.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
1604
1605 static bool classof(const Gadget *G) {
1606 return G->getKind() == Kind::PointerArithmetic;
1607 }
1608
1609 static bool matches(const Stmt *S, const ASTContext &Ctx,
1611 const auto *BO = dyn_cast<BinaryOperator>(S);
1612 if (!BO)
1613 return false;
1614 const auto *LHS = BO->getLHS();
1615 const auto *RHS = BO->getRHS();
1616 // ptr at left
1617 if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub ||
1618 BO->getOpcode() == BO_AddAssign || BO->getOpcode() == BO_SubAssign) {
1619 if (hasPointerType(*LHS) && (RHS->getType()->isIntegerType() ||
1620 RHS->getType()->isEnumeralType())) {
1621 Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*LHS));
1622 Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
1623 return true;
1624 }
1625 }
1626 // ptr at right
1627 if (BO->getOpcode() == BO_Add && hasPointerType(*RHS) &&
1628 (LHS->getType()->isIntegerType() || LHS->getType()->isEnumeralType())) {
1629 Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*RHS));
1630 Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
1631 return true;
1632 }
1633 return false;
1634 }
1635
1636 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1637 bool IsRelatedToDecl,
1638 ASTContext &Ctx) const override {
1639 Handler.handleUnsafeOperation(PA, IsRelatedToDecl, Ctx);
1640 }
1641 SourceLocation getSourceLoc() const override { return PA->getBeginLoc(); }
1642
1643 DeclUseList getClaimedVarUseSites() const override {
1644 if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
1645 return {DRE};
1646 }
1647
1648 return {};
1649 }
1650
1651 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1652 return {Ptr->IgnoreParenImpCasts()};
1653 }
1654
1655 // FIXME: pointer adding zero should be fine
1656 // FIXME: this gadge will need a fix-it
1657};
1658
1659class SpanTwoParamConstructorGadget : public WarningGadget {
1660 static constexpr const char *const SpanTwoParamConstructorTag =
1661 "spanTwoParamConstructor";
1662 const CXXConstructExpr *Ctor; // the span constructor expression
1663
1664public:
1665 SpanTwoParamConstructorGadget(const MatchResult &Result)
1666 : WarningGadget(Kind::SpanTwoParamConstructor),
1667 Ctor(Result.getNodeAs<CXXConstructExpr>(SpanTwoParamConstructorTag)) {}
1668
1669 static bool classof(const Gadget *G) {
1670 return G->getKind() == Kind::SpanTwoParamConstructor;
1671 }
1672
1673 static bool matches(const Stmt *S, ASTContext &Ctx, MatchResult &Result) {
1674 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1675 if (!CE)
1676 return false;
1677 const auto *CDecl = CE->getConstructor();
1678 const auto *CRecordDecl = CDecl->getParent();
1679 auto HasTwoParamSpanCtorDecl =
1680 CRecordDecl->isInStdNamespace() &&
1681 CDecl->getDeclName().getAsString() == "span" && CE->getNumArgs() == 2;
1682 if (!HasTwoParamSpanCtorDecl || isSafeSpanTwoParamConstruct(*CE, Ctx))
1683 return false;
1684 Result.addNode(SpanTwoParamConstructorTag, DynTypedNode::create(*CE));
1685 return true;
1686 }
1687
1688 static bool matches(const Stmt *S, ASTContext &Ctx,
1689 const UnsafeBufferUsageHandler *Handler,
1691 if (ignoreUnsafeBufferInContainer(*S, Handler))
1692 return false;
1693 return matches(S, Ctx, Result);
1694 }
1695
1696 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1697 bool IsRelatedToDecl,
1698 ASTContext &Ctx) const override {
1699 Handler.handleUnsafeOperationInContainer(Ctor, IsRelatedToDecl, Ctx);
1700 }
1701 SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); }
1702
1703 DeclUseList getClaimedVarUseSites() const override {
1704 // If the constructor call is of the form `std::span{var, n}`, `var` is
1705 // considered an unsafe variable.
1706 if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1707 if (isa<VarDecl>(DRE->getDecl()))
1708 return {DRE};
1709 }
1710 return {};
1711 }
1712
1713 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1714};
1715
1716/// A pointer initialization expression of the form:
1717/// \code
1718/// int *p = q;
1719/// \endcode
1720class PointerInitGadget : public FixableGadget {
1721private:
1722 static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
1723 static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
1724 const VarDecl *PtrInitLHS; // the LHS pointer expression in `PI`
1725 const DeclRefExpr *PtrInitRHS; // the RHS pointer expression in `PI`
1726
1727public:
1728 PointerInitGadget(const MatchResult &Result)
1729 : FixableGadget(Kind::PointerInit),
1730 PtrInitLHS(Result.getNodeAs<VarDecl>(PointerInitLHSTag)),
1731 PtrInitRHS(Result.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
1732
1733 static bool classof(const Gadget *G) {
1734 return G->getKind() == Kind::PointerInit;
1735 }
1736
1737 static bool matches(const Stmt *S,
1738 llvm::SmallVectorImpl<MatchResult> &Results) {
1739 const DeclStmt *DS = dyn_cast<DeclStmt>(S);
1740 if (!DS || !DS->isSingleDecl())
1741 return false;
1742 const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
1743 if (!VD)
1744 return false;
1745 const Expr *Init = VD->getAnyInitializer();
1746 if (!Init)
1747 return false;
1748 const auto *DRE = dyn_cast<DeclRefExpr>(Init->IgnoreImpCasts());
1749 if (!DRE || !hasPointerType(*DRE) || !isSupportedVariable(*DRE)) {
1750 return false;
1751 }
1752 MatchResult R;
1753 R.addNode(PointerInitLHSTag, DynTypedNode::create(*VD));
1754 R.addNode(PointerInitRHSTag, DynTypedNode::create(*DRE));
1755 Results.emplace_back(std::move(R));
1756 return true;
1757 }
1758
1759 virtual std::optional<FixItList>
1760 getFixits(const FixitStrategy &S) const override;
1761 SourceLocation getSourceLoc() const override {
1762 return PtrInitRHS->getBeginLoc();
1763 }
1764
1765 virtual DeclUseList getClaimedVarUseSites() const override {
1766 return DeclUseList{PtrInitRHS};
1767 }
1768
1769 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1770 getStrategyImplications() const override {
1771 return std::make_pair(PtrInitLHS, cast<VarDecl>(PtrInitRHS->getDecl()));
1772 }
1773};
1774
1775/// A pointer assignment expression of the form:
1776/// \code
1777/// p = q;
1778/// \endcode
1779/// where both `p` and `q` are pointers.
1780class PtrToPtrAssignmentGadget : public FixableGadget {
1781private:
1782 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
1783 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
1784 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
1785 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
1786
1787public:
1788 PtrToPtrAssignmentGadget(const MatchResult &Result)
1789 : FixableGadget(Kind::PtrToPtrAssignment),
1790 PtrLHS(Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
1791 PtrRHS(Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
1792
1793 static bool classof(const Gadget *G) {
1794 return G->getKind() == Kind::PtrToPtrAssignment;
1795 }
1796
1797 static bool matches(const Stmt *S,
1798 llvm::SmallVectorImpl<MatchResult> &Results) {
1799 size_t SizeBefore = Results.size();
1800 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
1801 const auto *BO = dyn_cast<BinaryOperator>(S);
1802 if (!BO || BO->getOpcode() != BO_Assign)
1803 return;
1804 const auto *RHS = BO->getRHS()->IgnoreParenImpCasts();
1805 if (const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
1806 !RHSRef || !hasPointerType(*RHSRef) ||
1807 !isSupportedVariable(*RHSRef)) {
1808 return;
1809 }
1810 const auto *LHS = BO->getLHS();
1811 if (const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
1812 !LHSRef || !hasPointerType(*LHSRef) ||
1813 !isSupportedVariable(*LHSRef)) {
1814 return;
1815 }
1816 MatchResult R;
1817 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
1818 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
1819 Results.emplace_back(std::move(R));
1820 });
1821 return SizeBefore != Results.size();
1822 }
1823
1824 virtual std::optional<FixItList>
1825 getFixits(const FixitStrategy &S) const override;
1826 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
1827
1828 virtual DeclUseList getClaimedVarUseSites() const override {
1829 return DeclUseList{PtrLHS, PtrRHS};
1830 }
1831
1832 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1833 getStrategyImplications() const override {
1834 return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
1835 cast<VarDecl>(PtrRHS->getDecl()));
1836 }
1837};
1838
1839/// An assignment expression of the form:
1840/// \code
1841/// ptr = array;
1842/// \endcode
1843/// where `p` is a pointer and `array` is a constant size array.
1844class CArrayToPtrAssignmentGadget : public FixableGadget {
1845private:
1846 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
1847 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
1848 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
1849 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
1850
1851public:
1852 CArrayToPtrAssignmentGadget(const MatchResult &Result)
1853 : FixableGadget(Kind::CArrayToPtrAssignment),
1854 PtrLHS(Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
1855 PtrRHS(Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
1856
1857 static bool classof(const Gadget *G) {
1858 return G->getKind() == Kind::CArrayToPtrAssignment;
1859 }
1860
1861 static bool matches(const Stmt *S,
1862 llvm::SmallVectorImpl<MatchResult> &Results) {
1863 size_t SizeBefore = Results.size();
1864 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
1865 const auto *BO = dyn_cast<BinaryOperator>(S);
1866 if (!BO || BO->getOpcode() != BO_Assign)
1867 return;
1868 const auto *RHS = BO->getRHS()->IgnoreParenImpCasts();
1869 if (const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
1870 !RHSRef ||
1871 !isa<ConstantArrayType>(RHSRef->getType().getCanonicalType()) ||
1872 !isSupportedVariable(*RHSRef)) {
1873 return;
1874 }
1875 const auto *LHS = BO->getLHS();
1876 if (const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
1877 !LHSRef || !hasPointerType(*LHSRef) ||
1878 !isSupportedVariable(*LHSRef)) {
1879 return;
1880 }
1881 MatchResult R;
1882 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
1883 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
1884 Results.emplace_back(std::move(R));
1885 });
1886 return SizeBefore != Results.size();
1887 }
1888
1889 virtual std::optional<FixItList>
1890 getFixits(const FixitStrategy &S) const override;
1891 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
1892
1893 virtual DeclUseList getClaimedVarUseSites() const override {
1894 return DeclUseList{PtrLHS, PtrRHS};
1895 }
1896
1897 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1898 getStrategyImplications() const override {
1899 return {};
1900 }
1901};
1902
1903/// A call of a function or method that performs unchecked buffer operations
1904/// over one of its pointer parameters.
1905class UnsafeBufferUsageAttrGadget : public WarningGadget {
1906 constexpr static const char *const OpTag = "attr_expr";
1907 const Expr *Op;
1908
1909public:
1910 UnsafeBufferUsageAttrGadget(const MatchResult &Result)
1911 : WarningGadget(Kind::UnsafeBufferUsageAttr),
1912 Op(Result.getNodeAs<Expr>(OpTag)) {}
1913
1914 static bool classof(const Gadget *G) {
1915 return G->getKind() == Kind::UnsafeBufferUsageAttr;
1916 }
1917
1918 static bool matches(const Stmt *S, const ASTContext &Ctx,
1920 if (auto *CE = dyn_cast<CallExpr>(S)) {
1921 if (CE->getDirectCallee() &&
1922 CE->getDirectCallee()->hasAttr<UnsafeBufferUsageAttr>()) {
1923 Result.addNode(OpTag, DynTypedNode::create(*CE));
1924 return true;
1925 }
1926 }
1927 if (auto *ME = dyn_cast<MemberExpr>(S)) {
1928 if (!isa<FieldDecl>(ME->getMemberDecl()))
1929 return false;
1930 if (ME->getMemberDecl()->hasAttr<UnsafeBufferUsageAttr>()) {
1931 Result.addNode(OpTag, DynTypedNode::create(*ME));
1932 return true;
1933 }
1934 }
1935 return false;
1936 }
1937
1938 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1939 bool IsRelatedToDecl,
1940 ASTContext &Ctx) const override {
1941 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1942 }
1943 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1944
1945 DeclUseList getClaimedVarUseSites() const override { return {}; }
1946
1947 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1948};
1949
1950/// A call of a constructor that performs unchecked buffer operations
1951/// over one of its pointer parameters, or constructs a class object that will
1952/// perform buffer operations that depend on the correctness of the parameters.
1953class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
1954 constexpr static const char *const OpTag = "cxx_construct_expr";
1955 const CXXConstructExpr *Op;
1956
1957public:
1958 UnsafeBufferUsageCtorAttrGadget(const MatchResult &Result)
1959 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
1960 Op(Result.getNodeAs<CXXConstructExpr>(OpTag)) {}
1961
1962 static bool classof(const Gadget *G) {
1963 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
1964 }
1965
1966 static bool matches(const Stmt *S, ASTContext &Ctx, MatchResult &Result) {
1967 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1968 if (!CE || !CE->getConstructor()->hasAttr<UnsafeBufferUsageAttr>())
1969 return false;
1970 // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget.
1971 MatchResult Tmp;
1972 if (SpanTwoParamConstructorGadget::matches(CE, Ctx, Tmp))
1973 return false;
1974 Result.addNode(OpTag, DynTypedNode::create(*CE));
1975 return true;
1976 }
1977
1978 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1979 bool IsRelatedToDecl,
1980 ASTContext &Ctx) const override {
1981 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1982 }
1983 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1984
1985 DeclUseList getClaimedVarUseSites() const override { return {}; }
1986
1987 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1988};
1989
1990// Warning gadget for unsafe invocation of span::data method.
1991// Triggers when the pointer returned by the invocation is immediately
1992// cast to a larger type.
1993
1994class DataInvocationGadget : public WarningGadget {
1995 constexpr static const char *const OpTag = "data_invocation_expr";
1996 const ExplicitCastExpr *Op;
1997
1998public:
1999 DataInvocationGadget(const MatchResult &Result)
2000 : WarningGadget(Kind::DataInvocation),
2001 Op(Result.getNodeAs<ExplicitCastExpr>(OpTag)) {}
2002
2003 static bool classof(const Gadget *G) {
2004 return G->getKind() == Kind::DataInvocation;
2005 }
2006
2007 static bool matches(const Stmt *S, const ASTContext &Ctx,
2009 auto *CE = dyn_cast<ExplicitCastExpr>(S);
2010 if (!CE)
2011 return false;
2012 for (auto *Child : CE->children()) {
2013 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Child);
2014 MCE && isDataFunction(MCE)) {
2015 Result.addNode(OpTag, DynTypedNode::create(*CE));
2016 return true;
2017 }
2018 if (auto *Paren = dyn_cast<ParenExpr>(Child)) {
2019 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Paren->getSubExpr());
2020 MCE && isDataFunction(MCE)) {
2021 Result.addNode(OpTag, DynTypedNode::create(*CE));
2022 return true;
2023 }
2024 }
2025 }
2026 return false;
2027 }
2028
2029 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2030 bool IsRelatedToDecl,
2031 ASTContext &Ctx) const override {
2032 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
2033 }
2034 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2035
2036 DeclUseList getClaimedVarUseSites() const override { return {}; }
2037
2038private:
2039 static bool isDataFunction(const CXXMemberCallExpr *call) {
2040 if (!call)
2041 return false;
2042 auto *callee = call->getDirectCallee();
2043 if (!callee || !isa<CXXMethodDecl>(callee))
2044 return false;
2045 auto *method = cast<CXXMethodDecl>(callee);
2046 if (method->getNameAsString() == "data" &&
2047 method->getParent()->isInStdNamespace() &&
2048 llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
2049 method->getParent()->getName()))
2050 return true;
2051 return false;
2052 }
2053
2054 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2055};
2056
2057class UnsafeLibcFunctionCallGadget : public WarningGadget {
2058 const CallExpr *const Call;
2059 const Expr *UnsafeArg = nullptr;
2060 constexpr static const char *const Tag = "UnsafeLibcFunctionCall";
2061 // Extra tags for additional information:
2062 constexpr static const char *const UnsafeSprintfTag =
2063 "UnsafeLibcFunctionCall_sprintf";
2064 constexpr static const char *const UnsafeSizedByTag =
2065 "UnsafeLibcFunctionCall_sized_by";
2066 constexpr static const char *const UnsafeStringTag =
2067 "UnsafeLibcFunctionCall_string";
2068 constexpr static const char *const UnsafeVaListTag =
2069 "UnsafeLibcFunctionCall_va_list";
2070
2071 enum UnsafeKind {
2072 OTHERS = 0, // no specific information, the callee function is unsafe
2073 SPRINTF = 1, // never call `-sprintf`s, call `-snprintf`s instead.
2074 SIZED_BY =
2075 2, // the first two arguments of `snprintf` function have
2076 // "__sized_by" relation but they do not conform to safe patterns
2077 STRING = 3, // an argument is a pointer-to-char-as-string but does not
2078 // guarantee null-termination
2079 VA_LIST = 4, // one of the `-printf`s function that take va_list, which is
2080 // considered unsafe as it is not compile-time check
2081 } WarnedFunKind = OTHERS;
2082
2083public:
2084 UnsafeLibcFunctionCallGadget(const MatchResult &Result)
2085 : WarningGadget(Kind::UnsafeLibcFunctionCall),
2086 Call(Result.getNodeAs<CallExpr>(Tag)) {
2087 if (Result.getNodeAs<Decl>(UnsafeSprintfTag))
2088 WarnedFunKind = SPRINTF;
2089 else if (auto *E = Result.getNodeAs<Expr>(UnsafeStringTag)) {
2090 WarnedFunKind = STRING;
2091 UnsafeArg = E;
2092 } else if (Result.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
2093 WarnedFunKind = SIZED_BY;
2094 UnsafeArg = Call->getArg(0);
2095 } else if (Result.getNodeAs<Decl>(UnsafeVaListTag))
2096 WarnedFunKind = VA_LIST;
2097 }
2098
2099 static bool matches(const Stmt *S, ASTContext &Ctx,
2100 const UnsafeBufferUsageHandler *Handler,
2102 if (ignoreUnsafeLibcCall(Ctx, *S, Handler))
2103 return false;
2104 auto *CE = dyn_cast<CallExpr>(S);
2105 if (!CE || !CE->getDirectCallee())
2106 return false;
2107 const auto *FD = dyn_cast<FunctionDecl>(CE->getDirectCallee());
2108 if (!FD)
2109 return false;
2110
2111 bool IsGlobalAndNotInAnyNamespace =
2112 FD->isGlobal() && !FD->getEnclosingNamespaceContext()->isNamespace();
2113
2114 // A libc function must either be in the std:: namespace or a global
2115 // function that is not in any namespace:
2116 if (!FD->isInStdNamespace() && !IsGlobalAndNotInAnyNamespace)
2117 return false;
2118 // If the call has a sole null-terminated argument, e.g., strlen,
2119 // printf, atoi, we consider it safe:
2120 if (CE->getNumArgs() == 1 && isNullTermPointer(CE->getArg(0), Ctx))
2121 return false;
2122 auto isSingleStringLiteralArg = false;
2123 if (CE->getNumArgs() == 1) {
2124 isSingleStringLiteralArg =
2125 isa<clang::StringLiteral>(CE->getArg(0)->IgnoreParenImpCasts());
2126 }
2127 if (!isSingleStringLiteralArg) {
2128 // (unless the call has a sole string literal argument):
2130 Result.addNode(Tag, DynTypedNode::create(*CE));
2131 return true;
2132 }
2134 Result.addNode(Tag, DynTypedNode::create(*CE));
2135 Result.addNode(UnsafeVaListTag, DynTypedNode::create(*FD));
2136 return true;
2137 }
2139 Result.addNode(Tag, DynTypedNode::create(*CE));
2140 Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*FD));
2141 return true;
2142 }
2143 }
2146 Result.addNode(Tag, DynTypedNode::create(*CE));
2147 Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*CE));
2148 return true;
2149 }
2151 UnsafeStringTag)) {
2152 Result.addNode(Tag, DynTypedNode::create(*CE));
2153 return true;
2154 }
2155 }
2156 return false;
2157 }
2158
2159 const Stmt *getBaseStmt() const { return Call; }
2160
2161 SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
2162
2163 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2164 bool IsRelatedToDecl,
2165 ASTContext &Ctx) const override {
2166 Handler.handleUnsafeLibcCall(Call, WarnedFunKind, Ctx, UnsafeArg);
2167 }
2168
2169 DeclUseList getClaimedVarUseSites() const override { return {}; }
2170
2171 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2172};
2173
2174// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
2175// Context (see `findStmtsInUnspecifiedLvalueContext`).
2176// Note here `[]` is the built-in subscript operator.
2177class ULCArraySubscriptGadget : public FixableGadget {
2178private:
2179 static constexpr const char *const ULCArraySubscriptTag =
2180 "ArraySubscriptUnderULC";
2181 const ArraySubscriptExpr *Node;
2182
2183public:
2184 ULCArraySubscriptGadget(const MatchResult &Result)
2185 : FixableGadget(Kind::ULCArraySubscript),
2186 Node(Result.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
2187 assert(Node != nullptr && "Expecting a non-null matching result");
2188 }
2189
2190 static bool classof(const Gadget *G) {
2191 return G->getKind() == Kind::ULCArraySubscript;
2192 }
2193
2194 static bool matches(const Stmt *S,
2195 llvm::SmallVectorImpl<MatchResult> &Results) {
2196 size_t SizeBefore = Results.size();
2197 findStmtsInUnspecifiedLvalueContext(S, [&Results](const Expr *E) {
2198 const auto *ASE = dyn_cast<ArraySubscriptExpr>(E);
2199 if (!ASE)
2200 return;
2201 const auto *DRE =
2202 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts());
2203 if (!DRE || !(hasPointerType(*DRE) || hasArrayType(*DRE)) ||
2204 !isSupportedVariable(*DRE))
2205 return;
2206 MatchResult R;
2207 R.addNode(ULCArraySubscriptTag, DynTypedNode::create(*ASE));
2208 Results.emplace_back(std::move(R));
2209 });
2210 return SizeBefore != Results.size();
2211 }
2212
2213 virtual std::optional<FixItList>
2214 getFixits(const FixitStrategy &S) const override;
2215 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2216
2217 virtual DeclUseList getClaimedVarUseSites() const override {
2218 if (const auto *DRE =
2219 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
2220 return {DRE};
2221 }
2222 return {};
2223 }
2224};
2225
2226// Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
2227// unspecified pointer context (findStmtsInUnspecifiedPointerContext). The
2228// gadget emits fixit of the form `UPC(DRE.data())`.
2229class UPCStandalonePointerGadget : public FixableGadget {
2230private:
2231 static constexpr const char *const DeclRefExprTag = "StandalonePointer";
2232 const DeclRefExpr *Node;
2233
2234public:
2235 UPCStandalonePointerGadget(const MatchResult &Result)
2236 : FixableGadget(Kind::UPCStandalonePointer),
2237 Node(Result.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
2238 assert(Node != nullptr && "Expecting a non-null matching result");
2239 }
2240
2241 static bool classof(const Gadget *G) {
2242 return G->getKind() == Kind::UPCStandalonePointer;
2243 }
2244
2245 static bool matches(const Stmt *S,
2246 llvm::SmallVectorImpl<MatchResult> &Results) {
2247 size_t SizeBefore = Results.size();
2248 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2249 auto *E = dyn_cast<Expr>(S);
2250 if (!E)
2251 return;
2252 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
2253 if (!DRE || (!hasPointerType(*DRE) && !hasArrayType(*DRE)) ||
2254 !isSupportedVariable(*DRE))
2255 return;
2256 MatchResult R;
2257 R.addNode(DeclRefExprTag, DynTypedNode::create(*DRE));
2258 Results.emplace_back(std::move(R));
2259 });
2260 return SizeBefore != Results.size();
2261 }
2262
2263 virtual std::optional<FixItList>
2264 getFixits(const FixitStrategy &S) const override;
2265 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2266
2267 virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }
2268};
2269
2270class PointerDereferenceGadget : public FixableGadget {
2271 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
2272 static constexpr const char *const OperatorTag = "op";
2273
2274 const DeclRefExpr *BaseDeclRefExpr = nullptr;
2275 const UnaryOperator *Op = nullptr;
2276
2277public:
2278 PointerDereferenceGadget(const MatchResult &Result)
2279 : FixableGadget(Kind::PointerDereference),
2280 BaseDeclRefExpr(Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2281 Op(Result.getNodeAs<UnaryOperator>(OperatorTag)) {}
2282
2283 static bool classof(const Gadget *G) {
2284 return G->getKind() == Kind::PointerDereference;
2285 }
2286
2287 static bool matches(const Stmt *S,
2288 llvm::SmallVectorImpl<MatchResult> &Results) {
2289 size_t SizeBefore = Results.size();
2290 findStmtsInUnspecifiedLvalueContext(S, [&Results](const Stmt *S) {
2291 const auto *UO = dyn_cast<UnaryOperator>(S);
2292 if (!UO || UO->getOpcode() != UO_Deref)
2293 return;
2294 const auto *CE = dyn_cast<Expr>(UO->getSubExpr());
2295 if (!CE)
2296 return;
2297 CE = CE->IgnoreParenImpCasts();
2298 const auto *DRE = dyn_cast<DeclRefExpr>(CE);
2299 if (!DRE || !isSupportedVariable(*DRE))
2300 return;
2301 MatchResult R;
2302 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2303 R.addNode(OperatorTag, DynTypedNode::create(*UO));
2304 Results.emplace_back(std::move(R));
2305 });
2306 return SizeBefore != Results.size();
2307 }
2308
2309 DeclUseList getClaimedVarUseSites() const override {
2310 return {BaseDeclRefExpr};
2311 }
2312
2313 virtual std::optional<FixItList>
2314 getFixits(const FixitStrategy &S) const override;
2315 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2316};
2317
2318// Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
2319// Context (see `findStmtsInUnspecifiedPointerContext`).
2320// Note here `[]` is the built-in subscript operator.
2321class UPCAddressofArraySubscriptGadget : public FixableGadget {
2322private:
2323 static constexpr const char *const UPCAddressofArraySubscriptTag =
2324 "AddressofArraySubscriptUnderUPC";
2325 const UnaryOperator *Node; // the `&DRE[any]` node
2326
2327public:
2328 UPCAddressofArraySubscriptGadget(const MatchResult &Result)
2329 : FixableGadget(Kind::ULCArraySubscript),
2330 Node(Result.getNodeAs<UnaryOperator>(UPCAddressofArraySubscriptTag)) {
2331 assert(Node != nullptr && "Expecting a non-null matching result");
2332 }
2333
2334 static bool classof(const Gadget *G) {
2335 return G->getKind() == Kind::UPCAddressofArraySubscript;
2336 }
2337
2338 static bool matches(const Stmt *S,
2339 llvm::SmallVectorImpl<MatchResult> &Results) {
2340 size_t SizeBefore = Results.size();
2341 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2342 auto *E = dyn_cast<Expr>(S);
2343 if (!E)
2344 return;
2345 const auto *UO = dyn_cast<UnaryOperator>(E->IgnoreImpCasts());
2346 if (!UO || UO->getOpcode() != UO_AddrOf)
2347 return;
2348 const auto *ASE = dyn_cast<ArraySubscriptExpr>(UO->getSubExpr());
2349 if (!ASE)
2350 return;
2351 const auto *DRE =
2352 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts());
2353 if (!DRE || !isSupportedVariable(*DRE))
2354 return;
2355 MatchResult R;
2356 R.addNode(UPCAddressofArraySubscriptTag, DynTypedNode::create(*UO));
2357 Results.emplace_back(std::move(R));
2358 });
2359 return SizeBefore != Results.size();
2360 }
2361
2362 virtual std::optional<FixItList>
2363 getFixits(const FixitStrategy &) const override;
2364 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2365
2366 virtual DeclUseList getClaimedVarUseSites() const override {
2367 const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
2368 const auto *DRE =
2369 cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts());
2370 return {DRE};
2371 }
2372};
2373} // namespace
2374
2375namespace {
2376// An auxiliary tracking facility for the fixit analysis. It helps connect
2377// declarations to its uses and make sure we've covered all uses with our
2378// analysis before we try to fix the declaration.
2379class DeclUseTracker {
2380 using UseSetTy = llvm::SmallPtrSet<const DeclRefExpr *, 16>;
2381 using DefMapTy = llvm::DenseMap<const VarDecl *, const DeclStmt *>;
2382
2383 // Allocate on the heap for easier move.
2384 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
2385 DefMapTy Defs{};
2386
2387public:
2388 DeclUseTracker() = default;
2389 DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
2390 DeclUseTracker &operator=(const DeclUseTracker &) = delete;
2391 DeclUseTracker(DeclUseTracker &&) = default;
2392 DeclUseTracker &operator=(DeclUseTracker &&) = default;
2393
2394 // Start tracking a freshly discovered DRE.
2395 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
2396
2397 // Stop tracking the DRE as it's been fully figured out.
2398 void claimUse(const DeclRefExpr *DRE) {
2399 assert(Uses->count(DRE) &&
2400 "DRE not found or claimed by multiple matchers!");
2401 Uses->erase(DRE);
2402 }
2403
2404 // A variable is unclaimed if at least one use is unclaimed.
2405 bool hasUnclaimedUses(const VarDecl *VD) const {
2406 // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
2407 return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
2408 return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
2409 });
2410 }
2411
2412 UseSetTy getUnclaimedUses(const VarDecl *VD) const {
2413 UseSetTy ReturnSet;
2414 for (auto use : *Uses) {
2415 if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
2416 ReturnSet.insert(use);
2417 }
2418 }
2419 return ReturnSet;
2420 }
2421
2422 void discoverDecl(const DeclStmt *DS) {
2423 for (const Decl *D : DS->decls()) {
2424 if (const auto *VD = dyn_cast<VarDecl>(D)) {
2425 // FIXME: Assertion temporarily disabled due to a bug in
2426 // ASTMatcher internal behavior in presence of GNU
2427 // statement-expressions. We need to properly investigate this
2428 // because it can screw up our algorithm in other ways.
2429 // assert(Defs.count(VD) == 0 && "Definition already discovered!");
2430 Defs[VD] = DS;
2431 }
2432 }
2433 }
2434
2435 const DeclStmt *lookupDecl(const VarDecl *VD) const {
2436 return Defs.lookup(VD);
2437 }
2438};
2439} // namespace
2440
2441// Representing a pointer type expression of the form `++Ptr` in an Unspecified
2442// Pointer Context (UPC):
2443class UPCPreIncrementGadget : public FixableGadget {
2444private:
2445 static constexpr const char *const UPCPreIncrementTag =
2446 "PointerPreIncrementUnderUPC";
2447 const UnaryOperator *Node; // the `++Ptr` node
2448
2449public:
2450 UPCPreIncrementGadget(const MatchResult &Result)
2451 : FixableGadget(Kind::UPCPreIncrement),
2452 Node(Result.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
2453 assert(Node != nullptr && "Expecting a non-null matching result");
2454 }
2455
2456 static bool classof(const Gadget *G) {
2457 return G->getKind() == Kind::UPCPreIncrement;
2458 }
2459
2460 static bool matches(const Stmt *S,
2462 // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
2463 // Although currently we can only provide fix-its when `Ptr` is a DRE, we
2464 // can have the matcher be general, so long as `getClaimedVarUseSites` does
2465 // things right.
2466 size_t SizeBefore = Results.size();
2467 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2468 auto *E = dyn_cast<Expr>(S);
2469 if (!E)
2470 return;
2471 const auto *UO = dyn_cast<UnaryOperator>(E->IgnoreImpCasts());
2472 if (!UO || UO->getOpcode() != UO_PreInc)
2473 return;
2474 const auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr());
2475 if (!DRE || !isSupportedVariable(*DRE))
2476 return;
2477 MatchResult R;
2478 R.addNode(UPCPreIncrementTag, DynTypedNode::create(*UO));
2479 Results.emplace_back(std::move(R));
2480 });
2481 return SizeBefore != Results.size();
2482 }
2483
2484 virtual std::optional<FixItList>
2485 getFixits(const FixitStrategy &S) const override;
2486 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2487
2488 virtual DeclUseList getClaimedVarUseSites() const override {
2489 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
2490 }
2491};
2492
2493// Representing a pointer type expression of the form `Ptr += n` in an
2494// Unspecified Untyped Context (UUC):
2495class UUCAddAssignGadget : public FixableGadget {
2496private:
2497 static constexpr const char *const UUCAddAssignTag =
2498 "PointerAddAssignUnderUUC";
2499 static constexpr const char *const OffsetTag = "Offset";
2500
2501 const BinaryOperator *Node; // the `Ptr += n` node
2502 const Expr *Offset = nullptr;
2503
2504public:
2505 UUCAddAssignGadget(const MatchResult &Result)
2506 : FixableGadget(Kind::UUCAddAssign),
2507 Node(Result.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
2508 Offset(Result.getNodeAs<Expr>(OffsetTag)) {
2509 assert(Node != nullptr && "Expecting a non-null matching result");
2510 }
2511
2512 static bool classof(const Gadget *G) {
2513 return G->getKind() == Kind::UUCAddAssign;
2514 }
2515
2516 static bool matches(const Stmt *S,
2518 size_t SizeBefore = Results.size();
2519 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
2520 const auto *E = dyn_cast<Expr>(S);
2521 if (!E)
2522 return;
2523 const auto *BO = dyn_cast<BinaryOperator>(E->IgnoreImpCasts());
2524 if (!BO || BO->getOpcode() != BO_AddAssign)
2525 return;
2526 const auto *DRE = dyn_cast<DeclRefExpr>(BO->getLHS());
2527 if (!DRE || !hasPointerType(*DRE) || !isSupportedVariable(*DRE))
2528 return;
2529 MatchResult R;
2530 R.addNode(UUCAddAssignTag, DynTypedNode::create(*BO));
2531 R.addNode(OffsetTag, DynTypedNode::create(*BO->getRHS()));
2532 Results.emplace_back(std::move(R));
2533 });
2534 return SizeBefore != Results.size();
2535 }
2536
2537 virtual std::optional<FixItList>
2538 getFixits(const FixitStrategy &S) const override;
2539 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2540
2541 virtual DeclUseList getClaimedVarUseSites() const override {
2542 return {dyn_cast<DeclRefExpr>(Node->getLHS())};
2543 }
2544};
2545
2546// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
2547// ptr)`:
2548class DerefSimplePtrArithFixableGadget : public FixableGadget {
2549 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
2550 static constexpr const char *const DerefOpTag = "DerefOp";
2551 static constexpr const char *const AddOpTag = "AddOp";
2552 static constexpr const char *const OffsetTag = "Offset";
2553
2554 const DeclRefExpr *BaseDeclRefExpr = nullptr;
2555 const UnaryOperator *DerefOp = nullptr;
2556 const BinaryOperator *AddOp = nullptr;
2557 const IntegerLiteral *Offset = nullptr;
2558
2559public:
2561 : FixableGadget(Kind::DerefSimplePtrArithFixable),
2562 BaseDeclRefExpr(Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2563 DerefOp(Result.getNodeAs<UnaryOperator>(DerefOpTag)),
2564 AddOp(Result.getNodeAs<BinaryOperator>(AddOpTag)),
2565 Offset(Result.getNodeAs<IntegerLiteral>(OffsetTag)) {}
2566
2567 static bool matches(const Stmt *S,
2569 auto IsPtr = [](const Expr *E, MatchResult &R) {
2570 if (!E || !hasPointerType(*E))
2571 return false;
2572 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImpCasts());
2573 if (!DRE || !isSupportedVariable(*DRE))
2574 return false;
2575 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2576 return true;
2577 };
2578 const auto IsPlusOverPtrAndInteger = [&IsPtr](const Expr *E,
2579 MatchResult &R) {
2580 const auto *BO = dyn_cast<BinaryOperator>(E);
2581 if (!BO || BO->getOpcode() != BO_Add)
2582 return false;
2583
2584 const auto *LHS = BO->getLHS();
2585 const auto *RHS = BO->getRHS();
2586 if (isa<IntegerLiteral>(RHS) && IsPtr(LHS, R)) {
2587 R.addNode(OffsetTag, DynTypedNode::create(*RHS));
2588 R.addNode(AddOpTag, DynTypedNode::create(*BO));
2589 return true;
2590 }
2591 if (isa<IntegerLiteral>(LHS) && IsPtr(RHS, R)) {
2592 R.addNode(OffsetTag, DynTypedNode::create(*LHS));
2593 R.addNode(AddOpTag, DynTypedNode::create(*BO));
2594 return true;
2595 }
2596 return false;
2597 };
2598 size_t SizeBefore = Results.size();
2599 const auto InnerMatcher = [&IsPlusOverPtrAndInteger,
2600 &Results](const Expr *E) {
2601 const auto *UO = dyn_cast<UnaryOperator>(E);
2602 if (!UO || UO->getOpcode() != UO_Deref)
2603 return;
2604
2605 const auto *Operand = UO->getSubExpr()->IgnoreParens();
2606 MatchResult R;
2607 if (IsPlusOverPtrAndInteger(Operand, R)) {
2608 R.addNode(DerefOpTag, DynTypedNode::create(*UO));
2609 Results.emplace_back(std::move(R));
2610 }
2611 };
2612 findStmtsInUnspecifiedLvalueContext(S, InnerMatcher);
2613 return SizeBefore != Results.size();
2614 }
2615
2616 virtual std::optional<FixItList>
2617 getFixits(const FixitStrategy &s) const final;
2618 SourceLocation getSourceLoc() const override {
2619 return DerefOp->getBeginLoc();
2620 }
2621
2622 virtual DeclUseList getClaimedVarUseSites() const final {
2623 return {BaseDeclRefExpr};
2624 }
2625};
2626
2627class WarningGadgetMatcher : public FastMatcher {
2628
2629public:
2630 WarningGadgetMatcher(WarningGadgetList &WarningGadgets)
2631 : WarningGadgets(WarningGadgets) {}
2632
2633 bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
2634 const UnsafeBufferUsageHandler &Handler) override {
2635 const Stmt *S = DynNode.get<Stmt>();
2636 if (!S)
2637 return false;
2638
2639 MatchResult Result;
2640#define WARNING_GADGET(name) \
2641 if (name##Gadget::matches(S, Ctx, Result) && \
2642 notInSafeBufferOptOut(*S, &Handler)) { \
2643 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
2644 return true; \
2645 }
2646#define WARNING_OPTIONAL_GADGET(name) \
2647 if (name##Gadget::matches(S, Ctx, &Handler, Result) && \
2648 notInSafeBufferOptOut(*S, &Handler)) { \
2649 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
2650 return true; \
2651 }
2652#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2653 return false;
2654 }
2655
2656private:
2657 WarningGadgetList &WarningGadgets;
2658};
2659
2660class FixableGadgetMatcher : public FastMatcher {
2661
2662public:
2663 FixableGadgetMatcher(FixableGadgetList &FixableGadgets,
2664 DeclUseTracker &Tracker)
2665 : FixableGadgets(FixableGadgets), Tracker(Tracker) {}
2666
2667 bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
2668 const UnsafeBufferUsageHandler &Handler) override {
2669 bool matchFound = false;
2670 const Stmt *S = DynNode.get<Stmt>();
2671 if (!S) {
2672 return matchFound;
2673 }
2674
2676#define FIXABLE_GADGET(name) \
2677 if (name##Gadget::matches(S, Results)) { \
2678 for (const auto &R : Results) { \
2679 FixableGadgets.push_back(std::make_unique<name##Gadget>(R)); \
2680 matchFound = true; \
2681 } \
2682 Results = {}; \
2683 }
2684#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2685 // In parallel, match all DeclRefExprs so that to find out
2686 // whether there are any uncovered by gadgets.
2687 if (auto *DRE = findDeclRefExpr(S); DRE) {
2688 Tracker.discoverUse(DRE);
2689 matchFound = true;
2690 }
2691 // Also match DeclStmts because we'll need them when fixing
2692 // their underlying VarDecls that otherwise don't have
2693 // any backreferences to DeclStmts.
2694 if (auto *DS = findDeclStmt(S); DS) {
2695 Tracker.discoverDecl(DS);
2696 matchFound = true;
2697 }
2698 return matchFound;
2699 }
2700
2701private:
2702 const DeclRefExpr *findDeclRefExpr(const Stmt *S) {
2703 const auto *DRE = dyn_cast<DeclRefExpr>(S);
2704 if (!DRE || (!hasPointerType(*DRE) && !hasArrayType(*DRE)))
2705 return nullptr;
2706 const Decl *D = DRE->getDecl();
2707 if (!D || (!isa<VarDecl>(D) && !isa<BindingDecl>(D)))
2708 return nullptr;
2709 return DRE;
2710 }
2711 const DeclStmt *findDeclStmt(const Stmt *S) {
2712 const auto *DS = dyn_cast<DeclStmt>(S);
2713 if (!DS)
2714 return nullptr;
2715 return DS;
2716 }
2717 FixableGadgetList &FixableGadgets;
2718 DeclUseTracker &Tracker;
2719};
2720
2721// Scan the function and return a list of gadgets found with provided kits.
2722static void findGadgets(const Stmt *S, ASTContext &Ctx,
2723 const UnsafeBufferUsageHandler &Handler,
2724 bool EmitSuggestions, FixableGadgetList &FixableGadgets,
2725 WarningGadgetList &WarningGadgets,
2726 DeclUseTracker &Tracker) {
2727 WarningGadgetMatcher WMatcher{WarningGadgets};
2728 forEachDescendantEvaluatedStmt(S, Ctx, Handler, WMatcher);
2729 if (EmitSuggestions) {
2730 FixableGadgetMatcher FMatcher{FixableGadgets, Tracker};
2731 forEachDescendantStmt(S, Ctx, Handler, FMatcher);
2732 }
2733}
2734
2735// Compares AST nodes by source locations.
2736template <typename NodeTy> struct CompareNode {
2737 bool operator()(const NodeTy *N1, const NodeTy *N2) const {
2738 return N1->getBeginLoc().getRawEncoding() <
2739 N2->getBeginLoc().getRawEncoding();
2740 }
2741};
2742
2743std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
2744 class MockReporter : public UnsafeBufferUsageHandler {
2745 public:
2746 MockReporter() {}
2747 void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {}
2748 void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &,
2749 const Expr *UnsafeArg = nullptr) override {}
2750 void handleUnsafeOperationInContainer(const Stmt *, bool,
2751 ASTContext &) override {}
2753 const VariableGroupsManager &, FixItList &&,
2754 const Decl *,
2755 const FixitStrategy &) override {}
2757 bool IsRelatedToDecl,
2758 ASTContext &Ctx) override {}
2759 bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
2760 return false;
2761 }
2762 bool isSafeBufferOptOut(const SourceLocation &) const override {
2763 return false;
2764 }
2765 bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
2766 return false;
2767 }
2769 SourceLocation, StringRef WSSuffix = "") const override {
2770 return "";
2771 }
2772 };
2773
2774 FixableGadgetList FixableGadgets;
2775 WarningGadgetList WarningGadgets;
2776 DeclUseTracker Tracker;
2777 MockReporter IgnoreHandler;
2778
2779 findGadgets(FD->getBody(), FD->getASTContext(), IgnoreHandler, false,
2780 FixableGadgets, WarningGadgets, Tracker);
2781
2782 std::set<const Expr *> Result;
2783 for (auto &G : WarningGadgets) {
2784 for (const Expr *E : G->getUnsafePtrs()) {
2785 Result.insert(E);
2786 }
2787 }
2788
2789 return Result;
2790}
2791
2793 std::map<const VarDecl *, std::set<const WarningGadget *>,
2794 // To keep keys sorted by their locations in the map so that the
2795 // order is deterministic:
2798 // These Gadgets are not related to pointer variables (e. g. temporaries).
2800};
2801
2802static WarningGadgetSets
2803groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
2804 WarningGadgetSets result;
2805 // If some gadgets cover more than one
2806 // variable, they'll appear more than once in the map.
2807 for (auto &G : AllUnsafeOperations) {
2808 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
2809
2810 bool AssociatedWithVarDecl = false;
2811 for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
2812 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2813 result.byVar[VD].insert(G.get());
2814 AssociatedWithVarDecl = true;
2815 }
2816 }
2817
2818 if (!AssociatedWithVarDecl) {
2819 result.noVar.push_back(G.get());
2820 continue;
2821 }
2822 }
2823 return result;
2824}
2825
2827 std::map<const VarDecl *, std::set<const FixableGadget *>,
2828 // To keep keys sorted by their locations in the map so that the
2829 // order is deterministic:
2832};
2833
2834static FixableGadgetSets
2835groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
2836 FixableGadgetSets FixablesForUnsafeVars;
2837 for (auto &F : AllFixableOperations) {
2838 DeclUseList DREs = F->getClaimedVarUseSites();
2839
2840 for (const DeclRefExpr *DRE : DREs) {
2841 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2842 FixablesForUnsafeVars.byVar[VD].insert(F.get());
2843 }
2844 }
2845 }
2846 return FixablesForUnsafeVars;
2847}
2848
2850 const SourceManager &SM) {
2851 // A simple interval overlap detection algorithm. Sorts all ranges by their
2852 // begin location then finds the first overlap in one pass.
2853 std::vector<const FixItHint *> All; // a copy of `FixIts`
2854
2855 for (const FixItHint &H : FixIts)
2856 All.push_back(&H);
2857 std::sort(All.begin(), All.end(),
2858 [&SM](const FixItHint *H1, const FixItHint *H2) {
2859 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
2860 H2->RemoveRange.getBegin());
2861 });
2862
2863 const FixItHint *CurrHint = nullptr;
2864
2865 for (const FixItHint *Hint : All) {
2866 if (!CurrHint ||
2867 SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
2868 Hint->RemoveRange.getBegin())) {
2869 // Either to initialize `CurrHint` or `CurrHint` does not
2870 // overlap with `Hint`:
2871 CurrHint = Hint;
2872 } else
2873 // In case `Hint` overlaps the `CurrHint`, we found at least one
2874 // conflict:
2875 return true;
2876 }
2877 return false;
2878}
2879
2880std::optional<FixItList>
2881PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
2882 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
2883 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
2884 switch (S.lookup(LeftVD)) {
2886 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
2887 return FixItList{};
2888 return std::nullopt;
2890 return std::nullopt;
2893 return std::nullopt;
2895 llvm_unreachable("unsupported strategies for FixableGadgets");
2896 }
2897 return std::nullopt;
2898}
2899
2900/// \returns fixit that adds .data() call after \DRE.
2901static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
2902 const DeclRefExpr *DRE);
2903
2904std::optional<FixItList>
2905CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
2906 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
2907 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
2908 // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
2909 // non-trivial.
2910 //
2911 // CArrayToPtrAssignmentGadget doesn't have strategy implications because
2912 // constant size array propagates its bounds. Because of that LHS and RHS are
2913 // addressed by two different fixits.
2914 //
2915 // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
2916 // to and can't be generally relied on in multi-variable Fixables!
2917 //
2918 // E. g. If an instance of this gadget is fixing variable on LHS then the
2919 // variable on RHS is fixed by a different fixit and its strategy for LHS
2920 // fixit is as if Wontfix.
2921 //
2922 // The only exception is Wontfix strategy for a given variable as that is
2923 // valid for any fixit produced for the given input source code.
2924 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
2925 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
2926 return FixItList{};
2927 }
2928 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
2929 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
2930 return createDataFixit(RightVD->getASTContext(), PtrRHS);
2931 }
2932 }
2933 return std::nullopt;
2934}
2935
2936std::optional<FixItList>
2937PointerInitGadget::getFixits(const FixitStrategy &S) const {
2938 const auto *LeftVD = PtrInitLHS;
2939 const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
2940 switch (S.lookup(LeftVD)) {
2941 case FixitStrategy::Kind::Span:
2942 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
2943 return FixItList{};
2944 return std::nullopt;
2945 case FixitStrategy::Kind::Wontfix:
2946 return std::nullopt;
2947 case FixitStrategy::Kind::Iterator:
2948 case FixitStrategy::Kind::Array:
2949 return std::nullopt;
2950 case FixitStrategy::Kind::Vector:
2951 llvm_unreachable("unsupported strategies for FixableGadgets");
2952 }
2953 return std::nullopt;
2954}
2955
2956static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
2957 const ASTContext &Ctx) {
2958 if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
2959 if (ConstVal->isNegative())
2960 return false;
2961 } else if (!Expr->getType()->isUnsignedIntegerType())
2962 return false;
2963 return true;
2964}
2965
2966std::optional<FixItList>
2967ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2968 if (const auto *DRE =
2969 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
2970 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
2971 switch (S.lookup(VD)) {
2972 case FixitStrategy::Kind::Span: {
2973
2974 // If the index has a negative constant value, we give up as no valid
2975 // fix-it can be generated:
2976 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
2977 VD->getASTContext();
2978 if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
2979 return std::nullopt;
2980 // no-op is a good fix-it, otherwise
2981 return FixItList{};
2982 }
2983 case FixitStrategy::Kind::Array:
2984 return FixItList{};
2985 case FixitStrategy::Kind::Wontfix:
2986 case FixitStrategy::Kind::Iterator:
2987 case FixitStrategy::Kind::Vector:
2988 llvm_unreachable("unsupported strategies for FixableGadgets");
2989 }
2990 }
2991 return std::nullopt;
2992}
2993
2994static std::optional<FixItList> // forward declaration
2995fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node);
2996
2997std::optional<FixItList>
2998UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
2999 auto DREs = getClaimedVarUseSites();
3000 const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
3001
3002 switch (S.lookup(VD)) {
3003 case FixitStrategy::Kind::Span:
3005 case FixitStrategy::Kind::Wontfix:
3006 case FixitStrategy::Kind::Iterator:
3007 case FixitStrategy::Kind::Array:
3008 return std::nullopt;
3009 case FixitStrategy::Kind::Vector:
3010 llvm_unreachable("unsupported strategies for FixableGadgets");
3011 }
3012 return std::nullopt; // something went wrong, no fix-it
3013}
3014
3015// FIXME: this function should be customizable through format
3016static StringRef getEndOfLine() {
3017 static const char *const EOL = "\n";
3018 return EOL;
3019}
3020
3021// Returns the text indicating that the user needs to provide input there:
3022static std::string
3023getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
3024 std::string s = std::string("<# ");
3025 s += HintTextToUser;
3026 s += " #>";
3027 return s;
3028}
3029
3030// Return the source location of the last character of the AST `Node`.
3031template <typename NodeTy>
3032static std::optional<SourceLocation>
3033getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
3034 const LangOptions &LangOpts) {
3035 if (unsigned TkLen =
3036 Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts)) {
3037 SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
3038
3039 if (Loc.isValid())
3040 return Loc;
3041 }
3042 return std::nullopt;
3043}
3044
3045// We cannot fix a variable declaration if it has some other specifiers than the
3046// type specifier. Because the source ranges of those specifiers could overlap
3047// with the source range that is being replaced using fix-its. Especially when
3048// we often cannot obtain accurate source ranges of cv-qualified type
3049// specifiers.
3050// FIXME: also deal with type attributes
3051static bool hasUnsupportedSpecifiers(const VarDecl *VD,
3052 const SourceManager &SM) {
3053 // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
3054 // source range of `VD`:
3055 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
3056 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
3057 VD->getBeginLoc())) &&
3058 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
3059 At->getRange().getBegin()));
3060 });
3061 return VD->isInlineSpecified() || VD->isConstexpr() ||
3063 AttrRangeOverlapping;
3064}
3065
3066// Returns the `SourceRange` of `D`. The reason why this function exists is
3067// that `D->getSourceRange()` may return a range where the end location is the
3068// starting location of the last token. The end location of the source range
3069// returned by this function is the last location of the last token.
3071 const SourceManager &SM,
3072 const LangOptions &LangOpts) {
3073 SourceLocation Begin = D->getBeginLoc();
3075 End = // `D->getEndLoc` should always return the starting location of the
3076 // last token, so we should get the end of the token
3077 Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
3078
3079 return SourceRange(Begin, End);
3080}
3081
3082// Returns the text of the name (with qualifiers) of a `FunctionDecl`.
3083static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
3084 const SourceManager &SM,
3085 const LangOptions &LangOpts) {
3086 SourceLocation BeginLoc = FD->getQualifier()
3088 : FD->getNameInfo().getBeginLoc();
3089 // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
3090 // last token:
3092 FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
3093 SourceRange NameRange{BeginLoc, EndLoc};
3094
3095 return getRangeText(NameRange, SM, LangOpts);
3096}
3097
3098// Returns the text representing a `std::span` type where the element type is
3099// represented by `EltTyText`.
3100//
3101// Note the optional parameter `Qualifiers`: one needs to pass qualifiers
3102// explicitly if the element type needs to be qualified.
3103static std::string
3104getSpanTypeText(StringRef EltTyText,
3105 std::optional<Qualifiers> Quals = std::nullopt) {
3106 const char *const SpanOpen = "std::span<";
3107
3108 if (Quals)
3109 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
3110 return SpanOpen + EltTyText.str() + '>';
3111}
3112
3113std::optional<FixItList>
3115 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
3116
3117 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
3118 ASTContext &Ctx = VD->getASTContext();
3119 // std::span can't represent elements before its begin()
3120 if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
3121 if (ConstVal->isNegative())
3122 return std::nullopt;
3123
3124 // note that the expr may (oddly) has multiple layers of parens
3125 // example:
3126 // *((..(pointer + 123)..))
3127 // goal:
3128 // pointer[123]
3129 // Fix-It:
3130 // remove '*('
3131 // replace ' + ' with '['
3132 // replace ')' with ']'
3133
3134 // example:
3135 // *((..(123 + pointer)..))
3136 // goal:
3137 // 123[pointer]
3138 // Fix-It:
3139 // remove '*('
3140 // replace ' + ' with '['
3141 // replace ')' with ']'
3142
3143 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
3144 const SourceManager &SM = Ctx.getSourceManager();
3145 const LangOptions &LangOpts = Ctx.getLangOpts();
3146 CharSourceRange StarWithTrailWhitespace =
3147 clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(),
3148 LHS->getBeginLoc());
3149
3150 std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
3151 if (!LHSLocation)
3152 return std::nullopt;
3153
3154 CharSourceRange PlusWithSurroundingWhitespace =
3155 clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
3156
3157 std::optional<SourceLocation> AddOpLocation =
3158 getPastLoc(AddOp, SM, LangOpts);
3159 std::optional<SourceLocation> DerefOpLocation =
3160 getPastLoc(DerefOp, SM, LangOpts);
3161
3162 if (!AddOpLocation || !DerefOpLocation)
3163 return std::nullopt;
3164
3165 CharSourceRange ClosingParenWithPrecWhitespace =
3166 clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
3167
3168 return FixItList{
3169 {FixItHint::CreateRemoval(StarWithTrailWhitespace),
3170 FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
3171 FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
3172 }
3173 return std::nullopt; // something wrong or unsupported, give up
3174}
3175
3176std::optional<FixItList>
3177PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
3178 const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
3179 switch (S.lookup(VD)) {
3181 ASTContext &Ctx = VD->getASTContext();
3183 // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
3184 // Deletes the *operand
3186 Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
3187 // Inserts the [0]
3188 if (auto LocPastOperand =
3189 getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
3190 return FixItList{{FixItHint::CreateRemoval(derefRange),
3191 FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
3192 }
3193 break;
3194 }
3195 case FixitStrategy::Kind::Iterator:
3196 case FixitStrategy::Kind::Array:
3197 return std::nullopt;
3198 case FixitStrategy::Kind::Vector:
3199 llvm_unreachable("FixitStrategy not implemented yet!");
3200 case FixitStrategy::Kind::Wontfix:
3201 llvm_unreachable("Invalid strategy!");
3202 }
3203
3204 return std::nullopt;
3205}
3206
3207static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
3208 const DeclRefExpr *DRE) {
3209 const SourceManager &SM = Ctx.getSourceManager();
3210 // Inserts the .data() after the DRE
3211 std::optional<SourceLocation> EndOfOperand =
3212 getPastLoc(DRE, SM, Ctx.getLangOpts());
3213
3214 if (EndOfOperand)
3215 return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
3216
3217 return std::nullopt;
3218}
3219
3220// Generates fix-its replacing an expression of the form UPC(DRE) with
3221// `DRE.data()`
3222std::optional<FixItList>
3223UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
3224 const auto VD = cast<VarDecl>(Node->getDecl());
3225 switch (S.lookup(VD)) {
3226 case FixitStrategy::Kind::Array:
3227 case FixitStrategy::Kind::Span: {
3228 return createDataFixit(VD->getASTContext(), Node);
3229 // FIXME: Points inside a macro expansion.
3230 break;
3231 }
3232 case FixitStrategy::Kind::Wontfix:
3233 case FixitStrategy::Kind::Iterator:
3234 return std::nullopt;
3235 case FixitStrategy::Kind::Vector:
3236 llvm_unreachable("unsupported strategies for FixableGadgets");
3237 }
3238
3239 return std::nullopt;
3240}
3241
3242// Generates fix-its replacing an expression of the form `&DRE[e]` with
3243// `&DRE.data()[e]`:
3244static std::optional<FixItList>
3246 const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
3247 const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
3248 // FIXME: this `getASTContext` call is costly, we should pass the
3249 // ASTContext in:
3250 const ASTContext &Ctx = DRE->getDecl()->getASTContext();
3251 const Expr *Idx = ArraySub->getIdx();
3252 const SourceManager &SM = Ctx.getSourceManager();
3253 const LangOptions &LangOpts = Ctx.getLangOpts();
3254 std::stringstream SS;
3255 bool IdxIsLitZero = false;
3256
3257 if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
3258 if ((*ICE).isZero())
3259 IdxIsLitZero = true;
3260 std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
3261 if (!DreString)
3262 return std::nullopt;
3263
3264 if (IdxIsLitZero) {
3265 // If the index is literal zero, we produce the most concise fix-it:
3266 SS << (*DreString).str() << ".data()";
3267 } else {
3268 std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
3269 if (!IndexString)
3270 return std::nullopt;
3271
3272 SS << "&" << (*DreString).str() << ".data()"
3273 << "[" << (*IndexString).str() << "]";
3274 }
3275 return FixItList{
3276 FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())};
3277}
3278
3279std::optional<FixItList>
3281 DeclUseList DREs = getClaimedVarUseSites();
3282
3283 if (DREs.size() != 1)
3284 return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
3285 // give up
3286 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3287 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3288 FixItList Fixes;
3289
3290 const Stmt *AddAssignNode = Node;
3291 StringRef varName = VD->getName();
3292 const ASTContext &Ctx = VD->getASTContext();
3293
3294 if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
3295 return std::nullopt;
3296
3297 // To transform UUC(p += n) to UUC(p = p.subspan(..)):
3298 bool NotParenExpr =
3299 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
3300 std::string SS = varName.str() + " = " + varName.str() + ".subspan";
3301 if (NotParenExpr)
3302 SS += "(";
3303
3304 std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
3305 AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
3306 if (!AddAssignLocation)
3307 return std::nullopt;
3308
3309 Fixes.push_back(FixItHint::CreateReplacement(
3310 SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
3311 SS));
3312 if (NotParenExpr)
3313 Fixes.push_back(FixItHint::CreateInsertion(
3314 Offset->getEndLoc().getLocWithOffset(1), ")"));
3315 return Fixes;
3316 }
3317 }
3318 return std::nullopt; // Not in the cases that we can handle for now, give up.
3319}
3320
3321std::optional<FixItList>
3323 DeclUseList DREs = getClaimedVarUseSites();
3324
3325 if (DREs.size() != 1)
3326 return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
3327 // give up
3328 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3329 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3330 FixItList Fixes;
3331 std::stringstream SS;
3332 StringRef varName = VD->getName();
3333 const ASTContext &Ctx = VD->getASTContext();
3334
3335 // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
3336 SS << "(" << varName.data() << " = " << varName.data()
3337 << ".subspan(1)).data()";
3338 std::optional<SourceLocation> PreIncLocation =
3339 getEndCharLoc(Node, Ctx.getSourceManager(), Ctx.getLangOpts());
3340 if (!PreIncLocation)
3341 return std::nullopt;
3342
3343 Fixes.push_back(FixItHint::CreateReplacement(
3344 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
3345 return Fixes;
3346 }
3347 }
3348 return std::nullopt; // Not in the cases that we can handle for now, give up.
3349}
3350
3351// For a non-null initializer `Init` of `T *` type, this function returns
3352// `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
3353// to output stream.
3354// In many cases, this function cannot figure out the actual extent `S`. It
3355// then will use a place holder to replace `S` to ask users to fill `S` in. The
3356// initializer shall be used to initialize a variable of type `std::span<T>`.
3357// In some cases (e. g. constant size array) the initializer should remain
3358// unchanged and the function returns empty list. In case the function can't
3359// provide the right fixit it will return nullopt.
3360//
3361// FIXME: Support multi-level pointers
3362//
3363// Parameters:
3364// `Init` a pointer to the initializer expression
3365// `Ctx` a reference to the ASTContext
3366static std::optional<FixItList>
3368 const StringRef UserFillPlaceHolder) {
3369 const SourceManager &SM = Ctx.getSourceManager();
3370 const LangOptions &LangOpts = Ctx.getLangOpts();
3371
3372 // If `Init` has a constant value that is (or equivalent to) a
3373 // NULL pointer, we use the default constructor to initialize the span
3374 // object, i.e., a `std:span` variable declaration with no initializer.
3375 // So the fix-it is just to remove the initializer.
3376 if (Init->isNullPointerConstant(
3377 Ctx,
3378 // FIXME: Why does this function not ask for `const ASTContext
3379 // &`? It should. Maybe worth an NFC patch later.
3381 NPC_ValueDependentIsNotNull)) {
3382 std::optional<SourceLocation> InitLocation =
3383 getEndCharLoc(Init, SM, LangOpts);
3384 if (!InitLocation)
3385 return std::nullopt;
3386
3387 SourceRange SR(Init->getBeginLoc(), *InitLocation);
3388
3389 return FixItList{FixItHint::CreateRemoval(SR)};
3390 }
3391
3392 FixItList FixIts{};
3393 std::string ExtentText = UserFillPlaceHolder.data();
3394 StringRef One = "1";
3395
3396 // Insert `{` before `Init`:
3397 FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
3398 // Try to get the data extent. Break into different cases:
3399 if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
3400 // In cases `Init` is `new T[n]` and there is no explicit cast over
3401 // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
3402 // of `T`. So the extent is `n` unless `n` has side effects. Similar but
3403 // simpler for the case where `Init` is `new T`.
3404 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
3405 if (!Ext->HasSideEffects(Ctx)) {
3406 std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
3407 if (!ExtentString)
3408 return std::nullopt;
3409 ExtentText = *ExtentString;
3410 }
3411 } else if (!CxxNew->isArray())
3412 // Although the initializer is not allocating a buffer, the pointer
3413 // variable could still be used in buffer access operations.
3414 ExtentText = One;
3415 } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) {
3416 // std::span has a single parameter constructor for initialization with
3417 // constant size array. The size is auto-deduced as the constructor is a
3418 // function template. The correct fixit is empty - no changes should happen.
3419 return FixItList{};
3420 } else {
3421 // In cases `Init` is of the form `&Var` after stripping of implicit
3422 // casts, where `&` is the built-in operator, the extent is 1.
3423 if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
3424 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
3425 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
3426 ExtentText = One;
3427 // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
3428 // and explicit casting, etc. etc.
3429 }
3430
3431 SmallString<32> StrBuffer{};
3432 std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
3433
3434 if (!LocPassInit)
3435 return std::nullopt;
3436
3437 StrBuffer.append(", ");
3438 StrBuffer.append(ExtentText);
3439 StrBuffer.append("}");
3440 FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
3441 return FixIts;
3442}
3443
3444#ifndef NDEBUG
3445#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
3446 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
3447 "failed to produce fixit for declaration '" + \
3448 (D)->getNameAsString() + "'" + (Msg))
3449#else
3450#define DEBUG_NOTE_DECL_FAIL(D, Msg)
3451#endif
3452
3453// For the given variable declaration with a pointer-to-T type, returns the text
3454// `std::span<T>`. If it is unable to generate the text, returns
3455// `std::nullopt`.
3456static std::optional<std::string>
3458 assert(VD->getType()->isPointerType());
3459
3460 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3461 std::optional<std::string> PteTyText = getPointeeTypeText(
3462 VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
3463
3464 if (!PteTyText)
3465 return std::nullopt;
3466
3467 std::string SpanTyText = "std::span<";
3468
3469 SpanTyText.append(*PteTyText);
3470 // Append qualifiers to span element type if any:
3471 if (PteTyQualifiers) {
3472 SpanTyText.append(" ");
3473 SpanTyText.append(PteTyQualifiers->getAsString());
3474 }
3475 SpanTyText.append(">");
3476 return SpanTyText;
3477}
3478
3479// For a `VarDecl` of the form `T * var (= Init)?`, this
3480// function generates fix-its that
3481// 1) replace `T * var` with `std::span<T> var`; and
3482// 2) change `Init` accordingly to a span constructor, if it exists.
3483//
3484// FIXME: support Multi-level pointers
3485//
3486// Parameters:
3487// `D` a pointer the variable declaration node
3488// `Ctx` a reference to the ASTContext
3489// `UserFillPlaceHolder` the user-input placeholder text
3490// Returns:
3491// the non-empty fix-it list, if fix-its are successfuly generated; empty
3492// list otherwise.
3493static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
3494 const StringRef UserFillPlaceHolder,
3495 UnsafeBufferUsageHandler &Handler) {
3497 return {};
3498
3499 FixItList FixIts{};
3500 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
3501
3502 if (!SpanTyText) {
3503 DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
3504 return {};
3505 }
3506
3507 // Will hold the text for `std::span<T> Ident`:
3508 std::stringstream SS;
3509
3510 SS << *SpanTyText;
3511 // Fix the initializer if it exists:
3512 if (const Expr *Init = D->getInit()) {
3513 std::optional<FixItList> InitFixIts =
3514 FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
3515 if (!InitFixIts)
3516 return {};
3517 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
3518 std::make_move_iterator(InitFixIts->end()));
3519 }
3520 // For declaration of the form `T * ident = init;`, we want to replace
3521 // `T * ` with `std::span<T>`.
3522 // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
3523 // just `T *` with `std::span<T>`.
3524 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
3525 if (!EndLocForReplacement.isValid()) {
3526 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
3527 return {};
3528 }
3529 // The only exception is that for `T *ident` we'll add a single space between
3530 // "std::span<T>" and "ident".
3531 // FIXME: The condition is false for identifiers expended from macros.
3532 if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
3533 SS << " ";
3534
3535 FixIts.push_back(FixItHint::CreateReplacement(
3536 SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
3537 return FixIts;
3538}
3539
3540static bool hasConflictingOverload(const FunctionDecl *FD) {
3541 return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
3542}
3543
3544// For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
3545// types, this function produces fix-its to make the change self-contained. Let
3546// 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
3547// entity defined by the `FunctionDecl` after the change to the parameters.
3548// Fix-its produced by this function are
3549// 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
3550// of 'F';
3551// 2. Create a declaration of "NewF" next to each declaration of `F`;
3552// 3. Create a definition of "F" (as its' original definition is now belongs
3553// to "NewF") next to its original definition. The body of the creating
3554// definition calls to "NewF".
3555//
3556// Example:
3557//
3558// void f(int *p); // original declaration
3559// void f(int *p) { // original definition
3560// p[5];
3561// }
3562//
3563// To change the parameter `p` to be of `std::span<int>` type, we
3564// also add overloads:
3565//
3566// [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
3567// void f(std::span<int> p); // added overload decl
3568// void f(std::span<int> p) { // original def where param is changed
3569// p[5];
3570// }
3571// [[clang::unsafe_buffer_usage]] void f(int *p) { // added def
3572// return f(std::span(p, <# size #>));
3573// }
3574//
3575static std::optional<FixItList>
3577 const ASTContext &Ctx,
3578 UnsafeBufferUsageHandler &Handler) {
3579 // FIXME: need to make this conflict checking better:
3580 if (hasConflictingOverload(FD))
3581 return std::nullopt;
3582
3583 const SourceManager &SM = Ctx.getSourceManager();
3584 const LangOptions &LangOpts = Ctx.getLangOpts();
3585 const unsigned NumParms = FD->getNumParams();
3586 std::vector<std::string> NewTysTexts(NumParms);
3587 std::vector<bool> ParmsMask(NumParms, false);
3588 bool AtLeastOneParmToFix = false;
3589
3590 for (unsigned i = 0; i < NumParms; i++) {
3591 const ParmVarDecl *PVD = FD->getParamDecl(i);
3592
3594 continue;
3595 if (S.lookup(PVD) != FixitStrategy::Kind::Span)
3596 // Not supported, not suppose to happen:
3597 return std::nullopt;
3598
3599 std::optional<Qualifiers> PteTyQuals = std::nullopt;
3600 std::optional<std::string> PteTyText =
3601 getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
3602
3603 if (!PteTyText)
3604 // something wrong in obtaining the text of the pointee type, give up
3605 return std::nullopt;
3606 // FIXME: whether we should create std::span type depends on the
3607 // FixitStrategy.
3608 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
3609 ParmsMask[i] = true;
3610 AtLeastOneParmToFix = true;
3611 }
3612 if (!AtLeastOneParmToFix)
3613 // No need to create function overloads:
3614 return {};
3615 // FIXME Respect indentation of the original code.
3616
3617 // A lambda that creates the text representation of a function declaration
3618 // with the new type signatures:
3619 const auto NewOverloadSignatureCreator =
3620 [&SM, &LangOpts, &NewTysTexts,
3621 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3622 std::stringstream SS;
3623
3624 SS << ";";
3625 SS << getEndOfLine().str();
3626 // Append: ret-type func-name "("
3627 if (auto Prefix = getRangeText(
3628 SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
3629 SM, LangOpts))
3630 SS << Prefix->str();
3631 else
3632 return std::nullopt; // give up
3633 // Append: parameter-type-list
3634 const unsigned NumParms = FD->getNumParams();
3635
3636 for (unsigned i = 0; i < NumParms; i++) {
3637 const ParmVarDecl *Parm = FD->getParamDecl(i);
3638
3639 if (Parm->isImplicit())
3640 continue;
3641 if (ParmsMask[i]) {
3642 // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
3643 // new type:
3644 SS << NewTysTexts[i];
3645 // print parameter name if provided:
3646 if (IdentifierInfo *II = Parm->getIdentifier())
3647 SS << ' ' << II->getName().str();
3648 } else if (auto ParmTypeText =
3649 getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts),
3650 SM, LangOpts)) {
3651 // print the whole `Parm` without modification:
3652 SS << ParmTypeText->str();
3653 } else
3654 return std::nullopt; // something wrong, give up
3655 if (i != NumParms - 1)
3656 SS << ", ";
3657 }
3658 SS << ")";
3659 return SS.str();
3660 };
3661
3662 // A lambda that creates the text representation of a function definition with
3663 // the original signature:
3664 const auto OldOverloadDefCreator =
3665 [&Handler, &SM, &LangOpts, &NewTysTexts,
3666 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3667 std::stringstream SS;
3668
3669 SS << getEndOfLine().str();
3670 // Append: attr-name ret-type func-name "(" param-list ")" "{"
3671 if (auto FDPrefix = getRangeText(
3672 SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
3673 LangOpts))
3674 SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
3675 << FDPrefix->str() << "{";
3676 else
3677 return std::nullopt;
3678 // Append: "return" func-name "("
3679 if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
3680 SS << "return " << FunQualName->str() << "(";
3681 else
3682 return std::nullopt;
3683
3684 // Append: arg-list
3685 const unsigned NumParms = FD->getNumParams();
3686 for (unsigned i = 0; i < NumParms; i++) {
3687 const ParmVarDecl *Parm = FD->getParamDecl(i);
3688
3689 if (Parm->isImplicit())
3690 continue;
3691 // FIXME: If a parameter has no name, it is unused in the
3692 // definition. So we could just leave it as it is.
3693 if (!Parm->getIdentifier())
3694 // If a parameter of a function definition has no name:
3695 return std::nullopt;
3696 if (ParmsMask[i])
3697 // This is our spanified paramter!
3698 SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
3699 << ", " << getUserFillPlaceHolder("size") << ")";
3700 else
3701 SS << Parm->getIdentifier()->getName().str();
3702 if (i != NumParms - 1)
3703 SS << ", ";
3704 }
3705 // finish call and the body
3706 SS << ");}" << getEndOfLine().str();
3707 // FIXME: 80-char line formatting?
3708 return SS.str();
3709 };
3710
3711 FixItList FixIts{};
3712 for (FunctionDecl *FReDecl : FD->redecls()) {
3713 std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
3714
3715 if (!Loc)
3716 return {};
3717 if (FReDecl->isThisDeclarationADefinition()) {
3718 assert(FReDecl == FD && "inconsistent function definition");
3719 // Inserts a definition with the old signature to the end of
3720 // `FReDecl`:
3721 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
3722 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
3723 else
3724 return {}; // give up
3725 } else {
3726 // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
3727 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
3728 FixIts.emplace_back(FixItHint::CreateInsertion(
3729 FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
3730 FReDecl->getBeginLoc(), " ")));
3731 }
3732 // Inserts a declaration with the new signature to the end of `FReDecl`:
3733 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
3734 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
3735 else
3736 return {};
3737 }
3738 }
3739 return FixIts;
3740}
3741
3742// To fix a `ParmVarDecl` to be of `std::span` type.
3743static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
3744 UnsafeBufferUsageHandler &Handler) {
3746 DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
3747 return {};
3748 }
3749 if (PVD->hasDefaultArg()) {
3750 // FIXME: generate fix-its for default values:
3751 DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
3752 return {};
3753 }
3754
3755 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3756 std::optional<std::string> PteTyText = getPointeeTypeText(
3757 PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
3758
3759 if (!PteTyText) {
3760 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
3761 return {};
3762 }
3763
3764 std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
3765
3766 if (!PVDNameText) {
3767 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
3768 return {};
3769 }
3770
3771 std::stringstream SS;
3772 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
3773
3774 if (PteTyQualifiers)
3775 // Append qualifiers if they exist:
3776 SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
3777 else
3778 SS << getSpanTypeText(*PteTyText);
3779 // Append qualifiers to the type of the parameter:
3780 if (PVD->getType().hasQualifiers())
3781 SS << ' ' << PVD->getType().getQualifiers().getAsString();
3782 // Append parameter's name:
3783 SS << ' ' << PVDNameText->str();
3784 // Add replacement fix-it:
3785 return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
3786}
3787
3788static FixItList fixVariableWithSpan(const VarDecl *VD,
3789 const DeclUseTracker &Tracker,
3790 ASTContext &Ctx,
3791 UnsafeBufferUsageHandler &Handler) {
3792 const DeclStmt *DS = Tracker.lookupDecl(VD);
3793 if (!DS) {
3795 " : variables declared this way not implemented yet");
3796 return {};
3797 }
3798 if (!DS->isSingleDecl()) {
3799 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
3800 DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
3801 return {};
3802 }
3803 // Currently DS is an unused variable but we'll need it when
3804 // non-single decls are implemented, where the pointee type name
3805 // and the '*' are spread around the place.
3806 (void)DS;
3807
3808 // FIXME: handle cases where DS has multiple declarations
3809 return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
3810}
3811
3812static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
3813 UnsafeBufferUsageHandler &Handler) {
3814 FixItList FixIts{};
3815
3816 // Note: the code below expects the declaration to not use any type sugar like
3817 // typedef.
3818 if (auto CAT = Ctx.getAsConstantArrayType(D->getType())) {
3819 const QualType &ArrayEltT = CAT->getElementType();
3820 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
3821 // FIXME: support multi-dimensional arrays
3822 if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType()))
3823 return {};
3824
3826
3827 // Get the spelling of the element type as written in the source file
3828 // (including macros, etc.).
3829 auto MaybeElemTypeTxt =
3831 Ctx.getLangOpts());
3832 if (!MaybeElemTypeTxt)
3833 return {};
3834 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
3835
3836 // Find the '[' token.
3837 std::optional<Token> NextTok = Lexer::findNextToken(
3839 while (NextTok && !NextTok->is(tok::l_square) &&
3840 NextTok->getLocation() <= D->getSourceRange().getEnd())
3841 NextTok = Lexer::findNextToken(NextTok->getLocation(),
3842 Ctx.getSourceManager(), Ctx.getLangOpts());
3843 if (!NextTok)
3844 return {};
3845 const SourceLocation LSqBracketLoc = NextTok->getLocation();
3846
3847 // Get the spelling of the array size as written in the source file
3848 // (including macros, etc.).
3849 auto MaybeArraySizeTxt = getRangeText(
3850 {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
3851 Ctx.getSourceManager(), Ctx.getLangOpts());
3852 if (!MaybeArraySizeTxt)
3853 return {};
3854 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
3855 if (ArraySizeTxt.empty()) {
3856 // FIXME: Support array size getting determined from the initializer.
3857 // Examples:
3858 // int arr1[] = {0, 1, 2};
3859 // int arr2{3, 4, 5};
3860 // We might be able to preserve the non-specified size with `auto` and
3861 // `std::to_array`:
3862 // auto arr1 = std::to_array<int>({0, 1, 2});
3863 return {};
3864 }
3865
3866 std::optional<StringRef> IdentText =
3868
3869 if (!IdentText) {
3870 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
3871 return {};
3872 }
3873
3874 SmallString<32> Replacement;
3875 llvm::raw_svector_ostream OS(Replacement);
3876 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
3877 << IdentText->str();
3878
3879 FixIts.push_back(FixItHint::CreateReplacement(
3880 SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));
3881 }
3882
3883 return FixIts;
3884}
3885
3886static FixItList fixVariableWithArray(const VarDecl *VD,
3887 const DeclUseTracker &Tracker,
3888 const ASTContext &Ctx,
3889 UnsafeBufferUsageHandler &Handler) {
3890 const DeclStmt *DS = Tracker.lookupDecl(VD);
3891 assert(DS && "Fixing non-local variables not implemented yet!");
3892 if (!DS->isSingleDecl()) {
3893 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
3894 return {};
3895 }
3896 // Currently DS is an unused variable but we'll need it when
3897 // non-single decls are implemented, where the pointee type name
3898 // and the '*' are spread around the place.
3899 (void)DS;
3900
3901 // FIXME: handle cases where DS has multiple declarations
3902 return fixVarDeclWithArray(VD, Ctx, Handler);
3903}
3904
3905// TODO: we should be consistent to use `std::nullopt` to represent no-fix due
3906// to any unexpected problem.
3907static FixItList
3909 /* The function decl under analysis */ const Decl *D,
3910 const DeclUseTracker &Tracker, ASTContext &Ctx,
3911 UnsafeBufferUsageHandler &Handler) {
3912 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
3913 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
3914 if (!FD || FD != D) {
3915 // `FD != D` means that `PVD` belongs to a function that is not being
3916 // analyzed currently. Thus `FD` may not be complete.
3917 DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
3918 return {};
3919 }
3920
3921 // TODO If function has a try block we can't change params unless we check
3922 // also its catch block for their use.
3923 // FIXME We might support static class methods, some select methods,
3924 // operators and possibly lamdas.
3925 if (FD->isMain() || FD->isConstexpr() ||
3926 FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
3927 FD->isVariadic() ||
3928 // also covers call-operator of lamdas
3929 isa<CXXMethodDecl>(FD) ||
3930 // skip when the function body is a try-block
3931 (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
3932 FD->isOverloadedOperator()) {
3933 DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
3934 return {}; // TODO test all these cases
3935 }
3936 }
3937
3938 switch (K) {
3940 if (VD->getType()->isPointerType()) {
3941 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
3942 return fixParamWithSpan(PVD, Ctx, Handler);
3943
3944 if (VD->isLocalVarDecl())
3945 return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
3946 }
3947 DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
3948 return {};
3949 }
3951 if (VD->isLocalVarDecl() && Ctx.getAsConstantArrayType(VD->getType()))
3952 return fixVariableWithArray(VD, Tracker, Ctx, Handler);
3953
3954 DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
3955 return {};
3956 }
3959 llvm_unreachable("FixitStrategy not implemented yet!");
3961 llvm_unreachable("Invalid strategy!");
3962 }
3963 llvm_unreachable("Unknown strategy!");
3964}
3965
3966// Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
3967// `RemoveRange` of 'h' overlaps with a macro use.
3968static bool overlapWithMacro(const FixItList &FixIts) {
3969 // FIXME: For now we only check if the range (or the first token) is (part of)
3970 // a macro expansion. Ideally, we want to check for all tokens in the range.
3971 return llvm::any_of(FixIts, [](const FixItHint &Hint) {
3972 auto Range = Hint.RemoveRange;
3973 if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
3974 // If the range (or the first token) is (part of) a macro expansion:
3975 return true;
3976 return false;
3977 });
3978}
3979
3980// Returns true iff `VD` is a parameter of the declaration `D`:
3981static bool isParameterOf(const VarDecl *VD, const Decl *D) {
3982 return isa<ParmVarDecl>(VD) &&
3983 VD->getDeclContext() == dyn_cast<DeclContext>(D);
3984}
3985
3986// Erases variables in `FixItsForVariable`, if such a variable has an unfixable
3987// group mate. A variable `v` is unfixable iff `FixItsForVariable` does not
3988// contain `v`.
3990 std::map<const VarDecl *, FixItList> &FixItsForVariable,
3991 const VariableGroupsManager &VarGrpMgr) {
3992 // Variables will be removed from `FixItsForVariable`:
3994
3995 for (const auto &[VD, Ignore] : FixItsForVariable) {
3996 VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
3997 if (llvm::any_of(Grp,
3998 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
3999 return !FixItsForVariable.count(GrpMember);
4000 })) {
4001 // At least one group member cannot be fixed, so we have to erase the
4002 // whole group:
4003 for (const VarDecl *Member : Grp)
4004 ToErase.push_back(Member);
4005 }
4006 }
4007 for (auto *VarToErase : ToErase)
4008 FixItsForVariable.erase(VarToErase);
4009}
4010
4011// Returns the fix-its that create bounds-safe function overloads for the
4012// function `D`, if `D`'s parameters will be changed to safe-types through
4013// fix-its in `FixItsForVariable`.
4014//
4015// NOTE: In case `D`'s parameters will be changed but bounds-safe function
4016// overloads cannot created, the whole group that contains the parameters will
4017// be erased from `FixItsForVariable`.
4019 std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
4020 const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
4021 const FixitStrategy &S, ASTContext &Ctx,
4022 UnsafeBufferUsageHandler &Handler) {
4023 FixItList FixItsSharedByParms{};
4024
4025 std::optional<FixItList> OverloadFixes =
4026 createOverloadsForFixedParams(S, FD, Ctx, Handler);
4027
4028 if (OverloadFixes) {
4029 FixItsSharedByParms.append(*OverloadFixes);
4030 } else {
4031 // Something wrong in generating `OverloadFixes`, need to remove the
4032 // whole group, where parameters are in, from `FixItsForVariable` (Note
4033 // that all parameters should be in the same group):
4034 for (auto *Member : VarGrpMgr.getGroupOfParms())
4035 FixItsForVariable.erase(Member);
4036 }
4037 return FixItsSharedByParms;
4038}
4039
4040// Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
4041static std::map<const VarDecl *, FixItList>
4042getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S,
4043 ASTContext &Ctx,
4044 /* The function decl under analysis */ const Decl *D,
4045 const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
4046 const VariableGroupsManager &VarGrpMgr) {
4047 // `FixItsForVariable` will map each variable to a set of fix-its directly
4048 // associated to the variable itself. Fix-its of distinct variables in
4049 // `FixItsForVariable` are disjoint.
4050 std::map<const VarDecl *, FixItList> FixItsForVariable;
4051
4052 // Populate `FixItsForVariable` with fix-its directly associated with each
4053 // variable. Fix-its directly associated to a variable 'v' are the ones
4054 // produced by the `FixableGadget`s whose claimed variable is 'v'.
4055 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
4056 FixItsForVariable[VD] =
4057 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
4058 // If we fail to produce Fix-It for the declaration we have to skip the
4059 // variable entirely.
4060 if (FixItsForVariable[VD].empty()) {
4061 FixItsForVariable.erase(VD);
4062 continue;
4063 }
4064 for (const auto &F : Fixables) {
4065 std::optional<FixItList> Fixits = F->getFixits(S);
4066
4067 if (Fixits) {
4068 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
4069 Fixits->begin(), Fixits->end());
4070 continue;
4071 }
4072#ifndef NDEBUG
4073 Handler.addDebugNoteForVar(
4074 VD, F->getSourceLoc(),
4075 ("gadget '" + F->getDebugName() + "' refused to produce a fix")
4076 .str());
4077#endif
4078 FixItsForVariable.erase(VD);
4079 break;
4080 }
4081 }
4082
4083 // `FixItsForVariable` now contains only variables that can be
4084 // fixed. A variable can be fixed if its' declaration and all Fixables
4085 // associated to it can all be fixed.
4086
4087 // To further remove from `FixItsForVariable` variables whose group mates
4088 // cannot be fixed...
4089 eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
4090 // Now `FixItsForVariable` gets further reduced: a variable is in
4091 // `FixItsForVariable` iff it can be fixed and all its group mates can be
4092 // fixed.
4093
4094 // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
4095 // That is, when fixing multiple parameters in one step, these fix-its will
4096 // be applied only once (instead of being applied per parameter).
4097 FixItList FixItsSharedByParms{};
4098
4099 if (auto *FD = dyn_cast<FunctionDecl>(D))
4100 FixItsSharedByParms = createFunctionOverloadsForParms(
4101 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
4102
4103 // The map that maps each variable `v` to fix-its for the whole group where
4104 // `v` is in:
4105 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
4106 FixItsForVariable};
4107
4108 for (auto &[Var, Ignore] : FixItsForVariable) {
4109 bool AnyParm = false;
4110 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
4111
4112 for (const VarDecl *GrpMate : VarGroupForVD) {
4113 if (Var == GrpMate)
4114 continue;
4115 if (FixItsForVariable.count(GrpMate))
4116 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
4117 }
4118 if (AnyParm) {
4119 // This assertion should never fail. Otherwise we have a bug.
4120 assert(!FixItsSharedByParms.empty() &&
4121 "Should not try to fix a parameter that does not belong to a "
4122 "FunctionDecl");
4123 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
4124 }
4125 }
4126 // Fix-its that will be applied in one step shall NOT:
4127 // 1. overlap with macros or/and templates; or
4128 // 2. conflict with each other.
4129 // Otherwise, the fix-its will be dropped.
4130 for (auto Iter = FinalFixItsForVariable.begin();
4131 Iter != FinalFixItsForVariable.end();)
4132 if (overlapWithMacro(Iter->second) ||
4133 clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) {
4134 Iter = FinalFixItsForVariable.erase(Iter);
4135 } else
4136 Iter++;
4137 return FinalFixItsForVariable;
4138}
4139
4140template <typename VarDeclIterTy>
4141static FixitStrategy
4142getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
4143 FixitStrategy S;
4144 for (const VarDecl *VD : UnsafeVars) {
4147 else
4149 }
4150 return S;
4151}
4152
4153// Manages variable groups:
4155 const std::vector<VarGrpTy> Groups;
4156 const std::map<const VarDecl *, unsigned> &VarGrpMap;
4157 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
4158
4159public:
4161 const std::vector<VarGrpTy> &Groups,
4162 const std::map<const VarDecl *, unsigned> &VarGrpMap,
4163 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
4164 : Groups(Groups), VarGrpMap(VarGrpMap),
4165 GrpsUnionForParms(GrpsUnionForParms) {}
4166
4167 VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
4168 if (GrpsUnionForParms.contains(Var)) {
4169 if (HasParm)
4170 *HasParm = true;
4171 return GrpsUnionForParms.getArrayRef();
4172 }
4173 if (HasParm)
4174 *HasParm = false;
4175
4176 auto It = VarGrpMap.find(Var);
4177
4178 if (It == VarGrpMap.end())
4179 return {};
4180 return Groups[It->second];
4181 }
4182
4183 VarGrpRef getGroupOfParms() const override {
4184 return GrpsUnionForParms.getArrayRef();
4185 }
4186};
4187
4188static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
4189 WarningGadgetList WarningGadgets,
4190 DeclUseTracker Tracker,
4191 UnsafeBufferUsageHandler &Handler,
4192 bool EmitSuggestions) {
4193 if (!EmitSuggestions) {
4194 // Our job is very easy without suggestions. Just warn about
4195 // every problematic operation and consider it done. No need to deal
4196 // with fixable gadgets, no need to group operations by variable.
4197 for (const auto &G : WarningGadgets) {
4198 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4199 D->getASTContext());
4200 }
4201
4202 // This return guarantees that most of the machine doesn't run when
4203 // suggestions aren't requested.
4204 assert(FixableGadgets.empty() &&
4205 "Fixable gadgets found but suggestions not requested!");
4206 return;
4207 }
4208
4209 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4210 // function under the analysis. No need to fix any Fixables.
4211 if (!WarningGadgets.empty()) {
4212 // Gadgets "claim" variables they're responsible for. Once this loop
4213 // finishes, the tracker will only track DREs that weren't claimed by any
4214 // gadgets, i.e. not understood by the analysis.
4215 for (const auto &G : FixableGadgets) {
4216 for (const auto *DRE : G->getClaimedVarUseSites()) {
4217 Tracker.claimUse(DRE);
4218 }
4219 }
4220 }
4221
4222 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4223 // function under the analysis. Thus, it early returns here as there is
4224 // nothing needs to be fixed.
4225 //
4226 // Note this claim is based on the assumption that there is no unsafe
4227 // variable whose declaration is invisible from the analyzing function.
4228 // Otherwise, we need to consider if the uses of those unsafe varuables needs
4229 // fix.
4230 // So far, we are not fixing any global variables or class members. And,
4231 // lambdas will be analyzed along with the enclosing function. So this early
4232 // return is correct for now.
4233 if (WarningGadgets.empty())
4234 return;
4235
4236 WarningGadgetSets UnsafeOps =
4237 groupWarningGadgetsByVar(std::move(WarningGadgets));
4238 FixableGadgetSets FixablesForAllVars =
4239 groupFixablesByVar(std::move(FixableGadgets));
4240
4241 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
4242
4243 // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
4244 for (auto it = FixablesForAllVars.byVar.cbegin();
4245 it != FixablesForAllVars.byVar.cend();) {
4246 // FIXME: need to deal with global variables later
4247 if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
4248#ifndef NDEBUG
4249 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4250 ("failed to produce fixit for '" +
4251 it->first->getNameAsString() +
4252 "' : neither local nor a parameter"));
4253#endif
4254 it = FixablesForAllVars.byVar.erase(it);
4255 } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
4256#ifndef NDEBUG
4257 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4258 ("failed to produce fixit for '" +
4259 it->first->getNameAsString() +
4260 "' : has a reference type"));
4261#endif
4262 it = FixablesForAllVars.byVar.erase(it);
4263 } else if (Tracker.hasUnclaimedUses(it->first)) {
4264 it = FixablesForAllVars.byVar.erase(it);
4265 } else if (it->first->isInitCapture()) {
4266#ifndef NDEBUG
4267 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4268 ("failed to produce fixit for '" +
4269 it->first->getNameAsString() +
4270 "' : init capture"));
4271#endif
4272 it = FixablesForAllVars.byVar.erase(it);
4273 } else {
4274 ++it;
4275 }
4276 }
4277
4278#ifndef NDEBUG
4279 for (const auto &it : UnsafeOps.byVar) {
4280 const VarDecl *const UnsafeVD = it.first;
4281 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
4282 if (UnclaimedDREs.empty())
4283 continue;
4284 const auto UnfixedVDName = UnsafeVD->getNameAsString();
4285 for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
4286 std::string UnclaimedUseTrace =
4287 getDREAncestorString(UnclaimedDRE, D->getASTContext());
4288
4289 Handler.addDebugNoteForVar(
4290 UnsafeVD, UnclaimedDRE->getBeginLoc(),
4291 ("failed to produce fixit for '" + UnfixedVDName +
4292 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
4293 UnclaimedUseTrace));
4294 }
4295 }
4296#endif
4297
4298 // Fixpoint iteration for pointer assignments
4299 using DepMapTy =
4300 llvm::DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
4301 DepMapTy DependenciesMap{};
4302 DepMapTy PtrAssignmentGraph{};
4303
4304 for (const auto &it : FixablesForAllVars.byVar) {
4305 for (const FixableGadget *fixable : it.second) {
4306 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
4307 fixable->getStrategyImplications();
4308 if (ImplPair) {
4309 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
4310 PtrAssignmentGraph[Impl.first].insert(Impl.second);
4311 }
4312 }
4313 }
4314
4315 /*
4316 The following code does a BFS traversal of the `PtrAssignmentGraph`
4317 considering all unsafe vars as starting nodes and constructs an undirected
4318 graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
4319 elimiates all variables that are unreachable from any unsafe var. In other
4320 words, this removes all dependencies that don't include any unsafe variable
4321 and consequently don't need any fixit generation.
4322 Note: A careful reader would observe that the code traverses
4323 `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
4324 `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
4325 achieve the same result but the one used here dramatically cuts the
4326 amount of hoops the second part of the algorithm needs to jump, given that
4327 a lot of these connections become "direct". The reader is advised not to
4328 imagine how the graph is transformed because of using `Var` instead of
4329 `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
4330 and think about why it's equivalent later.
4331 */
4332 std::set<const VarDecl *> VisitedVarsDirected{};
4333 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4334 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
4335
4336 std::queue<const VarDecl *> QueueDirected{};
4337 QueueDirected.push(Var);
4338 while (!QueueDirected.empty()) {
4339 const VarDecl *CurrentVar = QueueDirected.front();
4340 QueueDirected.pop();
4341 VisitedVarsDirected.insert(CurrentVar);
4342 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
4343 for (const VarDecl *Adj : AdjacentNodes) {
4344 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
4345 QueueDirected.push(Adj);
4346 }
4347 DependenciesMap[Var].insert(Adj);
4348 DependenciesMap[Adj].insert(Var);
4349 }
4350 }
4351 }
4352 }
4353
4354 // `Groups` stores the set of Connected Components in the graph.
4355 std::vector<VarGrpTy> Groups;
4356 // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
4357 // variables belong to. Group indexes refer to the elements in `Groups`.
4358 // `VarGrpMap` is complete in that every variable that needs fix is in it.
4359 std::map<const VarDecl *, unsigned> VarGrpMap;
4360 // The union group over the ones in "Groups" that contain parameters of `D`:
4361 llvm::SetVector<const VarDecl *>
4362 GrpsUnionForParms; // these variables need to be fixed in one step
4363
4364 // Group Connected Components for Unsafe Vars
4365 // (Dependencies based on pointer assignments)
4366 std::set<const VarDecl *> VisitedVars{};
4367 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4368 if (VisitedVars.find(Var) == VisitedVars.end()) {
4369 VarGrpTy &VarGroup = Groups.emplace_back();
4370 std::queue<const VarDecl *> Queue{};
4371
4372 Queue.push(Var);
4373 while (!Queue.empty()) {
4374 const VarDecl *CurrentVar = Queue.front();
4375 Queue.pop();
4376 VisitedVars.insert(CurrentVar);
4377 VarGroup.push_back(CurrentVar);
4378 auto AdjacentNodes = DependenciesMap[CurrentVar];
4379 for (const VarDecl *Adj : AdjacentNodes) {
4380 if (VisitedVars.find(Adj) == VisitedVars.end()) {
4381 Queue.push(Adj);
4382 }
4383 }
4384 }
4385
4386 bool HasParm = false;
4387 unsigned GrpIdx = Groups.size() - 1;
4388
4389 for (const VarDecl *V : VarGroup) {
4390 VarGrpMap[V] = GrpIdx;
4391 if (!HasParm && isParameterOf(V, D))
4392 HasParm = true;
4393 }
4394 if (HasParm)
4395 GrpsUnionForParms.insert_range(VarGroup);
4396 }
4397 }
4398
4399 // Remove a `FixableGadget` if the associated variable is not in the graph
4400 // computed above. We do not want to generate fix-its for such variables,
4401 // since they are neither warned nor reachable from a warned one.
4402 //
4403 // Note a variable is not warned if it is not directly used in any unsafe
4404 // operation. A variable `v` is NOT reachable from an unsafe variable, if it
4405 // does not exist another variable `u` such that `u` is warned and fixing `u`
4406 // (transitively) implicates fixing `v`.
4407 //
4408 // For example,
4409 // ```
4410 // void f(int * p) {
4411 // int * a = p; *p = 0;
4412 // }
4413 // ```
4414 // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
4415 // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of
4416 // the function above, `p` becomes reachable from a warned variable.
4417 for (auto I = FixablesForAllVars.byVar.begin();
4418 I != FixablesForAllVars.byVar.end();) {
4419 // Note `VisitedVars` contain all the variables in the graph:
4420 if (!VisitedVars.count((*I).first)) {
4421 // no such var in graph:
4422 I = FixablesForAllVars.byVar.erase(I);
4423 } else
4424 ++I;
4425 }
4426
4427 // We assign strategies to variables that are 1) in the graph and 2) can be
4428 // fixed. Other variables have the default "Won't fix" strategy.
4429 FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
4430 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
4431 // If a warned variable has no "Fixable", it is considered unfixable:
4432 return FixablesForAllVars.byVar.count(V);
4433 }));
4434 VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
4435
4436 if (isa<NamedDecl>(D))
4437 // The only case where `D` is not a `NamedDecl` is when `D` is a
4438 // `BlockDecl`. Let's not fix variables in blocks for now
4439 FixItsForVariableGroup =
4440 getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
4441 Tracker, Handler, VarGrpMgr);
4442
4443 for (const auto &G : UnsafeOps.noVar) {
4444 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4445 D->getASTContext());
4446 }
4447
4448 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
4449 auto FixItsIt = FixItsForVariableGroup.find(VD);
4450 Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
4451 FixItsIt != FixItsForVariableGroup.end()
4452 ? std::move(FixItsIt->second)
4453 : FixItList{},
4454 D, NaiveStrategy);
4455 for (const auto &G : WarningGadgets) {
4456 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
4457 D->getASTContext());
4458 }
4459 }
4460}
4461
4463 UnsafeBufferUsageHandler &Handler,
4464 bool EmitSuggestions) {
4465#ifndef NDEBUG
4466 Handler.clearDebugNotes();
4467#endif
4468
4469 assert(D);
4470
4471 SmallVector<Stmt *> Stmts;
4472
4473 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
4474 // We do not want to visit a Lambda expression defined inside a method
4475 // independently. Instead, it should be visited along with the outer method.
4476 // FIXME: do we want to do the same thing for `BlockDecl`s?
4477 if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
4478 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
4479 return;
4480 }
4481
4482 for (FunctionDecl *FReDecl : FD->redecls()) {
4483 if (FReDecl->isExternC()) {
4484 // Do not emit fixit suggestions for functions declared in an
4485 // extern "C" block.
4486 EmitSuggestions = false;
4487 break;
4488 }
4489 }
4490
4491 Stmts.push_back(FD->getBody());
4492
4493 if (const auto *ID = dyn_cast<CXXConstructorDecl>(D)) {
4494 for (const CXXCtorInitializer *CI : ID->inits()) {
4495 Stmts.push_back(CI->getInit());
4496 }
4497 }
4498 } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
4499 Stmts.push_back(D->getBody());
4500 }
4501
4502 assert(!Stmts.empty());
4503
4504 FixableGadgetList FixableGadgets;
4505 WarningGadgetList WarningGadgets;
4506 DeclUseTracker Tracker;
4507 for (Stmt *S : Stmts) {
4508 findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
4509 WarningGadgets, Tracker);
4510 }
4511 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
4512 std::move(Tracker), Handler, EmitSuggestions);
4513}
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:2001
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:3275
unsigned getBuiltinID(bool ConsiderWrapperFunctions=false) const
Returns a value indicating whether this function corresponds to a builtin function.
Definition Decl.cpp:3758
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:3822
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:3054
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2976
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:8382
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:8333
QualType getCanonicalType() const
Definition TypeBase.h:8345
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition TypeBase.h:8366
std::string getAsString() const
Represents a struct/union/class.
Definition Decl.h:4321
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:2425
bool isArrayType() const
Definition TypeBase.h:8629
bool isCharType() const
Definition Type.cpp:2132
bool isPointerType() const
Definition TypeBase.h:8530
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition TypeBase.h:8936
const T * castAs() const
Member-template castAs<specific type>.
Definition TypeBase.h:9179
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:752
bool isAnyCharacterType() const
Determine whether this type is any of the built-in character types.
Definition Type.cpp:2168
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:2253
bool isAnyPointerType() const
Definition TypeBase.h:8538
const Type * getUnqualifiedDesugaredType() const
Return the specified type with any "sugar" removed from the type, removing any typedefs,...
Definition Type.cpp:653
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:2197
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition Decl.cpp:2264
bool isInlineSpecified() const
Definition Decl.h:1554
bool hasConstantInitialization() const
Determine whether this variable has constant initialization.
Definition Decl.cpp:2655
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 hasUnsafeFormatOrSArg(const CallExpr *Call, const Expr *&UnsafeArg, const unsigned FmtArgIdx, ASTContext &Ctx, bool isKprintf=false)
static bool isPredefinedUnsafeLibcFunc(const FunctionDecl &Node)
static bool hasUnsafeSnprintfBuffer(const CallExpr &Node, ASTContext &Ctx)
static bool isUnsafeVaListPrintfFunc(const FunctionDecl &Node)
static bool isUnsafeSprintfFunc(const FunctionDecl &Node)
static bool isNormalPrintfFunc(const FunctionDecl &Node)
#define false
Definition stdbool.h:26
bool operator()(const NodeTy *N1, const NodeTy *N2) const
std::map< const VarDecl *, std::set< const FixableGadget * >, CompareNode< VarDecl > > byVar
std::map< const VarDecl *, std::set< const WarningGadget * >, CompareNode< VarDecl > > byVar
llvm::SmallVector< const WarningGadget *, 16 > noVar
SourceLocation getBeginLoc() const
getBeginLoc - Retrieve the location of the first token.
SourceLocation getEndLoc() const LLVM_READONLY
EvalResult is a struct with detailed info about an evaluated expression.
Definition Expr.h:645
APValue Val
Val - This is the value the expression can be folded to.
Definition Expr.h:647
const BoundNodes Nodes
Contains the nodes bound on the current match.
StringRef matchName(StringRef FunName, bool isBuiltin)