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 std::unique_ptr<std::set<StringRef>> PredefinedNames = nullptr;
1036 if (!PredefinedNames)
1037 PredefinedNames =
1038 std::make_unique<std::set<StringRef>, std::set<StringRef>>({
1039 // numeric conversion:
1040 "atof",
1041 "atoi",
1042 "atol",
1043 "atoll",
1044 "strtol",
1045 "strtoll",
1046 "strtoul",
1047 "strtoull",
1048 "strtof",
1049 "strtod",
1050 "strtold",
1051 "strtoimax",
1052 "strtoumax",
1053 // "strfromf", "strfromd", "strfroml", // C23?
1054 // string manipulation:
1055 "strcpy",
1056 "strncpy",
1057 "strlcpy",
1058 "strcat",
1059 "strncat",
1060 "strlcat",
1061 "strxfrm",
1062 "strdup",
1063 "strndup",
1064 // string examination:
1065 "strlen",
1066 "strnlen",
1067 "strcmp",
1068 "strncmp",
1069 "stricmp",
1070 "strcasecmp",
1071 "strcoll",
1072 "strchr",
1073 "strrchr",
1074 "strspn",
1075 "strcspn",
1076 "strpbrk",
1077 "strstr",
1078 "strtok",
1079 // "mem-" functions
1080 "memchr",
1081 "wmemchr",
1082 "memcmp",
1083 "wmemcmp",
1084 "memcpy",
1085 "memccpy",
1086 "mempcpy",
1087 "wmemcpy",
1088 "memmove",
1089 "wmemmove",
1090 "wmemset",
1091 // IO:
1092 "fread",
1093 "fwrite",
1094 "fgets",
1095 "fgetws",
1096 "gets",
1097 "fputs",
1098 "fputws",
1099 "puts",
1100 // others
1101 "strerror_s",
1102 "strerror_r",
1103 "bcopy",
1104 "bzero",
1105 "bsearch",
1106 "qsort",
1107 });
1108
1109 auto *II = Node.getIdentifier();
1110
1111 if (!II)
1112 return false;
1113
1114 StringRef Name = matchName(II->getName(), Node.getBuiltinID());
1115
1116 // Match predefined names:
1117 if (PredefinedNames->find(Name) != PredefinedNames->end())
1118 return true;
1119
1120 std::string NameWCS = Name.str();
1121 size_t WcsPos = NameWCS.find("wcs");
1122
1123 while (WcsPos != std::string::npos) {
1124 NameWCS[WcsPos++] = 's';
1125 NameWCS[WcsPos++] = 't';
1126 NameWCS[WcsPos++] = 'r';
1127 WcsPos = NameWCS.find("wcs", WcsPos);
1128 }
1129 if (PredefinedNames->find(NameWCS) != PredefinedNames->end())
1130 return true;
1131 // All `scanf` functions are unsafe (including `sscanf`, `vsscanf`, etc.. They
1132 // all should end with "scanf"):
1133 return Name.ends_with("scanf");
1134}
1135
1136// Returns true if this is an unsafe call to `memset`.
1137// The only call we currently consider safe is of the form
1138// `memset(&x, 0, sizeof(x))`, with possible variations in parentheses.
1139static bool isUnsafeMemset(const CallExpr &Node, ASTContext &Ctx) {
1140 const FunctionDecl *FD = Node.getDirectCallee();
1141 assert(FD && "It should have been checked that FD is non-null.");
1142
1143 const IdentifierInfo *II = FD->getIdentifier();
1144 if (!II)
1145 return false;
1146
1147 StringRef Name = matchName(II->getName(), FD->getBuiltinID());
1148 if (Name != "memset")
1149 return false;
1150
1151 // We currently only handle the basic forms of `memset` with 3 parameters.
1152 // There is also `__builtin___memset_chk()` which takes a 4th `destlen`
1153 // parameter for bounds checking, but we don't consider its safe forms yet.
1154 // https://refspecs.linuxbase.org/LSB_4.1.0/LSB-Core-generic/LSB-Core-generic/libc---memset-chk-1.html
1155 if (FD->getNumParams() != 3)
1156 return true;
1157
1158 // Now we have a known version of `memset`, consider it unsafe unless it's in
1159 // the form `memset(&x, 0, sizeof(x))`.
1160 const auto *AddressOfVar = dyn_cast_if_present<DeclRefExpr>(
1162 if (!AddressOfVar)
1163 return true;
1164
1165 const auto *SizeOfVar =
1166 dyn_cast_if_present<DeclRefExpr>(getSubExprInSizeOfExpr(*Node.getArg(2)));
1167 if (!SizeOfVar)
1168 return true;
1169
1170 return AddressOfVar->getDecl() != SizeOfVar->getDecl();
1171}
1172
1173// Match a call to one of the `v*printf` functions taking `va_list`. We cannot
1174// check safety for these functions so they should be changed to their
1175// non-va_list versions.
1176static bool isUnsafeVaListPrintfFunc(const FunctionDecl &Node) {
1177 auto *II = Node.getIdentifier();
1178
1179 if (!II)
1180 return false;
1181
1182 StringRef Name = matchName(II->getName(), Node.getBuiltinID());
1183
1184 return Name.starts_with("v") && Name.ends_with("printf");
1185}
1186
1187// Matches a call to one of the `sprintf` functions as they are always unsafe
1188// and should be changed to `snprintf`.
1189static bool isUnsafeSprintfFunc(const FunctionDecl &Node) {
1190 auto *II = Node.getIdentifier();
1191
1192 if (!II)
1193 return false;
1194
1195 StringRef Name = matchName(II->getName(), Node.getBuiltinID());
1196
1197 return Name == "sprintf" || Name == "swprintf";
1198}
1199
1200// Match function declarations of `printf`, `fprintf`, `snprintf` and their wide
1201// character versions. Calls to these functions can be safe if their arguments
1202// are carefully made safe.
1203static bool isNormalPrintfFunc(const FunctionDecl &Node) {
1204 auto *II = Node.getIdentifier();
1205
1206 if (!II)
1207 return false;
1208
1209 StringRef Name = matchName(II->getName(), Node.getBuiltinID());
1210
1211 if (!Name.ends_with("printf"))
1212 return false;
1213
1214 StringRef Prefix = Name.drop_back(6);
1215
1216 if (Prefix.ends_with("w"))
1217 Prefix = Prefix.drop_back(1);
1218
1219 return Prefix.empty() || Prefix == "k" || Prefix == "f" || Prefix == "sn";
1220}
1221
1222// This matcher requires that it is known that the callee `isNormalPrintf`.
1223// Then if the format string is a string literal, this matcher matches when at
1224// least one string argument is unsafe. If the format is not a string literal,
1225// this matcher matches when at least one pointer type argument is unsafe.
1226static bool hasUnsafePrintfStringArg(const CallExpr &Node, ASTContext &Ctx,
1227 MatchResult &Result, llvm::StringRef Tag) {
1228 // Determine what printf it is by examining formal parameters:
1229 const FunctionDecl *FD = Node.getDirectCallee();
1230
1231 assert(FD && "It should have been checked that FD is non-null.");
1232
1233 unsigned NumParms = FD->getNumParams();
1234
1235 if (NumParms < 1)
1236 return false; // possibly some user-defined printf function
1237
1238 QualType FirstParmTy = FD->getParamDecl(0)->getType();
1239
1240 if (!FirstParmTy->isPointerType())
1241 return false; // possibly some user-defined printf function
1242
1243 QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
1244
1245 if (!Ctx.getFILEType()
1246 .isNull() && //`FILE *` must be in the context if it is fprintf
1247 FirstPteTy.getCanonicalType() == Ctx.getFILEType().getCanonicalType()) {
1248 // It is a fprintf:
1249 const Expr *UnsafeArg;
1250
1251 if (hasUnsafeFormatOrSArg(Ctx, &Node, UnsafeArg, /* FmtIdx= */ 1)) {
1252 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1253 return true;
1254 }
1255 return false;
1256 }
1257
1258 if (FirstPteTy.isConstQualified()) {
1259 // If the first parameter is a `const char *`, it is a printf/kprintf:
1260 bool isKprintf = false;
1261 const Expr *UnsafeArg;
1262
1263 if (auto *II = FD->getIdentifier())
1264 isKprintf = II->getName() == "kprintf";
1265 if (hasUnsafeFormatOrSArg(Ctx, &Node, UnsafeArg, /* FmtIdx= */ 0,
1266 /* FmtArgIdx= */ std::nullopt, isKprintf)) {
1267 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1268 return true;
1269 }
1270 return false;
1271 }
1272
1273 if (NumParms > 2) {
1274 QualType SecondParmTy = FD->getParamDecl(1)->getType();
1275
1276 if (!FirstPteTy.isConstQualified() && SecondParmTy->isIntegerType()) {
1277 // If the first parameter type is non-const qualified `char *` and the
1278 // second is an integer, it is a snprintf:
1279 const Expr *UnsafeArg;
1280
1281 if (hasUnsafeFormatOrSArg(Ctx, &Node, UnsafeArg, /* FmtIdx= */ 2)) {
1282 Result.addNode(Tag, DynTypedNode::create(*UnsafeArg));
1283 return true;
1284 }
1285 return false;
1286 }
1287 }
1288 // We don't really recognize this "normal" printf, the only thing we
1289 // can do is to require all pointers to be null-terminated:
1290 for (const auto *Arg : Node.arguments())
1291 if (Arg->getType()->isPointerType() && !isNullTermPointer(Arg, Ctx)) {
1292 Result.addNode(Tag, DynTypedNode::create(*Arg));
1293 return true;
1294 }
1295 return false;
1296}
1297
1298// This function requires that it is known that the callee `isNormalPrintf`.
1299// It returns true iff the first two arguments of the call is a pointer
1300// `Ptr` and an unsigned integer `Size` and they are NOT safe, i.e.,
1301// `!isPtrBufferSafe(Ptr, Size)`.
1302static bool hasUnsafeSnprintfBuffer(const CallExpr &Node, ASTContext &Ctx) {
1303 const FunctionDecl *FD = Node.getDirectCallee();
1304
1305 assert(FD && "It should have been checked that FD is non-null.");
1306
1307 if (FD->getNumParams() < 3)
1308 return false; // Not an snprint
1309
1310 QualType FirstParmTy = FD->getParamDecl(0)->getType();
1311
1312 if (!FirstParmTy->isPointerType())
1313 return false; // Not an snprint
1314
1315 QualType FirstPteTy = FirstParmTy->castAs<PointerType>()->getPointeeType();
1316 const Expr *Buf = Node.getArg(0), *Size = Node.getArg(1);
1317
1318 if (FirstPteTy.isConstQualified() || !FirstPteTy->isAnyCharacterType() ||
1319 !Buf->getType()->isPointerType() ||
1320 !Size->getType()->isUnsignedIntegerType())
1321 return false; // not an snprintf call
1322
1323 return !isPtrBufferSafe(Buf, Size, Ctx);
1324}
1325} // namespace libc_func_matchers
1326
1327namespace {
1328// Because the analysis revolves around variables and their types, we'll need to
1329// track uses of variables (aka DeclRefExprs).
1330using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
1331
1332// Convenience typedef.
1333using FixItList = SmallVector<FixItHint, 4>;
1334} // namespace
1335
1336namespace {
1337/// Gadget is an individual operation in the code that may be of interest to
1338/// this analysis. Each (non-abstract) subclass corresponds to a specific
1339/// rigid AST structure that constitutes an operation on a pointer-type object.
1340/// Discovery of a gadget in the code corresponds to claiming that we understand
1341/// what this part of code is doing well enough to potentially improve it.
1342/// Gadgets can be warning (immediately deserving a warning) or fixable (not
1343/// always deserving a warning per se, but requires our attention to identify
1344/// it warrants a fixit).
1345class Gadget {
1346public:
1347 enum class Kind {
1348#define GADGET(x) x,
1349#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1350 };
1351
1352 Gadget(Kind K) : K(K) {}
1353
1354 Kind getKind() const { return K; }
1355
1356#ifndef NDEBUG
1357 StringRef getDebugName() const {
1358 switch (K) {
1359#define GADGET(x) \
1360 case Kind::x: \
1361 return #x;
1362#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1363 }
1364 llvm_unreachable("Unhandled Gadget::Kind enum");
1365 }
1366#endif
1367
1368 virtual bool isWarningGadget() const = 0;
1369 // TODO remove this method from WarningGadget interface. It's only used for
1370 // debug prints in FixableGadget.
1371 virtual SourceLocation getSourceLoc() const = 0;
1372
1373 /// Returns the list of pointer-type variables on which this gadget performs
1374 /// its operation. Typically, there's only one variable. This isn't a list
1375 /// of all DeclRefExprs in the gadget's AST!
1376 virtual DeclUseList getClaimedVarUseSites() const = 0;
1377
1378 virtual ~Gadget() = default;
1379
1380private:
1381 Kind K;
1382};
1383
1384/// Warning gadgets correspond to unsafe code patterns that warrants
1385/// an immediate warning.
1386class WarningGadget : public Gadget {
1387public:
1388 WarningGadget(Kind K) : Gadget(K) {}
1389
1390 static bool classof(const Gadget *G) { return G->isWarningGadget(); }
1391 bool isWarningGadget() const final { return true; }
1392
1393 virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1394 bool IsRelatedToDecl,
1395 ASTContext &Ctx) const = 0;
1396
1397 virtual SmallVector<const Expr *, 1> getUnsafePtrs() const = 0;
1398};
1399
1400/// Fixable gadgets correspond to code patterns that aren't always unsafe but
1401/// need to be properly recognized in order to emit fixes. For example, if a raw
1402/// pointer-type variable is replaced by a safe C++ container, every use of such
1403/// variable must be carefully considered and possibly updated.
1404class FixableGadget : public Gadget {
1405public:
1406 FixableGadget(Kind K) : Gadget(K) {}
1407
1408 static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
1409 bool isWarningGadget() const final { return false; }
1410
1411 /// Returns a fixit that would fix the current gadget according to
1412 /// the current strategy. Returns std::nullopt if the fix cannot be produced;
1413 /// returns an empty list if no fixes are necessary.
1414 virtual std::optional<FixItList> getFixits(const FixitStrategy &) const {
1415 return std::nullopt;
1416 }
1417
1418 /// Returns a list of two elements where the first element is the LHS of a
1419 /// pointer assignment statement and the second element is the RHS. This
1420 /// two-element list represents the fact that the LHS buffer gets its bounds
1421 /// information from the RHS buffer. This information will be used later to
1422 /// group all those variables whose types must be modified together to prevent
1423 /// type mismatches.
1424 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1425 getStrategyImplications() const {
1426 return std::nullopt;
1427 }
1428};
1429
1430static bool isSupportedVariable(const DeclRefExpr &Node) {
1431 const Decl *D = Node.getDecl();
1432 return D != nullptr && isa<VarDecl>(D);
1433}
1434
1435// Returns true for RecordDecl of type std::unique_ptr<T[]>
1436static bool isUniquePtrArray(const CXXRecordDecl *RecordDecl) {
1438 RecordDecl->getNameAsString() != "unique_ptr")
1439 return false;
1440
1441 const ClassTemplateSpecializationDecl *class_template_specialization_decl =
1442 dyn_cast<ClassTemplateSpecializationDecl>(RecordDecl);
1443 if (!class_template_specialization_decl)
1444 return false;
1445
1446 const TemplateArgumentList &template_args =
1447 class_template_specialization_decl->getTemplateArgs();
1448 if (template_args.size() == 0)
1449 return false;
1450
1451 const TemplateArgument &first_arg = template_args[0];
1452 if (first_arg.getKind() != TemplateArgument::Type)
1453 return false;
1454
1455 QualType referred_type = first_arg.getAsType();
1456 return referred_type->isArrayType();
1457}
1458
1459class UniquePtrArrayAccessGadget : public WarningGadget {
1460private:
1461 static constexpr const char *const AccessorTag = "unique_ptr_array_access";
1462 const CXXOperatorCallExpr *AccessorExpr;
1463
1464public:
1465 UniquePtrArrayAccessGadget(const MatchResult &Result)
1466 : WarningGadget(Kind::UniquePtrArrayAccess),
1467 AccessorExpr(Result.getNodeAs<CXXOperatorCallExpr>(AccessorTag)) {
1468 assert(AccessorExpr &&
1469 "UniquePtrArrayAccessGadget requires a matched CXXOperatorCallExpr");
1470 }
1471
1472 static bool classof(const Gadget *G) {
1473 return G->getKind() == Kind::UniquePtrArrayAccess;
1474 }
1475
1476 static bool matches(const Stmt *S, const ASTContext &Ctx,
1478
1479 const CXXOperatorCallExpr *OpCall = dyn_cast<CXXOperatorCallExpr>(S);
1480 if (!OpCall || OpCall->getOperator() != OO_Subscript)
1481 return false;
1482
1483 const Expr *Callee = OpCall->getCallee()->IgnoreParenImpCasts();
1484 if (!Callee)
1485 return false;
1486
1487 const CXXMethodDecl *Method =
1488 dyn_cast_or_null<CXXMethodDecl>(OpCall->getDirectCallee());
1489 if (!Method)
1490 return false;
1491
1492 if (Method->getOverloadedOperator() != OO_Subscript)
1493 return false;
1494
1495 const CXXRecordDecl *RecordDecl = Method->getParent();
1496 if (!isUniquePtrArray(RecordDecl))
1497 return false;
1498
1499 const Expr *IndexExpr = OpCall->getArg(1);
1500 clang::Expr::EvalResult Eval;
1501
1502 // Allow [0]
1503 if (IndexExpr->EvaluateAsInt(Eval, Ctx) && Eval.Val.getInt().isZero())
1504 return false;
1505
1506 Result.addNode(AccessorTag, DynTypedNode::create(*OpCall));
1507 return true;
1508 }
1509 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1510 bool IsRelatedToDecl,
1511 ASTContext &Ctx) const override {
1513 DynTypedNode::create(*AccessorExpr), IsRelatedToDecl, Ctx);
1514 }
1515
1516 SourceLocation getSourceLoc() const override {
1517 if (AccessorExpr)
1518 return AccessorExpr->getOperatorLoc();
1519 return SourceLocation();
1520 }
1521
1522 DeclUseList getClaimedVarUseSites() const override { return {}; }
1523 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1524};
1525
1526using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
1527using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
1528
1529/// An increment of a pointer-type value is unsafe as it may run the pointer
1530/// out of bounds.
1531class IncrementGadget : public WarningGadget {
1532 static constexpr const char *const OpTag = "op";
1533 const UnaryOperator *Op;
1534
1535public:
1536 IncrementGadget(const MatchResult &Result)
1537 : WarningGadget(Kind::Increment),
1538 Op(Result.getNodeAs<UnaryOperator>(OpTag)) {}
1539
1540 static bool classof(const Gadget *G) {
1541 return G->getKind() == Kind::Increment;
1542 }
1543
1544 static bool matches(const Stmt *S, const ASTContext &Ctx,
1546 const auto *UO = dyn_cast<UnaryOperator>(S);
1547 if (!UO || !UO->isIncrementOp())
1548 return false;
1550 return false;
1551 Result.addNode(OpTag, DynTypedNode::create(*UO));
1552 return true;
1553 }
1554
1555 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1556 bool IsRelatedToDecl,
1557 ASTContext &Ctx) const override {
1558 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1559 }
1560 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1561
1562 DeclUseList getClaimedVarUseSites() const override {
1563 SmallVector<const DeclRefExpr *, 2> Uses;
1564 if (const auto *DRE =
1565 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1566 Uses.push_back(DRE);
1567 }
1568
1569 return std::move(Uses);
1570 }
1571
1572 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1573 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1574 }
1575};
1576
1577/// A decrement of a pointer-type value is unsafe as it may run the pointer
1578/// out of bounds.
1579class DecrementGadget : public WarningGadget {
1580 static constexpr const char *const OpTag = "op";
1581 const UnaryOperator *Op;
1582
1583public:
1584 DecrementGadget(const MatchResult &Result)
1585 : WarningGadget(Kind::Decrement),
1586 Op(Result.getNodeAs<UnaryOperator>(OpTag)) {}
1587
1588 static bool classof(const Gadget *G) {
1589 return G->getKind() == Kind::Decrement;
1590 }
1591
1592 static bool matches(const Stmt *S, const ASTContext &Ctx,
1594 const auto *UO = dyn_cast<UnaryOperator>(S);
1595 if (!UO || !UO->isDecrementOp())
1596 return false;
1598 return false;
1599 Result.addNode(OpTag, DynTypedNode::create(*UO));
1600 return true;
1601 }
1602
1603 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1604 bool IsRelatedToDecl,
1605 ASTContext &Ctx) const override {
1606 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1607 }
1608 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1609
1610 DeclUseList getClaimedVarUseSites() const override {
1611 if (const auto *DRE =
1612 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
1613 return {DRE};
1614 }
1615
1616 return {};
1617 }
1618
1619 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1620 return {Op->getSubExpr()->IgnoreParenImpCasts()};
1621 }
1622};
1623
1624/// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
1625/// it doesn't have any bounds checks for the array.
1626class ArraySubscriptGadget : public WarningGadget {
1627 static constexpr const char *const ArraySubscrTag = "ArraySubscript";
1628 const ArraySubscriptExpr *ASE;
1629
1630public:
1631 ArraySubscriptGadget(const MatchResult &Result)
1632 : WarningGadget(Kind::ArraySubscript),
1633 ASE(Result.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
1634
1635 static bool classof(const Gadget *G) {
1636 return G->getKind() == Kind::ArraySubscript;
1637 }
1638
1639 static bool matches(const Stmt *S, const ASTContext &Ctx,
1640 const UnsafeBufferUsageHandler *Handler,
1642 const auto *ASE = dyn_cast<ArraySubscriptExpr>(S);
1643 if (!ASE)
1644 return false;
1645 const auto *const Base = ASE->getBase()->IgnoreParenImpCasts();
1646 if (!hasPointerType(*Base) && !hasArrayType(*Base))
1647 return false;
1648 const auto *Idx = dyn_cast<IntegerLiteral>(ASE->getIdx());
1649 bool IsSafeIndex = (Idx && Idx->getValue().isZero()) ||
1650 isa<ArrayInitIndexExpr>(ASE->getIdx());
1651 if (IsSafeIndex ||
1653 *ASE, Ctx,
1655 return false;
1656 Result.addNode(ArraySubscrTag, DynTypedNode::create(*ASE));
1657 return true;
1658 }
1659
1660 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1661 bool IsRelatedToDecl,
1662 ASTContext &Ctx) const override {
1663 Handler.handleUnsafeOperation(ASE, IsRelatedToDecl, Ctx);
1664 }
1665 SourceLocation getSourceLoc() const override { return ASE->getBeginLoc(); }
1666
1667 DeclUseList getClaimedVarUseSites() const override {
1668 if (const auto *DRE =
1669 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
1670 return {DRE};
1671 }
1672
1673 return {};
1674 }
1675
1676 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1677 return {ASE->getBase()->IgnoreParenImpCasts()};
1678 }
1679};
1680
1681/// A pointer arithmetic expression of one of the forms:
1682/// \code
1683/// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
1684/// \endcode
1685class PointerArithmeticGadget : public WarningGadget {
1686 static constexpr const char *const PointerArithmeticTag = "ptrAdd";
1687 static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
1688 const BinaryOperator *PA; // pointer arithmetic expression
1689 const Expr *Ptr; // the pointer expression in `PA`
1690
1691public:
1692 PointerArithmeticGadget(const MatchResult &Result)
1693 : WarningGadget(Kind::PointerArithmetic),
1694 PA(Result.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
1695 Ptr(Result.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
1696
1697 static bool classof(const Gadget *G) {
1698 return G->getKind() == Kind::PointerArithmetic;
1699 }
1700
1701 static bool matches(const Stmt *S, const ASTContext &Ctx,
1703 const auto *BO = dyn_cast<BinaryOperator>(S);
1704 if (!BO)
1705 return false;
1706 const auto *LHS = BO->getLHS();
1707 const auto *RHS = BO->getRHS();
1708 // ptr at left
1709 if (BO->getOpcode() == BO_Add || BO->getOpcode() == BO_Sub ||
1710 BO->getOpcode() == BO_AddAssign || BO->getOpcode() == BO_SubAssign) {
1711 if (hasPointerType(*LHS) && (RHS->getType()->isIntegerType() ||
1712 RHS->getType()->isEnumeralType())) {
1713 Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*LHS));
1714 Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
1715 return true;
1716 }
1717 }
1718 // ptr at right
1719 if (BO->getOpcode() == BO_Add && hasPointerType(*RHS) &&
1720 (LHS->getType()->isIntegerType() || LHS->getType()->isEnumeralType())) {
1721 Result.addNode(PointerArithmeticPointerTag, DynTypedNode::create(*RHS));
1722 Result.addNode(PointerArithmeticTag, DynTypedNode::create(*BO));
1723 return true;
1724 }
1725 return false;
1726 }
1727
1728 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1729 bool IsRelatedToDecl,
1730 ASTContext &Ctx) const override {
1731 Handler.handleUnsafeOperation(PA, IsRelatedToDecl, Ctx);
1732 }
1733 SourceLocation getSourceLoc() const override { return PA->getBeginLoc(); }
1734
1735 DeclUseList getClaimedVarUseSites() const override {
1736 if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
1737 return {DRE};
1738 }
1739
1740 return {};
1741 }
1742
1743 SmallVector<const Expr *, 1> getUnsafePtrs() const override {
1744 return {Ptr->IgnoreParenImpCasts()};
1745 }
1746
1747 // FIXME: pointer adding zero should be fine
1748 // FIXME: this gadge will need a fix-it
1749};
1750
1751class SpanTwoParamConstructorGadget : public WarningGadget {
1752 static constexpr const char *const SpanTwoParamConstructorTag =
1753 "spanTwoParamConstructor";
1754 const CXXConstructExpr *Ctor; // the span constructor expression
1755
1756public:
1757 SpanTwoParamConstructorGadget(const MatchResult &Result)
1758 : WarningGadget(Kind::SpanTwoParamConstructor),
1759 Ctor(Result.getNodeAs<CXXConstructExpr>(SpanTwoParamConstructorTag)) {}
1760
1761 static bool classof(const Gadget *G) {
1762 return G->getKind() == Kind::SpanTwoParamConstructor;
1763 }
1764
1765 static bool matches(const Stmt *S, ASTContext &Ctx, MatchResult &Result) {
1766 const auto *CE = dyn_cast<CXXConstructExpr>(S);
1767 if (!CE)
1768 return false;
1769 const auto *CDecl = CE->getConstructor();
1770 const auto *CRecordDecl = CDecl->getParent();
1771 auto HasTwoParamSpanCtorDecl =
1772 CRecordDecl->isInStdNamespace() &&
1773 CDecl->getDeclName().getAsString() == "span" && CE->getNumArgs() == 2;
1774 if (!HasTwoParamSpanCtorDecl || isSafeSpanTwoParamConstruct(*CE, Ctx))
1775 return false;
1776 Result.addNode(SpanTwoParamConstructorTag, DynTypedNode::create(*CE));
1777 return true;
1778 }
1779
1780 static bool matches(const Stmt *S, ASTContext &Ctx,
1781 const UnsafeBufferUsageHandler *Handler,
1783 if (ignoreUnsafeBufferInContainer(*S, Handler))
1784 return false;
1785 return matches(S, Ctx, Result);
1786 }
1787
1788 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1789 bool IsRelatedToDecl,
1790 ASTContext &Ctx) const override {
1791 Handler.handleUnsafeOperationInContainer(Ctor, IsRelatedToDecl, Ctx);
1792 }
1793 SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); }
1794
1795 DeclUseList getClaimedVarUseSites() const override {
1796 // If the constructor call is of the form `std::span{var, n}`, `var` is
1797 // considered an unsafe variable.
1798 if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
1799 if (isa<VarDecl>(DRE->getDecl()))
1800 return {DRE};
1801 }
1802 return {};
1803 }
1804
1805 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
1806};
1807
1808/// A pointer initialization expression of the form:
1809/// \code
1810/// int *p = q;
1811/// \endcode
1812class PointerInitGadget : public FixableGadget {
1813private:
1814 static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
1815 static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
1816 const VarDecl *PtrInitLHS; // the LHS pointer expression in `PI`
1817 const DeclRefExpr *PtrInitRHS; // the RHS pointer expression in `PI`
1818
1819public:
1820 PointerInitGadget(const MatchResult &Result)
1821 : FixableGadget(Kind::PointerInit),
1822 PtrInitLHS(Result.getNodeAs<VarDecl>(PointerInitLHSTag)),
1823 PtrInitRHS(Result.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
1824
1825 static bool classof(const Gadget *G) {
1826 return G->getKind() == Kind::PointerInit;
1827 }
1828
1829 static bool matches(const Stmt *S,
1830 llvm::SmallVectorImpl<MatchResult> &Results) {
1831 const DeclStmt *DS = dyn_cast<DeclStmt>(S);
1832 if (!DS || !DS->isSingleDecl())
1833 return false;
1834 const VarDecl *VD = dyn_cast<VarDecl>(DS->getSingleDecl());
1835 if (!VD)
1836 return false;
1837 const Expr *Init = VD->getAnyInitializer();
1838 if (!Init)
1839 return false;
1840 const auto *DRE = dyn_cast<DeclRefExpr>(Init->IgnoreImpCasts());
1841 if (!DRE || !hasPointerType(*DRE) || !isSupportedVariable(*DRE)) {
1842 return false;
1843 }
1844 MatchResult R;
1845 R.addNode(PointerInitLHSTag, DynTypedNode::create(*VD));
1846 R.addNode(PointerInitRHSTag, DynTypedNode::create(*DRE));
1847 Results.emplace_back(std::move(R));
1848 return true;
1849 }
1850
1851 virtual std::optional<FixItList>
1852 getFixits(const FixitStrategy &S) const override;
1853 SourceLocation getSourceLoc() const override {
1854 return PtrInitRHS->getBeginLoc();
1855 }
1856
1857 virtual DeclUseList getClaimedVarUseSites() const override {
1858 return DeclUseList{PtrInitRHS};
1859 }
1860
1861 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1862 getStrategyImplications() const override {
1863 return std::make_pair(PtrInitLHS, cast<VarDecl>(PtrInitRHS->getDecl()));
1864 }
1865};
1866
1867/// A pointer assignment expression of the form:
1868/// \code
1869/// p = q;
1870/// \endcode
1871/// where both `p` and `q` are pointers.
1872class PtrToPtrAssignmentGadget : public FixableGadget {
1873private:
1874 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
1875 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
1876 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
1877 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
1878
1879public:
1880 PtrToPtrAssignmentGadget(const MatchResult &Result)
1881 : FixableGadget(Kind::PtrToPtrAssignment),
1882 PtrLHS(Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
1883 PtrRHS(Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
1884
1885 static bool classof(const Gadget *G) {
1886 return G->getKind() == Kind::PtrToPtrAssignment;
1887 }
1888
1889 static bool matches(const Stmt *S,
1890 llvm::SmallVectorImpl<MatchResult> &Results) {
1891 size_t SizeBefore = Results.size();
1892 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
1893 const auto *BO = dyn_cast<BinaryOperator>(S);
1894 if (!BO || BO->getOpcode() != BO_Assign)
1895 return;
1896 const auto *RHS = BO->getRHS()->IgnoreParenImpCasts();
1897 if (const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
1898 !RHSRef || !hasPointerType(*RHSRef) ||
1899 !isSupportedVariable(*RHSRef)) {
1900 return;
1901 }
1902 const auto *LHS = BO->getLHS();
1903 if (const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
1904 !LHSRef || !hasPointerType(*LHSRef) ||
1905 !isSupportedVariable(*LHSRef)) {
1906 return;
1907 }
1908 MatchResult R;
1909 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
1910 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
1911 Results.emplace_back(std::move(R));
1912 });
1913 return SizeBefore != Results.size();
1914 }
1915
1916 virtual std::optional<FixItList>
1917 getFixits(const FixitStrategy &S) const override;
1918 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
1919
1920 virtual DeclUseList getClaimedVarUseSites() const override {
1921 return DeclUseList{PtrLHS, PtrRHS};
1922 }
1923
1924 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1925 getStrategyImplications() const override {
1926 return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
1927 cast<VarDecl>(PtrRHS->getDecl()));
1928 }
1929};
1930
1931/// An assignment expression of the form:
1932/// \code
1933/// ptr = array;
1934/// \endcode
1935/// where `p` is a pointer and `array` is a constant size array.
1936class CArrayToPtrAssignmentGadget : public FixableGadget {
1937private:
1938 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
1939 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
1940 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
1941 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
1942
1943public:
1944 CArrayToPtrAssignmentGadget(const MatchResult &Result)
1945 : FixableGadget(Kind::CArrayToPtrAssignment),
1946 PtrLHS(Result.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
1947 PtrRHS(Result.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
1948
1949 static bool classof(const Gadget *G) {
1950 return G->getKind() == Kind::CArrayToPtrAssignment;
1951 }
1952
1953 static bool matches(const Stmt *S,
1954 llvm::SmallVectorImpl<MatchResult> &Results) {
1955 size_t SizeBefore = Results.size();
1956 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
1957 const auto *BO = dyn_cast<BinaryOperator>(S);
1958 if (!BO || BO->getOpcode() != BO_Assign)
1959 return;
1960 const auto *RHS = BO->getRHS()->IgnoreParenImpCasts();
1961 if (const auto *RHSRef = dyn_cast<DeclRefExpr>(RHS);
1962 !RHSRef ||
1963 !isa<ConstantArrayType>(RHSRef->getType().getCanonicalType()) ||
1964 !isSupportedVariable(*RHSRef)) {
1965 return;
1966 }
1967 const auto *LHS = BO->getLHS();
1968 if (const auto *LHSRef = dyn_cast<DeclRefExpr>(LHS);
1969 !LHSRef || !hasPointerType(*LHSRef) ||
1970 !isSupportedVariable(*LHSRef)) {
1971 return;
1972 }
1973 MatchResult R;
1974 R.addNode(PointerAssignLHSTag, DynTypedNode::create(*LHS));
1975 R.addNode(PointerAssignRHSTag, DynTypedNode::create(*RHS));
1976 Results.emplace_back(std::move(R));
1977 });
1978 return SizeBefore != Results.size();
1979 }
1980
1981 virtual std::optional<FixItList>
1982 getFixits(const FixitStrategy &S) const override;
1983 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
1984
1985 virtual DeclUseList getClaimedVarUseSites() const override {
1986 return DeclUseList{PtrLHS, PtrRHS};
1987 }
1988
1989 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
1990 getStrategyImplications() const override {
1991 return {};
1992 }
1993};
1994
1995/// A call of a function or method that performs unchecked buffer operations
1996/// over one of its pointer parameters.
1997class UnsafeBufferUsageAttrGadget : public WarningGadget {
1998 constexpr static const char *const OpTag = "attr_expr";
1999 const Expr *Op;
2000
2001public:
2002 UnsafeBufferUsageAttrGadget(const MatchResult &Result)
2003 : WarningGadget(Kind::UnsafeBufferUsageAttr),
2004 Op(Result.getNodeAs<Expr>(OpTag)) {}
2005
2006 static bool classof(const Gadget *G) {
2007 return G->getKind() == Kind::UnsafeBufferUsageAttr;
2008 }
2009
2010 static bool matches(const Stmt *S, const ASTContext &Ctx,
2012 if (auto *CE = dyn_cast<CallExpr>(S)) {
2013 if (CE->getDirectCallee() &&
2014 CE->getDirectCallee()->hasAttr<UnsafeBufferUsageAttr>()) {
2015 Result.addNode(OpTag, DynTypedNode::create(*CE));
2016 return true;
2017 }
2018 }
2019 if (auto *ME = dyn_cast<MemberExpr>(S)) {
2020 if (!isa<FieldDecl>(ME->getMemberDecl()))
2021 return false;
2022 if (ME->getMemberDecl()->hasAttr<UnsafeBufferUsageAttr>()) {
2023 Result.addNode(OpTag, DynTypedNode::create(*ME));
2024 return true;
2025 }
2026 }
2027 return false;
2028 }
2029
2030 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2031 bool IsRelatedToDecl,
2032 ASTContext &Ctx) const override {
2033 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
2034 }
2035 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2036
2037 DeclUseList getClaimedVarUseSites() const override { return {}; }
2038
2039 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2040};
2041
2042/// A call of a constructor that performs unchecked buffer operations
2043/// over one of its pointer parameters, or constructs a class object that will
2044/// perform buffer operations that depend on the correctness of the parameters.
2045class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
2046 constexpr static const char *const OpTag = "cxx_construct_expr";
2047 const CXXConstructExpr *Op;
2048
2049public:
2050 UnsafeBufferUsageCtorAttrGadget(const MatchResult &Result)
2051 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
2052 Op(Result.getNodeAs<CXXConstructExpr>(OpTag)) {}
2053
2054 static bool classof(const Gadget *G) {
2055 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
2056 }
2057
2058 static bool matches(const Stmt *S, ASTContext &Ctx, MatchResult &Result) {
2059 const auto *CE = dyn_cast<CXXConstructExpr>(S);
2060 if (!CE || !CE->getConstructor()->hasAttr<UnsafeBufferUsageAttr>())
2061 return false;
2062 // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget.
2063 MatchResult Tmp;
2064 if (SpanTwoParamConstructorGadget::matches(CE, Ctx, Tmp))
2065 return false;
2066 Result.addNode(OpTag, DynTypedNode::create(*CE));
2067 return true;
2068 }
2069
2070 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2071 bool IsRelatedToDecl,
2072 ASTContext &Ctx) const override {
2073 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
2074 }
2075 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2076
2077 DeclUseList getClaimedVarUseSites() const override { return {}; }
2078
2079 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2080};
2081
2082// Warning gadget for unsafe invocation of span::data method.
2083// Triggers when the pointer returned by the invocation is immediately
2084// cast to a larger type.
2085
2086class DataInvocationGadget : public WarningGadget {
2087 constexpr static const char *const OpTag = "data_invocation_expr";
2088 const ExplicitCastExpr *Op;
2089
2090public:
2091 DataInvocationGadget(const MatchResult &Result)
2092 : WarningGadget(Kind::DataInvocation),
2093 Op(Result.getNodeAs<ExplicitCastExpr>(OpTag)) {}
2094
2095 static bool classof(const Gadget *G) {
2096 return G->getKind() == Kind::DataInvocation;
2097 }
2098
2099 static bool matches(const Stmt *S, const ASTContext &Ctx,
2101 auto *CE = dyn_cast<ExplicitCastExpr>(S);
2102 if (!CE)
2103 return false;
2104 for (auto *Child : CE->children()) {
2105 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Child);
2106 MCE && isDataFunction(MCE)) {
2107 Result.addNode(OpTag, DynTypedNode::create(*CE));
2108 return true;
2109 }
2110 if (auto *Paren = dyn_cast<ParenExpr>(Child)) {
2111 if (auto *MCE = dyn_cast<CXXMemberCallExpr>(Paren->getSubExpr());
2112 MCE && isDataFunction(MCE)) {
2113 Result.addNode(OpTag, DynTypedNode::create(*CE));
2114 return true;
2115 }
2116 }
2117 }
2118 return false;
2119 }
2120
2121 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2122 bool IsRelatedToDecl,
2123 ASTContext &Ctx) const override {
2124 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
2125 }
2126 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2127
2128 DeclUseList getClaimedVarUseSites() const override { return {}; }
2129
2130private:
2131 static bool isDataFunction(const CXXMemberCallExpr *call) {
2132 if (!call)
2133 return false;
2134 auto *callee = call->getDirectCallee();
2135 if (!callee || !isa<CXXMethodDecl>(callee))
2136 return false;
2137 auto *method = cast<CXXMethodDecl>(callee);
2138 if (method->getNameAsString() == "data" &&
2139 method->getParent()->isInStdNamespace() &&
2140 llvm::is_contained({SIZED_CONTAINER_OR_VIEW_LIST},
2141 method->getParent()->getName()))
2142 return true;
2143 return false;
2144 }
2145
2146 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2147};
2148
2149class UnsafeLibcFunctionCallGadget : public WarningGadget {
2150 const CallExpr *const Call;
2151 const Expr *UnsafeArg = nullptr;
2152 constexpr static const char *const Tag = "UnsafeLibcFunctionCall";
2153 // Extra tags for additional information:
2154 constexpr static const char *const UnsafeSprintfTag =
2155 "UnsafeLibcFunctionCall_sprintf";
2156 constexpr static const char *const UnsafeSizedByTag =
2157 "UnsafeLibcFunctionCall_sized_by";
2158 constexpr static const char *const UnsafeStringTag =
2159 "UnsafeLibcFunctionCall_string";
2160 constexpr static const char *const UnsafeVaListTag =
2161 "UnsafeLibcFunctionCall_va_list";
2162
2163public:
2164 enum UnsafeKind {
2165 OTHERS = 0, // no specific information, the callee function is unsafe
2166 SPRINTF = 1, // never call `-sprintf`s, call `-snprintf`s instead.
2167 SIZED_BY =
2168 2, // the first two arguments of `snprintf` function have
2169 // "__sized_by" relation but they do not conform to safe patterns
2170 STRING = 3, // an argument is a pointer-to-char-as-string but does not
2171 // guarantee null-termination
2172 VA_LIST = 4, // one of the `-printf`s function that take va_list, which is
2173 // considered unsafe as it is not compile-time check
2174 FORMAT_ATTR = 8, // flag: the callee has the format attribute
2175 } WarnedFunKind = OTHERS;
2176
2177 UnsafeLibcFunctionCallGadget(const MatchResult &Result)
2178 : WarningGadget(Kind::UnsafeLibcFunctionCall),
2179 Call(Result.getNodeAs<CallExpr>(Tag)) {
2180 if (Result.getNodeAs<Decl>(UnsafeSprintfTag))
2181 WarnedFunKind = SPRINTF;
2182 else if (auto *E = Result.getNodeAs<Expr>(UnsafeStringTag)) {
2183 WarnedFunKind = STRING;
2184 UnsafeArg = E;
2185 } else if (Result.getNodeAs<CallExpr>(UnsafeSizedByTag)) {
2186 WarnedFunKind = SIZED_BY;
2187 UnsafeArg = Call->getArg(0);
2188 } else if (Result.getNodeAs<Decl>(UnsafeVaListTag))
2189 WarnedFunKind = VA_LIST;
2190 }
2191
2192 static bool matches(const Stmt *S, ASTContext &Ctx,
2193 const UnsafeBufferUsageHandler *Handler,
2195 if (ignoreUnsafeLibcCall(Ctx, *S, Handler))
2196 return false;
2197 const auto *CE = dyn_cast<CallExpr>(S);
2198 if (!CE)
2199 return false;
2200 const auto *FD = CE->getDirectCallee();
2201 if (!FD)
2202 return false;
2203
2204 const bool IsGlobalAndNotInAnyNamespace =
2205 FD->isGlobal() && !FD->getEnclosingNamespaceContext()->isNamespace();
2206
2207 // A libc function must either be in the std:: namespace or a global
2208 // function that is not in any namespace:
2209 if (!FD->isInStdNamespace() && !IsGlobalAndNotInAnyNamespace)
2210 return false;
2211 // If the call has a sole null-terminated argument, e.g., strlen,
2212 // printf, atoi, we consider it safe:
2213 if (CE->getNumArgs() == 1 && isNullTermPointer(CE->getArg(0), Ctx))
2214 return false;
2215
2216 const bool isSingleStringLiteralArg =
2217 CE->getNumArgs() == 1 &&
2218 isa<clang::StringLiteral>(CE->getArg(0)->IgnoreParenImpCasts());
2219 if (!isSingleStringLiteralArg) {
2220 // (unless the call has a sole string literal argument):
2222 Result.addNode(Tag, DynTypedNode::create(*CE));
2223 return true;
2224 }
2225 if (libc_func_matchers::isUnsafeMemset(*CE, Ctx)) {
2226 Result.addNode(Tag, DynTypedNode::create(*CE));
2227 return true;
2228 }
2230 Result.addNode(Tag, DynTypedNode::create(*CE));
2231 Result.addNode(UnsafeVaListTag, DynTypedNode::create(*FD));
2232 return true;
2233 }
2235 Result.addNode(Tag, DynTypedNode::create(*CE));
2236 Result.addNode(UnsafeSprintfTag, DynTypedNode::create(*FD));
2237 return true;
2238 }
2239 }
2242 Result.addNode(Tag, DynTypedNode::create(*CE));
2243 Result.addNode(UnsafeSizedByTag, DynTypedNode::create(*CE));
2244 return true;
2245 }
2247 UnsafeStringTag)) {
2248 Result.addNode(Tag, DynTypedNode::create(*CE));
2249 return true;
2250 }
2251 }
2252 return false;
2253 }
2254
2255 const Stmt *getBaseStmt() const { return Call; }
2256
2257 SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
2258
2259 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2260 bool IsRelatedToDecl,
2261 ASTContext &Ctx) const override {
2262 Handler.handleUnsafeLibcCall(Call, WarnedFunKind, Ctx, UnsafeArg);
2263 }
2264
2265 DeclUseList getClaimedVarUseSites() const override { return {}; }
2266
2267 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2268};
2269
2270class UnsafeFormatAttributedFunctionCallGadget : public WarningGadget {
2271 const CallExpr *const Call;
2272 const Expr *UnsafeArg = nullptr;
2273 constexpr static const char *const Tag = "UnsafeFormatAttributedFunctionCall";
2274 constexpr static const char *const UnsafeStringTag =
2275 "UnsafeFormatAttributedFunctionCall_string";
2276
2277public:
2278 UnsafeFormatAttributedFunctionCallGadget(const MatchResult &Result)
2279 : WarningGadget(Kind::UnsafeLibcFunctionCall),
2280 Call(Result.getNodeAs<CallExpr>(Tag)),
2281 UnsafeArg(Result.getNodeAs<Expr>(UnsafeStringTag)) {}
2282
2283 static bool matches(const Stmt *S, ASTContext &Ctx,
2284 const UnsafeBufferUsageHandler *Handler,
2286 if (ignoreUnsafeLibcCall(Ctx, *S, Handler))
2287 return false;
2288 auto *CE = dyn_cast<CallExpr>(S);
2289 if (!CE || !CE->getDirectCallee())
2290 return false;
2291 const auto *FD = dyn_cast<FunctionDecl>(CE->getDirectCallee());
2292 if (!FD)
2293 return false;
2294
2295 const FormatAttr *Attr = nullptr;
2296 bool IsPrintf = false;
2297 bool AnyAttr = llvm::any_of(
2298 FD->specific_attrs<FormatAttr>(),
2299 [&Attr, &IsPrintf](const FormatAttr *FA) -> bool {
2300 if (const auto *II = FA->getType()) {
2301 if (II->getName() == "printf" || II->getName() == "scanf") {
2302 Attr = FA;
2303 IsPrintf = II->getName() == "printf";
2304 return true;
2305 }
2306 }
2307 return false;
2308 });
2309 const Expr *UnsafeArg;
2310
2311 if (!AnyAttr)
2312 return false;
2313
2314 // FormatAttribute indexes are 1-based:
2315 unsigned FmtIdx = Attr->getFormatIdx() - 1;
2316 std::optional<unsigned> FmtArgIdx = Attr->getFirstArg() - 1;
2317
2318 if (isa<CXXMemberCallExpr>(CE)) {
2319 // For CXX member calls, attribute parameters are specified as if there is
2320 // an implicit "this". The implicit "this" is invisible through CallExpr
2321 // `CE`. (What makes it even less ergonomic is that the
2322 // implicit "this" is visible through CallExpr `CE` for CXX operator
2323 // calls!)
2324 --FmtIdx;
2325 --*FmtArgIdx;
2326 } else if (CE->getStmtClass() != Stmt::CallExprClass &&
2328 return false; // Ignore unsupported CallExpr subclasses
2329 if (*FmtArgIdx >= CE->getNumArgs())
2330 // Format arguments are allowed to be absent when variadic parameter is
2331 // used. So we need to check if those arguments exist. Moreover, when
2332 // variadic parameter is NOT used, `Attr->getFirstArg()` could be an
2333 // out-of-bound value. E.g.,
2334 // clang does not complain about `__attribute__((__format__(__printf__, 2,
2335 // 99))) void f(int, char *);`.
2336 FmtArgIdx = std::nullopt;
2337
2338 if (AnyAttr && !IsPrintf && FmtArgIdx) {
2339 // For scanf-like functions, any format argument is considered unsafe:
2340 Result.addNode(Tag, DynTypedNode::create(*CE));
2341 return true;
2342 }
2343 // For printf-like functions:
2345 Ctx, CE, UnsafeArg, FmtIdx, FmtArgIdx)) {
2346 Result.addNode(Tag, DynTypedNode::create(*CE));
2347 Result.addNode(UnsafeStringTag, DynTypedNode::create(*UnsafeArg));
2348 return true;
2349 }
2350 return false;
2351 }
2352
2353 const Stmt *getBaseStmt() const { return Call; }
2354
2355 SourceLocation getSourceLoc() const override { return Call->getBeginLoc(); }
2356
2357 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
2358 bool IsRelatedToDecl,
2359 ASTContext &Ctx) const override {
2360 if (UnsafeArg)
2361 Handler.handleUnsafeLibcCall(
2362 Call,
2363 UnsafeLibcFunctionCallGadget::UnsafeKind::STRING |
2364 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2365 Ctx, UnsafeArg);
2366 else
2367 Handler.handleUnsafeLibcCall(
2368 Call,
2369 UnsafeLibcFunctionCallGadget::UnsafeKind::OTHERS |
2370 UnsafeLibcFunctionCallGadget::UnsafeKind::FORMAT_ATTR,
2371 Ctx);
2372 }
2373
2374 DeclUseList getClaimedVarUseSites() const override { return {}; }
2375
2376 SmallVector<const Expr *, 1> getUnsafePtrs() const override { return {}; }
2377};
2378
2379// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
2380// Context (see `findStmtsInUnspecifiedLvalueContext`).
2381// Note here `[]` is the built-in subscript operator.
2382class ULCArraySubscriptGadget : public FixableGadget {
2383private:
2384 static constexpr const char *const ULCArraySubscriptTag =
2385 "ArraySubscriptUnderULC";
2386 const ArraySubscriptExpr *Node;
2387
2388public:
2389 ULCArraySubscriptGadget(const MatchResult &Result)
2390 : FixableGadget(Kind::ULCArraySubscript),
2391 Node(Result.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
2392 assert(Node != nullptr && "Expecting a non-null matching result");
2393 }
2394
2395 static bool classof(const Gadget *G) {
2396 return G->getKind() == Kind::ULCArraySubscript;
2397 }
2398
2399 static bool matches(const Stmt *S,
2400 llvm::SmallVectorImpl<MatchResult> &Results) {
2401 size_t SizeBefore = Results.size();
2402 findStmtsInUnspecifiedLvalueContext(S, [&Results](const Expr *E) {
2403 const auto *ASE = dyn_cast<ArraySubscriptExpr>(E);
2404 if (!ASE)
2405 return;
2406 const auto *DRE =
2407 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts());
2408 if (!DRE || !(hasPointerType(*DRE) || hasArrayType(*DRE)) ||
2409 !isSupportedVariable(*DRE))
2410 return;
2411 MatchResult R;
2412 R.addNode(ULCArraySubscriptTag, DynTypedNode::create(*ASE));
2413 Results.emplace_back(std::move(R));
2414 });
2415 return SizeBefore != Results.size();
2416 }
2417
2418 virtual std::optional<FixItList>
2419 getFixits(const FixitStrategy &S) const override;
2420 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2421
2422 virtual DeclUseList getClaimedVarUseSites() const override {
2423 if (const auto *DRE =
2424 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
2425 return {DRE};
2426 }
2427 return {};
2428 }
2429};
2430
2431// Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
2432// unspecified pointer context (findStmtsInUnspecifiedPointerContext). The
2433// gadget emits fixit of the form `UPC(DRE.data())`.
2434class UPCStandalonePointerGadget : public FixableGadget {
2435private:
2436 static constexpr const char *const DeclRefExprTag = "StandalonePointer";
2437 const DeclRefExpr *Node;
2438
2439public:
2440 UPCStandalonePointerGadget(const MatchResult &Result)
2441 : FixableGadget(Kind::UPCStandalonePointer),
2442 Node(Result.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
2443 assert(Node != nullptr && "Expecting a non-null matching result");
2444 }
2445
2446 static bool classof(const Gadget *G) {
2447 return G->getKind() == Kind::UPCStandalonePointer;
2448 }
2449
2450 static bool matches(const Stmt *S,
2451 llvm::SmallVectorImpl<MatchResult> &Results) {
2452 size_t SizeBefore = Results.size();
2453 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2454 auto *E = dyn_cast<Expr>(S);
2455 if (!E)
2456 return;
2457 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreParenImpCasts());
2458 if (!DRE || (!hasPointerType(*DRE) && !hasArrayType(*DRE)) ||
2459 !isSupportedVariable(*DRE))
2460 return;
2461 MatchResult R;
2462 R.addNode(DeclRefExprTag, DynTypedNode::create(*DRE));
2463 Results.emplace_back(std::move(R));
2464 });
2465 return SizeBefore != Results.size();
2466 }
2467
2468 virtual std::optional<FixItList>
2469 getFixits(const FixitStrategy &S) const override;
2470 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2471
2472 virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }
2473};
2474
2475class PointerDereferenceGadget : public FixableGadget {
2476 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
2477 static constexpr const char *const OperatorTag = "op";
2478
2479 const DeclRefExpr *BaseDeclRefExpr = nullptr;
2480 const UnaryOperator *Op = nullptr;
2481
2482public:
2483 PointerDereferenceGadget(const MatchResult &Result)
2484 : FixableGadget(Kind::PointerDereference),
2485 BaseDeclRefExpr(Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2486 Op(Result.getNodeAs<UnaryOperator>(OperatorTag)) {}
2487
2488 static bool classof(const Gadget *G) {
2489 return G->getKind() == Kind::PointerDereference;
2490 }
2491
2492 static bool matches(const Stmt *S,
2493 llvm::SmallVectorImpl<MatchResult> &Results) {
2494 size_t SizeBefore = Results.size();
2495 findStmtsInUnspecifiedLvalueContext(S, [&Results](const Stmt *S) {
2496 const auto *UO = dyn_cast<UnaryOperator>(S);
2497 if (!UO || UO->getOpcode() != UO_Deref)
2498 return;
2499 const auto *CE = dyn_cast<Expr>(UO->getSubExpr());
2500 if (!CE)
2501 return;
2502 CE = CE->IgnoreParenImpCasts();
2503 const auto *DRE = dyn_cast<DeclRefExpr>(CE);
2504 if (!DRE || !isSupportedVariable(*DRE))
2505 return;
2506 MatchResult R;
2507 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2508 R.addNode(OperatorTag, DynTypedNode::create(*UO));
2509 Results.emplace_back(std::move(R));
2510 });
2511 return SizeBefore != Results.size();
2512 }
2513
2514 DeclUseList getClaimedVarUseSites() const override {
2515 return {BaseDeclRefExpr};
2516 }
2517
2518 virtual std::optional<FixItList>
2519 getFixits(const FixitStrategy &S) const override;
2520 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
2521};
2522
2523// Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
2524// Context (see `findStmtsInUnspecifiedPointerContext`).
2525// Note here `[]` is the built-in subscript operator.
2526class UPCAddressofArraySubscriptGadget : public FixableGadget {
2527private:
2528 static constexpr const char *const UPCAddressofArraySubscriptTag =
2529 "AddressofArraySubscriptUnderUPC";
2530 const UnaryOperator *Node; // the `&DRE[any]` node
2531
2532public:
2533 UPCAddressofArraySubscriptGadget(const MatchResult &Result)
2534 : FixableGadget(Kind::ULCArraySubscript),
2535 Node(Result.getNodeAs<UnaryOperator>(UPCAddressofArraySubscriptTag)) {
2536 assert(Node != nullptr && "Expecting a non-null matching result");
2537 }
2538
2539 static bool classof(const Gadget *G) {
2540 return G->getKind() == Kind::UPCAddressofArraySubscript;
2541 }
2542
2543 static bool matches(const Stmt *S,
2544 llvm::SmallVectorImpl<MatchResult> &Results) {
2545 size_t SizeBefore = Results.size();
2546 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2547 auto *E = dyn_cast<Expr>(S);
2548 if (!E)
2549 return;
2550 const auto *UO = dyn_cast<UnaryOperator>(E->IgnoreImpCasts());
2551 if (!UO || UO->getOpcode() != UO_AddrOf)
2552 return;
2553 const auto *ASE = dyn_cast<ArraySubscriptExpr>(UO->getSubExpr());
2554 if (!ASE)
2555 return;
2556 const auto *DRE =
2557 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts());
2558 if (!DRE || !isSupportedVariable(*DRE))
2559 return;
2560 MatchResult R;
2561 R.addNode(UPCAddressofArraySubscriptTag, DynTypedNode::create(*UO));
2562 Results.emplace_back(std::move(R));
2563 });
2564 return SizeBefore != Results.size();
2565 }
2566
2567 virtual std::optional<FixItList>
2568 getFixits(const FixitStrategy &) const override;
2569 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2570
2571 virtual DeclUseList getClaimedVarUseSites() const override {
2572 const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
2573 const auto *DRE =
2574 cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts());
2575 return {DRE};
2576 }
2577};
2578} // namespace
2579
2580namespace {
2581// An auxiliary tracking facility for the fixit analysis. It helps connect
2582// declarations to its uses and make sure we've covered all uses with our
2583// analysis before we try to fix the declaration.
2584class DeclUseTracker {
2585 using UseSetTy = llvm::SmallPtrSet<const DeclRefExpr *, 16>;
2586 using DefMapTy = llvm::DenseMap<const VarDecl *, const DeclStmt *>;
2587
2588 // Allocate on the heap for easier move.
2589 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
2590 DefMapTy Defs{};
2591
2592public:
2593 DeclUseTracker() = default;
2594 DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
2595 DeclUseTracker &operator=(const DeclUseTracker &) = delete;
2596 DeclUseTracker(DeclUseTracker &&) = default;
2597 DeclUseTracker &operator=(DeclUseTracker &&) = default;
2598
2599 // Start tracking a freshly discovered DRE.
2600 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
2601
2602 // Stop tracking the DRE as it's been fully figured out.
2603 void claimUse(const DeclRefExpr *DRE) {
2604 assert(Uses->count(DRE) &&
2605 "DRE not found or claimed by multiple matchers!");
2606 Uses->erase(DRE);
2607 }
2608
2609 // A variable is unclaimed if at least one use is unclaimed.
2610 bool hasUnclaimedUses(const VarDecl *VD) const {
2611 // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
2612 return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
2613 return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
2614 });
2615 }
2616
2617 UseSetTy getUnclaimedUses(const VarDecl *VD) const {
2618 UseSetTy ReturnSet;
2619 for (auto use : *Uses) {
2620 if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
2621 ReturnSet.insert(use);
2622 }
2623 }
2624 return ReturnSet;
2625 }
2626
2627 void discoverDecl(const DeclStmt *DS) {
2628 for (const Decl *D : DS->decls()) {
2629 if (const auto *VD = dyn_cast<VarDecl>(D)) {
2630 // FIXME: Assertion temporarily disabled due to a bug in
2631 // ASTMatcher internal behavior in presence of GNU
2632 // statement-expressions. We need to properly investigate this
2633 // because it can screw up our algorithm in other ways.
2634 // assert(Defs.count(VD) == 0 && "Definition already discovered!");
2635 Defs[VD] = DS;
2636 }
2637 }
2638 }
2639
2640 const DeclStmt *lookupDecl(const VarDecl *VD) const {
2641 return Defs.lookup(VD);
2642 }
2643};
2644} // namespace
2645
2646// Representing a pointer type expression of the form `++Ptr` in an Unspecified
2647// Pointer Context (UPC):
2648class UPCPreIncrementGadget : public FixableGadget {
2649private:
2650 static constexpr const char *const UPCPreIncrementTag =
2651 "PointerPreIncrementUnderUPC";
2652 const UnaryOperator *Node; // the `++Ptr` node
2653
2654public:
2655 UPCPreIncrementGadget(const MatchResult &Result)
2656 : FixableGadget(Kind::UPCPreIncrement),
2657 Node(Result.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
2658 assert(Node != nullptr && "Expecting a non-null matching result");
2659 }
2660
2661 static bool classof(const Gadget *G) {
2662 return G->getKind() == Kind::UPCPreIncrement;
2663 }
2664
2665 static bool matches(const Stmt *S,
2667 // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
2668 // Although currently we can only provide fix-its when `Ptr` is a DRE, we
2669 // can have the matcher be general, so long as `getClaimedVarUseSites` does
2670 // things right.
2671 size_t SizeBefore = Results.size();
2672 findStmtsInUnspecifiedPointerContext(S, [&Results](const Stmt *S) {
2673 auto *E = dyn_cast<Expr>(S);
2674 if (!E)
2675 return;
2676 const auto *UO = dyn_cast<UnaryOperator>(E->IgnoreImpCasts());
2677 if (!UO || UO->getOpcode() != UO_PreInc)
2678 return;
2679 const auto *DRE = dyn_cast<DeclRefExpr>(UO->getSubExpr());
2680 if (!DRE || !isSupportedVariable(*DRE))
2681 return;
2682 MatchResult R;
2683 R.addNode(UPCPreIncrementTag, DynTypedNode::create(*UO));
2684 Results.emplace_back(std::move(R));
2685 });
2686 return SizeBefore != Results.size();
2687 }
2688
2689 virtual std::optional<FixItList>
2690 getFixits(const FixitStrategy &S) const override;
2691 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2692
2693 virtual DeclUseList getClaimedVarUseSites() const override {
2694 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
2695 }
2696};
2697
2698// Representing a pointer type expression of the form `Ptr += n` in an
2699// Unspecified Untyped Context (UUC):
2700class UUCAddAssignGadget : public FixableGadget {
2701private:
2702 static constexpr const char *const UUCAddAssignTag =
2703 "PointerAddAssignUnderUUC";
2704 static constexpr const char *const OffsetTag = "Offset";
2705
2706 const BinaryOperator *Node; // the `Ptr += n` node
2707 const Expr *Offset = nullptr;
2708
2709public:
2710 UUCAddAssignGadget(const MatchResult &Result)
2711 : FixableGadget(Kind::UUCAddAssign),
2712 Node(Result.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
2713 Offset(Result.getNodeAs<Expr>(OffsetTag)) {
2714 assert(Node != nullptr && "Expecting a non-null matching result");
2715 }
2716
2717 static bool classof(const Gadget *G) {
2718 return G->getKind() == Kind::UUCAddAssign;
2719 }
2720
2721 static bool matches(const Stmt *S,
2723 size_t SizeBefore = Results.size();
2724 findStmtsInUnspecifiedUntypedContext(S, [&Results](const Stmt *S) {
2725 const auto *E = dyn_cast<Expr>(S);
2726 if (!E)
2727 return;
2728 const auto *BO = dyn_cast<BinaryOperator>(E->IgnoreImpCasts());
2729 if (!BO || BO->getOpcode() != BO_AddAssign)
2730 return;
2731 const auto *DRE = dyn_cast<DeclRefExpr>(BO->getLHS());
2732 if (!DRE || !hasPointerType(*DRE) || !isSupportedVariable(*DRE))
2733 return;
2734 MatchResult R;
2735 R.addNode(UUCAddAssignTag, DynTypedNode::create(*BO));
2736 R.addNode(OffsetTag, DynTypedNode::create(*BO->getRHS()));
2737 Results.emplace_back(std::move(R));
2738 });
2739 return SizeBefore != Results.size();
2740 }
2741
2742 virtual std::optional<FixItList>
2743 getFixits(const FixitStrategy &S) const override;
2744 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
2745
2746 virtual DeclUseList getClaimedVarUseSites() const override {
2747 return {dyn_cast<DeclRefExpr>(Node->getLHS())};
2748 }
2749};
2750
2751// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
2752// ptr)`:
2753class DerefSimplePtrArithFixableGadget : public FixableGadget {
2754 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
2755 static constexpr const char *const DerefOpTag = "DerefOp";
2756 static constexpr const char *const AddOpTag = "AddOp";
2757 static constexpr const char *const OffsetTag = "Offset";
2758
2759 const DeclRefExpr *BaseDeclRefExpr = nullptr;
2760 const UnaryOperator *DerefOp = nullptr;
2761 const BinaryOperator *AddOp = nullptr;
2762 const IntegerLiteral *Offset = nullptr;
2763
2764public:
2766 : FixableGadget(Kind::DerefSimplePtrArithFixable),
2767 BaseDeclRefExpr(Result.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
2768 DerefOp(Result.getNodeAs<UnaryOperator>(DerefOpTag)),
2769 AddOp(Result.getNodeAs<BinaryOperator>(AddOpTag)),
2770 Offset(Result.getNodeAs<IntegerLiteral>(OffsetTag)) {}
2771
2772 static bool matches(const Stmt *S,
2774 auto IsPtr = [](const Expr *E, MatchResult &R) {
2775 if (!E || !hasPointerType(*E))
2776 return false;
2777 const auto *DRE = dyn_cast<DeclRefExpr>(E->IgnoreImpCasts());
2778 if (!DRE || !isSupportedVariable(*DRE))
2779 return false;
2780 R.addNode(BaseDeclRefExprTag, DynTypedNode::create(*DRE));
2781 return true;
2782 };
2783 const auto IsPlusOverPtrAndInteger = [&IsPtr](const Expr *E,
2784 MatchResult &R) {
2785 const auto *BO = dyn_cast<BinaryOperator>(E);
2786 if (!BO || BO->getOpcode() != BO_Add)
2787 return false;
2788
2789 const auto *LHS = BO->getLHS();
2790 const auto *RHS = BO->getRHS();
2791 if (isa<IntegerLiteral>(RHS) && IsPtr(LHS, R)) {
2792 R.addNode(OffsetTag, DynTypedNode::create(*RHS));
2793 R.addNode(AddOpTag, DynTypedNode::create(*BO));
2794 return true;
2795 }
2796 if (isa<IntegerLiteral>(LHS) && IsPtr(RHS, R)) {
2797 R.addNode(OffsetTag, DynTypedNode::create(*LHS));
2798 R.addNode(AddOpTag, DynTypedNode::create(*BO));
2799 return true;
2800 }
2801 return false;
2802 };
2803 size_t SizeBefore = Results.size();
2804 const auto InnerMatcher = [&IsPlusOverPtrAndInteger,
2805 &Results](const Expr *E) {
2806 const auto *UO = dyn_cast<UnaryOperator>(E);
2807 if (!UO || UO->getOpcode() != UO_Deref)
2808 return;
2809
2810 const auto *Operand = UO->getSubExpr()->IgnoreParens();
2811 MatchResult R;
2812 if (IsPlusOverPtrAndInteger(Operand, R)) {
2813 R.addNode(DerefOpTag, DynTypedNode::create(*UO));
2814 Results.emplace_back(std::move(R));
2815 }
2816 };
2817 findStmtsInUnspecifiedLvalueContext(S, InnerMatcher);
2818 return SizeBefore != Results.size();
2819 }
2820
2821 virtual std::optional<FixItList>
2822 getFixits(const FixitStrategy &s) const final;
2823 SourceLocation getSourceLoc() const override {
2824 return DerefOp->getBeginLoc();
2825 }
2826
2827 virtual DeclUseList getClaimedVarUseSites() const final {
2828 return {BaseDeclRefExpr};
2829 }
2830};
2831
2832class WarningGadgetMatcher : public FastMatcher {
2833
2834public:
2835 WarningGadgetMatcher(WarningGadgetList &WarningGadgets)
2836 : WarningGadgets(WarningGadgets) {}
2837
2838 bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
2839 const UnsafeBufferUsageHandler &Handler) override {
2840 const Stmt *S = DynNode.get<Stmt>();
2841 if (!S)
2842 return false;
2843
2844 MatchResult Result;
2845#define WARNING_GADGET(name) \
2846 if (name##Gadget::matches(S, Ctx, Result) && \
2847 notInSafeBufferOptOut(*S, &Handler)) { \
2848 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
2849 return true; \
2850 }
2851#define WARNING_OPTIONAL_GADGET(name) \
2852 if (name##Gadget::matches(S, Ctx, &Handler, Result) && \
2853 notInSafeBufferOptOut(*S, &Handler)) { \
2854 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
2855 return true; \
2856 }
2857#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2858 return false;
2859 }
2860
2861private:
2862 WarningGadgetList &WarningGadgets;
2863};
2864
2865class FixableGadgetMatcher : public FastMatcher {
2866
2867public:
2868 FixableGadgetMatcher(FixableGadgetList &FixableGadgets,
2869 DeclUseTracker &Tracker)
2870 : FixableGadgets(FixableGadgets), Tracker(Tracker) {}
2871
2872 bool matches(const DynTypedNode &DynNode, ASTContext &Ctx,
2873 const UnsafeBufferUsageHandler &Handler) override {
2874 bool matchFound = false;
2875 const Stmt *S = DynNode.get<Stmt>();
2876 if (!S) {
2877 return matchFound;
2878 }
2879
2881#define FIXABLE_GADGET(name) \
2882 if (name##Gadget::matches(S, Results)) { \
2883 for (const auto &R : Results) { \
2884 FixableGadgets.push_back(std::make_unique<name##Gadget>(R)); \
2885 matchFound = true; \
2886 } \
2887 Results = {}; \
2888 }
2889#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
2890 // In parallel, match all DeclRefExprs so that to find out
2891 // whether there are any uncovered by gadgets.
2892 if (auto *DRE = findDeclRefExpr(S); DRE) {
2893 Tracker.discoverUse(DRE);
2894 matchFound = true;
2895 }
2896 // Also match DeclStmts because we'll need them when fixing
2897 // their underlying VarDecls that otherwise don't have
2898 // any backreferences to DeclStmts.
2899 if (auto *DS = findDeclStmt(S); DS) {
2900 Tracker.discoverDecl(DS);
2901 matchFound = true;
2902 }
2903 return matchFound;
2904 }
2905
2906private:
2907 const DeclRefExpr *findDeclRefExpr(const Stmt *S) {
2908 const auto *DRE = dyn_cast<DeclRefExpr>(S);
2909 if (!DRE || (!hasPointerType(*DRE) && !hasArrayType(*DRE)))
2910 return nullptr;
2911 const Decl *D = DRE->getDecl();
2912 if (!D || (!isa<VarDecl>(D) && !isa<BindingDecl>(D)))
2913 return nullptr;
2914 return DRE;
2915 }
2916 const DeclStmt *findDeclStmt(const Stmt *S) {
2917 const auto *DS = dyn_cast<DeclStmt>(S);
2918 if (!DS)
2919 return nullptr;
2920 return DS;
2921 }
2922 FixableGadgetList &FixableGadgets;
2923 DeclUseTracker &Tracker;
2924};
2925
2926// Scan the function and return a list of gadgets found with provided kits.
2927static void findGadgets(const Stmt *S, ASTContext &Ctx,
2928 const UnsafeBufferUsageHandler &Handler,
2929 bool EmitSuggestions, FixableGadgetList &FixableGadgets,
2930 WarningGadgetList &WarningGadgets,
2931 DeclUseTracker &Tracker) {
2932 WarningGadgetMatcher WMatcher{WarningGadgets};
2933 forEachDescendantEvaluatedStmt(S, Ctx, Handler, WMatcher);
2934 if (EmitSuggestions) {
2935 FixableGadgetMatcher FMatcher{FixableGadgets, Tracker};
2936 forEachDescendantStmt(S, Ctx, Handler, FMatcher);
2937 }
2938}
2939
2940// Compares AST nodes by source locations.
2941template <typename NodeTy> struct CompareNode {
2942 bool operator()(const NodeTy *N1, const NodeTy *N2) const {
2943 return N1->getBeginLoc().getRawEncoding() <
2944 N2->getBeginLoc().getRawEncoding();
2945 }
2946};
2947
2948std::set<const Expr *> clang::findUnsafePointers(const FunctionDecl *FD) {
2949 class MockReporter : public UnsafeBufferUsageHandler {
2950 public:
2951 MockReporter() {}
2952 void handleUnsafeOperation(const Stmt *, bool, ASTContext &) override {}
2953 void handleUnsafeLibcCall(const CallExpr *, unsigned, ASTContext &,
2954 const Expr *UnsafeArg = nullptr) override {}
2955 void handleUnsafeOperationInContainer(const Stmt *, bool,
2956 ASTContext &) override {}
2958 const VariableGroupsManager &, FixItList &&,
2959 const Decl *,
2960 const FixitStrategy &) override {}
2962 bool IsRelatedToDecl,
2963 ASTContext &Ctx) override {}
2964 bool ignoreUnsafeBufferInContainer(const SourceLocation &) const override {
2965 return false;
2966 }
2967 bool isSafeBufferOptOut(const SourceLocation &) const override {
2968 return false;
2969 }
2970 bool ignoreUnsafeBufferInLibcCall(const SourceLocation &) const override {
2971 return false;
2972 }
2974 const SourceLocation &Loc) const override {
2975 return false;
2976 }
2978 SourceLocation, StringRef WSSuffix = "") const override {
2979 return "";
2980 }
2981 };
2982
2983 FixableGadgetList FixableGadgets;
2984 WarningGadgetList WarningGadgets;
2985 DeclUseTracker Tracker;
2986 MockReporter IgnoreHandler;
2987
2988 findGadgets(FD->getBody(), FD->getASTContext(), IgnoreHandler, false,
2989 FixableGadgets, WarningGadgets, Tracker);
2990
2991 std::set<const Expr *> Result;
2992 for (auto &G : WarningGadgets) {
2993 for (const Expr *E : G->getUnsafePtrs()) {
2994 Result.insert(E);
2995 }
2996 }
2997
2998 return Result;
2999}
3000
3002 std::map<const VarDecl *, std::set<const WarningGadget *>,
3003 // To keep keys sorted by their locations in the map so that the
3004 // order is deterministic:
3007 // These Gadgets are not related to pointer variables (e. g. temporaries).
3009};
3010
3011static WarningGadgetSets
3012groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
3013 WarningGadgetSets result;
3014 // If some gadgets cover more than one
3015 // variable, they'll appear more than once in the map.
3016 for (auto &G : AllUnsafeOperations) {
3017 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
3018
3019 bool AssociatedWithVarDecl = false;
3020 for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
3021 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3022 result.byVar[VD].insert(G.get());
3023 AssociatedWithVarDecl = true;
3024 }
3025 }
3026
3027 if (!AssociatedWithVarDecl) {
3028 result.noVar.push_back(G.get());
3029 continue;
3030 }
3031 }
3032 return result;
3033}
3034
3036 std::map<const VarDecl *, std::set<const FixableGadget *>,
3037 // To keep keys sorted by their locations in the map so that the
3038 // order is deterministic:
3041};
3042
3043static FixableGadgetSets
3044groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
3045 FixableGadgetSets FixablesForUnsafeVars;
3046 for (auto &F : AllFixableOperations) {
3047 DeclUseList DREs = F->getClaimedVarUseSites();
3048
3049 for (const DeclRefExpr *DRE : DREs) {
3050 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3051 FixablesForUnsafeVars.byVar[VD].insert(F.get());
3052 }
3053 }
3054 }
3055 return FixablesForUnsafeVars;
3056}
3057
3059 const SourceManager &SM) {
3060 // A simple interval overlap detection algorithm. Sorts all ranges by their
3061 // begin location then finds the first overlap in one pass.
3062 std::vector<const FixItHint *> All; // a copy of `FixIts`
3063
3064 for (const FixItHint &H : FixIts)
3065 All.push_back(&H);
3066 std::sort(All.begin(), All.end(),
3067 [&SM](const FixItHint *H1, const FixItHint *H2) {
3068 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
3069 H2->RemoveRange.getBegin());
3070 });
3071
3072 const FixItHint *CurrHint = nullptr;
3073
3074 for (const FixItHint *Hint : All) {
3075 if (!CurrHint ||
3076 SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
3077 Hint->RemoveRange.getBegin())) {
3078 // Either to initialize `CurrHint` or `CurrHint` does not
3079 // overlap with `Hint`:
3080 CurrHint = Hint;
3081 } else
3082 // In case `Hint` overlaps the `CurrHint`, we found at least one
3083 // conflict:
3084 return true;
3085 }
3086 return false;
3087}
3088
3089std::optional<FixItList>
3090PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
3091 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
3092 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
3093 switch (S.lookup(LeftVD)) {
3095 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
3096 return FixItList{};
3097 return std::nullopt;
3099 return std::nullopt;
3102 return std::nullopt;
3104 llvm_unreachable("unsupported strategies for FixableGadgets");
3105 }
3106 return std::nullopt;
3107}
3108
3109/// \returns fixit that adds .data() call after \DRE.
3110static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
3111 const DeclRefExpr *DRE);
3112
3113std::optional<FixItList>
3114CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
3115 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
3116 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
3117 // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
3118 // non-trivial.
3119 //
3120 // CArrayToPtrAssignmentGadget doesn't have strategy implications because
3121 // constant size array propagates its bounds. Because of that LHS and RHS are
3122 // addressed by two different fixits.
3123 //
3124 // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
3125 // to and can't be generally relied on in multi-variable Fixables!
3126 //
3127 // E. g. If an instance of this gadget is fixing variable on LHS then the
3128 // variable on RHS is fixed by a different fixit and its strategy for LHS
3129 // fixit is as if Wontfix.
3130 //
3131 // The only exception is Wontfix strategy for a given variable as that is
3132 // valid for any fixit produced for the given input source code.
3133 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
3134 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
3135 return FixItList{};
3136 }
3137 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
3138 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
3139 return createDataFixit(RightVD->getASTContext(), PtrRHS);
3140 }
3141 }
3142 return std::nullopt;
3143}
3144
3145std::optional<FixItList>
3146PointerInitGadget::getFixits(const FixitStrategy &S) const {
3147 const auto *LeftVD = PtrInitLHS;
3148 const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
3149 switch (S.lookup(LeftVD)) {
3150 case FixitStrategy::Kind::Span:
3151 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
3152 return FixItList{};
3153 return std::nullopt;
3154 case FixitStrategy::Kind::Wontfix:
3155 return std::nullopt;
3156 case FixitStrategy::Kind::Iterator:
3157 case FixitStrategy::Kind::Array:
3158 return std::nullopt;
3159 case FixitStrategy::Kind::Vector:
3160 llvm_unreachable("unsupported strategies for FixableGadgets");
3161 }
3162 return std::nullopt;
3163}
3164
3165static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
3166 const ASTContext &Ctx) {
3167 if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
3168 if (ConstVal->isNegative())
3169 return false;
3170 } else if (!Expr->getType()->isUnsignedIntegerType())
3171 return false;
3172 return true;
3173}
3174
3175std::optional<FixItList>
3176ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
3177 if (const auto *DRE =
3178 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
3179 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
3180 switch (S.lookup(VD)) {
3181 case FixitStrategy::Kind::Span: {
3182
3183 // If the index has a negative constant value, we give up as no valid
3184 // fix-it can be generated:
3185 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
3186 VD->getASTContext();
3187 if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
3188 return std::nullopt;
3189 // no-op is a good fix-it, otherwise
3190 return FixItList{};
3191 }
3192 case FixitStrategy::Kind::Array:
3193 return FixItList{};
3194 case FixitStrategy::Kind::Wontfix:
3195 case FixitStrategy::Kind::Iterator:
3196 case FixitStrategy::Kind::Vector:
3197 llvm_unreachable("unsupported strategies for FixableGadgets");
3198 }
3199 }
3200 return std::nullopt;
3201}
3202
3203static std::optional<FixItList> // forward declaration
3204fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node);
3205
3206std::optional<FixItList>
3207UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
3208 auto DREs = getClaimedVarUseSites();
3209 const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
3210
3211 switch (S.lookup(VD)) {
3212 case FixitStrategy::Kind::Span:
3214 case FixitStrategy::Kind::Wontfix:
3215 case FixitStrategy::Kind::Iterator:
3216 case FixitStrategy::Kind::Array:
3217 return std::nullopt;
3218 case FixitStrategy::Kind::Vector:
3219 llvm_unreachable("unsupported strategies for FixableGadgets");
3220 }
3221 return std::nullopt; // something went wrong, no fix-it
3222}
3223
3224// FIXME: this function should be customizable through format
3225static StringRef getEndOfLine() {
3226 static const char *const EOL = "\n";
3227 return EOL;
3228}
3229
3230// Returns the text indicating that the user needs to provide input there:
3231static std::string
3232getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
3233 std::string s = std::string("<# ");
3234 s += HintTextToUser;
3235 s += " #>";
3236 return s;
3237}
3238
3239// Return the source location of the last character of the AST `Node`.
3240template <typename NodeTy>
3241static std::optional<SourceLocation>
3242getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
3243 const LangOptions &LangOpts) {
3244 if (unsigned TkLen =
3245 Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts)) {
3246 SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
3247
3248 if (Loc.isValid())
3249 return Loc;
3250 }
3251 return std::nullopt;
3252}
3253
3254// We cannot fix a variable declaration if it has some other specifiers than the
3255// type specifier. Because the source ranges of those specifiers could overlap
3256// with the source range that is being replaced using fix-its. Especially when
3257// we often cannot obtain accurate source ranges of cv-qualified type
3258// specifiers.
3259// FIXME: also deal with type attributes
3260static bool hasUnsupportedSpecifiers(const VarDecl *VD,
3261 const SourceManager &SM) {
3262 // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
3263 // source range of `VD`:
3264 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
3265 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
3266 VD->getBeginLoc())) &&
3267 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
3268 At->getRange().getBegin()));
3269 });
3270 return VD->isInlineSpecified() || VD->isConstexpr() ||
3272 AttrRangeOverlapping;
3273}
3274
3275// Returns the `SourceRange` of `D`. The reason why this function exists is
3276// that `D->getSourceRange()` may return a range where the end location is the
3277// starting location of the last token. The end location of the source range
3278// returned by this function is the last location of the last token.
3280 const SourceManager &SM,
3281 const LangOptions &LangOpts) {
3282 SourceLocation Begin = D->getBeginLoc();
3284 End = // `D->getEndLoc` should always return the starting location of the
3285 // last token, so we should get the end of the token
3286 Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
3287
3288 return SourceRange(Begin, End);
3289}
3290
3291// Returns the text of the name (with qualifiers) of a `FunctionDecl`.
3292static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
3293 const SourceManager &SM,
3294 const LangOptions &LangOpts) {
3295 SourceLocation BeginLoc = FD->getQualifier()
3297 : FD->getNameInfo().getBeginLoc();
3298 // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
3299 // last token:
3301 FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
3302 SourceRange NameRange{BeginLoc, EndLoc};
3303
3304 return getRangeText(NameRange, SM, LangOpts);
3305}
3306
3307// Returns the text representing a `std::span` type where the element type is
3308// represented by `EltTyText`.
3309//
3310// Note the optional parameter `Qualifiers`: one needs to pass qualifiers
3311// explicitly if the element type needs to be qualified.
3312static std::string
3313getSpanTypeText(StringRef EltTyText,
3314 std::optional<Qualifiers> Quals = std::nullopt) {
3315 const char *const SpanOpen = "std::span<";
3316
3317 if (Quals)
3318 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
3319 return SpanOpen + EltTyText.str() + '>';
3320}
3321
3322std::optional<FixItList>
3324 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
3325
3326 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
3327 ASTContext &Ctx = VD->getASTContext();
3328 // std::span can't represent elements before its begin()
3329 if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
3330 if (ConstVal->isNegative())
3331 return std::nullopt;
3332
3333 // note that the expr may (oddly) has multiple layers of parens
3334 // example:
3335 // *((..(pointer + 123)..))
3336 // goal:
3337 // pointer[123]
3338 // Fix-It:
3339 // remove '*('
3340 // replace ' + ' with '['
3341 // replace ')' with ']'
3342
3343 // example:
3344 // *((..(123 + pointer)..))
3345 // goal:
3346 // 123[pointer]
3347 // Fix-It:
3348 // remove '*('
3349 // replace ' + ' with '['
3350 // replace ')' with ']'
3351
3352 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
3353 const SourceManager &SM = Ctx.getSourceManager();
3354 const LangOptions &LangOpts = Ctx.getLangOpts();
3355 CharSourceRange StarWithTrailWhitespace =
3356 clang::CharSourceRange::getCharRange(DerefOp->getOperatorLoc(),
3357 LHS->getBeginLoc());
3358
3359 std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
3360 if (!LHSLocation)
3361 return std::nullopt;
3362
3363 CharSourceRange PlusWithSurroundingWhitespace =
3364 clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
3365
3366 std::optional<SourceLocation> AddOpLocation =
3367 getPastLoc(AddOp, SM, LangOpts);
3368 std::optional<SourceLocation> DerefOpLocation =
3369 getPastLoc(DerefOp, SM, LangOpts);
3370
3371 if (!AddOpLocation || !DerefOpLocation)
3372 return std::nullopt;
3373
3374 CharSourceRange ClosingParenWithPrecWhitespace =
3375 clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
3376
3377 return FixItList{
3378 {FixItHint::CreateRemoval(StarWithTrailWhitespace),
3379 FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
3380 FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
3381 }
3382 return std::nullopt; // something wrong or unsupported, give up
3383}
3384
3385std::optional<FixItList>
3386PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
3387 const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
3388 switch (S.lookup(VD)) {
3390 ASTContext &Ctx = VD->getASTContext();
3392 // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
3393 // Deletes the *operand
3395 Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
3396 // Inserts the [0]
3397 if (auto LocPastOperand =
3398 getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
3399 return FixItList{{FixItHint::CreateRemoval(derefRange),
3400 FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
3401 }
3402 break;
3403 }
3404 case FixitStrategy::Kind::Iterator:
3405 case FixitStrategy::Kind::Array:
3406 return std::nullopt;
3407 case FixitStrategy::Kind::Vector:
3408 llvm_unreachable("FixitStrategy not implemented yet!");
3409 case FixitStrategy::Kind::Wontfix:
3410 llvm_unreachable("Invalid strategy!");
3411 }
3412
3413 return std::nullopt;
3414}
3415
3416static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
3417 const DeclRefExpr *DRE) {
3418 const SourceManager &SM = Ctx.getSourceManager();
3419 // Inserts the .data() after the DRE
3420 std::optional<SourceLocation> EndOfOperand =
3421 getPastLoc(DRE, SM, Ctx.getLangOpts());
3422
3423 if (EndOfOperand)
3424 return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
3425
3426 return std::nullopt;
3427}
3428
3429// Generates fix-its replacing an expression of the form UPC(DRE) with
3430// `DRE.data()`
3431std::optional<FixItList>
3432UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
3433 const auto VD = cast<VarDecl>(Node->getDecl());
3434 switch (S.lookup(VD)) {
3435 case FixitStrategy::Kind::Array:
3436 case FixitStrategy::Kind::Span: {
3437 return createDataFixit(VD->getASTContext(), Node);
3438 // FIXME: Points inside a macro expansion.
3439 break;
3440 }
3441 case FixitStrategy::Kind::Wontfix:
3442 case FixitStrategy::Kind::Iterator:
3443 return std::nullopt;
3444 case FixitStrategy::Kind::Vector:
3445 llvm_unreachable("unsupported strategies for FixableGadgets");
3446 }
3447
3448 return std::nullopt;
3449}
3450
3451// Generates fix-its replacing an expression of the form `&DRE[e]` with
3452// `&DRE.data()[e]`:
3453static std::optional<FixItList>
3455 const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
3456 const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
3457 // FIXME: this `getASTContext` call is costly, we should pass the
3458 // ASTContext in:
3459 const ASTContext &Ctx = DRE->getDecl()->getASTContext();
3460 const Expr *Idx = ArraySub->getIdx();
3461 const SourceManager &SM = Ctx.getSourceManager();
3462 const LangOptions &LangOpts = Ctx.getLangOpts();
3463 std::stringstream SS;
3464 bool IdxIsLitZero = false;
3465
3466 if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
3467 if ((*ICE).isZero())
3468 IdxIsLitZero = true;
3469 std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
3470 if (!DreString)
3471 return std::nullopt;
3472
3473 if (IdxIsLitZero) {
3474 // If the index is literal zero, we produce the most concise fix-it:
3475 SS << (*DreString).str() << ".data()";
3476 } else {
3477 std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
3478 if (!IndexString)
3479 return std::nullopt;
3480
3481 SS << "&" << (*DreString).str() << ".data()"
3482 << "[" << (*IndexString).str() << "]";
3483 }
3484 return FixItList{
3485 FixItHint::CreateReplacement(Node->getSourceRange(), SS.str())};
3486}
3487
3488std::optional<FixItList>
3490 DeclUseList DREs = getClaimedVarUseSites();
3491
3492 if (DREs.size() != 1)
3493 return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
3494 // give up
3495 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3496 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3497 FixItList Fixes;
3498
3499 const Stmt *AddAssignNode = Node;
3500 StringRef varName = VD->getName();
3501 const ASTContext &Ctx = VD->getASTContext();
3502
3503 if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
3504 return std::nullopt;
3505
3506 // To transform UUC(p += n) to UUC(p = p.subspan(..)):
3507 bool NotParenExpr =
3508 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
3509 std::string SS = varName.str() + " = " + varName.str() + ".subspan";
3510 if (NotParenExpr)
3511 SS += "(";
3512
3513 std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
3514 AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
3515 if (!AddAssignLocation)
3516 return std::nullopt;
3517
3518 Fixes.push_back(FixItHint::CreateReplacement(
3519 SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
3520 SS));
3521 if (NotParenExpr)
3522 Fixes.push_back(FixItHint::CreateInsertion(
3523 Offset->getEndLoc().getLocWithOffset(1), ")"));
3524 return Fixes;
3525 }
3526 }
3527 return std::nullopt; // Not in the cases that we can handle for now, give up.
3528}
3529
3530std::optional<FixItList>
3532 DeclUseList DREs = getClaimedVarUseSites();
3533
3534 if (DREs.size() != 1)
3535 return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
3536 // give up
3537 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
3538 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
3539 FixItList Fixes;
3540 std::stringstream SS;
3541 StringRef varName = VD->getName();
3542 const ASTContext &Ctx = VD->getASTContext();
3543
3544 // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
3545 SS << "(" << varName.data() << " = " << varName.data()
3546 << ".subspan(1)).data()";
3547 std::optional<SourceLocation> PreIncLocation =
3548 getEndCharLoc(Node, Ctx.getSourceManager(), Ctx.getLangOpts());
3549 if (!PreIncLocation)
3550 return std::nullopt;
3551
3552 Fixes.push_back(FixItHint::CreateReplacement(
3553 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
3554 return Fixes;
3555 }
3556 }
3557 return std::nullopt; // Not in the cases that we can handle for now, give up.
3558}
3559
3560// For a non-null initializer `Init` of `T *` type, this function returns
3561// `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
3562// to output stream.
3563// In many cases, this function cannot figure out the actual extent `S`. It
3564// then will use a place holder to replace `S` to ask users to fill `S` in. The
3565// initializer shall be used to initialize a variable of type `std::span<T>`.
3566// In some cases (e. g. constant size array) the initializer should remain
3567// unchanged and the function returns empty list. In case the function can't
3568// provide the right fixit it will return nullopt.
3569//
3570// FIXME: Support multi-level pointers
3571//
3572// Parameters:
3573// `Init` a pointer to the initializer expression
3574// `Ctx` a reference to the ASTContext
3575static std::optional<FixItList>
3577 const StringRef UserFillPlaceHolder) {
3578 const SourceManager &SM = Ctx.getSourceManager();
3579 const LangOptions &LangOpts = Ctx.getLangOpts();
3580
3581 // If `Init` has a constant value that is (or equivalent to) a
3582 // NULL pointer, we use the default constructor to initialize the span
3583 // object, i.e., a `std:span` variable declaration with no initializer.
3584 // So the fix-it is just to remove the initializer.
3585 if (Init->isNullPointerConstant(
3586 Ctx,
3587 // FIXME: Why does this function not ask for `const ASTContext
3588 // &`? It should. Maybe worth an NFC patch later.
3590 NPC_ValueDependentIsNotNull)) {
3591 std::optional<SourceLocation> InitLocation =
3592 getEndCharLoc(Init, SM, LangOpts);
3593 if (!InitLocation)
3594 return std::nullopt;
3595
3596 SourceRange SR(Init->getBeginLoc(), *InitLocation);
3597
3598 return FixItList{FixItHint::CreateRemoval(SR)};
3599 }
3600
3601 FixItList FixIts{};
3602 std::string ExtentText = UserFillPlaceHolder.data();
3603 StringRef One = "1";
3604
3605 // Insert `{` before `Init`:
3606 FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
3607 // Try to get the data extent. Break into different cases:
3608 if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
3609 // In cases `Init` is `new T[n]` and there is no explicit cast over
3610 // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
3611 // of `T`. So the extent is `n` unless `n` has side effects. Similar but
3612 // simpler for the case where `Init` is `new T`.
3613 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
3614 if (!Ext->HasSideEffects(Ctx)) {
3615 std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
3616 if (!ExtentString)
3617 return std::nullopt;
3618 ExtentText = *ExtentString;
3619 }
3620 } else if (!CxxNew->isArray())
3621 // Although the initializer is not allocating a buffer, the pointer
3622 // variable could still be used in buffer access operations.
3623 ExtentText = One;
3624 } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) {
3625 // std::span has a single parameter constructor for initialization with
3626 // constant size array. The size is auto-deduced as the constructor is a
3627 // function template. The correct fixit is empty - no changes should happen.
3628 return FixItList{};
3629 } else {
3630 // In cases `Init` is of the form `&Var` after stripping of implicit
3631 // casts, where `&` is the built-in operator, the extent is 1.
3632 if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
3633 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
3634 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
3635 ExtentText = One;
3636 // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
3637 // and explicit casting, etc. etc.
3638 }
3639
3640 SmallString<32> StrBuffer{};
3641 std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
3642
3643 if (!LocPassInit)
3644 return std::nullopt;
3645
3646 StrBuffer.append(", ");
3647 StrBuffer.append(ExtentText);
3648 StrBuffer.append("}");
3649 FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
3650 return FixIts;
3651}
3652
3653#ifndef NDEBUG
3654#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
3655 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
3656 "failed to produce fixit for declaration '" + \
3657 (D)->getNameAsString() + "'" + (Msg))
3658#else
3659#define DEBUG_NOTE_DECL_FAIL(D, Msg)
3660#endif
3661
3662// For the given variable declaration with a pointer-to-T type, returns the text
3663// `std::span<T>`. If it is unable to generate the text, returns
3664// `std::nullopt`.
3665static std::optional<std::string>
3667 assert(VD->getType()->isPointerType());
3668
3669 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3670 std::optional<std::string> PteTyText = getPointeeTypeText(
3671 VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
3672
3673 if (!PteTyText)
3674 return std::nullopt;
3675
3676 std::string SpanTyText = "std::span<";
3677
3678 SpanTyText.append(*PteTyText);
3679 // Append qualifiers to span element type if any:
3680 if (PteTyQualifiers) {
3681 SpanTyText.append(" ");
3682 SpanTyText.append(PteTyQualifiers->getAsString());
3683 }
3684 SpanTyText.append(">");
3685 return SpanTyText;
3686}
3687
3688// For a `VarDecl` of the form `T * var (= Init)?`, this
3689// function generates fix-its that
3690// 1) replace `T * var` with `std::span<T> var`; and
3691// 2) change `Init` accordingly to a span constructor, if it exists.
3692//
3693// FIXME: support Multi-level pointers
3694//
3695// Parameters:
3696// `D` a pointer the variable declaration node
3697// `Ctx` a reference to the ASTContext
3698// `UserFillPlaceHolder` the user-input placeholder text
3699// Returns:
3700// the non-empty fix-it list, if fix-its are successfuly generated; empty
3701// list otherwise.
3702static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
3703 const StringRef UserFillPlaceHolder,
3704 UnsafeBufferUsageHandler &Handler) {
3706 return {};
3707
3708 FixItList FixIts{};
3709 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
3710
3711 if (!SpanTyText) {
3712 DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
3713 return {};
3714 }
3715
3716 // Will hold the text for `std::span<T> Ident`:
3717 std::stringstream SS;
3718
3719 SS << *SpanTyText;
3720 // Fix the initializer if it exists:
3721 if (const Expr *Init = D->getInit()) {
3722 std::optional<FixItList> InitFixIts =
3723 FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
3724 if (!InitFixIts)
3725 return {};
3726 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
3727 std::make_move_iterator(InitFixIts->end()));
3728 }
3729 // For declaration of the form `T * ident = init;`, we want to replace
3730 // `T * ` with `std::span<T>`.
3731 // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
3732 // just `T *` with `std::span<T>`.
3733 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
3734 if (!EndLocForReplacement.isValid()) {
3735 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
3736 return {};
3737 }
3738 // The only exception is that for `T *ident` we'll add a single space between
3739 // "std::span<T>" and "ident".
3740 // FIXME: The condition is false for identifiers expended from macros.
3741 if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
3742 SS << " ";
3743
3744 FixIts.push_back(FixItHint::CreateReplacement(
3745 SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
3746 return FixIts;
3747}
3748
3749static bool hasConflictingOverload(const FunctionDecl *FD) {
3750 return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
3751}
3752
3753// For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
3754// types, this function produces fix-its to make the change self-contained. Let
3755// 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
3756// entity defined by the `FunctionDecl` after the change to the parameters.
3757// Fix-its produced by this function are
3758// 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
3759// of 'F';
3760// 2. Create a declaration of "NewF" next to each declaration of `F`;
3761// 3. Create a definition of "F" (as its original definition is now belongs
3762// to "NewF") next to its original definition. The body of the creating
3763// definition calls to "NewF".
3764//
3765// Example:
3766//
3767// void f(int *p); // original declaration
3768// void f(int *p) { // original definition
3769// p[5];
3770// }
3771//
3772// To change the parameter `p` to be of `std::span<int>` type, we
3773// also add overloads:
3774//
3775// [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
3776// void f(std::span<int> p); // added overload decl
3777// void f(std::span<int> p) { // original def where param is changed
3778// p[5];
3779// }
3780// [[clang::unsafe_buffer_usage]] void f(int *p) { // added def
3781// return f(std::span(p, <# size #>));
3782// }
3783//
3784static std::optional<FixItList>
3786 const ASTContext &Ctx,
3787 UnsafeBufferUsageHandler &Handler) {
3788 // FIXME: need to make this conflict checking better:
3789 if (hasConflictingOverload(FD))
3790 return std::nullopt;
3791
3792 const SourceManager &SM = Ctx.getSourceManager();
3793 const LangOptions &LangOpts = Ctx.getLangOpts();
3794 const unsigned NumParms = FD->getNumParams();
3795 std::vector<std::string> NewTysTexts(NumParms);
3796 std::vector<bool> ParmsMask(NumParms, false);
3797 bool AtLeastOneParmToFix = false;
3798
3799 for (unsigned i = 0; i < NumParms; i++) {
3800 const ParmVarDecl *PVD = FD->getParamDecl(i);
3801
3803 continue;
3804 if (S.lookup(PVD) != FixitStrategy::Kind::Span)
3805 // Not supported, not suppose to happen:
3806 return std::nullopt;
3807
3808 std::optional<Qualifiers> PteTyQuals = std::nullopt;
3809 std::optional<std::string> PteTyText =
3810 getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
3811
3812 if (!PteTyText)
3813 // something wrong in obtaining the text of the pointee type, give up
3814 return std::nullopt;
3815 // FIXME: whether we should create std::span type depends on the
3816 // FixitStrategy.
3817 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
3818 ParmsMask[i] = true;
3819 AtLeastOneParmToFix = true;
3820 }
3821 if (!AtLeastOneParmToFix)
3822 // No need to create function overloads:
3823 return {};
3824 // FIXME Respect indentation of the original code.
3825
3826 // A lambda that creates the text representation of a function declaration
3827 // with the new type signatures:
3828 const auto NewOverloadSignatureCreator =
3829 [&SM, &LangOpts, &NewTysTexts,
3830 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3831 std::stringstream SS;
3832
3833 SS << ";";
3834 SS << getEndOfLine().str();
3835 // Append: ret-type func-name "("
3836 if (auto Prefix = getRangeText(
3837 SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
3838 SM, LangOpts))
3839 SS << Prefix->str();
3840 else
3841 return std::nullopt; // give up
3842 // Append: parameter-type-list
3843 const unsigned NumParms = FD->getNumParams();
3844
3845 for (unsigned i = 0; i < NumParms; i++) {
3846 const ParmVarDecl *Parm = FD->getParamDecl(i);
3847
3848 if (Parm->isImplicit())
3849 continue;
3850 if (ParmsMask[i]) {
3851 // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
3852 // new type:
3853 SS << NewTysTexts[i];
3854 // print parameter name if provided:
3855 if (IdentifierInfo *II = Parm->getIdentifier())
3856 SS << ' ' << II->getName().str();
3857 } else if (auto ParmTypeText =
3858 getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts),
3859 SM, LangOpts)) {
3860 // print the whole `Parm` without modification:
3861 SS << ParmTypeText->str();
3862 } else
3863 return std::nullopt; // something wrong, give up
3864 if (i != NumParms - 1)
3865 SS << ", ";
3866 }
3867 SS << ")";
3868 return SS.str();
3869 };
3870
3871 // A lambda that creates the text representation of a function definition with
3872 // the original signature:
3873 const auto OldOverloadDefCreator =
3874 [&Handler, &SM, &LangOpts, &NewTysTexts,
3875 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
3876 std::stringstream SS;
3877
3878 SS << getEndOfLine().str();
3879 // Append: attr-name ret-type func-name "(" param-list ")" "{"
3880 if (auto FDPrefix = getRangeText(
3881 SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
3882 LangOpts))
3883 SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
3884 << FDPrefix->str() << "{";
3885 else
3886 return std::nullopt;
3887 // Append: "return" func-name "("
3888 if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
3889 SS << "return " << FunQualName->str() << "(";
3890 else
3891 return std::nullopt;
3892
3893 // Append: arg-list
3894 const unsigned NumParms = FD->getNumParams();
3895 for (unsigned i = 0; i < NumParms; i++) {
3896 const ParmVarDecl *Parm = FD->getParamDecl(i);
3897
3898 if (Parm->isImplicit())
3899 continue;
3900 // FIXME: If a parameter has no name, it is unused in the
3901 // definition. So we could just leave it as it is.
3902 if (!Parm->getIdentifier())
3903 // If a parameter of a function definition has no name:
3904 return std::nullopt;
3905 if (ParmsMask[i])
3906 // This is our spanified paramter!
3907 SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
3908 << ", " << getUserFillPlaceHolder("size") << ")";
3909 else
3910 SS << Parm->getIdentifier()->getName().str();
3911 if (i != NumParms - 1)
3912 SS << ", ";
3913 }
3914 // finish call and the body
3915 SS << ");}" << getEndOfLine().str();
3916 // FIXME: 80-char line formatting?
3917 return SS.str();
3918 };
3919
3920 FixItList FixIts{};
3921 for (FunctionDecl *FReDecl : FD->redecls()) {
3922 std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
3923
3924 if (!Loc)
3925 return {};
3926 if (FReDecl->isThisDeclarationADefinition()) {
3927 assert(FReDecl == FD && "inconsistent function definition");
3928 // Inserts a definition with the old signature to the end of
3929 // `FReDecl`:
3930 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
3931 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
3932 else
3933 return {}; // give up
3934 } else {
3935 // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
3936 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
3937 FixIts.emplace_back(FixItHint::CreateInsertion(
3938 FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
3939 FReDecl->getBeginLoc(), " ")));
3940 }
3941 // Inserts a declaration with the new signature to the end of `FReDecl`:
3942 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
3943 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
3944 else
3945 return {};
3946 }
3947 }
3948 return FixIts;
3949}
3950
3951// To fix a `ParmVarDecl` to be of `std::span` type.
3952static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
3953 UnsafeBufferUsageHandler &Handler) {
3955 DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
3956 return {};
3957 }
3958 if (PVD->hasDefaultArg()) {
3959 // FIXME: generate fix-its for default values:
3960 DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
3961 return {};
3962 }
3963
3964 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
3965 std::optional<std::string> PteTyText = getPointeeTypeText(
3966 PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
3967
3968 if (!PteTyText) {
3969 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
3970 return {};
3971 }
3972
3973 std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
3974
3975 if (!PVDNameText) {
3976 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
3977 return {};
3978 }
3979
3980 std::stringstream SS;
3981 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
3982
3983 if (PteTyQualifiers)
3984 // Append qualifiers if they exist:
3985 SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
3986 else
3987 SS << getSpanTypeText(*PteTyText);
3988 // Append qualifiers to the type of the parameter:
3989 if (PVD->getType().hasQualifiers())
3990 SS << ' ' << PVD->getType().getQualifiers().getAsString();
3991 // Append parameter's name:
3992 SS << ' ' << PVDNameText->str();
3993 // Add replacement fix-it:
3994 return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
3995}
3996
3997static FixItList fixVariableWithSpan(const VarDecl *VD,
3998 const DeclUseTracker &Tracker,
3999 ASTContext &Ctx,
4000 UnsafeBufferUsageHandler &Handler) {
4001 const DeclStmt *DS = Tracker.lookupDecl(VD);
4002 if (!DS) {
4004 " : variables declared this way not implemented yet");
4005 return {};
4006 }
4007 if (!DS->isSingleDecl()) {
4008 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
4009 DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
4010 return {};
4011 }
4012 // Currently DS is an unused variable but we'll need it when
4013 // non-single decls are implemented, where the pointee type name
4014 // and the '*' are spread around the place.
4015 (void)DS;
4016
4017 // FIXME: handle cases where DS has multiple declarations
4018 return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
4019}
4020
4021static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
4022 UnsafeBufferUsageHandler &Handler) {
4023 FixItList FixIts{};
4024
4025 // Note: the code below expects the declaration to not use any type sugar like
4026 // typedef.
4027 if (auto CAT = Ctx.getAsConstantArrayType(D->getType())) {
4028 const QualType &ArrayEltT = CAT->getElementType();
4029 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
4030 // FIXME: support multi-dimensional arrays
4031 if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType()))
4032 return {};
4033
4035
4036 // Get the spelling of the element type as written in the source file
4037 // (including macros, etc.).
4038 auto MaybeElemTypeTxt =
4040 Ctx.getLangOpts());
4041 if (!MaybeElemTypeTxt)
4042 return {};
4043 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
4044
4045 // Find the '[' token.
4046 std::optional<Token> NextTok = Lexer::findNextToken(
4048 while (NextTok && !NextTok->is(tok::l_square) &&
4049 NextTok->getLocation() <= D->getSourceRange().getEnd())
4050 NextTok = Lexer::findNextToken(NextTok->getLocation(),
4051 Ctx.getSourceManager(), Ctx.getLangOpts());
4052 if (!NextTok)
4053 return {};
4054 const SourceLocation LSqBracketLoc = NextTok->getLocation();
4055
4056 // Get the spelling of the array size as written in the source file
4057 // (including macros, etc.).
4058 auto MaybeArraySizeTxt = getRangeText(
4059 {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
4060 Ctx.getSourceManager(), Ctx.getLangOpts());
4061 if (!MaybeArraySizeTxt)
4062 return {};
4063 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
4064 if (ArraySizeTxt.empty()) {
4065 // FIXME: Support array size getting determined from the initializer.
4066 // Examples:
4067 // int arr1[] = {0, 1, 2};
4068 // int arr2{3, 4, 5};
4069 // We might be able to preserve the non-specified size with `auto` and
4070 // `std::to_array`:
4071 // auto arr1 = std::to_array<int>({0, 1, 2});
4072 return {};
4073 }
4074
4075 std::optional<StringRef> IdentText =
4077
4078 if (!IdentText) {
4079 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
4080 return {};
4081 }
4082
4083 SmallString<32> Replacement;
4084 llvm::raw_svector_ostream OS(Replacement);
4085 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
4086 << IdentText->str();
4087
4088 FixIts.push_back(FixItHint::CreateReplacement(
4089 SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));
4090 }
4091
4092 return FixIts;
4093}
4094
4095static FixItList fixVariableWithArray(const VarDecl *VD,
4096 const DeclUseTracker &Tracker,
4097 const ASTContext &Ctx,
4098 UnsafeBufferUsageHandler &Handler) {
4099 const DeclStmt *DS = Tracker.lookupDecl(VD);
4100 assert(DS && "Fixing non-local variables not implemented yet!");
4101 if (!DS->isSingleDecl()) {
4102 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
4103 return {};
4104 }
4105 // Currently DS is an unused variable but we'll need it when
4106 // non-single decls are implemented, where the pointee type name
4107 // and the '*' are spread around the place.
4108 (void)DS;
4109
4110 // FIXME: handle cases where DS has multiple declarations
4111 return fixVarDeclWithArray(VD, Ctx, Handler);
4112}
4113
4114// TODO: we should be consistent to use `std::nullopt` to represent no-fix due
4115// to any unexpected problem.
4116static FixItList
4118 /* The function decl under analysis */ const Decl *D,
4119 const DeclUseTracker &Tracker, ASTContext &Ctx,
4120 UnsafeBufferUsageHandler &Handler) {
4121 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
4122 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
4123 if (!FD || FD != D) {
4124 // `FD != D` means that `PVD` belongs to a function that is not being
4125 // analyzed currently. Thus `FD` may not be complete.
4126 DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
4127 return {};
4128 }
4129
4130 // TODO If function has a try block we can't change params unless we check
4131 // also its catch block for their use.
4132 // FIXME We might support static class methods, some select methods,
4133 // operators and possibly lamdas.
4134 if (FD->isMain() || FD->isConstexpr() ||
4135 FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
4136 FD->isVariadic() ||
4137 // also covers call-operator of lamdas
4138 isa<CXXMethodDecl>(FD) ||
4139 // skip when the function body is a try-block
4140 (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
4141 FD->isOverloadedOperator()) {
4142 DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
4143 return {}; // TODO test all these cases
4144 }
4145 }
4146
4147 switch (K) {
4149 if (VD->getType()->isPointerType()) {
4150 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
4151 return fixParamWithSpan(PVD, Ctx, Handler);
4152
4153 if (VD->isLocalVarDecl())
4154 return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
4155 }
4156 DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
4157 return {};
4158 }
4160 if (VD->isLocalVarDecl() && Ctx.getAsConstantArrayType(VD->getType()))
4161 return fixVariableWithArray(VD, Tracker, Ctx, Handler);
4162
4163 DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
4164 return {};
4165 }
4168 llvm_unreachable("FixitStrategy not implemented yet!");
4170 llvm_unreachable("Invalid strategy!");
4171 }
4172 llvm_unreachable("Unknown strategy!");
4173}
4174
4175// Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
4176// `RemoveRange` of 'h' overlaps with a macro use.
4177static bool overlapWithMacro(const FixItList &FixIts) {
4178 // FIXME: For now we only check if the range (or the first token) is (part of)
4179 // a macro expansion. Ideally, we want to check for all tokens in the range.
4180 return llvm::any_of(FixIts, [](const FixItHint &Hint) {
4181 auto Range = Hint.RemoveRange;
4182 if (Range.getBegin().isMacroID() || Range.getEnd().isMacroID())
4183 // If the range (or the first token) is (part of) a macro expansion:
4184 return true;
4185 return false;
4186 });
4187}
4188
4189// Returns true iff `VD` is a parameter of the declaration `D`:
4190static bool isParameterOf(const VarDecl *VD, const Decl *D) {
4191 return isa<ParmVarDecl>(VD) &&
4192 VD->getDeclContext() == dyn_cast<DeclContext>(D);
4193}
4194
4195// Erases variables in `FixItsForVariable`, if such a variable has an unfixable
4196// group mate. A variable `v` is unfixable iff `FixItsForVariable` does not
4197// contain `v`.
4199 std::map<const VarDecl *, FixItList> &FixItsForVariable,
4200 const VariableGroupsManager &VarGrpMgr) {
4201 // Variables will be removed from `FixItsForVariable`:
4203
4204 for (const auto &[VD, Ignore] : FixItsForVariable) {
4205 VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
4206 if (llvm::any_of(Grp,
4207 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
4208 return !FixItsForVariable.count(GrpMember);
4209 })) {
4210 // At least one group member cannot be fixed, so we have to erase the
4211 // whole group:
4212 for (const VarDecl *Member : Grp)
4213 ToErase.push_back(Member);
4214 }
4215 }
4216 for (auto *VarToErase : ToErase)
4217 FixItsForVariable.erase(VarToErase);
4218}
4219
4220// Returns the fix-its that create bounds-safe function overloads for the
4221// function `D`, if `D`'s parameters will be changed to safe-types through
4222// fix-its in `FixItsForVariable`.
4223//
4224// NOTE: In case `D`'s parameters will be changed but bounds-safe function
4225// overloads cannot created, the whole group that contains the parameters will
4226// be erased from `FixItsForVariable`.
4228 std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
4229 const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
4230 const FixitStrategy &S, ASTContext &Ctx,
4231 UnsafeBufferUsageHandler &Handler) {
4232 FixItList FixItsSharedByParms{};
4233
4234 std::optional<FixItList> OverloadFixes =
4235 createOverloadsForFixedParams(S, FD, Ctx, Handler);
4236
4237 if (OverloadFixes) {
4238 FixItsSharedByParms.append(*OverloadFixes);
4239 } else {
4240 // Something wrong in generating `OverloadFixes`, need to remove the
4241 // whole group, where parameters are in, from `FixItsForVariable` (Note
4242 // that all parameters should be in the same group):
4243 for (auto *Member : VarGrpMgr.getGroupOfParms())
4244 FixItsForVariable.erase(Member);
4245 }
4246 return FixItsSharedByParms;
4247}
4248
4249// Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
4250static std::map<const VarDecl *, FixItList>
4251getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S,
4252 ASTContext &Ctx,
4253 /* The function decl under analysis */ const Decl *D,
4254 const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
4255 const VariableGroupsManager &VarGrpMgr) {
4256 // `FixItsForVariable` will map each variable to a set of fix-its directly
4257 // associated to the variable itself. Fix-its of distinct variables in
4258 // `FixItsForVariable` are disjoint.
4259 std::map<const VarDecl *, FixItList> FixItsForVariable;
4260
4261 // Populate `FixItsForVariable` with fix-its directly associated with each
4262 // variable. Fix-its directly associated to a variable 'v' are the ones
4263 // produced by the `FixableGadget`s whose claimed variable is 'v'.
4264 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
4265 FixItsForVariable[VD] =
4266 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
4267 // If we fail to produce Fix-It for the declaration we have to skip the
4268 // variable entirely.
4269 if (FixItsForVariable[VD].empty()) {
4270 FixItsForVariable.erase(VD);
4271 continue;
4272 }
4273 for (const auto &F : Fixables) {
4274 std::optional<FixItList> Fixits = F->getFixits(S);
4275
4276 if (Fixits) {
4277 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
4278 Fixits->begin(), Fixits->end());
4279 continue;
4280 }
4281#ifndef NDEBUG
4282 Handler.addDebugNoteForVar(
4283 VD, F->getSourceLoc(),
4284 ("gadget '" + F->getDebugName() + "' refused to produce a fix")
4285 .str());
4286#endif
4287 FixItsForVariable.erase(VD);
4288 break;
4289 }
4290 }
4291
4292 // `FixItsForVariable` now contains only variables that can be
4293 // fixed. A variable can be fixed if its declaration and all Fixables
4294 // associated to it can all be fixed.
4295
4296 // To further remove from `FixItsForVariable` variables whose group mates
4297 // cannot be fixed...
4298 eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
4299 // Now `FixItsForVariable` gets further reduced: a variable is in
4300 // `FixItsForVariable` iff it can be fixed and all its group mates can be
4301 // fixed.
4302
4303 // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
4304 // That is, when fixing multiple parameters in one step, these fix-its will
4305 // be applied only once (instead of being applied per parameter).
4306 FixItList FixItsSharedByParms{};
4307
4308 if (auto *FD = dyn_cast<FunctionDecl>(D))
4309 FixItsSharedByParms = createFunctionOverloadsForParms(
4310 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
4311
4312 // The map that maps each variable `v` to fix-its for the whole group where
4313 // `v` is in:
4314 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
4315 FixItsForVariable};
4316
4317 for (auto &[Var, Ignore] : FixItsForVariable) {
4318 bool AnyParm = false;
4319 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
4320
4321 for (const VarDecl *GrpMate : VarGroupForVD) {
4322 if (Var == GrpMate)
4323 continue;
4324 if (FixItsForVariable.count(GrpMate))
4325 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
4326 }
4327 if (AnyParm) {
4328 // This assertion should never fail. Otherwise we have a bug.
4329 assert(!FixItsSharedByParms.empty() &&
4330 "Should not try to fix a parameter that does not belong to a "
4331 "FunctionDecl");
4332 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
4333 }
4334 }
4335 // Fix-its that will be applied in one step shall NOT:
4336 // 1. overlap with macros or/and templates; or
4337 // 2. conflict with each other.
4338 // Otherwise, the fix-its will be dropped.
4339 for (auto Iter = FinalFixItsForVariable.begin();
4340 Iter != FinalFixItsForVariable.end();)
4341 if (overlapWithMacro(Iter->second) ||
4342 clang::internal::anyConflict(Iter->second, Ctx.getSourceManager())) {
4343 Iter = FinalFixItsForVariable.erase(Iter);
4344 } else
4345 Iter++;
4346 return FinalFixItsForVariable;
4347}
4348
4349template <typename VarDeclIterTy>
4350static FixitStrategy
4351getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
4352 FixitStrategy S;
4353 for (const VarDecl *VD : UnsafeVars) {
4356 else
4358 }
4359 return S;
4360}
4361
4362// Manages variable groups:
4364 const std::vector<VarGrpTy> &Groups;
4365 const std::map<const VarDecl *, unsigned> &VarGrpMap;
4366 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
4367
4368public:
4370 const std::vector<VarGrpTy> &Groups,
4371 const std::map<const VarDecl *, unsigned> &VarGrpMap,
4372 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
4373 : Groups(Groups), VarGrpMap(VarGrpMap),
4374 GrpsUnionForParms(GrpsUnionForParms) {}
4375
4376 VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
4377 if (GrpsUnionForParms.contains(Var)) {
4378 if (HasParm)
4379 *HasParm = true;
4380 return GrpsUnionForParms.getArrayRef();
4381 }
4382 if (HasParm)
4383 *HasParm = false;
4384
4385 auto It = VarGrpMap.find(Var);
4386
4387 if (It == VarGrpMap.end())
4388 return {};
4389 return Groups[It->second];
4390 }
4391
4392 VarGrpRef getGroupOfParms() const override {
4393 return GrpsUnionForParms.getArrayRef();
4394 }
4395};
4396
4397static void applyGadgets(const Decl *D, FixableGadgetList FixableGadgets,
4398 WarningGadgetList WarningGadgets,
4399 DeclUseTracker Tracker,
4400 UnsafeBufferUsageHandler &Handler,
4401 bool EmitSuggestions) {
4402 if (!EmitSuggestions) {
4403 // Our job is very easy without suggestions. Just warn about
4404 // every problematic operation and consider it done. No need to deal
4405 // with fixable gadgets, no need to group operations by variable.
4406 for (const auto &G : WarningGadgets) {
4407 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4408 D->getASTContext());
4409 }
4410
4411 // This return guarantees that most of the machine doesn't run when
4412 // suggestions aren't requested.
4413 assert(FixableGadgets.empty() &&
4414 "Fixable gadgets found but suggestions not requested!");
4415 return;
4416 }
4417
4418 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4419 // function under the analysis. No need to fix any Fixables.
4420 if (!WarningGadgets.empty()) {
4421 // Gadgets "claim" variables they're responsible for. Once this loop
4422 // finishes, the tracker will only track DREs that weren't claimed by any
4423 // gadgets, i.e. not understood by the analysis.
4424 for (const auto &G : FixableGadgets) {
4425 for (const auto *DRE : G->getClaimedVarUseSites()) {
4426 Tracker.claimUse(DRE);
4427 }
4428 }
4429 }
4430
4431 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
4432 // function under the analysis. Thus, it early returns here as there is
4433 // nothing needs to be fixed.
4434 //
4435 // Note this claim is based on the assumption that there is no unsafe
4436 // variable whose declaration is invisible from the analyzing function.
4437 // Otherwise, we need to consider if the uses of those unsafe varuables needs
4438 // fix.
4439 // So far, we are not fixing any global variables or class members. And,
4440 // lambdas will be analyzed along with the enclosing function. So this early
4441 // return is correct for now.
4442 if (WarningGadgets.empty())
4443 return;
4444
4445 WarningGadgetSets UnsafeOps =
4446 groupWarningGadgetsByVar(std::move(WarningGadgets));
4447 FixableGadgetSets FixablesForAllVars =
4448 groupFixablesByVar(std::move(FixableGadgets));
4449
4450 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
4451
4452 // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
4453 for (auto it = FixablesForAllVars.byVar.cbegin();
4454 it != FixablesForAllVars.byVar.cend();) {
4455 // FIXME: need to deal with global variables later
4456 if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
4457#ifndef NDEBUG
4458 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4459 ("failed to produce fixit for '" +
4460 it->first->getNameAsString() +
4461 "' : neither local nor a parameter"));
4462#endif
4463 it = FixablesForAllVars.byVar.erase(it);
4464 } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
4465#ifndef NDEBUG
4466 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4467 ("failed to produce fixit for '" +
4468 it->first->getNameAsString() +
4469 "' : has a reference type"));
4470#endif
4471 it = FixablesForAllVars.byVar.erase(it);
4472 } else if (Tracker.hasUnclaimedUses(it->first)) {
4473 it = FixablesForAllVars.byVar.erase(it);
4474 } else if (it->first->isInitCapture()) {
4475#ifndef NDEBUG
4476 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
4477 ("failed to produce fixit for '" +
4478 it->first->getNameAsString() +
4479 "' : init capture"));
4480#endif
4481 it = FixablesForAllVars.byVar.erase(it);
4482 } else {
4483 ++it;
4484 }
4485 }
4486
4487#ifndef NDEBUG
4488 for (const auto &it : UnsafeOps.byVar) {
4489 const VarDecl *const UnsafeVD = it.first;
4490 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
4491 if (UnclaimedDREs.empty())
4492 continue;
4493 const auto UnfixedVDName = UnsafeVD->getNameAsString();
4494 for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
4495 std::string UnclaimedUseTrace =
4496 getDREAncestorString(UnclaimedDRE, D->getASTContext());
4497
4498 Handler.addDebugNoteForVar(
4499 UnsafeVD, UnclaimedDRE->getBeginLoc(),
4500 ("failed to produce fixit for '" + UnfixedVDName +
4501 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
4502 UnclaimedUseTrace));
4503 }
4504 }
4505#endif
4506
4507 // Fixpoint iteration for pointer assignments
4508 using DepMapTy =
4509 llvm::DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
4510 DepMapTy DependenciesMap{};
4511 DepMapTy PtrAssignmentGraph{};
4512
4513 for (const auto &it : FixablesForAllVars.byVar) {
4514 for (const FixableGadget *fixable : it.second) {
4515 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
4516 fixable->getStrategyImplications();
4517 if (ImplPair) {
4518 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
4519 PtrAssignmentGraph[Impl.first].insert(Impl.second);
4520 }
4521 }
4522 }
4523
4524 /*
4525 The following code does a BFS traversal of the `PtrAssignmentGraph`
4526 considering all unsafe vars as starting nodes and constructs an undirected
4527 graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
4528 elimiates all variables that are unreachable from any unsafe var. In other
4529 words, this removes all dependencies that don't include any unsafe variable
4530 and consequently don't need any fixit generation.
4531 Note: A careful reader would observe that the code traverses
4532 `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
4533 `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
4534 achieve the same result but the one used here dramatically cuts the
4535 amount of hoops the second part of the algorithm needs to jump, given that
4536 a lot of these connections become "direct". The reader is advised not to
4537 imagine how the graph is transformed because of using `Var` instead of
4538 `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
4539 and think about why it's equivalent later.
4540 */
4541 std::set<const VarDecl *> VisitedVarsDirected{};
4542 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4543 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
4544
4545 std::queue<const VarDecl *> QueueDirected{};
4546 QueueDirected.push(Var);
4547 while (!QueueDirected.empty()) {
4548 const VarDecl *CurrentVar = QueueDirected.front();
4549 QueueDirected.pop();
4550 VisitedVarsDirected.insert(CurrentVar);
4551 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
4552 for (const VarDecl *Adj : AdjacentNodes) {
4553 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
4554 QueueDirected.push(Adj);
4555 }
4556 DependenciesMap[Var].insert(Adj);
4557 DependenciesMap[Adj].insert(Var);
4558 }
4559 }
4560 }
4561 }
4562
4563 // `Groups` stores the set of Connected Components in the graph.
4564 std::vector<VarGrpTy> Groups;
4565 // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
4566 // variables belong to. Group indexes refer to the elements in `Groups`.
4567 // `VarGrpMap` is complete in that every variable that needs fix is in it.
4568 std::map<const VarDecl *, unsigned> VarGrpMap;
4569 // The union group over the ones in "Groups" that contain parameters of `D`:
4570 llvm::SetVector<const VarDecl *>
4571 GrpsUnionForParms; // these variables need to be fixed in one step
4572
4573 // Group Connected Components for Unsafe Vars
4574 // (Dependencies based on pointer assignments)
4575 std::set<const VarDecl *> VisitedVars{};
4576 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
4577 if (VisitedVars.find(Var) == VisitedVars.end()) {
4578 VarGrpTy &VarGroup = Groups.emplace_back();
4579 std::queue<const VarDecl *> Queue{};
4580
4581 Queue.push(Var);
4582 while (!Queue.empty()) {
4583 const VarDecl *CurrentVar = Queue.front();
4584 Queue.pop();
4585 VisitedVars.insert(CurrentVar);
4586 VarGroup.push_back(CurrentVar);
4587 auto AdjacentNodes = DependenciesMap[CurrentVar];
4588 for (const VarDecl *Adj : AdjacentNodes) {
4589 if (VisitedVars.find(Adj) == VisitedVars.end()) {
4590 Queue.push(Adj);
4591 }
4592 }
4593 }
4594
4595 bool HasParm = false;
4596 unsigned GrpIdx = Groups.size() - 1;
4597
4598 for (const VarDecl *V : VarGroup) {
4599 VarGrpMap[V] = GrpIdx;
4600 if (!HasParm && isParameterOf(V, D))
4601 HasParm = true;
4602 }
4603 if (HasParm)
4604 GrpsUnionForParms.insert_range(VarGroup);
4605 }
4606 }
4607
4608 // Remove a `FixableGadget` if the associated variable is not in the graph
4609 // computed above. We do not want to generate fix-its for such variables,
4610 // since they are neither warned nor reachable from a warned one.
4611 //
4612 // Note a variable is not warned if it is not directly used in any unsafe
4613 // operation. A variable `v` is NOT reachable from an unsafe variable, if it
4614 // does not exist another variable `u` such that `u` is warned and fixing `u`
4615 // (transitively) implicates fixing `v`.
4616 //
4617 // For example,
4618 // ```
4619 // void f(int * p) {
4620 // int * a = p; *p = 0;
4621 // }
4622 // ```
4623 // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
4624 // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of
4625 // the function above, `p` becomes reachable from a warned variable.
4626 for (auto I = FixablesForAllVars.byVar.begin();
4627 I != FixablesForAllVars.byVar.end();) {
4628 // Note `VisitedVars` contain all the variables in the graph:
4629 if (!VisitedVars.count((*I).first)) {
4630 // no such var in graph:
4631 I = FixablesForAllVars.byVar.erase(I);
4632 } else
4633 ++I;
4634 }
4635
4636 // We assign strategies to variables that are 1) in the graph and 2) can be
4637 // fixed. Other variables have the default "Won't fix" strategy.
4638 FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
4639 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
4640 // If a warned variable has no "Fixable", it is considered unfixable:
4641 return FixablesForAllVars.byVar.count(V);
4642 }));
4643 VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
4644
4645 if (isa<NamedDecl>(D))
4646 // The only case where `D` is not a `NamedDecl` is when `D` is a
4647 // `BlockDecl`. Let's not fix variables in blocks for now
4648 FixItsForVariableGroup =
4649 getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
4650 Tracker, Handler, VarGrpMgr);
4651
4652 for (const auto &G : UnsafeOps.noVar) {
4653 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
4654 D->getASTContext());
4655 }
4656
4657 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
4658 auto FixItsIt = FixItsForVariableGroup.find(VD);
4659 Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
4660 FixItsIt != FixItsForVariableGroup.end()
4661 ? std::move(FixItsIt->second)
4662 : FixItList{},
4663 D, NaiveStrategy);
4664 for (const auto &G : WarningGadgets) {
4665 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
4666 D->getASTContext());
4667 }
4668 }
4669}
4670
4672 UnsafeBufferUsageHandler &Handler,
4673 bool EmitSuggestions) {
4674#ifndef NDEBUG
4675 Handler.clearDebugNotes();
4676#endif
4677
4678 assert(D);
4679
4680 SmallVector<Stmt *> Stmts;
4681
4682 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
4683 // Consteval functions are free of UB by the spec, so we don't need to
4684 // visit them or produce diagnostics.
4685 if (FD->isConsteval())
4686 return;
4687 // We do not want to visit a Lambda expression defined inside a method
4688 // independently. Instead, it should be visited along with the outer method.
4689 // FIXME: do we want to do the same thing for `BlockDecl`s?
4690 if (const auto *MD = dyn_cast<CXXMethodDecl>(D)) {
4691 if (MD->getParent()->isLambda() && MD->getParent()->isLocalClass())
4692 return;
4693 }
4694
4695 for (FunctionDecl *FReDecl : FD->redecls()) {
4696 if (FReDecl->isExternC()) {
4697 // Do not emit fixit suggestions for functions declared in an
4698 // extern "C" block.
4699 EmitSuggestions = false;
4700 break;
4701 }
4702 }
4703
4704 Stmts.push_back(FD->getBody());
4705
4706 if (const auto *ID = dyn_cast<CXXConstructorDecl>(D)) {
4707 for (const CXXCtorInitializer *CI : ID->inits()) {
4708 Stmts.push_back(CI->getInit());
4709 }
4710 }
4711 } else if (isa<BlockDecl>(D) || isa<ObjCMethodDecl>(D)) {
4712 Stmts.push_back(D->getBody());
4713 }
4714
4715 assert(!Stmts.empty());
4716
4717 FixableGadgetList FixableGadgets;
4718 WarningGadgetList WarningGadgets;
4719 DeclUseTracker Tracker;
4720 for (Stmt *S : Stmts) {
4721 findGadgets(S, D->getASTContext(), Handler, EmitSuggestions, FixableGadgets,
4722 WarningGadgets, Tracker);
4723 }
4724 applyGadgets(D, std::move(FixableGadgets), std::move(WarningGadgets),
4725 std::move(Tracker), Handler, EmitSuggestions);
4726}
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:489
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:2375
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:2135
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:1331
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:337
unsigned getASTIndex() const
Get the parameter index as it would normally be encoded at the AST level of representation: zero-orig...
Definition Attr.h:356
Represents a parameter to a function.
Definition Decl.h:1790
bool hasDefaultArg() const
Determines whether this parameter has a default argument, either parsed or not.
Definition Decl.cpp: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:8438
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:8389
QualType getCanonicalType() const
Definition TypeBase.h:8401
bool isConstQualified() const
Determine whether this type is const-qualified.
Definition TypeBase.h:8422
std::string getAsString() const
Represents a struct/union/class.
Definition Decl.h:4324
Encodes a location in the source.
bool isValid() const
Return true if this is a valid SourceLocation object.
SourceLocation getLocWithOffset(IntTy Offset) const
Return a source location with the specified offset from this SourceLocation.
This class handles loading and caching of source files into memory.
A trivial tuple used to represent a source range.
SourceLocation getEnd() const
Stmt - This represents one statement.
Definition Stmt.h:86
StmtClass getStmtClass() const
Definition Stmt.h:1485
SourceRange getSourceRange() const LLVM_READONLY
SourceLocation tokens are not useful in isolation - they are low level value objects created/interpre...
Definition Stmt.cpp: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:226
A template argument list.
unsigned size() const
Retrieve the number of template arguments in this template argument list.
Represents a template argument.
QualType getAsType() const
Retrieve the type for a type template argument.
@ Type
The template argument is a type.
ArgKind getKind() const
Return the kind of stored template argument.
The base class of the type hierarchy.
Definition TypeBase.h: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:8685
bool isCharType() const
Definition Type.cpp:2157
bool isPointerType() const
Definition TypeBase.h:8586
bool isIntegerType() const
isIntegerType() does not include complex integers (a GCC extension).
Definition TypeBase.h:8996
const T * castAs() const
Member-template castAs<specific type>.
Definition TypeBase.h:9246
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:8594
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.