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