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