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