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
2978std::set<const Expr *> clang::findUnsafePointers(const Decl *D) {
2979 class MockReporter : public UnsafeBufferUsageHandler {
2980 public:
2981 MockReporter() {}
2982 void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {}
2983 void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &,
2984 const Expr *UnsafeArg = nullptr) override {}
2985 void handleUnsafeOperationInContainer(const Stmt *, bool,
2986 ASTContext &) override {}
2988 const VariableGroupsManager &, FixItList &&,
2989 const Decl *,
2990 const FixitStrategy &) override {}
2992 bool IsRelatedToDecl,
2993 ASTContext &Ctx) override {}
2994 bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
2995 return false;
2996 }
2997 bool isSafeBufferOptOut(const SourceLocation &) const override {
2998 return false;
2999 }
3000 bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
3001 return false;
3002 }
3004 const SourceLocation &Loc) const override {
3005 return false;
3006 }
3008 SourceLocation, StringRef WSSuffix = "") const override {
3009 return "";
3010 }
3011 };
3012
3013 FixableGadgetList FixableGadgets;
3014 WarningGadgetList WarningGadgets;
3015 DeclUseTracker Tracker;
3016 MockReporter IgnoreHandler;
3017 ASTContext &Ctx = D->getASTContext();
3019
3021 for (auto *Stmt : Stmts)
3022 findGadgets(Stmt, Ctx, IgnoreHandler, false, FixableGadgets, WarningGadgets,
3023 Tracker);
3024
3025 std::set<const Expr *> Result;
3026 for (auto &G : WarningGadgets) {
3027 for (const Expr *E : G->getUnsafePtrs()) {
3028 Result.insert(E);
3029 }
3030 }
3031
3032 return Result;
3033}
3034
3036 std::map<const VarDecl *, std::set<const WarningGadget *>,
3037 // To keep keys sorted by their locations in the map so that the
3038 // order is deterministic:
3041 // These Gadgets are not related to pointer variables (e. g. temporaries).
3043};
3044
3045static WarningGadgetSets
3046groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
3047 WarningGadgetSets result;
3048 // If some gadgets cover more than one
3049 // variable, they'll appear more than once in the map.
3050 for (auto &G : AllUnsafeOperations) {
3051 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
3052
3053 bool AssociatedWithVarDecl = false;
3054 for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
3055 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3056 result.byVar[VD].insert(G.get());
3057 AssociatedWithVarDecl = true;
3058 }
3059 }
3060
3061 if (!AssociatedWithVarDecl) {
3062 result.noVar.push_back(G.get());
3063 continue;
3064 }
3065 }
3066 return result;
3067}
3068
3070 std::map<const VarDecl *, std::set<const FixableGadget *>,
3071 // To keep keys sorted by their locations in the map so that the
3072 // order is deterministic:
3075};
3076
3077static FixableGadgetSets
3078groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
3079 FixableGadgetSets FixablesForUnsafeVars;
3080 for (auto &F : AllFixableOperations) {
3081 DeclUseList DREs = F->getClaimedVarUseSites();
3082
3083 for (const DeclRefExpr *DRE : DREs) {
3084 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3085 FixablesForUnsafeVars.byVar[VD].insert(F.get());
3086 }
3087 }
3088 }
3089 return FixablesForUnsafeVars;
3090}
3091
3093 const SourceManager &SM) {
3094 // A simple interval overlap detection algorithm. Sorts all ranges by their
3095 // begin location then finds the first overlap in one pass.
3096 std::vector<const FixItHint *> All; // a copy of `FixIts`
3097
3098 for (const FixItHint &H : FixIts)
3099 All.push_back(&H);
3100 std::sort(All.begin(), All.end(),
3101 [&SM](const FixItHint *H1, const FixItHint *H2) {
3102 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
3103 H2->RemoveRange.getBegin());
3104 });
3105
3106 const FixItHint *CurrHint = nullptr;
3107
3108 for (const FixItHint *Hint : All) {
3109 if (!CurrHint ||
3110 SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
3111 Hint->RemoveRange.getBegin())) {
3112 // Either to initialize `CurrHint` or `CurrHint` does not
3113 // overlap with `Hint`:
3114 CurrHint = Hint;
3115 } else
3116 // In case `Hint` overlaps the `CurrHint`, we found at least one
3117 // conflict:
3118 return true;
3119 }
3120 return false;
3121}
3122
3123std::optional<FixItList>
3124PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
3125 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
3126 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
3127 switch (S.lookup(LeftVD)) {
3129 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
3130 return FixItList{};
3131 return std::nullopt;
3133 return std::nullopt;
3136 return std::nullopt;
3138 llvm_unreachable("unsupported strategies for FixableGadgets");
3139 }
3140 return std::nullopt;
3141}
3142
3143/// \returns fixit that adds .data() call after \DRE.
3144static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
3145 const DeclRefExpr *DRE);
3146
3147std::optional<FixItList>
3148CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
3149 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
3150 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
3151 // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
3152 // non-trivial.
3153 //
3154 // CArrayToPtrAssignmentGadget doesn't have strategy implications because
3155 // constant size array propagates its bounds. Because of that LHS and RHS are
3156 // addressed by two different fixits.
3157 //
3158 // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
3159 // to and can't be generally relied on in multi-variable Fixables!
3160 //
3161 // E. g. If an instance of this gadget is fixing variable on LHS then the
3162 // variable on RHS is fixed by a different fixit and its strategy for LHS
3163 // fixit is as if Wontfix.
3164 //
3165 // The only exception is Wontfix strategy for a given variable as that is
3166 // valid for any fixit produced for the given input source code.
3167 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
3168 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
3169 return FixItList{};
3170 }
3171 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
3172 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
3173 return createDataFixit(RightVD->getASTContext(), PtrRHS);
3174 }
3175 }
3176 return std::nullopt;
3177}
3178
3179std::optional<FixItList>
3180PointerInitGadget::getFixits(const FixitStrategy &S) const {
3181 const auto *LeftVD = PtrInitLHS;
3182 const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
3183 switch (S.lookup(LeftVD)) {
3184 case FixitStrategy::Kind::Span:
3185 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
3186 return FixItList{};
3187 return std::nullopt;
3188 case FixitStrategy::Kind::Wontfix:
3189 return std::nullopt;
3190 case FixitStrategy::Kind::Iterator:
3191 case FixitStrategy::Kind::Array:
3192 return std::nullopt;
3193 case FixitStrategy::Kind::Vector:
3194 llvm_unreachable("unsupported strategies for FixableGadgets");
3195 }
3196 return std::nullopt;
3197}
3198
3199static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
3200 const ASTContext &Ctx) {
3201 if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
3202 if (ConstVal->isNegative())
3203 return false;
3204 } else if (!Expr->getType()->isUnsignedIntegerType())
3205 return false;
3206 return true;
3207}
3208
3209std::optional<FixItList>
3210ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
3211 if (const auto *DRE =
3212 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
3213 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3214 switch (S.lookup(VD)) {
3215 case FixitStrategy::Kind::Span: {
3216
3217 // If the index has a negative constant value, we give up as no valid
3218 // fix-it can be generated:
3219 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
3220 VD->getASTContext();
3221 if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
3222 return std::nullopt;
3223 // no-op is a good fix-it, otherwise
3224 return FixItList{};
3225 }
3226 case FixitStrategy::Kind::Array:
3227 return FixItList{};
3228 case FixitStrategy::Kind::Wontfix:
3229 case FixitStrategy::Kind::Iterator:
3230 case FixitStrategy::Kind::Vector:
3231 llvm_unreachable("unsupported strategies for FixableGadgets");
3232 }
3233 }
3234 return std::nullopt;
3235}
3236
3237static std::optional<FixItList> // forward declaration
3238fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node);
3239
3240std::optional<FixItList>
3241UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
3242 auto DREs = getClaimedVarUseSites();
3243 const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
3244
3245 switch (S.lookup(VD)) {
3246 case FixitStrategy::Kind::Span:
3248 case FixitStrategy::Kind::Wontfix:
3249 case FixitStrategy::Kind::Iterator:
3250 case FixitStrategy::Kind::Array:
3251 return std::nullopt;
3252 case FixitStrategy::Kind::Vector:
3253 llvm_unreachable("unsupported strategies for FixableGadgets");
3254 }
3255 return std::nullopt; // something went wrong, no fix-it
3256}
3257
3258// FIXME: this function should be customizable through format
3259static StringRef getEndOfLine() {
3260 static const char *const EOL = "\n";
3261 return EOL;
3262}
3263
3264// Returns the text indicating that the user needs to provide input there:
3265static std::string
3266getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
3267 std::string s = std::string("<# ");
3268 s += HintTextToUser;
3269 s += " #>";
3270 return s;
3271}
3272
3273// Return the source location of the last character of the AST `Node`.
3274template <typename NodeTy>
3275static std::optional<SourceLocation>
3276getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
3277 const LangOptions &LangOpts) {
3278 if (unsigned TkLen =
3279 Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts)) {
3280 SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
3281
3282 if (Loc.isValid())
3283 return Loc;
3284 }
3285 return std::nullopt;
3286}
3287
3288// We cannot fix a variable declaration if it has some other specifiers than the
3289// type specifier. Because the source ranges of those specifiers could overlap
3290// with the source range that is being replaced using fix-its. Especially when
3291// we often cannot obtain accurate source ranges of cv-qualified type
3292// specifiers.
3293// FIXME: also deal with type attributes
3294static bool hasUnsupportedSpecifiers(const VarDecl *VD,
3295 const SourceManager &SM) {
3296 // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
3297 // source range of `VD`:
3298 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
3299 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
3300 VD->getBeginLoc())) &&
3301 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
3302 At->getRange().getBegin()));
3303 });
3304 return VD->isInlineSpecified() || VD->isConstexpr() ||
3306 AttrRangeOverlapping;
3307}
3308
3309// Returns the `SourceRange` of `D`. The reason why this function exists is
3310// that `D->getSourceRange()` may return a range where the end location is the
3311// starting location of the last token. The end location of the source range
3312// returned by this function is the last location of the last token.
3314 const SourceManager &SM,
3315 const LangOptions &LangOpts) {
3316 SourceLocation Begin = D->getBeginLoc();
3318 End = // `D->getEndLoc` should always return the starting location of the
3319 // last token, so we should get the end of the token
3320 Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
3321
3322 return SourceRange(Begin, End);
3323}
3324
3325// Returns the text of the name (with qualifiers) of a `FunctionDecl`.
3326static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
3327 const SourceManager &SM,
3328 const LangOptions &LangOpts) {
3329 SourceLocation BeginLoc = FD->getQualifier()
3331 : FD->getNameInfo().getBeginLoc();
3332 // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
3333 // last token:
3335 FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
3336 SourceRange NameRange{BeginLoc, EndLoc};
3337
3338 return getRangeText(NameRange, SM, LangOpts);
3339}
3340
3341// Returns the text representing a `std::span` type where the element type is
3342// represented by `EltTyText`.
3343//
3344// Note the optional parameter `Qualifiers`: one needs to pass qualifiers
3345// explicitly if the element type needs to be qualified.
3346static std::string
3347getSpanTypeText(StringRef EltTyText,
3348 std::optional<Qualifiers> Quals = std::nullopt) {
3349 const char *const SpanOpen = "std::span<";
3350
3351 if (Quals)
3352 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
3353 return SpanOpen + EltTyText.str() + '>';
3354}
3355
3356std::optional<FixItList>
3358 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
3359
3360 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
3361 ASTContext &Ctx = VD->getASTContext();
3362 // std::span can't represent elements before its begin()
3363 if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
3364 if (ConstVal->isNegative())
3365 return std::nullopt;
3366
3367 // note that the expr may (oddly) has multiple layers of parens
3368 // example:
3369 // *((..(pointer + 123)..))
3370 // goal:
3371 // pointer[123]
3372 // Fix-It:
3373 // remove '*('
3374 // replace ' + ' with '['
3375 // replace ')' with ']'
3376
3377 // example:
3378 // *((..(123 + pointer)..))
3379 // goal:
3380 // 123[pointer]
3381 // Fix-It:
3382 // remove '*('
3383 // replace ' + ' with '['
3384 // replace ')' with ']'
3385
3386 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
3387 const SourceManager &SM = Ctx.getSourceManager();
3388 const LangOptions &LangOpts = Ctx.getLangOpts();
3389 CharSourceRange StarWithTrailWhitespace =
3390 clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(),
3391 LHS->getBeginLoc());
3392
3393 std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
3394 if (!LHSLocation)
3395 return std::nullopt;
3396
3397 CharSourceRange PlusWithSurroundingWhitespace =
3398 clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
3399
3400 std::optional<SourceLocation> AddOpLocation =
3401 getPastLoc(AddOp, SM, LangOpts);
3402 std::optional<SourceLocation> DerefOpLocation =
3403 getPastLoc(DerefOp, SM, LangOpts);
3404
3405 if (!AddOpLocation || !DerefOpLocation)
3406 return std::nullopt;
3407
3408 CharSourceRange ClosingParenWithPrecWhitespace =
3409 clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
3410
3411 return FixItList{
3412 {FixItHint::CreateRemoval(StarWithTrailWhitespace),
3413 FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
3414 FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
3415 }
3416 return std::nullopt; // something wrong or unsupported, give up
3417}
3418
3419std::optional<FixItList>
3420PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
3421 const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
3422 switch (S.lookup(VD)) {
3424 ASTContext &Ctx = VD->getASTContext();
3426 // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
3427 // Deletes the *operand
3429 Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
3430 // Inserts the [0]
3431 if (auto LocPastOperand =
3432 getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
3433 return FixItList{{FixItHint::CreateRemoval(derefRange),
3434 FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
3435 }
3436 break;
3437 }
3438 case FixitStrategy::Kind::Iterator:
3439 case FixitStrategy::Kind::Array:
3440 return std::nullopt;
3441 case FixitStrategy::Kind::Vector:
3442 llvm_unreachable("FixitStrategy not implemented yet!");
3443 case FixitStrategy::Kind::Wontfix:
3444 llvm_unreachable("Invalid strategy!");
3445 }
3446
3447 return std::nullopt;
3448}
3449
3450static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
3451 const DeclRefExpr *DRE) {
3452 const SourceManager &SM = Ctx.getSourceManager();
3453 // Inserts the .data() after the DRE
3454 std::optional<SourceLocation> EndOfOperand =
3455 getPastLoc(DRE, SM, Ctx.getLangOpts());
3456
3457 if (EndOfOperand)
3458 return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
3459
3460 return std::nullopt;
3461}
3462
3463// Generates fix-its replacing an expression of the form UPC(DRE) with
3464// `DRE.data()`
3465std::optional<FixItList>
3466UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
3467 const auto VD = cast<VarDecl>(Node->getDecl());
3468 switch (S.lookup(VD)) {
3469 case FixitStrategy::Kind::Array:
3470 case FixitStrategy::Kind::Span: {
3471 return createDataFixit(VD->getASTContext(), Node);
3472 // FIXME: Points inside a macro expansion.
3473 break;
3474 }
3475 case FixitStrategy::Kind::Wontfix:
3476 case FixitStrategy::Kind::Iterator:
3477 return std::nullopt;
3478 case FixitStrategy::Kind::Vector:
3479 llvm_unreachable("unsupported strategies for FixableGadgets");
3480 }
3481
3482 return std::nullopt;
3483}
3484
3485// Generates fix-its replacing an expression of the form `&DRE[e]` with
3486// `&DRE.data()[e]`:
3487static std::optional<FixItList>
3489 const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
3490 const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
3491 // FIXME: this `getASTContext` call is costly, we should pass the
3492 // ASTContext in:
3493 const ASTContext &Ctx = DRE->getDecl()->getASTContext();
3494 const Expr *Idx = ArraySub->getIdx();
3495 const SourceManager &SM = Ctx.getSourceManager();
3496 const LangOptions &LangOpts = Ctx.getLangOpts();
3497 std::stringstream SS;
3498 bool IdxIsLitZero = false;
3499
3500 if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
3501 if ((*ICE).isZero())
3502 IdxIsLitZero = true;
3503 std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
3504 if (!DreString)
3505 return std::nullopt;
3506
3507 if (IdxIsLitZero) {
3508 // If the index is literal zero, we produce the most concise fix-it:
3509 SS << (*DreString).str() << ".data()";
3510 } else {
3511 std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
3512 if (!IndexString)
3513 return std::nullopt;
3514
3515 SS << "&" << (*DreString).str() << ".data()"
3516 << "[" << (*IndexString).str() << "]";
3517 }
3518 return FixItList{
3519 FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())};
3520}
3521
3522std::optional<FixItList>
3524 DeclUseList DREs = getClaimedVarUseSites();
3525
3526 if (DREs.size() != 1)
3527 return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
3528 // give up
3529 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3530 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3531 FixItList Fixes;
3532
3533 const Stmt *AddAssignNode = Node;
3534 StringRef varName = VD->getName();
3535 const ASTContext &Ctx = VD->getASTContext();
3536
3537 if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
3538 return std::nullopt;
3539
3540 // To transform UUC(p += n) to UUC(p = p.subspan(..)):
3541 bool NotParenExpr =
3542 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
3543 std::string SS = varName.str() + " = " + varName.str() + ".subspan";
3544 if (NotParenExpr)
3545 SS += "(";
3546
3547 std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
3548 AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
3549 if (!AddAssignLocation)
3550 return std::nullopt;
3551
3552 Fixes.push_back(FixItHint::CreateReplacement(
3553 SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
3554 SS));
3555 if (NotParenExpr)
3556 Fixes.push_back(FixItHint::CreateInsertion(
3557 Offset->getEndLoc().getLocWithOffset(1), ")"));
3558 return Fixes;
3559 }
3560 }
3561 return std::nullopt; // Not in the cases that we can handle for now, give up.
3562}
3563
3564std::optional<FixItList>
3566 DeclUseList DREs = getClaimedVarUseSites();
3567
3568 if (DREs.size() != 1)
3569 return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
3570 // give up
3571 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3572 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3573 FixItList Fixes;
3574 std::stringstream SS;
3575 StringRef varName = VD->getName();
3576 const ASTContext &Ctx = VD->getASTContext();
3577
3578 // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
3579 SS << "(" << varName.data() << " = " << varName.data()
3580 << ".subspan(1)).data()";
3581 std::optional<SourceLocation> PreIncLocation =
3582 getEndCharLoc(Node, Ctx.getSourceManager(), Ctx.getLangOpts());
3583 if (!PreIncLocation)
3584 return std::nullopt;
3585
3586 Fixes.push_back(FixItHint::CreateReplacement(
3587 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
3588 return Fixes;
3589 }
3590 }
3591 return std::nullopt; // Not in the cases that we can handle for now, give up.
3592}
3593
3594// For a non-null initializer `Init` of `T *` type, this function returns
3595// `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
3596// to output stream.
3597// In many cases, this function cannot figure out the actual extent `S`. It
3598// then will use a place holder to replace `S` to ask users to fill `S` in. The
3599// initializer shall be used to initialize a variable of type `std::span<T>`.
3600// In some cases (e. g. constant size array) the initializer should remain
3601// unchanged and the function returns empty list. In case the function can't
3602// provide the right fixit it will return nullopt.
3603//
3604// FIXME: Support multi-level pointers
3605//
3606// Parameters:
3607// `Init` a pointer to the initializer expression
3608// `Ctx` a reference to the ASTContext
3609static std::optional<FixItList>
3611 const StringRef UserFillPlaceHolder) {
3612 const SourceManager &SM = Ctx.getSourceManager();
3613 const LangOptions &LangOpts = Ctx.getLangOpts();
3614
3615 // If `Init` has a constant value that is (or equivalent to) a
3616 // NULL pointer, we use the default constructor to initialize the span
3617 // object, i.e., a `std:span` variable declaration with no initializer.
3618 // So the fix-it is just to remove the initializer.
3619 if (Init->isNullPointerConstant(
3620 Ctx,
3621 // FIXME: Why does this function not ask for `const ASTContext
3622 // &`? It should. Maybe worth an NFC patch later.
3624 NPC_ValueDependentIsNotNull)) {
3625 std::optional<SourceLocation> InitLocation =
3626 getEndCharLoc(Init, SM, LangOpts);
3627 if (!InitLocation)
3628 return std::nullopt;
3629
3630 SourceRange SR(Init->getBeginLoc(), *InitLocation);
3631
3632 return FixItList{FixItHint::CreateRemoval(SR)};
3633 }
3634
3635 FixItList FixIts{};
3636 std::string ExtentText = UserFillPlaceHolder.data();
3637 StringRef One = "1";
3638
3639 // Insert `{` before `Init`:
3640 FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
3641 // Try to get the data extent. Break into different cases:
3642 if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
3643 // In cases `Init` is `new T[n]` and there is no explicit cast over
3644 // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
3645 // of `T`. So the extent is `n` unless `n` has side effects. Similar but
3646 // simpler for the case where `Init` is `new T`.
3647 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
3648 if (!Ext->HasSideEffects(Ctx)) {
3649 std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
3650 if (!ExtentString)
3651 return std::nullopt;
3652 ExtentText = *ExtentString;
3653 }
3654 } else if (!CxxNew->isArray())
3655 // Although the initializer is not allocating a buffer, the pointer
3656 // variable could still be used in buffer access operations.
3657 ExtentText = One;
3658 } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) {
3659 // std::span has a single parameter constructor for initialization with
3660 // constant size array. The size is auto-deduced as the constructor is a
3661 // function template. The correct fixit is empty - no changes should happen.
3662 return FixItList{};
3663 } else {
3664 // In cases `Init` is of the form `&Var` after stripping of implicit
3665 // casts, where `&` is the built-in operator, the extent is 1.
3666 if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
3667 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
3668 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
3669 ExtentText = One;
3670 // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
3671 // and explicit casting, etc. etc.
3672 }
3673
3674 SmallString<32> StrBuffer{};
3675 std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
3676
3677 if (!LocPassInit)
3678 return std::nullopt;
3679
3680 StrBuffer.append(", ");
3681 StrBuffer.append(ExtentText);
3682 StrBuffer.append("}");
3683 FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
3684 return FixIts;
3685}
3686
3687#ifndef NDEBUG
3688#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
3689 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
3690 "failed to produce fixit for declaration '" + \
3691 (D)->getNameAsString() + "'" + (Msg))
3692#else
3693#define DEBUG_NOTE_DECL_FAIL(D, Msg)
3694#endif
3695
3696// For the given variable declaration with a pointer-to-T type, returns the text
3697// `std::span<T>`. If it is unable to generate the text, returns
3698// `std::nullopt`.
3699static std::optional<std::string>
3701 assert(VD->getType()->isPointerType());
3702
3703 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3704 std::optional<std::string> PteTyText = getPointeeTypeText(
3705 VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
3706
3707 if (!PteTyText)
3708 return std::nullopt;
3709
3710 std::string SpanTyText = "std::span<";
3711
3712 SpanTyText.append(*PteTyText);
3713 // Append qualifiers to span element type if any:
3714 if (PteTyQualifiers) {
3715 SpanTyText.append(" ");
3716 SpanTyText.append(PteTyQualifiers->getAsString());
3717 }
3718 SpanTyText.append(">");
3719 return SpanTyText;
3720}
3721
3722// For a `VarDecl` of the form `T * var (= Init)?`, this
3723// function generates fix-its that
3724// 1) replace `T * var` with `std::span<T> var`; and
3725// 2) change `Init` accordingly to a span constructor, if it exists.
3726//
3727// FIXME: support Multi-level pointers
3728//
3729// Parameters:
3730// `D` a pointer the variable declaration node
3731// `Ctx` a reference to the ASTContext
3732// `UserFillPlaceHolder` the user-input placeholder text
3733// Returns:
3734// the non-empty fix-it list, if fix-its are successfuly generated; empty
3735// list otherwise.
3736static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
3737 const StringRef UserFillPlaceHolder,
3738 UnsafeBufferUsageHandler &Handler) {
3740 return {};
3741
3742 FixItList FixIts{};
3743 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
3744
3745 if (!SpanTyText) {
3746 DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
3747 return {};
3748 }
3749
3750 // Will hold the text for `std::span<T> Ident`:
3751 std::stringstream SS;
3752
3753 SS << *SpanTyText;
3754 // Fix the initializer if it exists:
3755 if (const Expr *Init = D->getInit()) {
3756 std::optional<FixItList> InitFixIts =
3757 FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
3758 if (!InitFixIts)
3759 return {};
3760 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
3761 std::make_move_iterator(InitFixIts->end()));
3762 }
3763 // For declaration of the form `T * ident = init;`, we want to replace
3764 // `T * ` with `std::span<T>`.
3765 // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
3766 // just `T *` with `std::span<T>`.
3767 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
3768 if (!EndLocForReplacement.isValid()) {
3769 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
3770 return {};
3771 }
3772 // The only exception is that for `T *ident` we'll add a single space between
3773 // "std::span<T>" and "ident".
3774 // FIXME: The condition is false for identifiers expended from macros.
3775 if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
3776 SS << " ";
3777
3778 FixIts.push_back(FixItHint::CreateReplacement(
3779 SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
3780 return FixIts;
3781}
3782
3783static bool hasConflictingOverload(const FunctionDecl *FD) {
3784 return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
3785}
3786
3787// For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
3788// types, this function produces fix-its to make the change self-contained. Let
3789// 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
3790// entity defined by the `FunctionDecl` after the change to the parameters.
3791// Fix-its produced by this function are
3792// 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
3793// of 'F';
3794// 2. Create a declaration of "NewF" next to each declaration of `F`;
3795// 3. Create a definition of "F" (as its original definition is now belongs
3796// to "NewF") next to its original definition. The body of the creating
3797// definition calls to "NewF".
3798//
3799// Example:
3800//
3801// void f(int *p); // original declaration
3802// void f(int *p) { // original definition
3803// p[5];
3804// }
3805//
3806// To change the parameter `p` to be of `std::span<int>` type, we
3807// also add overloads:
3808//
3809// [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
3810// void f(std::span<int> p); // added overload decl
3811// void f(std::span<int> p) { // original def where param is changed
3812// p[5];
3813// }
3814// [[clang::unsafe_buffer_usage]] void f(int *p) { // added def
3815// return f(std::span(p, <# size #>));
3816// }
3817//
3818static std::optional<FixItList>
3820 const ASTContext &Ctx,
3821 UnsafeBufferUsageHandler &Handler) {
3822 // FIXME: need to make this conflict checking better:
3823 if (hasConflictingOverload(FD))
3824 return std::nullopt;
3825
3826 const SourceManager &SM = Ctx.getSourceManager();
3827 const LangOptions &LangOpts = Ctx.getLangOpts();
3828 const unsigned NumParms = FD->getNumParams();
3829 std::vector<std::string> NewTysTexts(NumParms);
3830 std::vector<bool> ParmsMask(NumParms, false);
3831 bool AtLeastOneParmToFix = false;
3832
3833 for (unsigned i = 0; i < NumParms; i++) {
3834 const ParmVarDecl *PVD = FD->getParamDecl(i);
3835
3837 continue;
3838 if (S.lookup(PVD) != FixitStrategy::Kind::Span)
3839 // Not supported, not suppose to happen:
3840 return std::nullopt;
3841
3842 std::optional<Qualifiers> PteTyQuals = std::nullopt;
3843 std::optional<std::string> PteTyText =
3844 getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
3845
3846 if (!PteTyText)
3847 // something wrong in obtaining the text of the pointee type, give up
3848 return std::nullopt;
3849 // FIXME: whether we should create std::span type depends on the
3850 // FixitStrategy.
3851 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
3852 ParmsMask[i] = true;
3853 AtLeastOneParmToFix = true;
3854 }
3855 if (!AtLeastOneParmToFix)
3856 // No need to create function overloads:
3857 return {};
3858 // FIXME Respect indentation of the original code.
3859
3860 // A lambda that creates the text representation of a function declaration
3861 // with the new type signatures:
3862 const auto NewOverloadSignatureCreator =
3863 [&SM, &LangOpts, &NewTysTexts,
3864 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3865 std::stringstream SS;
3866
3867 SS << ";";
3868 SS << getEndOfLine().str();
3869 // Append: ret-type func-name "("
3870 if (auto Prefix = getRangeText(
3871 SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
3872 SM, LangOpts))
3873 SS << Prefix->str();
3874 else
3875 return std::nullopt; // give up
3876 // Append: parameter-type-list
3877 const unsigned NumParms = FD->getNumParams();
3878
3879 for (unsigned i = 0; i < NumParms; i++) {
3880 const ParmVarDecl *Parm = FD->getParamDecl(i);
3881
3882 if (Parm->isImplicit())
3883 continue;
3884 if (ParmsMask[i]) {
3885 // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
3886 // new type:
3887 SS << NewTysTexts[i];
3888 // print parameter name if provided:
3889 if (IdentifierInfo *II = Parm->getIdentifier())
3890 SS << ' ' << II->getName().str();
3891 } else if (auto ParmTypeText =
3892 getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts),
3893 SM, LangOpts)) {
3894 // print the whole `Parm` without modification:
3895 SS << ParmTypeText->str();
3896 } else
3897 return std::nullopt; // something wrong, give up
3898 if (i != NumParms - 1)
3899 SS << ", ";
3900 }
3901 SS << ")";
3902 return SS.str();
3903 };
3904
3905 // A lambda that creates the text representation of a function definition with
3906 // the original signature:
3907 const auto OldOverloadDefCreator =
3908 [&Handler, &SM, &LangOpts, &NewTysTexts,
3909 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3910 std::stringstream SS;
3911
3912 SS << getEndOfLine().str();
3913 // Append: attr-name ret-type func-name "(" param-list ")" "{"
3914 if (auto FDPrefix = getRangeText(
3915 SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
3916 LangOpts))
3917 SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
3918 << FDPrefix->str() << "{";
3919 else
3920 return std::nullopt;
3921 // Append: "return" func-name "("
3922 if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
3923 SS << "return " << FunQualName->str() << "(";
3924 else
3925 return std::nullopt;
3926
3927 // Append: arg-list
3928 const unsigned NumParms = FD->getNumParams();
3929 for (unsigned i = 0; i < NumParms; i++) {
3930 const ParmVarDecl *Parm = FD->getParamDecl(i);
3931
3932 if (Parm->isImplicit())
3933 continue;
3934 // FIXME: If a parameter has no name, it is unused in the
3935 // definition. So we could just leave it as it is.
3936 if (!Parm->getIdentifier())
3937 // If a parameter of a function definition has no name:
3938 return std::nullopt;
3939 if (ParmsMask[i])
3940 // This is our spanified paramter!
3941 SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
3942 << ", " << getUserFillPlaceHolder("size") << ")";
3943 else
3944 SS << Parm->getIdentifier()->getName().str();
3945 if (i != NumParms - 1)
3946 SS << ", ";
3947 }
3948 // finish call and the body
3949 SS << ");}" << getEndOfLine().str();
3950 // FIXME: 80-char line formatting?
3951 return SS.str();
3952 };
3953
3954 FixItList FixIts{};
3955 for (FunctionDecl *FReDecl : FD->redecls()) {
3956 std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
3957
3958 if (!Loc)
3959 return {};
3960 if (FReDecl->isThisDeclarationADefinition()) {
3961 assert(FReDecl == FD && "inconsistent function definition");
3962 // Inserts a definition with the old signature to the end of
3963 // `FReDecl`:
3964 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
3965 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
3966 else
3967 return {}; // give up
3968 } else {
3969 // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
3970 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
3971 FixIts.emplace_back(FixItHint::CreateInsertion(
3972 FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
3973 FReDecl->getBeginLoc(), " ")));
3974 }
3975 // Inserts a declaration with the new signature to the end of `FReDecl`:
3976 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
3977 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
3978 else
3979 return {};
3980 }
3981 }
3982 return FixIts;
3983}
3984
3985// To fix a `ParmVarDecl` to be of `std::span` type.
3986static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
3987 UnsafeBufferUsageHandler &Handler) {
3989 DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
3990 return {};
3991 }
3992 if (PVD->hasDefaultArg()) {
3993 // FIXME: generate fix-its for default values:
3994 DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
3995 return {};
3996 }
3997
3998 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3999 std::optional<std::string> PteTyText = getPointeeTypeText(
4000 PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
4001
4002 if (!PteTyText) {
4003 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
4004 return {};
4005 }
4006
4007 std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
4008
4009 if (!PVDNameText) {
4010 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
4011 return {};
4012 }
4013
4014 std::stringstream SS;
4015 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
4016
4017 if (PteTyQualifiers)
4018 // Append qualifiers if they exist:
4019 SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
4020 else
4021 SS << getSpanTypeText(*PteTyText);
4022 // Append qualifiers to the type of the parameter:
4023 if (PVD->getType().hasQualifiers())
4024 SS << ' ' << PVD->getType().getQualifiers().getAsString();
4025 // Append parameter's name:
4026 SS << ' ' << PVDNameText->str();
4027 // Add replacement fix-it:
4028 return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
4029}
4030
4031static FixItList fixVariableWithSpan(const VarDecl *VD,
4032 const DeclUseTracker &Tracker,
4033 ASTContext &Ctx,
4034 UnsafeBufferUsageHandler &Handler) {
4035 const DeclStmt *DS = Tracker.lookupDecl(VD);
4036 if (!DS) {
4038 " : variables declared this way not implemented yet");
4039 return {};
4040 }
4041 if (!DS->isSingleDecl()) {
4042 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
4043 DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
4044 return {};
4045 }
4046 // Currently DS is an unused variable but we'll need it when
4047 // non-single decls are implemented, where the pointee type name
4048 // and the '*' are spread around the place.
4049 (void)DS;
4050
4051 // FIXME: handle cases where DS has multiple declarations
4052 return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
4053}
4054
4055static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
4056 UnsafeBufferUsageHandler &Handler) {
4057 FixItList FixIts{};
4058
4059 // Note: the code below expects the declaration to not use any type sugar like
4060 // typedef.
4061 if (auto CAT = Ctx.getAsConstantArrayType(D->getType())) {
4062 const QualType &ArrayEltT = CAT->getElementType();
4063 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
4064 // FIXME: support multi-dimensional arrays
4065 if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType()))
4066 return {};
4067
4069
4070 // Get the spelling of the element type as written in the source file
4071 // (including macros, etc.).
4072 auto MaybeElemTypeTxt =
4074 Ctx.getLangOpts());
4075 if (!MaybeElemTypeTxt)
4076 return {};
4077 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
4078
4079 // Find the '[' token.
4080 std::optional<Token> NextTok = Lexer::findNextToken(
4082 while (NextTok && !NextTok->is(tok::l_square) &&
4083 NextTok->getLocation() <= D->getSourceRange().getEnd())
4084 NextTok = Lexer::findNextToken(NextTok->getLocation(),
4085 Ctx.getSourceManager(), Ctx.getLangOpts());
4086 if (!NextTok)
4087 return {};
4088 const SourceLocation LSqBracketLoc = NextTok->getLocation();
4089
4090 // Get the spelling of the array size as written in the source file
4091 // (including macros, etc.).
4092 auto MaybeArraySizeTxt = getRangeText(
4093 {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
4094 Ctx.getSourceManager(), Ctx.getLangOpts());
4095 if (!MaybeArraySizeTxt)
4096 return {};
4097 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
4098 if (ArraySizeTxt.empty()) {
4099 // FIXME: Support array size getting determined from the initializer.
4100 // Examples:
4101 // int arr1[] = {0, 1, 2};
4102 // int arr2{3, 4, 5};
4103 // We might be able to preserve the non-specified size with `auto` and
4104 // `std::to_array`:
4105 // auto arr1 = std::to_array<int>({0, 1, 2});
4106 return {};
4107 }
4108
4109 std::optional<StringRef> IdentText =
4111
4112 if (!IdentText) {
4113 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
4114 return {};
4115 }
4116
4117 SmallString<32> Replacement;
4118 llvm::raw_svector_ostream OS(Replacement);
4119 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
4120 << IdentText->str();
4121
4122 FixIts.push_back(FixItHint::CreateReplacement(
4123 SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));
4124 }
4125
4126 return FixIts;
4127}
4128
4129static FixItList fixVariableWithArray(const VarDecl *VD,
4130 const DeclUseTracker &Tracker,
4131 const ASTContext &Ctx,
4132 UnsafeBufferUsageHandler &Handler) {
4133 const DeclStmt *DS = Tracker.lookupDecl(VD);
4134 assert(DS && "Fixing non-local variables not implemented yet!");
4135 if (!DS->isSingleDecl()) {
4136 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
4137 return {};
4138 }
4139 // Currently DS is an unused variable but we'll need it when
4140 // non-single decls are implemented, where the pointee type name
4141 // and the '*' are spread around the place.
4142 (void)DS;
4143
4144 // FIXME: handle cases where DS has multiple declarations
4145 return fixVarDeclWithArray(VD, Ctx, Handler);
4146}
4147
4148// TODO: we should be consistent to use `std::nullopt` to represent no-fix due
4149// to any unexpected problem.
4150static FixItList
4152 /* The function decl under analysis */ const Decl *D,
4153 const DeclUseTracker &Tracker, ASTContext &Ctx,
4154 UnsafeBufferUsageHandler &Handler) {
4155 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
4156 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
4157 if (!FD || FD != D) {
4158 // `FD != D` means that `PVD` belongs to a function that is not being
4159 // analyzed currently. Thus `FD` may not be complete.
4160 DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
4161 return {};
4162 }
4163
4164 // TODO If function has a try block we can't change params unless we check
4165 // also its catch block for their use.
4166 // FIXME We might support static class methods, some select methods,
4167 // operators and possibly lamdas.
4168 if (FD->isMain() || FD->isConstexpr() ||
4170 FD->isVariadic() ||
4171 // also covers call-operator of lamdas
4172 isa<CXXMethodDecl>(FD) ||
4173 // skip when the function body is a try-block
4174 (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
4175 FD->isOverloadedOperator()) {
4176 DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
4177 return {}; // TODO test all these cases
4178 }
4179 }
4180
4181 switch (K) {
4183 if (VD->getType()->isPointerType()) {
4184 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
4185 return fixParamWithSpan(PVD, Ctx, Handler);
4186
4187 if (VD->isLocalVarDecl())
4188 return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
4189 }
4190 DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
4191 return {};
4192 }
4194 if (VD->isLocalVarDecl() && Ctx.getAsConstantArrayType(VD->getType()))
4195 return fixVariableWithArray(VD, Tracker, Ctx, Handler);
4196
4197 DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
4198 return {};
4199 }
4202 llvm_unreachable("FixitStrategy not implemented yet!");
4204 llvm_unreachable("Invalid strategy!");
4205 }
4206 llvm_unreachable("Unknown strategy!");
4207}
4208
4209// Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
4210// `RemoveRange` of 'h' overlaps with a macro use.
4211static bool overlapWithMacro(const FixItList &FixIts) {
4212 // FIXME: For now we only check if the range (or the first token) is (part of)
4213 // a macro expansion. Ideally, we want to check for all tokens in the range.
4214 return llvm::any_of(FixIts, [](const FixItHint &Hint) {
4215 auto Range = Hint.RemoveRange;
4216 if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
4217 // If the range (or the first token) is (part of) a macro expansion:
4218 return true;
4219 return false;
4220 });
4221}
4222
4223// Returns true iff `VD` is a parameter of the declaration `D`:
4224static bool isParameterOf(const VarDecl *VD, const Decl *D) {
4225 return isa<ParmVarDecl>(VD) &&
4226 VD->getDeclContext() == dyn_cast<DeclContext>(D);
4227}
4228
4229// Erases variables in `FixItsForVariable`, if such a variable has an unfixable
4230// group mate. A variable `v` is unfixable iff `FixItsForVariable` does not
4231// contain `v`.
4233 std::map<const VarDecl *, FixItList> &FixItsForVariable,
4234 const VariableGroupsManager &VarGrpMgr) {
4235 // Variables will be removed from `FixItsForVariable`:
4237
4238 for (const auto &[VD, Ignore] : FixItsForVariable) {
4239 VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
4240 if (llvm::any_of(Grp,
4241 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
4242 return !FixItsForVariable.count(GrpMember);
4243 })) {
4244 // At least one group member cannot be fixed, so we have to erase the
4245 // whole group:
4246 for (const VarDecl *Member : Grp)
4247 ToErase.push_back(Member);
4248 }
4249 }
4250 for (auto *VarToErase : ToErase)
4251 FixItsForVariable.erase(VarToErase);
4252}
4253
4254// Returns the fix-its that create bounds-safe function overloads for the
4255// function `D`, if `D`'s parameters will be changed to safe-types through
4256// fix-its in `FixItsForVariable`.
4257//
4258// NOTE: In case `D`'s parameters will be changed but bounds-safe function
4259// overloads cannot created, the whole group that contains the parameters will
4260// be erased from `FixItsForVariable`.
4262 std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
4263 const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
4264 const FixitStrategy &S, ASTContext &Ctx,
4265 UnsafeBufferUsageHandler &Handler) {
4266 FixItList FixItsSharedByParms{};
4267
4268 std::optional<FixItList> OverloadFixes =
4269 createOverloadsForFixedParams(S, FD, Ctx, Handler);
4270
4271 if (OverloadFixes) {
4272 FixItsSharedByParms.append(*OverloadFixes);
4273 } else {
4274 // Something wrong in generating `OverloadFixes`, need to remove the
4275 // whole group, where parameters are in, from `FixItsForVariable` (Note
4276 // that all parameters should be in the same group):
4277 for (auto *Member : VarGrpMgr.getGroupOfParms())
4278 FixItsForVariable.erase(Member);
4279 }
4280 return FixItsSharedByParms;
4281}
4282
4283// Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
4284static std::map<const VarDecl *, FixItList>
4285getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S,
4286 ASTContext &Ctx,
4287 /* The function decl under analysis */ const Decl *D,
4288 const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
4289 const VariableGroupsManager &VarGrpMgr) {
4290 // `FixItsForVariable` will map each variable to a set of fix-its directly
4291 // associated to the variable itself. Fix-its of distinct variables in
4292 // `FixItsForVariable` are disjoint.
4293 std::map<const VarDecl *, FixItList> FixItsForVariable;
4294
4295 // Populate `FixItsForVariable` with fix-its directly associated with each
4296 // variable. Fix-its directly associated to a variable 'v' are the ones
4297 // produced by the `FixableGadget`s whose claimed variable is 'v'.
4298 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
4299 FixItsForVariable[VD] =
4300 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
4301 // If we fail to produce Fix-It for the declaration we have to skip the
4302 // variable entirely.
4303 if (FixItsForVariable[VD].empty()) {
4304 FixItsForVariable.erase(VD);
4305 continue;
4306 }
4307 for (const auto &F : Fixables) {
4308 std::optional<FixItList> Fixits = F->getFixits(S);
4309
4310 if (Fixits) {
4311 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
4312 Fixits->begin(), Fixits->end());
4313 continue;
4314 }
4315#ifndef NDEBUG
4316 Handler.addDebugNoteForVar(
4317 VD, F->getSourceLoc(),
4318 ("gadget '" + F->getDebugName() + "' refused to produce a fix")
4319 .str());
4320#endif
4321 FixItsForVariable.erase(VD);
4322 break;
4323 }
4324 }
4325
4326 // `FixItsForVariable` now contains only variables that can be
4327 // fixed. A variable can be fixed if its declaration and all Fixables
4328 // associated to it can all be fixed.
4329
4330 // To further remove from `FixItsForVariable` variables whose group mates
4331 // cannot be fixed...
4332 eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
4333 // Now `FixItsForVariable` gets further reduced: a variable is in
4334 // `FixItsForVariable` iff it can be fixed and all its group mates can be
4335 // fixed.
4336
4337 // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
4338 // That is, when fixing multiple parameters in one step, these fix-its will
4339 // be applied only once (instead of being applied per parameter).
4340 FixItList FixItsSharedByParms{};
4341
4342 if (auto *FD = dyn_cast<FunctionDecl>(D))
4343 FixItsSharedByParms = createFunctionOverloadsForParms(
4344 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
4345
4346 // The map that maps each variable `v` to fix-its for the whole group where
4347 // `v` is in:
4348 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
4349 FixItsForVariable};
4350
4351 for (auto &[Var, Ignore] : FixItsForVariable) {
4352 bool AnyParm = false;
4353 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
4354
4355 for (const VarDecl *GrpMate : VarGroupForVD) {
4356 if (Var == GrpMate)
4357 continue;
4358 if (FixItsForVariable.count(GrpMate))
4359 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
4360 }
4361 if (AnyParm) {
4362 // This assertion should never fail. Otherwise we have a bug.
4363 assert(!FixItsSharedByParms.empty() &&
4364 "Should not try to fix a parameter that does not belong to a "
4365 "FunctionDecl");
4366 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
4367 }
4368 }
4369 // Fix-its that will be applied in one step shall NOT:
4370 // 1. overlap with macros or/and templates; or
4371 // 2. conflict with each other.
4372 // Otherwise, the fix-its will be dropped.
4373 for (auto Iter = FinalFixItsForVariable.begin();
4374 Iter != FinalFixItsForVariable.end();)
4375 if (overlapWithMacro(Iter->second) ||
4376 clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) {
4377 Iter = FinalFixItsForVariable.erase(Iter);
4378 } else
4379 Iter++;
4380 return FinalFixItsForVariable;
4381}
4382
4383template <typename VarDeclIterTy>
4384static FixitStrategy
4385getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
4386 FixitStrategy S;
4387 for (const VarDecl *VD : UnsafeVars) {
4390 else
4392 }
4393 return S;
4394}
4395
4396// Manages variable groups:
4398 const std::vector<VarGrpTy> &Groups;
4399 const std::map<const VarDecl *, unsigned> &VarGrpMap;
4400 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
4401
4402public:
4404 const std::vector<VarGrpTy> &Groups,
4405 const std::map<const VarDecl *, unsigned> &VarGrpMap,
4406 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
4407 : Groups(Groups), VarGrpMap(VarGrpMap),
4408 GrpsUnionForParms(GrpsUnionForParms) {}
4409
4410 VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
4411 if (GrpsUnionForParms.contains(Var)) {
4412 if (HasParm)
4413 *HasParm = true;
4414 return GrpsUnionForParms.getArrayRef();
4415 }
4416 if (HasParm)
4417 *HasParm = false;
4418
4419 auto It = VarGrpMap.find(Var);
4420
4421 if (It == VarGrpMap.end())
4422 return {};
4423 return Groups[It->second];
4424 }
4425
4426 VarGrpRef getGroupOfParms() const override {
4427 return GrpsUnionForParms.getArrayRef();
4428 }
4429};
4430
4431static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
4432 WarningGadgetList WarningGadgets,
4433 DeclUseTracker Tracker,
4434 UnsafeBufferUsageHandler &Handler,
4435 bool EmitSuggestions) {
4436 if (!EmitSuggestions) {
4437 // Our job is very easy without suggestions. Just warn about
4438 // every problematic operation and consider it done. No need to deal
4439 // with fixable gadgets, no need to group operations by variable.
4440 for (const auto &G : WarningGadgets) {
4441 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4442 D->getASTContext());
4443 }
4444
4445 // This return guarantees that most of the machine doesn't run when
4446 // suggestions aren't requested.
4447 assert(FixableGadgets.empty() &&
4448 "Fixable gadgets found but suggestions not requested!");
4449 return;
4450 }
4451
4452 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4453 // function under the analysis. No need to fix any Fixables.
4454 if (!WarningGadgets.empty()) {
4455 // Gadgets "claim" variables they're responsible for. Once this loop
4456 // finishes, the tracker will only track DREs that weren't claimed by any
4457 // gadgets, i.e. not understood by the analysis.
4458 for (const auto &G : FixableGadgets) {
4459 for (const auto *DRE : G->getClaimedVarUseSites()) {
4460 Tracker.claimUse(DRE);
4461 }
4462 }
4463 }
4464
4465 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4466 // function under the analysis. Thus, it early returns here as there is
4467 // nothing needs to be fixed.
4468 //
4469 // Note this claim is based on the assumption that there is no unsafe
4470 // variable whose declaration is invisible from the analyzing function.
4471 // Otherwise, we need to consider if the uses of those unsafe varuables needs
4472 // fix.
4473 // So far, we are not fixing any global variables or class members. And,
4474 // lambdas will be analyzed along with the enclosing function. So this early
4475 // return is correct for now.
4476 if (WarningGadgets.empty())
4477 return;
4478
4479 WarningGadgetSets UnsafeOps =
4480 groupWarningGadgetsByVar(std::move(WarningGadgets));
4481 FixableGadgetSets FixablesForAllVars =
4482 groupFixablesByVar(std::move(FixableGadgets));
4483
4484 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
4485
4486 // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
4487 for (auto it = FixablesForAllVars.byVar.cbegin();
4488 it != FixablesForAllVars.byVar.cend();) {
4489 // FIXME: need to deal with global variables later
4490 if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
4491#ifndef NDEBUG
4492 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4493 ("failed to produce fixit for '" +
4494 it->first->getNameAsString() +
4495 "' : neither local nor a parameter"));
4496#endif
4497 it = FixablesForAllVars.byVar.erase(it);
4498 } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
4499#ifndef NDEBUG
4500 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4501 ("failed to produce fixit for '" +
4502 it->first->getNameAsString() +
4503 "' : has a reference type"));
4504#endif
4505 it = FixablesForAllVars.byVar.erase(it);
4506 } else if (Tracker.hasUnclaimedUses(it->first)) {
4507 it = FixablesForAllVars.byVar.erase(it);
4508 } else if (it->first->isInitCapture()) {
4509#ifndef NDEBUG
4510 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4511 ("failed to produce fixit for '" +
4512 it->first->getNameAsString() +
4513 "' : init capture"));
4514#endif
4515 it = FixablesForAllVars.byVar.erase(it);
4516 } else {
4517 ++it;
4518 }
4519 }
4520
4521#ifndef NDEBUG
4522 for (const auto &it : UnsafeOps.byVar) {
4523 const VarDecl *const UnsafeVD = it.first;
4524 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
4525 if (UnclaimedDREs.empty())
4526 continue;
4527 const auto UnfixedVDName = UnsafeVD->getNameAsString();
4528 for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
4529 std::string UnclaimedUseTrace =
4530 getDREAncestorString(UnclaimedDRE, D->getASTContext());
4531
4532 Handler.addDebugNoteForVar(
4533 UnsafeVD, UnclaimedDRE->getBeginLoc(),
4534 ("failed to produce fixit for '" + UnfixedVDName +
4535 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
4536 UnclaimedUseTrace));
4537 }
4538 }
4539#endif
4540
4541 // Fixpoint iteration for pointer assignments
4542 using DepMapTy =
4543 llvm::DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
4544 DepMapTy DependenciesMap{};
4545 DepMapTy PtrAssignmentGraph{};
4546
4547 for (const auto &it : FixablesForAllVars.byVar) {
4548 for (const FixableGadget *fixable : it.second) {
4549 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
4550 fixable->getStrategyImplications();
4551 if (ImplPair) {
4552 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
4553 PtrAssignmentGraph[Impl.first].insert(Impl.second);
4554 }
4555 }
4556 }
4557
4558 /*
4559 The following code does a BFS traversal of the `PtrAssignmentGraph`
4560 considering all unsafe vars as starting nodes and constructs an undirected
4561 graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
4562 elimiates all variables that are unreachable from any unsafe var. In other
4563 words, this removes all dependencies that don't include any unsafe variable
4564 and consequently don't need any fixit generation.
4565 Note: A careful reader would observe that the code traverses
4566 `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
4567 `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
4568 achieve the same result but the one used here dramatically cuts the
4569 amount of hoops the second part of the algorithm needs to jump, given that
4570 a lot of these connections become "direct". The reader is advised not to
4571 imagine how the graph is transformed because of using `Var` instead of
4572 `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
4573 and think about why it's equivalent later.
4574 */
4575 std::set<const VarDecl *> VisitedVarsDirected{};
4576 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4577 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
4578
4579 std::queue<const VarDecl *> QueueDirected{};
4580 QueueDirected.push(Var);
4581 while (!QueueDirected.empty()) {
4582 const VarDecl *CurrentVar = QueueDirected.front();
4583 QueueDirected.pop();
4584 VisitedVarsDirected.insert(CurrentVar);
4585 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
4586 for (const VarDecl *Adj : AdjacentNodes) {
4587 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
4588 QueueDirected.push(Adj);
4589 }
4590 DependenciesMap[Var].insert(Adj);
4591 DependenciesMap[Adj].insert(Var);
4592 }
4593 }
4594 }
4595 }
4596
4597 // `Groups` stores the set of Connected Components in the graph.
4598 std::vector<VarGrpTy> Groups;
4599 // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
4600 // variables belong to. Group indexes refer to the elements in `Groups`.
4601 // `VarGrpMap` is complete in that every variable that needs fix is in it.
4602 std::map<const VarDecl *, unsigned> VarGrpMap;
4603 // The union group over the ones in "Groups" that contain parameters of `D`:
4604 llvm::SetVector<const VarDecl *>
4605 GrpsUnionForParms; // these variables need to be fixed in one step
4606
4607 // Group Connected Components for Unsafe Vars
4608 // (Dependencies based on pointer assignments)
4609 std::set<const VarDecl *> VisitedVars{};
4610 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4611 if (VisitedVars.find(Var) == VisitedVars.end()) {
4612 VarGrpTy &VarGroup = Groups.emplace_back();
4613 std::queue<const VarDecl *> Queue{};
4614
4615 Queue.push(Var);
4616 while (!Queue.empty()) {
4617 const VarDecl *CurrentVar = Queue.front();
4618 Queue.pop();
4619 VisitedVars.insert(CurrentVar);
4620 VarGroup.push_back(CurrentVar);
4621 auto AdjacentNodes = DependenciesMap[CurrentVar];
4622 for (const VarDecl *Adj : AdjacentNodes) {
4623 if (VisitedVars.find(Adj) == VisitedVars.end()) {
4624 Queue.push(Adj);
4625 }
4626 }
4627 }
4628
4629 bool HasParm = false;
4630 unsigned GrpIdx = Groups.size() - 1;
4631
4632 for (const VarDecl *V : VarGroup) {
4633 VarGrpMap[V] = GrpIdx;
4634 if (!HasParm && isParameterOf(V, D))
4635 HasParm = true;
4636 }
4637 if (HasParm)
4638 GrpsUnionForParms.insert_range(VarGroup);
4639 }
4640 }
4641
4642 // Remove a `FixableGadget` if the associated variable is not in the graph
4643 // computed above. We do not want to generate fix-its for such variables,
4644 // since they are neither warned nor reachable from a warned one.
4645 //
4646 // Note a variable is not warned if it is not directly used in any unsafe
4647 // operation. A variable `v` is NOT reachable from an unsafe variable, if it
4648 // does not exist another variable `u` such that `u` is warned and fixing `u`
4649 // (transitively) implicates fixing `v`.
4650 //
4651 // For example,
4652 // ```
4653 // void f(int * p) {
4654 // int * a = p; *p = 0;
4655 // }
4656 // ```
4657 // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
4658 // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of
4659 // the function above, `p` becomes reachable from a warned variable.
4660 for (auto I = FixablesForAllVars.byVar.begin();
4661 I != FixablesForAllVars.byVar.end();) {
4662 // Note `VisitedVars` contain all the variables in the graph:
4663 if (!VisitedVars.count((*I).first)) {
4664 // no such var in graph:
4665 I = FixablesForAllVars.byVar.erase(I);
4666 } else
4667 ++I;
4668 }
4669
4670 // We assign strategies to variables that are 1) in the graph and 2) can be
4671 // fixed. Other variables have the default "Won't fix" strategy.
4672 FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
4673 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
4674 // If a warned variable has no "Fixable", it is considered unfixable:
4675 return FixablesForAllVars.byVar.count(V);
4676 }));
4677 VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
4678
4679 if (isa<NamedDecl>(D))
4680 // The only case where `D` is not a `NamedDecl` is when `D` is a
4681 // `BlockDecl`. Let's not fix variables in blocks for now
4682 FixItsForVariableGroup =
4683 getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
4684 Tracker, Handler, VarGrpMgr);
4685
4686 for (const auto &G : UnsafeOps.noVar) {
4687 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4688 D->getASTContext());
4689 }
4690
4691 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
4692 auto FixItsIt = FixItsForVariableGroup.find(VD);
4693 Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
4694 FixItsIt != FixItsForVariableGroup.end()
4695 ? std::move(FixItsIt->second)
4696 : FixItList{},
4697 D, NaiveStrategy);
4698 for (const auto &G : WarningGadgets) {
4699 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
4700 D->getASTContext());
4701 }
4702 }
4703}
4704
4706 UnsafeBufferUsageHandler &Handler,
4707 bool EmitSuggestions) {
4708#ifndef NDEBUG
4709 Handler.clearDebugNotes();
4710#endif
4711
4712 assert(D);
4713 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
4714 // Consteval functions are free of UB by the spec, so we don't need to
4715 // visit them or produce diagnostics.
4716 if (FD->isConsteval())
4717 return;
4718 // We do not want to visit a Lambda expression defined inside a method
4719 // independently. Instead, it should be visited along with the outer method.
4720 // FIXME: do we want to do the same thing for `BlockDecl`s?
4721 if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
4722 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
4723 return;
4724 }
4725
4726 for (FunctionDecl *FReDecl : FD->redecls()) {
4727 if (FReDecl->isExternC()) {
4728 // Do not emit fixit suggestions for functions declared in an
4729 // extern "C" block.
4730 EmitSuggestions = false;
4731 break;
4732 }
4733 }
4734 }
4735
4737
4739
4740 assert(!Stmts.empty());
4741
4742 FixableGadgetList FixableGadgets;
4743 WarningGadgetList WarningGadgets;
4744 DeclUseTracker Tracker;
4745 for (const Stmt *S : Stmts) {
4746 findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
4747 WarningGadgets, Tracker);
4748 }
4749 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
4750 std::move(Tracker), Handler, EmitSuggestions);
4751}
Defines the clang::ASTContext interface.
#define V(N, I)
static Decl::Kind getKind(const Decl *D)
Defines the C++ Decl subclasses, other than those for templates (found in DeclTemplate....
Defines the C++ template declaration subclasses.
#define SM(sm)
Defines the clang::Preprocessor interface.
MatchFinder::MatchResult MatchResult
Defines the clang::SourceLocation class and associated facilities.
static QualType getPointeeType(const MemRegion *R)
C Language Family Type Representation.
static bool ignoreUnsafeLibcCall(const ASTContext &Ctx, const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
static void findStmtsInUnspecifiedLvalueContext(const Stmt *S, const llvm::function_ref< void(const Expr *)> OnResult)
static std::string getUserFillPlaceHolder(StringRef HintTextToUser="placeholder")
static FixItList fixVariableWithSpan(const VarDecl *VD, const DeclUseTracker &Tracker, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static std::optional< FixItList > fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node)
static bool ignoreUnsafeBufferInContainer(const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
static WarningGadgetSets groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations)
static bool hasArrayType(const Expr &E)
static StringRef getEndOfLine()
static bool notInSafeBufferOptOut(const Stmt &Node, const UnsafeBufferUsageHandler *Handler)
static std::optional< FixItList > FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx, const StringRef UserFillPlaceHolder)
static std::optional< SourceLocation > getEndCharLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)
static FixItList fixVariableWithArray(const VarDecl *VD, const DeclUseTracker &Tracker, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static bool areEqualIntegralBinaryOperators(const BinaryOperator *E1, const Expr *E2_LHS, BinaryOperatorKind BOP, const Expr *E2_RHS, ASTContext &Ctx)
static bool hasPointerType(const Expr &E)
static std::string getSpanTypeText(StringRef EltTyText, std::optional< Qualifiers > Quals=std::nullopt)
static SourceRange getSourceRangeToTokenEnd(const Decl *D, const SourceManager &SM, const LangOptions &LangOpts)
static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx, const StringRef UserFillPlaceHolder, UnsafeBufferUsageHandler &Handler)
static bool isSafeArraySubscript(const ArraySubscriptExpr &Node, const ASTContext &Ctx, const bool IgnoreStaticSizedArrays)
static std::optional< FixItList > createDataFixit(const ASTContext &Ctx, const DeclRefExpr *DRE)
static FixItList createFunctionOverloadsForParms(std::map< const VarDecl *, FixItList > &FixItsForVariable, const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD, const FixitStrategy &S, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static bool isNullTermPointer(const Expr *Ptr, ASTContext &Ctx)
static bool isSafeSpanTwoParamConstruct(const CXXConstructExpr &Node, ASTContext &Ctx)
static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static FixItList fixVariable(const VarDecl *VD, FixitStrategy::Kind K, const Decl *D, const DeclUseTracker &Tracker, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static FixitStrategy getNaiveStrategy(llvm::iterator_range< VarDeclIterTy > UnsafeVars)
static std::optional< std::string > createSpanTypeForVarDecl(const VarDecl *VD, const ASTContext &Ctx)
static bool hasConflictingOverload(const FunctionDecl *FD)
static void findStmtsInUnspecifiedPointerContext(const Stmt *S, llvm::function_ref< void(const Stmt *)> InnerMatcher)
static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD, const ASTContext &Ctx)
static bool overlapWithMacro(const FixItList &FixIts)
static void forEachDescendantStmt(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, FastMatcher &Matcher)
static bool hasUnsupportedSpecifiers(const VarDecl *VD, const SourceManager &SM)
static const Expr * tryConstantFoldConditionalExpr(const Expr *E, const ASTContext &Ctx)
#define DEBUG_NOTE_DECL_FAIL(D, Msg)
static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets, WarningGadgetList WarningGadgets, DeclUseTracker Tracker, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
static bool areEqualIntegers(const Expr *E1, const Expr *E2, ASTContext &Ctx)
static void findGadgets(const Stmt *S, ASTContext &Ctx, const UnsafeBufferUsageHandler &Handler, bool EmitSuggestions, FixableGadgetList &FixableGadgets, WarningGadgetList &WarningGadgets, DeclUseTracker &Tracker)
static 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)
__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:226
SourceManager & getSourceManager()
Definition ASTContext.h:859
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:952
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:917
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:1549
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition ExprCXX.h:1692
unsigned getNumArgs() const
Return the number of arguments to the constructor call.
Definition ExprCXX.h:1689
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:1378
Expr * getExpr()
Get the initialization expression that will be used.
Definition ExprCXX.cpp:1105
Represents a static or instance method of a struct/union/class.
Definition DeclCXX.h:2136
Represents a C++11 noexcept expression (C++ [expr.unary.noexcept]).
Definition ExprCXX.h:4310
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:849
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:1632
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Definition Stmt.h:1645
decl_range decls()
Definition Stmt.h:1680
const Decl * getSingleDecl() const
Definition Stmt.h:1647
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:3281
unsigned getBuiltinID(bool ConsiderWrapperFunctions=false) const
Returns a value indicating whether this function corresponds to a builtin function.
Definition Decl.cpp:3764
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:3135
TemplatedKind getTemplatedKind() const
What kind of templated function this is.
Definition Decl.cpp:4146
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:3370
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:3828
DeclarationNameInfo getNameInfo() const
Definition Decl.h:2226
bool hasBody(const FunctionDecl *&Definition) const
Returns true if the function has a body.
Definition Decl.cpp:3201
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:1339
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:508
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:858
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:3060
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition Decl.cpp:2982
PointerType - C99 6.7.5.1 - Pointer Declarators.
Definition TypeBase.h:3378
A (possibly-)qualified type.
Definition TypeBase.h:937
bool hasQualifiers() const
Determine whether this type has any qualifiers.
Definition TypeBase.h:8520
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:8471
QualType getCanonicalType() const
Definition TypeBase.h:8483
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition TypeBase.h:8504
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:1494
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:1866
bool isConstantSizeType() const
Return true if this is not a variable sized type, according to the rules of C99 6....
Definition Type.cpp:2470
bool isArrayType() const
Definition TypeBase.h:8767
bool isCharType() const
Definition Type.cpp:2158
bool isPointerType() const
Definition TypeBase.h:8668
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition TypeBase.h:9078
const T * castAs() const
Member-template castAs<specific type>.
Definition TypeBase.h:9328
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition Type.cpp:754
bool isAnyCharacterType() const
Determine whether this type is any of the built-in character types.
Definition Type.cpp:2194
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:2285
bool isAnyPointerType() const
Definition TypeBase.h:8676
const Type * getUnqualifiedDesugaredType() const
Return the specified type with any "sugar" removed from the type, removing any typedefs,...
Definition Type.cpp:655
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:2203
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition Decl.cpp:2270
bool isInlineSpecified() const
Definition Decl.h:1569
bool hasConstantInitialization() const
Determine whether this variable has constant initialization.
Definition Decl.cpp:2661
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
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
std::set< const Expr * > findUnsafePointers(const Decl *D)
Find unsafe pointers in body/initializer of D, if D is one of the followings: VarDecl FieldDecl Funct...
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.