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