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