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