clang 19.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
11#include "clang/AST/Decl.h"
12#include "clang/AST/Expr.h"
14#include "clang/AST/Stmt.h"
20#include "clang/Lex/Lexer.h"
22#include "llvm/ADT/APSInt.h"
23#include "llvm/ADT/SmallVector.h"
24#include "llvm/ADT/StringRef.h"
25#include "llvm/Support/Casting.h"
26#include <memory>
27#include <optional>
28#include <queue>
29#include <sstream>
30
31using namespace llvm;
32using namespace clang;
33using namespace ast_matchers;
34
35#ifndef NDEBUG
36namespace {
37class StmtDebugPrinter
38 : public ConstStmtVisitor<StmtDebugPrinter, std::string> {
39public:
40 std::string VisitStmt(const Stmt *S) { return S->getStmtClassName(); }
41
42 std::string VisitBinaryOperator(const BinaryOperator *BO) {
43 return "BinaryOperator(" + BO->getOpcodeStr().str() + ")";
44 }
45
46 std::string VisitUnaryOperator(const UnaryOperator *UO) {
47 return "UnaryOperator(" + UO->getOpcodeStr(UO->getOpcode()).str() + ")";
48 }
49
50 std::string VisitImplicitCastExpr(const ImplicitCastExpr *ICE) {
51 return "ImplicitCastExpr(" + std::string(ICE->getCastKindName()) + ")";
52 }
53};
54
55// Returns a string of ancestor `Stmt`s of the given `DRE` in such a form:
56// "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...".
57static std::string getDREAncestorString(const DeclRefExpr *DRE,
58 ASTContext &Ctx) {
59 std::stringstream SS;
60 const Stmt *St = DRE;
61 StmtDebugPrinter StmtPriner;
62
63 do {
64 SS << StmtPriner.Visit(St);
65
66 DynTypedNodeList StParents = Ctx.getParents(*St);
67
68 if (StParents.size() > 1)
69 return "unavailable due to multiple parents";
70 if (StParents.size() == 0)
71 break;
72 St = StParents.begin()->get<Stmt>();
73 if (St)
74 SS << " ==> ";
75 } while (St);
76 return SS.str();
77}
78} // namespace
79#endif /* NDEBUG */
80
81namespace clang::ast_matchers {
82// A `RecursiveASTVisitor` that traverses all descendants of a given node "n"
83// except for those belonging to a different callable of "n".
85 : public RecursiveASTVisitor<MatchDescendantVisitor> {
86public:
88
89 // Creates an AST visitor that matches `Matcher` on all
90 // descendants of a given node "n" except for the ones
91 // belonging to a different callable of "n".
92 MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher,
93 internal::ASTMatchFinder *Finder,
94 internal::BoundNodesTreeBuilder *Builder,
95 internal::ASTMatchFinder::BindKind Bind,
96 const bool ignoreUnevaluatedContext)
97 : Matcher(Matcher), Finder(Finder), Builder(Builder), Bind(Bind),
98 Matches(false), ignoreUnevaluatedContext(ignoreUnevaluatedContext) {}
99
100 // Returns true if a match is found in a subtree of `DynNode`, which belongs
101 // to the same callable of `DynNode`.
102 bool findMatch(const DynTypedNode &DynNode) {
103 Matches = false;
104 if (const Stmt *StmtNode = DynNode.get<Stmt>()) {
105 TraverseStmt(const_cast<Stmt *>(StmtNode));
106 *Builder = ResultBindings;
107 return Matches;
108 }
109 return false;
110 }
111
112 // The following are overriding methods from the base visitor class.
113 // They are public only to allow CRTP to work. They are *not *part
114 // of the public API of this class.
115
116 // For the matchers so far used in safe buffers, we only need to match
117 // `Stmt`s. To override more as needed.
118
120 if (!Node)
121 return true;
122 if (!match(*Node))
123 return false;
124 // To skip callables:
125 if (isa<FunctionDecl, BlockDecl, ObjCMethodDecl>(Node))
126 return true;
127 // Traverse descendants
129 }
130
132 // These are unevaluated, except the result expression.
133 if (ignoreUnevaluatedContext)
134 return TraverseStmt(Node->getResultExpr());
135 return VisitorBase::TraverseGenericSelectionExpr(Node);
136 }
137
139 // Unevaluated context.
140 if (ignoreUnevaluatedContext)
141 return true;
142 return VisitorBase::TraverseUnaryExprOrTypeTraitExpr(Node);
143 }
144
146 // Unevaluated context.
147 if (ignoreUnevaluatedContext)
148 return true;
149 return VisitorBase::TraverseTypeOfExprTypeLoc(Node);
150 }
151
153 // Unevaluated context.
154 if (ignoreUnevaluatedContext)
155 return true;
156 return VisitorBase::TraverseDecltypeTypeLoc(Node);
157 }
158
160 // Unevaluated context.
161 if (ignoreUnevaluatedContext)
162 return true;
163 return VisitorBase::TraverseCXXNoexceptExpr(Node);
164 }
165
167 // Unevaluated context.
168 if (ignoreUnevaluatedContext)
169 return true;
170 return VisitorBase::TraverseCXXTypeidExpr(Node);
171 }
172
173 bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue = nullptr) {
174 if (!Node)
175 return true;
176 if (!match(*Node))
177 return false;
179 }
180
181 bool shouldVisitTemplateInstantiations() const { return true; }
183 // TODO: let's ignore implicit code for now
184 return false;
185 }
186
187private:
188 // Sets 'Matched' to true if 'Matcher' matches 'Node'
189 //
190 // Returns 'true' if traversal should continue after this function
191 // returns, i.e. if no match is found or 'Bind' is 'BK_All'.
192 template <typename T> bool match(const T &Node) {
193 internal::BoundNodesTreeBuilder RecursiveBuilder(*Builder);
194
195 if (Matcher->matches(DynTypedNode::create(Node), Finder,
196 &RecursiveBuilder)) {
197 ResultBindings.addMatch(RecursiveBuilder);
198 Matches = true;
199 if (Bind != internal::ASTMatchFinder::BK_All)
200 return false; // Abort as soon as a match is found.
201 }
202 return true;
203 }
204
205 const internal::DynTypedMatcher *const Matcher;
206 internal::ASTMatchFinder *const Finder;
207 internal::BoundNodesTreeBuilder *const Builder;
208 internal::BoundNodesTreeBuilder ResultBindings;
209 const internal::ASTMatchFinder::BindKind Bind;
210 bool Matches;
211 bool ignoreUnevaluatedContext;
212};
213
214// Because we're dealing with raw pointers, let's define what we mean by that.
215static auto hasPointerType() {
216 return hasType(hasCanonicalType(pointerType()));
217}
218
219static auto hasArrayType() { return hasType(hasCanonicalType(arrayType())); }
220
221AST_MATCHER_P(Stmt, forEachDescendantEvaluatedStmt, internal::Matcher<Stmt>,
222 innerMatcher) {
223 const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
224
225 MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All,
226 true);
227 return Visitor.findMatch(DynTypedNode::create(Node));
228}
229
230AST_MATCHER_P(Stmt, forEachDescendantStmt, internal::Matcher<Stmt>,
231 innerMatcher) {
232 const DynTypedMatcher &DTM = static_cast<DynTypedMatcher>(innerMatcher);
233
234 MatchDescendantVisitor Visitor(&DTM, Finder, Builder, ASTMatchFinder::BK_All,
235 false);
236 return Visitor.findMatch(DynTypedNode::create(Node));
237}
238
239// Matches a `Stmt` node iff the node is in a safe-buffer opt-out region
240AST_MATCHER_P(Stmt, notInSafeBufferOptOut, const UnsafeBufferUsageHandler *,
241 Handler) {
242 return !Handler->isSafeBufferOptOut(Node.getBeginLoc());
243}
244
245AST_MATCHER_P(Stmt, ignoreUnsafeBufferInContainer,
246 const UnsafeBufferUsageHandler *, Handler) {
247 return Handler->ignoreUnsafeBufferInContainer(Node.getBeginLoc());
248}
249
250AST_MATCHER_P(CastExpr, castSubExpr, internal::Matcher<Expr>, innerMatcher) {
251 return innerMatcher.matches(*Node.getSubExpr(), Finder, Builder);
252}
253
254// Matches a `UnaryOperator` whose operator is pre-increment:
256 return Node.getOpcode() == UnaryOperator::Opcode::UO_PreInc;
257}
258
259// Returns a matcher that matches any expression 'e' such that `innerMatcher`
260// matches 'e' and 'e' is in an Unspecified Lvalue Context.
261static auto isInUnspecifiedLvalueContext(internal::Matcher<Expr> innerMatcher) {
262 // clang-format off
263 return
264 expr(anyOf(
266 hasCastKind(CastKind::CK_LValueToRValue),
267 castSubExpr(innerMatcher)),
270 hasLHS(innerMatcher)
271 )
272 ));
273 // clang-format on
274}
275
276// Returns a matcher that matches any expression `e` such that `InnerMatcher`
277// matches `e` and `e` is in an Unspecified Pointer Context (UPC).
278static internal::Matcher<Stmt>
279isInUnspecifiedPointerContext(internal::Matcher<Stmt> InnerMatcher) {
280 // A UPC can be
281 // 1. an argument of a function call (except the callee has [[unsafe_...]]
282 // attribute), or
283 // 2. the operand of a pointer-to-(integer or bool) cast operation; or
284 // 3. the operand of a comparator operation; or
285 // 4. the operand of a pointer subtraction operation
286 // (i.e., computing the distance between two pointers); or ...
287
288 // clang-format off
289 auto CallArgMatcher = callExpr(
290 forEachArgumentWithParamType(
291 InnerMatcher,
292 isAnyPointer() /* array also decays to pointer type*/),
293 unless(callee(
294 functionDecl(hasAttr(attr::UnsafeBufferUsage)))));
295
296 auto CastOperandMatcher =
297 castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral),
298 hasCastKind(CastKind::CK_PointerToBoolean)),
299 castSubExpr(allOf(hasPointerType(), InnerMatcher)));
300
301 auto CompOperandMatcher =
302 binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="),
303 eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher)),
304 hasRHS(allOf(hasPointerType(), InnerMatcher))));
305
306 // A matcher that matches pointer subtractions:
307 auto PtrSubtractionMatcher =
308 binaryOperator(hasOperatorName("-"),
309 // Note that here we need both LHS and RHS to be
310 // pointer. Then the inner matcher can match any of
311 // them:
312 allOf(hasLHS(hasPointerType()),
313 hasRHS(hasPointerType())),
314 eachOf(hasLHS(InnerMatcher),
315 hasRHS(InnerMatcher)));
316 // clang-format on
317
318 return stmt(anyOf(CallArgMatcher, CastOperandMatcher, CompOperandMatcher,
319 PtrSubtractionMatcher));
320 // FIXME: any more cases? (UPC excludes the RHS of an assignment. For now we
321 // don't have to check that.)
322}
323
324// Returns a matcher that matches any expression 'e' such that `innerMatcher`
325// matches 'e' and 'e' is in an unspecified untyped context (i.e the expression
326// 'e' isn't evaluated to an RValue). For example, consider the following code:
327// int *p = new int[4];
328// int *q = new int[4];
329// if ((p = q)) {}
330// p = q;
331// The expression `p = q` in the conditional of the `if` statement
332// `if ((p = q))` is evaluated as an RValue, whereas the expression `p = q;`
333// in the assignment statement is in an untyped context.
334static internal::Matcher<Stmt>
335isInUnspecifiedUntypedContext(internal::Matcher<Stmt> InnerMatcher) {
336 // An unspecified context can be
337 // 1. A compound statement,
338 // 2. The body of an if statement
339 // 3. Body of a loop
340 auto CompStmt = compoundStmt(forEach(InnerMatcher));
341 auto IfStmtThen = ifStmt(hasThen(InnerMatcher));
342 auto IfStmtElse = ifStmt(hasElse(InnerMatcher));
343 // FIXME: Handle loop bodies.
344 return stmt(anyOf(CompStmt, IfStmtThen, IfStmtElse));
345}
346
347// Given a two-param std::span construct call, matches iff the call has the
348// following forms:
349// 1. `std::span<T>{new T[n], n}`, where `n` is a literal or a DRE
350// 2. `std::span<T>{new T, 1}`
351// 3. `std::span<T>{&var, 1}`
352// 4. `std::span<T>{a, n}`, where `a` is of an array-of-T with constant size
353// `n`
354// 5. `std::span<T>{any, 0}`
355AST_MATCHER(CXXConstructExpr, isSafeSpanTwoParamConstruct) {
356 assert(Node.getNumArgs() == 2 &&
357 "expecting a two-parameter std::span constructor");
358 const Expr *Arg0 = Node.getArg(0)->IgnoreImplicit();
359 const Expr *Arg1 = Node.getArg(1)->IgnoreImplicit();
360 auto HaveEqualConstantValues = [&Finder](const Expr *E0, const Expr *E1) {
361 if (auto E0CV = E0->getIntegerConstantExpr(Finder->getASTContext()))
362 if (auto E1CV = E1->getIntegerConstantExpr(Finder->getASTContext())) {
363 return APSInt::compareValues(*E0CV, *E1CV) == 0;
364 }
365 return false;
366 };
367 auto AreSameDRE = [](const Expr *E0, const Expr *E1) {
368 if (auto *DRE0 = dyn_cast<DeclRefExpr>(E0))
369 if (auto *DRE1 = dyn_cast<DeclRefExpr>(E1)) {
370 return DRE0->getDecl() == DRE1->getDecl();
371 }
372 return false;
373 };
374 std::optional<APSInt> Arg1CV =
375 Arg1->getIntegerConstantExpr(Finder->getASTContext());
376
377 if (Arg1CV && Arg1CV->isZero())
378 // Check form 5:
379 return true;
380 switch (Arg0->IgnoreImplicit()->getStmtClass()) {
381 case Stmt::CXXNewExprClass:
382 if (auto Size = cast<CXXNewExpr>(Arg0)->getArraySize()) {
383 // Check form 1:
384 return AreSameDRE((*Size)->IgnoreImplicit(), Arg1) ||
385 HaveEqualConstantValues(*Size, Arg1);
386 }
387 // TODO: what's placeholder type? avoid it for now.
388 if (!cast<CXXNewExpr>(Arg0)->hasPlaceholderType()) {
389 // Check form 2:
390 return Arg1CV && Arg1CV->isOne();
391 }
392 break;
393 case Stmt::UnaryOperatorClass:
394 if (cast<UnaryOperator>(Arg0)->getOpcode() ==
395 UnaryOperator::Opcode::UO_AddrOf)
396 // Check form 3:
397 return Arg1CV && Arg1CV->isOne();
398 break;
399 default:
400 break;
401 }
402
403 QualType Arg0Ty = Arg0->IgnoreImplicit()->getType();
404
405 if (Arg0Ty->isConstantArrayType()) {
406 const APSInt ConstArrSize =
407 APSInt(cast<ConstantArrayType>(Arg0Ty)->getSize());
408
409 // Check form 4:
410 return Arg1CV && APSInt::compareValues(ConstArrSize, *Arg1CV) == 0;
411 }
412 return false;
413}
414
415AST_MATCHER(ArraySubscriptExpr, isSafeArraySubscript) {
416 // FIXME: Proper solution:
417 // - refactor Sema::CheckArrayAccess
418 // - split safe/OOB/unknown decision logic from diagnostics emitting code
419 // - e. g. "Try harder to find a NamedDecl to point at in the note."
420 // already duplicated
421 // - call both from Sema and from here
422
423 const auto *BaseDRE =
424 dyn_cast<DeclRefExpr>(Node.getBase()->IgnoreParenImpCasts());
425 if (!BaseDRE)
426 return false;
427 if (!BaseDRE->getDecl())
428 return false;
429 const auto *CATy = Finder->getASTContext().getAsConstantArrayType(
430 BaseDRE->getDecl()->getType());
431 if (!CATy)
432 return false;
433
434 if (const auto *IdxLit = dyn_cast<IntegerLiteral>(Node.getIdx())) {
435 const APInt ArrIdx = IdxLit->getValue();
436 // FIXME: ArrIdx.isNegative() we could immediately emit an error as that's a
437 // bug
438 if (ArrIdx.isNonNegative() &&
439 ArrIdx.getLimitedValue() < CATy->getLimitedSize())
440 return true;
441 }
442
443 return false;
444}
445
446} // namespace clang::ast_matchers
447
448namespace {
449// Because the analysis revolves around variables and their types, we'll need to
450// track uses of variables (aka DeclRefExprs).
451using DeclUseList = SmallVector<const DeclRefExpr *, 1>;
452
453// Convenience typedef.
454using FixItList = SmallVector<FixItHint, 4>;
455} // namespace
456
457namespace {
458/// Gadget is an individual operation in the code that may be of interest to
459/// this analysis. Each (non-abstract) subclass corresponds to a specific
460/// rigid AST structure that constitutes an operation on a pointer-type object.
461/// Discovery of a gadget in the code corresponds to claiming that we understand
462/// what this part of code is doing well enough to potentially improve it.
463/// Gadgets can be warning (immediately deserving a warning) or fixable (not
464/// always deserving a warning per se, but requires our attention to identify
465/// it warrants a fixit).
466class Gadget {
467public:
468 enum class Kind {
469#define GADGET(x) x,
470#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
471 };
472
473 /// Common type of ASTMatchers used for discovering gadgets.
474 /// Useful for implementing the static matcher() methods
475 /// that are expected from all non-abstract subclasses.
476 using Matcher = decltype(stmt());
477
478 Gadget(Kind K) : K(K) {}
479
480 Kind getKind() const { return K; }
481
482#ifndef NDEBUG
483 StringRef getDebugName() const {
484 switch (K) {
485#define GADGET(x) \
486 case Kind::x: \
487 return #x;
488#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
489 }
490 llvm_unreachable("Unhandled Gadget::Kind enum");
491 }
492#endif
493
494 virtual bool isWarningGadget() const = 0;
495 // TODO remove this method from WarningGadget interface. It's only used for
496 // debug prints in FixableGadget.
497 virtual SourceLocation getSourceLoc() const = 0;
498
499 /// Returns the list of pointer-type variables on which this gadget performs
500 /// its operation. Typically, there's only one variable. This isn't a list
501 /// of all DeclRefExprs in the gadget's AST!
502 virtual DeclUseList getClaimedVarUseSites() const = 0;
503
504 virtual ~Gadget() = default;
505
506private:
507 Kind K;
508};
509
510/// Warning gadgets correspond to unsafe code patterns that warrants
511/// an immediate warning.
512class WarningGadget : public Gadget {
513public:
514 WarningGadget(Kind K) : Gadget(K) {}
515
516 static bool classof(const Gadget *G) { return G->isWarningGadget(); }
517 bool isWarningGadget() const final { return true; }
518
519 virtual void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
520 bool IsRelatedToDecl,
521 ASTContext &Ctx) const = 0;
522};
523
524/// Fixable gadgets correspond to code patterns that aren't always unsafe but
525/// need to be properly recognized in order to emit fixes. For example, if a raw
526/// pointer-type variable is replaced by a safe C++ container, every use of such
527/// variable must be carefully considered and possibly updated.
528class FixableGadget : public Gadget {
529public:
530 FixableGadget(Kind K) : Gadget(K) {}
531
532 static bool classof(const Gadget *G) { return !G->isWarningGadget(); }
533 bool isWarningGadget() const final { return false; }
534
535 /// Returns a fixit that would fix the current gadget according to
536 /// the current strategy. Returns std::nullopt if the fix cannot be produced;
537 /// returns an empty list if no fixes are necessary.
538 virtual std::optional<FixItList> getFixits(const FixitStrategy &) const {
539 return std::nullopt;
540 }
541
542 /// Returns a list of two elements where the first element is the LHS of a
543 /// pointer assignment statement and the second element is the RHS. This
544 /// two-element list represents the fact that the LHS buffer gets its bounds
545 /// information from the RHS buffer. This information will be used later to
546 /// group all those variables whose types must be modified together to prevent
547 /// type mismatches.
548 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
549 getStrategyImplications() const {
550 return std::nullopt;
551 }
552};
553
554static auto toSupportedVariable() { return to(varDecl()); }
555
556using FixableGadgetList = std::vector<std::unique_ptr<FixableGadget>>;
557using WarningGadgetList = std::vector<std::unique_ptr<WarningGadget>>;
558
559/// An increment of a pointer-type value is unsafe as it may run the pointer
560/// out of bounds.
561class IncrementGadget : public WarningGadget {
562 static constexpr const char *const OpTag = "op";
563 const UnaryOperator *Op;
564
565public:
566 IncrementGadget(const MatchFinder::MatchResult &Result)
567 : WarningGadget(Kind::Increment),
568 Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
569
570 static bool classof(const Gadget *G) {
571 return G->getKind() == Kind::Increment;
572 }
573
574 static Matcher matcher() {
575 return stmt(
576 unaryOperator(hasOperatorName("++"),
577 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
578 .bind(OpTag));
579 }
580
581 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
582 bool IsRelatedToDecl,
583 ASTContext &Ctx) const override {
584 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
585 }
586 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
587
588 DeclUseList getClaimedVarUseSites() const override {
590 if (const auto *DRE =
591 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
592 Uses.push_back(DRE);
593 }
594
595 return std::move(Uses);
596 }
597};
598
599/// A decrement of a pointer-type value is unsafe as it may run the pointer
600/// out of bounds.
601class DecrementGadget : public WarningGadget {
602 static constexpr const char *const OpTag = "op";
603 const UnaryOperator *Op;
604
605public:
606 DecrementGadget(const MatchFinder::MatchResult &Result)
607 : WarningGadget(Kind::Decrement),
608 Op(Result.Nodes.getNodeAs<UnaryOperator>(OpTag)) {}
609
610 static bool classof(const Gadget *G) {
611 return G->getKind() == Kind::Decrement;
612 }
613
614 static Matcher matcher() {
615 return stmt(
616 unaryOperator(hasOperatorName("--"),
617 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
618 .bind(OpTag));
619 }
620
621 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
622 bool IsRelatedToDecl,
623 ASTContext &Ctx) const override {
624 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
625 }
626 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
627
628 DeclUseList getClaimedVarUseSites() const override {
629 if (const auto *DRE =
630 dyn_cast<DeclRefExpr>(Op->getSubExpr()->IgnoreParenImpCasts())) {
631 return {DRE};
632 }
633
634 return {};
635 }
636};
637
638/// Array subscript expressions on raw pointers as if they're arrays. Unsafe as
639/// it doesn't have any bounds checks for the array.
640class ArraySubscriptGadget : public WarningGadget {
641 static constexpr const char *const ArraySubscrTag = "ArraySubscript";
642 const ArraySubscriptExpr *ASE;
643
644public:
645 ArraySubscriptGadget(const MatchFinder::MatchResult &Result)
646 : WarningGadget(Kind::ArraySubscript),
647 ASE(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ArraySubscrTag)) {}
648
649 static bool classof(const Gadget *G) {
650 return G->getKind() == Kind::ArraySubscript;
651 }
652
653 static Matcher matcher() {
654 // clang-format off
656 hasBase(ignoringParenImpCasts(
659 isSafeArraySubscript(),
660 hasIndex(
662 )
663 ))).bind(ArraySubscrTag));
664 // clang-format on
665 }
666
667 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
668 bool IsRelatedToDecl,
669 ASTContext &Ctx) const override {
670 Handler.handleUnsafeOperation(ASE, IsRelatedToDecl, Ctx);
671 }
672 SourceLocation getSourceLoc() const override { return ASE->getBeginLoc(); }
673
674 DeclUseList getClaimedVarUseSites() const override {
675 if (const auto *DRE =
676 dyn_cast<DeclRefExpr>(ASE->getBase()->IgnoreParenImpCasts())) {
677 return {DRE};
678 }
679
680 return {};
681 }
682};
683
684/// A pointer arithmetic expression of one of the forms:
685/// \code
686/// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
687/// \endcode
688class PointerArithmeticGadget : public WarningGadget {
689 static constexpr const char *const PointerArithmeticTag = "ptrAdd";
690 static constexpr const char *const PointerArithmeticPointerTag = "ptrAddPtr";
691 const BinaryOperator *PA; // pointer arithmetic expression
692 const Expr *Ptr; // the pointer expression in `PA`
693
694public:
695 PointerArithmeticGadget(const MatchFinder::MatchResult &Result)
696 : WarningGadget(Kind::PointerArithmetic),
697 PA(Result.Nodes.getNodeAs<BinaryOperator>(PointerArithmeticTag)),
698 Ptr(Result.Nodes.getNodeAs<Expr>(PointerArithmeticPointerTag)) {}
699
700 static bool classof(const Gadget *G) {
701 return G->getKind() == Kind::PointerArithmetic;
702 }
703
704 static Matcher matcher() {
705 auto HasIntegerType = anyOf(hasType(isInteger()), hasType(enumType()));
706 auto PtrAtRight =
707 allOf(hasOperatorName("+"),
708 hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
709 hasLHS(HasIntegerType));
710 auto PtrAtLeft =
711 allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"),
712 hasOperatorName("+="), hasOperatorName("-=")),
713 hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag)),
714 hasRHS(HasIntegerType));
715
716 return stmt(binaryOperator(anyOf(PtrAtLeft, PtrAtRight))
717 .bind(PointerArithmeticTag));
718 }
719
720 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
721 bool IsRelatedToDecl,
722 ASTContext &Ctx) const override {
723 Handler.handleUnsafeOperation(PA, IsRelatedToDecl, Ctx);
724 }
725 SourceLocation getSourceLoc() const override { return PA->getBeginLoc(); }
726
727 DeclUseList getClaimedVarUseSites() const override {
728 if (const auto *DRE = dyn_cast<DeclRefExpr>(Ptr->IgnoreParenImpCasts())) {
729 return {DRE};
730 }
731
732 return {};
733 }
734 // FIXME: pointer adding zero should be fine
735 // FIXME: this gadge will need a fix-it
736};
737
738class SpanTwoParamConstructorGadget : public WarningGadget {
739 static constexpr const char *const SpanTwoParamConstructorTag =
740 "spanTwoParamConstructor";
741 const CXXConstructExpr *Ctor; // the span constructor expression
742
743public:
744 SpanTwoParamConstructorGadget(const MatchFinder::MatchResult &Result)
745 : WarningGadget(Kind::SpanTwoParamConstructor),
746 Ctor(Result.Nodes.getNodeAs<CXXConstructExpr>(
747 SpanTwoParamConstructorTag)) {}
748
749 static bool classof(const Gadget *G) {
750 return G->getKind() == Kind::SpanTwoParamConstructor;
751 }
752
753 static Matcher matcher() {
754 auto HasTwoParamSpanCtorDecl = hasDeclaration(
755 cxxConstructorDecl(hasDeclContext(isInStdNamespace()), hasName("span"),
756 parameterCountIs(2)));
757
758 return stmt(cxxConstructExpr(HasTwoParamSpanCtorDecl,
759 unless(isSafeSpanTwoParamConstruct()))
760 .bind(SpanTwoParamConstructorTag));
761 }
762
763 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
764 bool IsRelatedToDecl,
765 ASTContext &Ctx) const override {
766 Handler.handleUnsafeOperationInContainer(Ctor, IsRelatedToDecl, Ctx);
767 }
768 SourceLocation getSourceLoc() const override { return Ctor->getBeginLoc(); }
769
770 DeclUseList getClaimedVarUseSites() const override {
771 // If the constructor call is of the form `std::span{var, n}`, `var` is
772 // considered an unsafe variable.
773 if (auto *DRE = dyn_cast<DeclRefExpr>(Ctor->getArg(0))) {
774 if (isa<VarDecl>(DRE->getDecl()))
775 return {DRE};
776 }
777 return {};
778 }
779};
780
781/// A pointer initialization expression of the form:
782/// \code
783/// int *p = q;
784/// \endcode
785class PointerInitGadget : public FixableGadget {
786private:
787 static constexpr const char *const PointerInitLHSTag = "ptrInitLHS";
788 static constexpr const char *const PointerInitRHSTag = "ptrInitRHS";
789 const VarDecl *PtrInitLHS; // the LHS pointer expression in `PI`
790 const DeclRefExpr *PtrInitRHS; // the RHS pointer expression in `PI`
791
792public:
793 PointerInitGadget(const MatchFinder::MatchResult &Result)
794 : FixableGadget(Kind::PointerInit),
795 PtrInitLHS(Result.Nodes.getNodeAs<VarDecl>(PointerInitLHSTag)),
796 PtrInitRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerInitRHSTag)) {}
797
798 static bool classof(const Gadget *G) {
799 return G->getKind() == Kind::PointerInit;
800 }
801
802 static Matcher matcher() {
803 auto PtrInitStmt = declStmt(hasSingleDecl(
804 varDecl(hasInitializer(ignoringImpCasts(
805 declRefExpr(hasPointerType(), toSupportedVariable())
806 .bind(PointerInitRHSTag))))
807 .bind(PointerInitLHSTag)));
808
809 return stmt(PtrInitStmt);
810 }
811
812 virtual std::optional<FixItList>
813 getFixits(const FixitStrategy &S) const override;
814 SourceLocation getSourceLoc() const override {
815 return PtrInitRHS->getBeginLoc();
816 }
817
818 virtual DeclUseList getClaimedVarUseSites() const override {
819 return DeclUseList{PtrInitRHS};
820 }
821
822 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
823 getStrategyImplications() const override {
824 return std::make_pair(PtrInitLHS, cast<VarDecl>(PtrInitRHS->getDecl()));
825 }
826};
827
828/// A pointer assignment expression of the form:
829/// \code
830/// p = q;
831/// \endcode
832/// where both `p` and `q` are pointers.
833class PtrToPtrAssignmentGadget : public FixableGadget {
834private:
835 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
836 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
837 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
838 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
839
840public:
841 PtrToPtrAssignmentGadget(const MatchFinder::MatchResult &Result)
842 : FixableGadget(Kind::PtrToPtrAssignment),
843 PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
844 PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
845
846 static bool classof(const Gadget *G) {
847 return G->getKind() == Kind::PtrToPtrAssignment;
848 }
849
850 static Matcher matcher() {
851 auto PtrAssignExpr = binaryOperator(
852 allOf(hasOperatorName("="),
853 hasRHS(ignoringParenImpCasts(
854 declRefExpr(hasPointerType(), toSupportedVariable())
855 .bind(PointerAssignRHSTag))),
856 hasLHS(declRefExpr(hasPointerType(), toSupportedVariable())
857 .bind(PointerAssignLHSTag))));
858
859 return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
860 }
861
862 virtual std::optional<FixItList>
863 getFixits(const FixitStrategy &S) const override;
864 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
865
866 virtual DeclUseList getClaimedVarUseSites() const override {
867 return DeclUseList{PtrLHS, PtrRHS};
868 }
869
870 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
871 getStrategyImplications() const override {
872 return std::make_pair(cast<VarDecl>(PtrLHS->getDecl()),
873 cast<VarDecl>(PtrRHS->getDecl()));
874 }
875};
876
877/// An assignment expression of the form:
878/// \code
879/// ptr = array;
880/// \endcode
881/// where `p` is a pointer and `array` is a constant size array.
882class CArrayToPtrAssignmentGadget : public FixableGadget {
883private:
884 static constexpr const char *const PointerAssignLHSTag = "ptrLHS";
885 static constexpr const char *const PointerAssignRHSTag = "ptrRHS";
886 const DeclRefExpr *PtrLHS; // the LHS pointer expression in `PA`
887 const DeclRefExpr *PtrRHS; // the RHS pointer expression in `PA`
888
889public:
890 CArrayToPtrAssignmentGadget(const MatchFinder::MatchResult &Result)
891 : FixableGadget(Kind::CArrayToPtrAssignment),
892 PtrLHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignLHSTag)),
893 PtrRHS(Result.Nodes.getNodeAs<DeclRefExpr>(PointerAssignRHSTag)) {}
894
895 static bool classof(const Gadget *G) {
896 return G->getKind() == Kind::CArrayToPtrAssignment;
897 }
898
899 static Matcher matcher() {
900 auto PtrAssignExpr = binaryOperator(
901 allOf(hasOperatorName("="),
902 hasRHS(ignoringParenImpCasts(
903 declRefExpr(hasType(hasCanonicalType(constantArrayType())),
904 toSupportedVariable())
905 .bind(PointerAssignRHSTag))),
906 hasLHS(declRefExpr(hasPointerType(), toSupportedVariable())
907 .bind(PointerAssignLHSTag))));
908
909 return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr));
910 }
911
912 virtual std::optional<FixItList>
913 getFixits(const FixitStrategy &S) const override;
914 SourceLocation getSourceLoc() const override { return PtrLHS->getBeginLoc(); }
915
916 virtual DeclUseList getClaimedVarUseSites() const override {
917 return DeclUseList{PtrLHS, PtrRHS};
918 }
919
920 virtual std::optional<std::pair<const VarDecl *, const VarDecl *>>
921 getStrategyImplications() const override {
922 return {};
923 }
924};
925
926/// A call of a function or method that performs unchecked buffer operations
927/// over one of its pointer parameters.
928class UnsafeBufferUsageAttrGadget : public WarningGadget {
929 constexpr static const char *const OpTag = "call_expr";
930 const CallExpr *Op;
931
932public:
933 UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result)
934 : WarningGadget(Kind::UnsafeBufferUsageAttr),
935 Op(Result.Nodes.getNodeAs<CallExpr>(OpTag)) {}
936
937 static bool classof(const Gadget *G) {
938 return G->getKind() == Kind::UnsafeBufferUsageAttr;
939 }
940
941 static Matcher matcher() {
942 auto HasUnsafeFnDecl =
943 callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)));
944 return stmt(callExpr(HasUnsafeFnDecl).bind(OpTag));
945 }
946
947 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
948 bool IsRelatedToDecl,
949 ASTContext &Ctx) const override {
950 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
951 }
952 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
953
954 DeclUseList getClaimedVarUseSites() const override { return {}; }
955};
956
957/// A call of a constructor that performs unchecked buffer operations
958/// over one of its pointer parameters, or constructs a class object that will
959/// perform buffer operations that depend on the correctness of the parameters.
960class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
961 constexpr static const char *const OpTag = "cxx_construct_expr";
962 const CXXConstructExpr *Op;
963
964public:
965 UnsafeBufferUsageCtorAttrGadget(const MatchFinder::MatchResult &Result)
966 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
967 Op(Result.Nodes.getNodeAs<CXXConstructExpr>(OpTag)) {}
968
969 static bool classof(const Gadget *G) {
970 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
971 }
972
973 static Matcher matcher() {
974 auto HasUnsafeCtorDecl =
975 hasDeclaration(cxxConstructorDecl(hasAttr(attr::UnsafeBufferUsage)));
976 // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget.
977 auto HasTwoParamSpanCtorDecl = SpanTwoParamConstructorGadget::matcher();
978 return stmt(
979 cxxConstructExpr(HasUnsafeCtorDecl, unless(HasTwoParamSpanCtorDecl))
980 .bind(OpTag));
981 }
982
983 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
984 bool IsRelatedToDecl,
985 ASTContext &Ctx) const override {
986 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
987 }
988 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
989
990 DeclUseList getClaimedVarUseSites() const override { return {}; }
991};
992
993// Warning gadget for unsafe invocation of span::data method.
994// Triggers when the pointer returned by the invocation is immediately
995// cast to a larger type.
996
997class DataInvocationGadget : public WarningGadget {
998 constexpr static const char *const OpTag = "data_invocation_expr";
999 const ExplicitCastExpr *Op;
1000
1001public:
1002 DataInvocationGadget(const MatchFinder::MatchResult &Result)
1003 : WarningGadget(Kind::DataInvocation),
1004 Op(Result.Nodes.getNodeAs<ExplicitCastExpr>(OpTag)) {}
1005
1006 static bool classof(const Gadget *G) {
1007 return G->getKind() == Kind::DataInvocation;
1008 }
1009
1010 static Matcher matcher() {
1011 Matcher callExpr = cxxMemberCallExpr(
1012 callee(cxxMethodDecl(hasName("data"), ofClass(hasName("std::span")))));
1013 return stmt(
1015 .bind(OpTag));
1016 }
1017
1018 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1019 bool IsRelatedToDecl,
1020 ASTContext &Ctx) const override {
1021 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1022 }
1023 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1024
1025 DeclUseList getClaimedVarUseSites() const override { return {}; }
1026};
1027
1028// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
1029// Context (see `isInUnspecifiedLvalueContext`).
1030// Note here `[]` is the built-in subscript operator.
1031class ULCArraySubscriptGadget : public FixableGadget {
1032private:
1033 static constexpr const char *const ULCArraySubscriptTag =
1034 "ArraySubscriptUnderULC";
1035 const ArraySubscriptExpr *Node;
1036
1037public:
1038 ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result)
1039 : FixableGadget(Kind::ULCArraySubscript),
1040 Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
1041 assert(Node != nullptr && "Expecting a non-null matching result");
1042 }
1043
1044 static bool classof(const Gadget *G) {
1045 return G->getKind() == Kind::ULCArraySubscript;
1046 }
1047
1048 static Matcher matcher() {
1049 auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
1050 auto BaseIsArrayOrPtrDRE = hasBase(
1051 ignoringParenImpCasts(declRefExpr(ArrayOrPtr, toSupportedVariable())));
1052 auto Target =
1053 arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag);
1054
1056 }
1057
1058 virtual std::optional<FixItList>
1059 getFixits(const FixitStrategy &S) const override;
1060 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1061
1062 virtual DeclUseList getClaimedVarUseSites() const override {
1063 if (const auto *DRE =
1064 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
1065 return {DRE};
1066 }
1067 return {};
1068 }
1069};
1070
1071// Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
1072// unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits
1073// fixit of the form `UPC(DRE.data())`.
1074class UPCStandalonePointerGadget : public FixableGadget {
1075private:
1076 static constexpr const char *const DeclRefExprTag = "StandalonePointer";
1077 const DeclRefExpr *Node;
1078
1079public:
1080 UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result)
1081 : FixableGadget(Kind::UPCStandalonePointer),
1082 Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
1083 assert(Node != nullptr && "Expecting a non-null matching result");
1084 }
1085
1086 static bool classof(const Gadget *G) {
1087 return G->getKind() == Kind::UPCStandalonePointer;
1088 }
1089
1090 static Matcher matcher() {
1091 auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
1092 auto target = expr(ignoringParenImpCasts(
1093 declRefExpr(allOf(ArrayOrPtr, toSupportedVariable()))
1094 .bind(DeclRefExprTag)));
1095 return stmt(isInUnspecifiedPointerContext(target));
1096 }
1097
1098 virtual std::optional<FixItList>
1099 getFixits(const FixitStrategy &S) const override;
1100 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1101
1102 virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }
1103};
1104
1105class PointerDereferenceGadget : public FixableGadget {
1106 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
1107 static constexpr const char *const OperatorTag = "op";
1108
1109 const DeclRefExpr *BaseDeclRefExpr = nullptr;
1110 const UnaryOperator *Op = nullptr;
1111
1112public:
1113 PointerDereferenceGadget(const MatchFinder::MatchResult &Result)
1114 : FixableGadget(Kind::PointerDereference),
1115 BaseDeclRefExpr(
1116 Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
1117 Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {}
1118
1119 static bool classof(const Gadget *G) {
1120 return G->getKind() == Kind::PointerDereference;
1121 }
1122
1123 static Matcher matcher() {
1124 auto Target =
1126 hasOperatorName("*"),
1127 has(expr(ignoringParenImpCasts(
1128 declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag)))))
1129 .bind(OperatorTag);
1130
1132 }
1133
1134 DeclUseList getClaimedVarUseSites() const override {
1135 return {BaseDeclRefExpr};
1136 }
1137
1138 virtual std::optional<FixItList>
1139 getFixits(const FixitStrategy &S) const override;
1140 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1141};
1142
1143// Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
1144// Context (see `isInUnspecifiedPointerContext`).
1145// Note here `[]` is the built-in subscript operator.
1146class UPCAddressofArraySubscriptGadget : public FixableGadget {
1147private:
1148 static constexpr const char *const UPCAddressofArraySubscriptTag =
1149 "AddressofArraySubscriptUnderUPC";
1150 const UnaryOperator *Node; // the `&DRE[any]` node
1151
1152public:
1153 UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result)
1154 : FixableGadget(Kind::ULCArraySubscript),
1155 Node(Result.Nodes.getNodeAs<UnaryOperator>(
1156 UPCAddressofArraySubscriptTag)) {
1157 assert(Node != nullptr && "Expecting a non-null matching result");
1158 }
1159
1160 static bool classof(const Gadget *G) {
1161 return G->getKind() == Kind::UPCAddressofArraySubscript;
1162 }
1163
1164 static Matcher matcher() {
1165 return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
1167 hasOperatorName("&"),
1168 hasUnaryOperand(arraySubscriptExpr(hasBase(
1169 ignoringParenImpCasts(declRefExpr(toSupportedVariable()))))))
1170 .bind(UPCAddressofArraySubscriptTag)))));
1171 }
1172
1173 virtual std::optional<FixItList>
1174 getFixits(const FixitStrategy &) const override;
1175 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1176
1177 virtual DeclUseList getClaimedVarUseSites() const override {
1178 const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
1179 const auto *DRE =
1180 cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts());
1181 return {DRE};
1182 }
1183};
1184} // namespace
1185
1186namespace {
1187// An auxiliary tracking facility for the fixit analysis. It helps connect
1188// declarations to its uses and make sure we've covered all uses with our
1189// analysis before we try to fix the declaration.
1190class DeclUseTracker {
1191 using UseSetTy = SmallSet<const DeclRefExpr *, 16>;
1192 using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;
1193
1194 // Allocate on the heap for easier move.
1195 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
1196 DefMapTy Defs{};
1197
1198public:
1199 DeclUseTracker() = default;
1200 DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
1201 DeclUseTracker &operator=(const DeclUseTracker &) = delete;
1202 DeclUseTracker(DeclUseTracker &&) = default;
1203 DeclUseTracker &operator=(DeclUseTracker &&) = default;
1204
1205 // Start tracking a freshly discovered DRE.
1206 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
1207
1208 // Stop tracking the DRE as it's been fully figured out.
1209 void claimUse(const DeclRefExpr *DRE) {
1210 assert(Uses->count(DRE) &&
1211 "DRE not found or claimed by multiple matchers!");
1212 Uses->erase(DRE);
1213 }
1214
1215 // A variable is unclaimed if at least one use is unclaimed.
1216 bool hasUnclaimedUses(const VarDecl *VD) const {
1217 // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
1218 return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
1219 return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
1220 });
1221 }
1222
1223 UseSetTy getUnclaimedUses(const VarDecl *VD) const {
1224 UseSetTy ReturnSet;
1225 for (auto use : *Uses) {
1226 if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
1227 ReturnSet.insert(use);
1228 }
1229 }
1230 return ReturnSet;
1231 }
1232
1233 void discoverDecl(const DeclStmt *DS) {
1234 for (const Decl *D : DS->decls()) {
1235 if (const auto *VD = dyn_cast<VarDecl>(D)) {
1236 // FIXME: Assertion temporarily disabled due to a bug in
1237 // ASTMatcher internal behavior in presence of GNU
1238 // statement-expressions. We need to properly investigate this
1239 // because it can screw up our algorithm in other ways.
1240 // assert(Defs.count(VD) == 0 && "Definition already discovered!");
1241 Defs[VD] = DS;
1242 }
1243 }
1244 }
1245
1246 const DeclStmt *lookupDecl(const VarDecl *VD) const {
1247 return Defs.lookup(VD);
1248 }
1249};
1250} // namespace
1251
1252// Representing a pointer type expression of the form `++Ptr` in an Unspecified
1253// Pointer Context (UPC):
1254class UPCPreIncrementGadget : public FixableGadget {
1255private:
1256 static constexpr const char *const UPCPreIncrementTag =
1257 "PointerPreIncrementUnderUPC";
1258 const UnaryOperator *Node; // the `++Ptr` node
1259
1260public:
1262 : FixableGadget(Kind::UPCPreIncrement),
1263 Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
1264 assert(Node != nullptr && "Expecting a non-null matching result");
1265 }
1266
1267 static bool classof(const Gadget *G) {
1268 return G->getKind() == Kind::UPCPreIncrement;
1269 }
1270
1271 static Matcher matcher() {
1272 // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
1273 // Although currently we can only provide fix-its when `Ptr` is a DRE, we
1274 // can have the matcher be general, so long as `getClaimedVarUseSites` does
1275 // things right.
1276 return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
1277 unaryOperator(isPreInc(),
1278 hasUnaryOperand(declRefExpr(toSupportedVariable())))
1279 .bind(UPCPreIncrementTag)))));
1280 }
1281
1282 virtual std::optional<FixItList>
1283 getFixits(const FixitStrategy &S) const override;
1284 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1285
1286 virtual DeclUseList getClaimedVarUseSites() const override {
1287 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
1288 }
1289};
1290
1291// Representing a pointer type expression of the form `Ptr += n` in an
1292// Unspecified Untyped Context (UUC):
1293class UUCAddAssignGadget : public FixableGadget {
1294private:
1295 static constexpr const char *const UUCAddAssignTag =
1296 "PointerAddAssignUnderUUC";
1297 static constexpr const char *const OffsetTag = "Offset";
1298
1299 const BinaryOperator *Node; // the `Ptr += n` node
1300 const Expr *Offset = nullptr;
1301
1302public:
1304 : FixableGadget(Kind::UUCAddAssign),
1305 Node(Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
1306 Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
1307 assert(Node != nullptr && "Expecting a non-null matching result");
1308 }
1309
1310 static bool classof(const Gadget *G) {
1311 return G->getKind() == Kind::UUCAddAssign;
1312 }
1313
1314 static Matcher matcher() {
1315 // clang-format off
1316 return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts(
1317 binaryOperator(hasOperatorName("+="),
1318 hasLHS(
1321 toSupportedVariable())),
1322 hasRHS(expr().bind(OffsetTag)))
1323 .bind(UUCAddAssignTag)))));
1324 // clang-format on
1325 }
1326
1327 virtual std::optional<FixItList>
1328 getFixits(const FixitStrategy &S) const override;
1329 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1330
1331 virtual DeclUseList getClaimedVarUseSites() const override {
1332 return {dyn_cast<DeclRefExpr>(Node->getLHS())};
1333 }
1334};
1335
1336// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
1337// ptr)`:
1338class DerefSimplePtrArithFixableGadget : public FixableGadget {
1339 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
1340 static constexpr const char *const DerefOpTag = "DerefOp";
1341 static constexpr const char *const AddOpTag = "AddOp";
1342 static constexpr const char *const OffsetTag = "Offset";
1343
1344 const DeclRefExpr *BaseDeclRefExpr = nullptr;
1345 const UnaryOperator *DerefOp = nullptr;
1346 const BinaryOperator *AddOp = nullptr;
1347 const IntegerLiteral *Offset = nullptr;
1348
1349public:
1351 : FixableGadget(Kind::DerefSimplePtrArithFixable),
1352 BaseDeclRefExpr(
1353 Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
1354 DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)),
1355 AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)),
1356 Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {}
1357
1358 static Matcher matcher() {
1359 // clang-format off
1360 auto ThePtr = expr(hasPointerType(),
1361 ignoringImpCasts(declRefExpr(toSupportedVariable()).
1362 bind(BaseDeclRefExprTag)));
1363 auto PlusOverPtrAndInteger = expr(anyOf(
1364 binaryOperator(hasOperatorName("+"), hasLHS(ThePtr),
1365 hasRHS(integerLiteral().bind(OffsetTag)))
1366 .bind(AddOpTag),
1367 binaryOperator(hasOperatorName("+"), hasRHS(ThePtr),
1368 hasLHS(integerLiteral().bind(OffsetTag)))
1369 .bind(AddOpTag)));
1371 hasOperatorName("*"),
1372 hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger)))
1373 .bind(DerefOpTag));
1374 // clang-format on
1375 }
1376
1377 virtual std::optional<FixItList>
1378 getFixits(const FixitStrategy &s) const final;
1379 SourceLocation getSourceLoc() const override {
1380 return DerefOp->getBeginLoc();
1381 }
1382
1383 virtual DeclUseList getClaimedVarUseSites() const final {
1384 return {BaseDeclRefExpr};
1385 }
1386};
1387
1388/// Scan the function and return a list of gadgets found with provided kits.
1389static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker>
1391 bool EmitSuggestions) {
1392
1393 struct GadgetFinderCallback : MatchFinder::MatchCallback {
1394 FixableGadgetList FixableGadgets;
1395 WarningGadgetList WarningGadgets;
1396 DeclUseTracker Tracker;
1397
1398 void run(const MatchFinder::MatchResult &Result) override {
1399 // In debug mode, assert that we've found exactly one gadget.
1400 // This helps us avoid conflicts in .bind() tags.
1401#if NDEBUG
1402#define NEXT return
1403#else
1404 [[maybe_unused]] int numFound = 0;
1405#define NEXT ++numFound
1406#endif
1407
1408 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) {
1409 Tracker.discoverUse(DRE);
1410 NEXT;
1411 }
1412
1413 if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {
1414 Tracker.discoverDecl(DS);
1415 NEXT;
1416 }
1417
1418 // Figure out which matcher we've found, and call the appropriate
1419 // subclass constructor.
1420 // FIXME: Can we do this more logarithmically?
1421#define FIXABLE_GADGET(name) \
1422 if (Result.Nodes.getNodeAs<Stmt>(#name)) { \
1423 FixableGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
1424 NEXT; \
1425 }
1426#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1427#define WARNING_GADGET(name) \
1428 if (Result.Nodes.getNodeAs<Stmt>(#name)) { \
1429 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
1430 NEXT; \
1431 }
1432#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1433
1434 assert(numFound >= 1 && "Gadgets not found in match result!");
1435 assert(numFound <= 1 && "Conflicting bind tags in gadgets!");
1436 }
1437 };
1438
1439 MatchFinder M;
1440 GadgetFinderCallback CB;
1441
1442 // clang-format off
1443 M.addMatcher(
1444 stmt(
1445 forEachDescendantEvaluatedStmt(stmt(anyOf(
1446 // Add Gadget::matcher() for every gadget in the registry.
1447#define WARNING_GADGET(x) \
1448 allOf(x ## Gadget::matcher().bind(#x), \
1449 notInSafeBufferOptOut(&Handler)),
1450#define WARNING_CONTAINER_GADGET(x) \
1451 allOf(x ## Gadget::matcher().bind(#x), \
1452 notInSafeBufferOptOut(&Handler), \
1453 unless(ignoreUnsafeBufferInContainer(&Handler))),
1454#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1455 // Avoid a hanging comma.
1456 unless(stmt())
1457 )))
1458 ),
1459 &CB
1460 );
1461 // clang-format on
1462
1463 if (EmitSuggestions) {
1464 // clang-format off
1465 M.addMatcher(
1466 stmt(
1467 forEachDescendantStmt(stmt(eachOf(
1468#define FIXABLE_GADGET(x) \
1469 x ## Gadget::matcher().bind(#x),
1470#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1471 // In parallel, match all DeclRefExprs so that to find out
1472 // whether there are any uncovered by gadgets.
1474 to(anyOf(varDecl(), bindingDecl()))).bind("any_dre"),
1475 // Also match DeclStmts because we'll need them when fixing
1476 // their underlying VarDecls that otherwise don't have
1477 // any backreferences to DeclStmts.
1478 declStmt().bind("any_ds")
1479 )))
1480 ),
1481 &CB
1482 );
1483 // clang-format on
1484 }
1485
1486 M.match(*D->getBody(), D->getASTContext());
1487 return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets),
1488 std::move(CB.Tracker)};
1489}
1490
1491// Compares AST nodes by source locations.
1492template <typename NodeTy> struct CompareNode {
1493 bool operator()(const NodeTy *N1, const NodeTy *N2) const {
1494 return N1->getBeginLoc().getRawEncoding() <
1495 N2->getBeginLoc().getRawEncoding();
1496 }
1497};
1498
1500 std::map<const VarDecl *, std::set<const WarningGadget *>,
1501 // To keep keys sorted by their locations in the map so that the
1502 // order is deterministic:
1505 // These Gadgets are not related to pointer variables (e. g. temporaries).
1507};
1508
1509static WarningGadgetSets
1510groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
1511 WarningGadgetSets result;
1512 // If some gadgets cover more than one
1513 // variable, they'll appear more than once in the map.
1514 for (auto &G : AllUnsafeOperations) {
1515 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
1516
1517 bool AssociatedWithVarDecl = false;
1518 for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
1519 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1520 result.byVar[VD].insert(G.get());
1521 AssociatedWithVarDecl = true;
1522 }
1523 }
1524
1525 if (!AssociatedWithVarDecl) {
1526 result.noVar.push_back(G.get());
1527 continue;
1528 }
1529 }
1530 return result;
1531}
1532
1534 std::map<const VarDecl *, std::set<const FixableGadget *>,
1535 // To keep keys sorted by their locations in the map so that the
1536 // order is deterministic:
1539};
1540
1541static FixableGadgetSets
1542groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
1543 FixableGadgetSets FixablesForUnsafeVars;
1544 for (auto &F : AllFixableOperations) {
1545 DeclUseList DREs = F->getClaimedVarUseSites();
1546
1547 for (const DeclRefExpr *DRE : DREs) {
1548 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1549 FixablesForUnsafeVars.byVar[VD].insert(F.get());
1550 }
1551 }
1552 }
1553 return FixablesForUnsafeVars;
1554}
1555
1557 const SourceManager &SM) {
1558 // A simple interval overlap detection algorithm. Sorts all ranges by their
1559 // begin location then finds the first overlap in one pass.
1560 std::vector<const FixItHint *> All; // a copy of `FixIts`
1561
1562 for (const FixItHint &H : FixIts)
1563 All.push_back(&H);
1564 std::sort(All.begin(), All.end(),
1565 [&SM](const FixItHint *H1, const FixItHint *H2) {
1566 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
1567 H2->RemoveRange.getBegin());
1568 });
1569
1570 const FixItHint *CurrHint = nullptr;
1571
1572 for (const FixItHint *Hint : All) {
1573 if (!CurrHint ||
1574 SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
1575 Hint->RemoveRange.getBegin())) {
1576 // Either to initialize `CurrHint` or `CurrHint` does not
1577 // overlap with `Hint`:
1578 CurrHint = Hint;
1579 } else
1580 // In case `Hint` overlaps the `CurrHint`, we found at least one
1581 // conflict:
1582 return true;
1583 }
1584 return false;
1585}
1586
1587std::optional<FixItList>
1588PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
1589 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
1590 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
1591 switch (S.lookup(LeftVD)) {
1592 case FixitStrategy::Kind::Span:
1593 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
1594 return FixItList{};
1595 return std::nullopt;
1596 case FixitStrategy::Kind::Wontfix:
1597 return std::nullopt;
1598 case FixitStrategy::Kind::Iterator:
1599 case FixitStrategy::Kind::Array:
1600 return std::nullopt;
1601 case FixitStrategy::Kind::Vector:
1602 llvm_unreachable("unsupported strategies for FixableGadgets");
1603 }
1604 return std::nullopt;
1605}
1606
1607/// \returns fixit that adds .data() call after \DRE.
1608static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
1609 const DeclRefExpr *DRE);
1610
1611std::optional<FixItList>
1612CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
1613 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
1614 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
1615 // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
1616 // non-trivial.
1617 //
1618 // CArrayToPtrAssignmentGadget doesn't have strategy implications because
1619 // constant size array propagates its bounds. Because of that LHS and RHS are
1620 // addressed by two different fixits.
1621 //
1622 // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
1623 // to and can't be generally relied on in multi-variable Fixables!
1624 //
1625 // E. g. If an instance of this gadget is fixing variable on LHS then the
1626 // variable on RHS is fixed by a different fixit and its strategy for LHS
1627 // fixit is as if Wontfix.
1628 //
1629 // The only exception is Wontfix strategy for a given variable as that is
1630 // valid for any fixit produced for the given input source code.
1631 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
1632 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
1633 return FixItList{};
1634 }
1635 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
1636 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
1637 return createDataFixit(RightVD->getASTContext(), PtrRHS);
1638 }
1639 }
1640 return std::nullopt;
1641}
1642
1643std::optional<FixItList>
1644PointerInitGadget::getFixits(const FixitStrategy &S) const {
1645 const auto *LeftVD = PtrInitLHS;
1646 const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
1647 switch (S.lookup(LeftVD)) {
1648 case FixitStrategy::Kind::Span:
1649 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
1650 return FixItList{};
1651 return std::nullopt;
1652 case FixitStrategy::Kind::Wontfix:
1653 return std::nullopt;
1654 case FixitStrategy::Kind::Iterator:
1655 case FixitStrategy::Kind::Array:
1656 return std::nullopt;
1657 case FixitStrategy::Kind::Vector:
1658 llvm_unreachable("unsupported strategies for FixableGadgets");
1659 }
1660 return std::nullopt;
1661}
1662
1663static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
1664 const ASTContext &Ctx) {
1665 if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
1666 if (ConstVal->isNegative())
1667 return false;
1668 } else if (!Expr->getType()->isUnsignedIntegerType())
1669 return false;
1670 return true;
1671}
1672
1673std::optional<FixItList>
1674ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
1675 if (const auto *DRE =
1676 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
1677 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1678 switch (S.lookup(VD)) {
1679 case FixitStrategy::Kind::Span: {
1680
1681 // If the index has a negative constant value, we give up as no valid
1682 // fix-it can be generated:
1683 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
1684 VD->getASTContext();
1685 if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
1686 return std::nullopt;
1687 // no-op is a good fix-it, otherwise
1688 return FixItList{};
1689 }
1690 case FixitStrategy::Kind::Array:
1691 return FixItList{};
1692 case FixitStrategy::Kind::Wontfix:
1693 case FixitStrategy::Kind::Iterator:
1694 case FixitStrategy::Kind::Vector:
1695 llvm_unreachable("unsupported strategies for FixableGadgets");
1696 }
1697 }
1698 return std::nullopt;
1699}
1700
1701static std::optional<FixItList> // forward declaration
1703
1704std::optional<FixItList>
1705UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
1706 auto DREs = getClaimedVarUseSites();
1707 const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
1708
1709 switch (S.lookup(VD)) {
1710 case FixitStrategy::Kind::Span:
1712 case FixitStrategy::Kind::Wontfix:
1713 case FixitStrategy::Kind::Iterator:
1714 case FixitStrategy::Kind::Array:
1715 return std::nullopt;
1716 case FixitStrategy::Kind::Vector:
1717 llvm_unreachable("unsupported strategies for FixableGadgets");
1718 }
1719 return std::nullopt; // something went wrong, no fix-it
1720}
1721
1722// FIXME: this function should be customizable through format
1723static StringRef getEndOfLine() {
1724 static const char *const EOL = "\n";
1725 return EOL;
1726}
1727
1728// Returns the text indicating that the user needs to provide input there:
1729std::string getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
1730 std::string s = std::string("<# ");
1731 s += HintTextToUser;
1732 s += " #>";
1733 return s;
1734}
1735
1736// Return the source location of the last character of the AST `Node`.
1737template <typename NodeTy>
1738static std::optional<SourceLocation>
1739getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
1740 const LangOptions &LangOpts) {
1741 unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts);
1742 SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
1743
1744 if (Loc.isValid())
1745 return Loc;
1746
1747 return std::nullopt;
1748}
1749
1750// Return the source location just past the last character of the AST `Node`.
1751template <typename NodeTy>
1752static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
1753 const SourceManager &SM,
1754 const LangOptions &LangOpts) {
1756 Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts);
1757 if (Loc.isValid())
1758 return Loc;
1759 return std::nullopt;
1760}
1761
1762// Return text representation of an `Expr`.
1763static std::optional<StringRef> getExprText(const Expr *E,
1764 const SourceManager &SM,
1765 const LangOptions &LangOpts) {
1766 std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts);
1767
1768 if (LastCharLoc)
1769 return Lexer::getSourceText(
1770 CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM,
1771 LangOpts);
1772
1773 return std::nullopt;
1774}
1775
1776// Returns the literal text in `SourceRange SR`, if `SR` is a valid range.
1777static std::optional<StringRef> getRangeText(SourceRange SR,
1778 const SourceManager &SM,
1779 const LangOptions &LangOpts) {
1780 bool Invalid = false;
1782 StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid);
1783
1784 if (!Invalid)
1785 return Text;
1786 return std::nullopt;
1787}
1788
1789// Returns the begin location of the identifier of the given variable
1790// declaration.
1792 // According to the implementation of `VarDecl`, `VD->getLocation()` actually
1793 // returns the begin location of the identifier of the declaration:
1794 return VD->getLocation();
1795}
1796
1797// Returns the literal text of the identifier of the given variable declaration.
1798static std::optional<StringRef>
1800 const LangOptions &LangOpts) {
1801 SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD);
1802 SourceLocation ParmIdentEndLoc =
1803 Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts);
1804
1805 if (ParmIdentEndLoc.isMacroID() &&
1806 !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts))
1807 return std::nullopt;
1808 return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
1809}
1810
1811// We cannot fix a variable declaration if it has some other specifiers than the
1812// type specifier. Because the source ranges of those specifiers could overlap
1813// with the source range that is being replaced using fix-its. Especially when
1814// we often cannot obtain accurate source ranges of cv-qualified type
1815// specifiers.
1816// FIXME: also deal with type attributes
1817static bool hasUnsupportedSpecifiers(const VarDecl *VD,
1818 const SourceManager &SM) {
1819 // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
1820 // source range of `VD`:
1821 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
1822 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
1823 VD->getBeginLoc())) &&
1824 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
1825 At->getRange().getBegin()));
1826 });
1827 return VD->isInlineSpecified() || VD->isConstexpr() ||
1829 AttrRangeOverlapping;
1830}
1831
1832// Returns the `SourceRange` of `D`. The reason why this function exists is
1833// that `D->getSourceRange()` may return a range where the end location is the
1834// starting location of the last token. The end location of the source range
1835// returned by this function is the last location of the last token.
1837 const SourceManager &SM,
1838 const LangOptions &LangOpts) {
1841 End = // `D->getEndLoc` should always return the starting location of the
1842 // last token, so we should get the end of the token
1843 Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
1844
1845 return SourceRange(Begin, End);
1846}
1847
1848// Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
1849// type. The text is obtained through from `TypeLoc`s. Since `TypeLoc` does not
1850// have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
1851// :( ), `Qualifiers` of the pointee type is returned separately through the
1852// output parameter `QualifiersToAppend`.
1853static std::optional<std::string>
1855 const LangOptions &LangOpts,
1856 std::optional<Qualifiers> *QualifiersToAppend) {
1857 QualType Ty = VD->getType();
1858 QualType PteTy;
1859
1860 assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
1861 "Expecting a VarDecl of type of pointer to object type");
1862 PteTy = Ty->getPointeeType();
1863
1865 TypeLoc PteTyLoc;
1866
1867 // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
1868 // the `TypeLoc` of the pointee type:
1869 switch (TyLoc.getTypeLocClass()) {
1870 case TypeLoc::ConstantArray:
1871 case TypeLoc::IncompleteArray:
1872 case TypeLoc::VariableArray:
1873 case TypeLoc::DependentSizedArray:
1874 case TypeLoc::Decayed:
1875 assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a "
1876 "pointer type unless it decays.");
1877 PteTyLoc = TyLoc.getNextTypeLoc();
1878 break;
1879 case TypeLoc::Pointer:
1880 PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc();
1881 break;
1882 default:
1883 return std::nullopt;
1884 }
1885 if (PteTyLoc.isNull())
1886 // Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
1887 // when the pointer type is `auto`.
1888 return std::nullopt;
1889
1891
1892 if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
1893 // We are expecting these locations to be valid. But in some cases, they are
1894 // not all valid. It is a Clang bug to me and we are not responsible for
1895 // fixing it. So we will just give up for now when it happens.
1896 return std::nullopt;
1897 }
1898
1899 // Note that TypeLoc.getEndLoc() returns the begin location of the last token:
1900 SourceLocation PteEndOfTokenLoc =
1901 Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
1902
1903 if (!PteEndOfTokenLoc.isValid())
1904 // Sometimes we cannot get the end location of the pointee type, e.g., when
1905 // there are macros involved.
1906 return std::nullopt;
1907 if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {
1908 // We only deal with the cases where the source text of the pointee type
1909 // appears on the left-hand side of the variable identifier completely,
1910 // including the following forms:
1911 // `T ident`,
1912 // `T ident[]`, where `T` is any type.
1913 // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`.
1914 return std::nullopt;
1915 }
1916 if (PteTy.hasQualifiers()) {
1917 // TypeLoc does not provide source ranges for qualifiers (it says it's
1918 // intentional but seems fishy to me), so we cannot get the full text
1919 // `PteTy` via source ranges.
1920 *QualifiersToAppend = PteTy.getQualifiers();
1921 }
1922 return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
1923 ->str();
1924}
1925
1926// Returns the text of the name (with qualifiers) of a `FunctionDecl`.
1927static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
1928 const SourceManager &SM,
1929 const LangOptions &LangOpts) {
1930 SourceLocation BeginLoc = FD->getQualifier()
1932 : FD->getNameInfo().getBeginLoc();
1933 // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
1934 // last token:
1936 FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
1937 SourceRange NameRange{BeginLoc, EndLoc};
1938
1939 return getRangeText(NameRange, SM, LangOpts);
1940}
1941
1942// Returns the text representing a `std::span` type where the element type is
1943// represented by `EltTyText`.
1944//
1945// Note the optional parameter `Qualifiers`: one needs to pass qualifiers
1946// explicitly if the element type needs to be qualified.
1947static std::string
1948getSpanTypeText(StringRef EltTyText,
1949 std::optional<Qualifiers> Quals = std::nullopt) {
1950 const char *const SpanOpen = "std::span<";
1951
1952 if (Quals)
1953 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
1954 return SpanOpen + EltTyText.str() + '>';
1955}
1956
1957std::optional<FixItList>
1959 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
1960
1961 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
1962 ASTContext &Ctx = VD->getASTContext();
1963 // std::span can't represent elements before its begin()
1964 if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
1965 if (ConstVal->isNegative())
1966 return std::nullopt;
1967
1968 // note that the expr may (oddly) has multiple layers of parens
1969 // example:
1970 // *((..(pointer + 123)..))
1971 // goal:
1972 // pointer[123]
1973 // Fix-It:
1974 // remove '*('
1975 // replace ' + ' with '['
1976 // replace ')' with ']'
1977
1978 // example:
1979 // *((..(123 + pointer)..))
1980 // goal:
1981 // 123[pointer]
1982 // Fix-It:
1983 // remove '*('
1984 // replace ' + ' with '['
1985 // replace ')' with ']'
1986
1987 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
1988 const SourceManager &SM = Ctx.getSourceManager();
1989 const LangOptions &LangOpts = Ctx.getLangOpts();
1990 CharSourceRange StarWithTrailWhitespace =
1992 LHS->getBeginLoc());
1993
1994 std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
1995 if (!LHSLocation)
1996 return std::nullopt;
1997
1998 CharSourceRange PlusWithSurroundingWhitespace =
1999 clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
2000
2001 std::optional<SourceLocation> AddOpLocation =
2002 getPastLoc(AddOp, SM, LangOpts);
2003 std::optional<SourceLocation> DerefOpLocation =
2004 getPastLoc(DerefOp, SM, LangOpts);
2005
2006 if (!AddOpLocation || !DerefOpLocation)
2007 return std::nullopt;
2008
2009 CharSourceRange ClosingParenWithPrecWhitespace =
2010 clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
2011
2012 return FixItList{
2013 {FixItHint::CreateRemoval(StarWithTrailWhitespace),
2014 FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
2015 FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
2016 }
2017 return std::nullopt; // something wrong or unsupported, give up
2018}
2019
2020std::optional<FixItList>
2021PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
2022 const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
2023 switch (S.lookup(VD)) {
2024 case FixitStrategy::Kind::Span: {
2025 ASTContext &Ctx = VD->getASTContext();
2027 // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
2028 // Deletes the *operand
2030 Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
2031 // Inserts the [0]
2032 if (auto LocPastOperand =
2033 getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
2034 return FixItList{{FixItHint::CreateRemoval(derefRange),
2035 FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
2036 }
2037 break;
2038 }
2039 case FixitStrategy::Kind::Iterator:
2040 case FixitStrategy::Kind::Array:
2041 return std::nullopt;
2042 case FixitStrategy::Kind::Vector:
2043 llvm_unreachable("FixitStrategy not implemented yet!");
2044 case FixitStrategy::Kind::Wontfix:
2045 llvm_unreachable("Invalid strategy!");
2046 }
2047
2048 return std::nullopt;
2049}
2050
2051static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
2052 const DeclRefExpr *DRE) {
2053 const SourceManager &SM = Ctx.getSourceManager();
2054 // Inserts the .data() after the DRE
2055 std::optional<SourceLocation> EndOfOperand =
2056 getPastLoc(DRE, SM, Ctx.getLangOpts());
2057
2058 if (EndOfOperand)
2059 return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
2060
2061 return std::nullopt;
2062}
2063
2064// Generates fix-its replacing an expression of the form UPC(DRE) with
2065// `DRE.data()`
2066std::optional<FixItList>
2067UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
2068 const auto VD = cast<VarDecl>(Node->getDecl());
2069 switch (S.lookup(VD)) {
2070 case FixitStrategy::Kind::Array:
2071 case FixitStrategy::Kind::Span: {
2072 return createDataFixit(VD->getASTContext(), Node);
2073 // FIXME: Points inside a macro expansion.
2074 break;
2075 }
2076 case FixitStrategy::Kind::Wontfix:
2077 case FixitStrategy::Kind::Iterator:
2078 return std::nullopt;
2079 case FixitStrategy::Kind::Vector:
2080 llvm_unreachable("unsupported strategies for FixableGadgets");
2081 }
2082
2083 return std::nullopt;
2084}
2085
2086// Generates fix-its replacing an expression of the form `&DRE[e]` with
2087// `&DRE.data()[e]`:
2088static std::optional<FixItList>
2090 const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
2091 const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
2092 // FIXME: this `getASTContext` call is costly, we should pass the
2093 // ASTContext in:
2094 const ASTContext &Ctx = DRE->getDecl()->getASTContext();
2095 const Expr *Idx = ArraySub->getIdx();
2096 const SourceManager &SM = Ctx.getSourceManager();
2097 const LangOptions &LangOpts = Ctx.getLangOpts();
2098 std::stringstream SS;
2099 bool IdxIsLitZero = false;
2100
2101 if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
2102 if ((*ICE).isZero())
2103 IdxIsLitZero = true;
2104 std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
2105 if (!DreString)
2106 return std::nullopt;
2107
2108 if (IdxIsLitZero) {
2109 // If the index is literal zero, we produce the most concise fix-it:
2110 SS << (*DreString).str() << ".data()";
2111 } else {
2112 std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
2113 if (!IndexString)
2114 return std::nullopt;
2115
2116 SS << "&" << (*DreString).str() << ".data()"
2117 << "[" << (*IndexString).str() << "]";
2118 }
2119 return FixItList{
2121}
2122
2123std::optional<FixItList>
2125 DeclUseList DREs = getClaimedVarUseSites();
2126
2127 if (DREs.size() != 1)
2128 return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
2129 // give up
2130 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
2131 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
2132 FixItList Fixes;
2133
2134 const Stmt *AddAssignNode = Node;
2135 StringRef varName = VD->getName();
2136 const ASTContext &Ctx = VD->getASTContext();
2137
2138 if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
2139 return std::nullopt;
2140
2141 // To transform UUC(p += n) to UUC(p = p.subspan(..)):
2142 bool NotParenExpr =
2143 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
2144 std::string SS = varName.str() + " = " + varName.str() + ".subspan";
2145 if (NotParenExpr)
2146 SS += "(";
2147
2148 std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
2149 AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
2150 if (!AddAssignLocation)
2151 return std::nullopt;
2152
2153 Fixes.push_back(FixItHint::CreateReplacement(
2154 SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
2155 SS));
2156 if (NotParenExpr)
2157 Fixes.push_back(FixItHint::CreateInsertion(
2158 Offset->getEndLoc().getLocWithOffset(1), ")"));
2159 return Fixes;
2160 }
2161 }
2162 return std::nullopt; // Not in the cases that we can handle for now, give up.
2163}
2164
2165std::optional<FixItList>
2167 DeclUseList DREs = getClaimedVarUseSites();
2168
2169 if (DREs.size() != 1)
2170 return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
2171 // give up
2172 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
2173 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
2174 FixItList Fixes;
2175 std::stringstream SS;
2176 StringRef varName = VD->getName();
2177 const ASTContext &Ctx = VD->getASTContext();
2178
2179 // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
2180 SS << "(" << varName.data() << " = " << varName.data()
2181 << ".subspan(1)).data()";
2182 std::optional<SourceLocation> PreIncLocation =
2184 if (!PreIncLocation)
2185 return std::nullopt;
2186
2187 Fixes.push_back(FixItHint::CreateReplacement(
2188 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
2189 return Fixes;
2190 }
2191 }
2192 return std::nullopt; // Not in the cases that we can handle for now, give up.
2193}
2194
2195// For a non-null initializer `Init` of `T *` type, this function returns
2196// `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
2197// to output stream.
2198// In many cases, this function cannot figure out the actual extent `S`. It
2199// then will use a place holder to replace `S` to ask users to fill `S` in. The
2200// initializer shall be used to initialize a variable of type `std::span<T>`.
2201// In some cases (e. g. constant size array) the initializer should remain
2202// unchanged and the function returns empty list. In case the function can't
2203// provide the right fixit it will return nullopt.
2204//
2205// FIXME: Support multi-level pointers
2206//
2207// Parameters:
2208// `Init` a pointer to the initializer expression
2209// `Ctx` a reference to the ASTContext
2210static std::optional<FixItList>
2212 const StringRef UserFillPlaceHolder) {
2213 const SourceManager &SM = Ctx.getSourceManager();
2214 const LangOptions &LangOpts = Ctx.getLangOpts();
2215
2216 // If `Init` has a constant value that is (or equivalent to) a
2217 // NULL pointer, we use the default constructor to initialize the span
2218 // object, i.e., a `std:span` variable declaration with no initializer.
2219 // So the fix-it is just to remove the initializer.
2220 if (Init->isNullPointerConstant(
2221 Ctx,
2222 // FIXME: Why does this function not ask for `const ASTContext
2223 // &`? It should. Maybe worth an NFC patch later.
2225 NPC_ValueDependentIsNotNull)) {
2226 std::optional<SourceLocation> InitLocation =
2227 getEndCharLoc(Init, SM, LangOpts);
2228 if (!InitLocation)
2229 return std::nullopt;
2230
2231 SourceRange SR(Init->getBeginLoc(), *InitLocation);
2232
2233 return FixItList{FixItHint::CreateRemoval(SR)};
2234 }
2235
2236 FixItList FixIts{};
2237 std::string ExtentText = UserFillPlaceHolder.data();
2238 StringRef One = "1";
2239
2240 // Insert `{` before `Init`:
2241 FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
2242 // Try to get the data extent. Break into different cases:
2243 if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
2244 // In cases `Init` is `new T[n]` and there is no explicit cast over
2245 // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
2246 // of `T`. So the extent is `n` unless `n` has side effects. Similar but
2247 // simpler for the case where `Init` is `new T`.
2248 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
2249 if (!Ext->HasSideEffects(Ctx)) {
2250 std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
2251 if (!ExtentString)
2252 return std::nullopt;
2253 ExtentText = *ExtentString;
2254 }
2255 } else if (!CxxNew->isArray())
2256 // Although the initializer is not allocating a buffer, the pointer
2257 // variable could still be used in buffer access operations.
2258 ExtentText = One;
2259 } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) {
2260 // std::span has a single parameter constructor for initialization with
2261 // constant size array. The size is auto-deduced as the constructor is a
2262 // function template. The correct fixit is empty - no changes should happen.
2263 return FixItList{};
2264 } else {
2265 // In cases `Init` is of the form `&Var` after stripping of implicit
2266 // casts, where `&` is the built-in operator, the extent is 1.
2267 if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
2268 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
2269 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
2270 ExtentText = One;
2271 // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
2272 // and explicit casting, etc. etc.
2273 }
2274
2275 SmallString<32> StrBuffer{};
2276 std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
2277
2278 if (!LocPassInit)
2279 return std::nullopt;
2280
2281 StrBuffer.append(", ");
2282 StrBuffer.append(ExtentText);
2283 StrBuffer.append("}");
2284 FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
2285 return FixIts;
2286}
2287
2288#ifndef NDEBUG
2289#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
2290 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
2291 "failed to produce fixit for declaration '" + \
2292 (D)->getNameAsString() + "'" + (Msg))
2293#else
2294#define DEBUG_NOTE_DECL_FAIL(D, Msg)
2295#endif
2296
2297// For the given variable declaration with a pointer-to-T type, returns the text
2298// `std::span<T>`. If it is unable to generate the text, returns
2299// `std::nullopt`.
2300static std::optional<std::string>
2302 assert(VD->getType()->isPointerType());
2303
2304 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
2305 std::optional<std::string> PteTyText = getPointeeTypeText(
2306 VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
2307
2308 if (!PteTyText)
2309 return std::nullopt;
2310
2311 std::string SpanTyText = "std::span<";
2312
2313 SpanTyText.append(*PteTyText);
2314 // Append qualifiers to span element type if any:
2315 if (PteTyQualifiers) {
2316 SpanTyText.append(" ");
2317 SpanTyText.append(PteTyQualifiers->getAsString());
2318 }
2319 SpanTyText.append(">");
2320 return SpanTyText;
2321}
2322
2323// For a `VarDecl` of the form `T * var (= Init)?`, this
2324// function generates fix-its that
2325// 1) replace `T * var` with `std::span<T> var`; and
2326// 2) change `Init` accordingly to a span constructor, if it exists.
2327//
2328// FIXME: support Multi-level pointers
2329//
2330// Parameters:
2331// `D` a pointer the variable declaration node
2332// `Ctx` a reference to the ASTContext
2333// `UserFillPlaceHolder` the user-input placeholder text
2334// Returns:
2335// the non-empty fix-it list, if fix-its are successfuly generated; empty
2336// list otherwise.
2337static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
2338 const StringRef UserFillPlaceHolder,
2339 UnsafeBufferUsageHandler &Handler) {
2341 return {};
2342
2343 FixItList FixIts{};
2344 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
2345
2346 if (!SpanTyText) {
2347 DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
2348 return {};
2349 }
2350
2351 // Will hold the text for `std::span<T> Ident`:
2352 std::stringstream SS;
2353
2354 SS << *SpanTyText;
2355 // Fix the initializer if it exists:
2356 if (const Expr *Init = D->getInit()) {
2357 std::optional<FixItList> InitFixIts =
2358 FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
2359 if (!InitFixIts)
2360 return {};
2361 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
2362 std::make_move_iterator(InitFixIts->end()));
2363 }
2364 // For declaration of the form `T * ident = init;`, we want to replace
2365 // `T * ` with `std::span<T>`.
2366 // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
2367 // just `T *` with `std::span<T>`.
2368 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
2369 if (!EndLocForReplacement.isValid()) {
2370 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
2371 return {};
2372 }
2373 // The only exception is that for `T *ident` we'll add a single space between
2374 // "std::span<T>" and "ident".
2375 // FIXME: The condition is false for identifiers expended from macros.
2376 if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
2377 SS << " ";
2378
2379 FixIts.push_back(FixItHint::CreateReplacement(
2380 SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
2381 return FixIts;
2382}
2383
2384static bool hasConflictingOverload(const FunctionDecl *FD) {
2385 return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
2386}
2387
2388// For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
2389// types, this function produces fix-its to make the change self-contained. Let
2390// 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
2391// entity defined by the `FunctionDecl` after the change to the parameters.
2392// Fix-its produced by this function are
2393// 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
2394// of 'F';
2395// 2. Create a declaration of "NewF" next to each declaration of `F`;
2396// 3. Create a definition of "F" (as its' original definition is now belongs
2397// to "NewF") next to its original definition. The body of the creating
2398// definition calls to "NewF".
2399//
2400// Example:
2401//
2402// void f(int *p); // original declaration
2403// void f(int *p) { // original definition
2404// p[5];
2405// }
2406//
2407// To change the parameter `p` to be of `std::span<int>` type, we
2408// also add overloads:
2409//
2410// [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
2411// void f(std::span<int> p); // added overload decl
2412// void f(std::span<int> p) { // original def where param is changed
2413// p[5];
2414// }
2415// [[clang::unsafe_buffer_usage]] void f(int *p) { // added def
2416// return f(std::span(p, <# size #>));
2417// }
2418//
2419static std::optional<FixItList>
2421 const ASTContext &Ctx,
2422 UnsafeBufferUsageHandler &Handler) {
2423 // FIXME: need to make this conflict checking better:
2424 if (hasConflictingOverload(FD))
2425 return std::nullopt;
2426
2427 const SourceManager &SM = Ctx.getSourceManager();
2428 const LangOptions &LangOpts = Ctx.getLangOpts();
2429 const unsigned NumParms = FD->getNumParams();
2430 std::vector<std::string> NewTysTexts(NumParms);
2431 std::vector<bool> ParmsMask(NumParms, false);
2432 bool AtLeastOneParmToFix = false;
2433
2434 for (unsigned i = 0; i < NumParms; i++) {
2435 const ParmVarDecl *PVD = FD->getParamDecl(i);
2436
2437 if (S.lookup(PVD) == FixitStrategy::Kind::Wontfix)
2438 continue;
2439 if (S.lookup(PVD) != FixitStrategy::Kind::Span)
2440 // Not supported, not suppose to happen:
2441 return std::nullopt;
2442
2443 std::optional<Qualifiers> PteTyQuals = std::nullopt;
2444 std::optional<std::string> PteTyText =
2445 getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
2446
2447 if (!PteTyText)
2448 // something wrong in obtaining the text of the pointee type, give up
2449 return std::nullopt;
2450 // FIXME: whether we should create std::span type depends on the
2451 // FixitStrategy.
2452 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
2453 ParmsMask[i] = true;
2454 AtLeastOneParmToFix = true;
2455 }
2456 if (!AtLeastOneParmToFix)
2457 // No need to create function overloads:
2458 return {};
2459 // FIXME Respect indentation of the original code.
2460
2461 // A lambda that creates the text representation of a function declaration
2462 // with the new type signatures:
2463 const auto NewOverloadSignatureCreator =
2464 [&SM, &LangOpts, &NewTysTexts,
2465 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
2466 std::stringstream SS;
2467
2468 SS << ";";
2469 SS << getEndOfLine().str();
2470 // Append: ret-type func-name "("
2471 if (auto Prefix = getRangeText(
2472 SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
2473 SM, LangOpts))
2474 SS << Prefix->str();
2475 else
2476 return std::nullopt; // give up
2477 // Append: parameter-type-list
2478 const unsigned NumParms = FD->getNumParams();
2479
2480 for (unsigned i = 0; i < NumParms; i++) {
2481 const ParmVarDecl *Parm = FD->getParamDecl(i);
2482
2483 if (Parm->isImplicit())
2484 continue;
2485 if (ParmsMask[i]) {
2486 // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
2487 // new type:
2488 SS << NewTysTexts[i];
2489 // print parameter name if provided:
2490 if (IdentifierInfo *II = Parm->getIdentifier())
2491 SS << ' ' << II->getName().str();
2492 } else if (auto ParmTypeText =
2493 getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts),
2494 SM, LangOpts)) {
2495 // print the whole `Parm` without modification:
2496 SS << ParmTypeText->str();
2497 } else
2498 return std::nullopt; // something wrong, give up
2499 if (i != NumParms - 1)
2500 SS << ", ";
2501 }
2502 SS << ")";
2503 return SS.str();
2504 };
2505
2506 // A lambda that creates the text representation of a function definition with
2507 // the original signature:
2508 const auto OldOverloadDefCreator =
2509 [&Handler, &SM, &LangOpts, &NewTysTexts,
2510 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
2511 std::stringstream SS;
2512
2513 SS << getEndOfLine().str();
2514 // Append: attr-name ret-type func-name "(" param-list ")" "{"
2515 if (auto FDPrefix = getRangeText(
2516 SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
2517 LangOpts))
2518 SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
2519 << FDPrefix->str() << "{";
2520 else
2521 return std::nullopt;
2522 // Append: "return" func-name "("
2523 if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
2524 SS << "return " << FunQualName->str() << "(";
2525 else
2526 return std::nullopt;
2527
2528 // Append: arg-list
2529 const unsigned NumParms = FD->getNumParams();
2530 for (unsigned i = 0; i < NumParms; i++) {
2531 const ParmVarDecl *Parm = FD->getParamDecl(i);
2532
2533 if (Parm->isImplicit())
2534 continue;
2535 // FIXME: If a parameter has no name, it is unused in the
2536 // definition. So we could just leave it as it is.
2537 if (!Parm->getIdentifier())
2538 // If a parameter of a function definition has no name:
2539 return std::nullopt;
2540 if (ParmsMask[i])
2541 // This is our spanified paramter!
2542 SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
2543 << ", " << getUserFillPlaceHolder("size") << ")";
2544 else
2545 SS << Parm->getIdentifier()->getName().str();
2546 if (i != NumParms - 1)
2547 SS << ", ";
2548 }
2549 // finish call and the body
2550 SS << ");}" << getEndOfLine().str();
2551 // FIXME: 80-char line formatting?
2552 return SS.str();
2553 };
2554
2555 FixItList FixIts{};
2556 for (FunctionDecl *FReDecl : FD->redecls()) {
2557 std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
2558
2559 if (!Loc)
2560 return {};
2561 if (FReDecl->isThisDeclarationADefinition()) {
2562 assert(FReDecl == FD && "inconsistent function definition");
2563 // Inserts a definition with the old signature to the end of
2564 // `FReDecl`:
2565 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
2566 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
2567 else
2568 return {}; // give up
2569 } else {
2570 // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
2571 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
2572 FixIts.emplace_back(FixItHint::CreateInsertion(
2573 FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
2574 FReDecl->getBeginLoc(), " ")));
2575 }
2576 // Inserts a declaration with the new signature to the end of `FReDecl`:
2577 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
2578 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
2579 else
2580 return {};
2581 }
2582 }
2583 return FixIts;
2584}
2585
2586// To fix a `ParmVarDecl` to be of `std::span` type.
2587static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
2588 UnsafeBufferUsageHandler &Handler) {
2590 DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
2591 return {};
2592 }
2593 if (PVD->hasDefaultArg()) {
2594 // FIXME: generate fix-its for default values:
2595 DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
2596 return {};
2597 }
2598
2599 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
2600 std::optional<std::string> PteTyText = getPointeeTypeText(
2601 PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
2602
2603 if (!PteTyText) {
2604 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
2605 return {};
2606 }
2607
2608 std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
2609
2610 if (!PVDNameText) {
2611 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
2612 return {};
2613 }
2614
2615 std::stringstream SS;
2616 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
2617
2618 if (PteTyQualifiers)
2619 // Append qualifiers if they exist:
2620 SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
2621 else
2622 SS << getSpanTypeText(*PteTyText);
2623 // Append qualifiers to the type of the parameter:
2624 if (PVD->getType().hasQualifiers())
2625 SS << ' ' << PVD->getType().getQualifiers().getAsString();
2626 // Append parameter's name:
2627 SS << ' ' << PVDNameText->str();
2628 // Add replacement fix-it:
2629 return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
2630}
2631
2632static FixItList fixVariableWithSpan(const VarDecl *VD,
2633 const DeclUseTracker &Tracker,
2634 ASTContext &Ctx,
2635 UnsafeBufferUsageHandler &Handler) {
2636 const DeclStmt *DS = Tracker.lookupDecl(VD);
2637 if (!DS) {
2639 " : variables declared this way not implemented yet");
2640 return {};
2641 }
2642 if (!DS->isSingleDecl()) {
2643 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
2644 DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
2645 return {};
2646 }
2647 // Currently DS is an unused variable but we'll need it when
2648 // non-single decls are implemented, where the pointee type name
2649 // and the '*' are spread around the place.
2650 (void)DS;
2651
2652 // FIXME: handle cases where DS has multiple declarations
2653 return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
2654}
2655
2656static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
2657 UnsafeBufferUsageHandler &Handler) {
2658 FixItList FixIts{};
2659
2660 // Note: the code below expects the declaration to not use any type sugar like
2661 // typedef.
2662 if (auto CAT = dyn_cast<clang::ConstantArrayType>(D->getType())) {
2663 const QualType &ArrayEltT = CAT->getElementType();
2664 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
2665 // FIXME: support multi-dimensional arrays
2666 if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType()))
2667 return {};
2668
2670
2671 // Get the spelling of the element type as written in the source file
2672 // (including macros, etc.).
2673 auto MaybeElemTypeTxt =
2675 Ctx.getLangOpts());
2676 if (!MaybeElemTypeTxt)
2677 return {};
2678 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
2679
2680 // Find the '[' token.
2681 std::optional<Token> NextTok = Lexer::findNextToken(
2683 while (NextTok && !NextTok->is(tok::l_square) &&
2684 NextTok->getLocation() <= D->getSourceRange().getEnd())
2685 NextTok = Lexer::findNextToken(NextTok->getLocation(),
2686 Ctx.getSourceManager(), Ctx.getLangOpts());
2687 if (!NextTok)
2688 return {};
2689 const SourceLocation LSqBracketLoc = NextTok->getLocation();
2690
2691 // Get the spelling of the array size as written in the source file
2692 // (including macros, etc.).
2693 auto MaybeArraySizeTxt = getRangeText(
2694 {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
2695 Ctx.getSourceManager(), Ctx.getLangOpts());
2696 if (!MaybeArraySizeTxt)
2697 return {};
2698 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
2699 if (ArraySizeTxt.empty()) {
2700 // FIXME: Support array size getting determined from the initializer.
2701 // Examples:
2702 // int arr1[] = {0, 1, 2};
2703 // int arr2{3, 4, 5};
2704 // We might be able to preserve the non-specified size with `auto` and
2705 // `std::to_array`:
2706 // auto arr1 = std::to_array<int>({0, 1, 2});
2707 return {};
2708 }
2709
2710 std::optional<StringRef> IdentText =
2712
2713 if (!IdentText) {
2714 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
2715 return {};
2716 }
2717
2718 SmallString<32> Replacement;
2719 raw_svector_ostream OS(Replacement);
2720 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
2721 << IdentText->str();
2722
2723 FixIts.push_back(FixItHint::CreateReplacement(
2724 SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));
2725 }
2726
2727 return FixIts;
2728}
2729
2730static FixItList fixVariableWithArray(const VarDecl *VD,
2731 const DeclUseTracker &Tracker,
2732 const ASTContext &Ctx,
2733 UnsafeBufferUsageHandler &Handler) {
2734 const DeclStmt *DS = Tracker.lookupDecl(VD);
2735 assert(DS && "Fixing non-local variables not implemented yet!");
2736 if (!DS->isSingleDecl()) {
2737 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
2738 return {};
2739 }
2740 // Currently DS is an unused variable but we'll need it when
2741 // non-single decls are implemented, where the pointee type name
2742 // and the '*' are spread around the place.
2743 (void)DS;
2744
2745 // FIXME: handle cases where DS has multiple declarations
2746 return fixVarDeclWithArray(VD, Ctx, Handler);
2747}
2748
2749// TODO: we should be consistent to use `std::nullopt` to represent no-fix due
2750// to any unexpected problem.
2751static FixItList
2753 /* The function decl under analysis */ const Decl *D,
2754 const DeclUseTracker &Tracker, ASTContext &Ctx,
2755 UnsafeBufferUsageHandler &Handler) {
2756 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
2757 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
2758 if (!FD || FD != D) {
2759 // `FD != D` means that `PVD` belongs to a function that is not being
2760 // analyzed currently. Thus `FD` may not be complete.
2761 DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
2762 return {};
2763 }
2764
2765 // TODO If function has a try block we can't change params unless we check
2766 // also its catch block for their use.
2767 // FIXME We might support static class methods, some select methods,
2768 // operators and possibly lamdas.
2769 if (FD->isMain() || FD->isConstexpr() ||
2770 FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
2771 FD->isVariadic() ||
2772 // also covers call-operator of lamdas
2773 isa<CXXMethodDecl>(FD) ||
2774 // skip when the function body is a try-block
2775 (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
2776 FD->isOverloadedOperator()) {
2777 DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
2778 return {}; // TODO test all these cases
2779 }
2780 }
2781
2782 switch (K) {
2783 case FixitStrategy::Kind::Span: {
2784 if (VD->getType()->isPointerType()) {
2785 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
2786 return fixParamWithSpan(PVD, Ctx, Handler);
2787
2788 if (VD->isLocalVarDecl())
2789 return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
2790 }
2791 DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
2792 return {};
2793 }
2794 case FixitStrategy::Kind::Array: {
2795 if (VD->isLocalVarDecl() &&
2796 isa<clang::ConstantArrayType>(VD->getType().getCanonicalType()))
2797 return fixVariableWithArray(VD, Tracker, Ctx, Handler);
2798
2799 DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
2800 return {};
2801 }
2802 case FixitStrategy::Kind::Iterator:
2803 case FixitStrategy::Kind::Vector:
2804 llvm_unreachable("FixitStrategy not implemented yet!");
2805 case FixitStrategy::Kind::Wontfix:
2806 llvm_unreachable("Invalid strategy!");
2807 }
2808 llvm_unreachable("Unknown strategy!");
2809}
2810
2811// Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
2812// `RemoveRange` of 'h' overlaps with a macro use.
2813static bool overlapWithMacro(const FixItList &FixIts) {
2814 // FIXME: For now we only check if the range (or the first token) is (part of)
2815 // a macro expansion. Ideally, we want to check for all tokens in the range.
2816 return llvm::any_of(FixIts, [](const FixItHint &Hint) {
2817 auto Range = Hint.RemoveRange;
2819 // If the range (or the first token) is (part of) a macro expansion:
2820 return true;
2821 return false;
2822 });
2823}
2824
2825// Returns true iff `VD` is a parameter of the declaration `D`:
2826static bool isParameterOf(const VarDecl *VD, const Decl *D) {
2827 return isa<ParmVarDecl>(VD) &&
2828 VD->getDeclContext() == dyn_cast<DeclContext>(D);
2829}
2830
2831// Erases variables in `FixItsForVariable`, if such a variable has an unfixable
2832// group mate. A variable `v` is unfixable iff `FixItsForVariable` does not
2833// contain `v`.
2835 std::map<const VarDecl *, FixItList> &FixItsForVariable,
2836 const VariableGroupsManager &VarGrpMgr) {
2837 // Variables will be removed from `FixItsForVariable`:
2839
2840 for (const auto &[VD, Ignore] : FixItsForVariable) {
2841 VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
2842 if (llvm::any_of(Grp,
2843 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
2844 return !FixItsForVariable.count(GrpMember);
2845 })) {
2846 // At least one group member cannot be fixed, so we have to erase the
2847 // whole group:
2848 for (const VarDecl *Member : Grp)
2849 ToErase.push_back(Member);
2850 }
2851 }
2852 for (auto *VarToErase : ToErase)
2853 FixItsForVariable.erase(VarToErase);
2854}
2855
2856// Returns the fix-its that create bounds-safe function overloads for the
2857// function `D`, if `D`'s parameters will be changed to safe-types through
2858// fix-its in `FixItsForVariable`.
2859//
2860// NOTE: In case `D`'s parameters will be changed but bounds-safe function
2861// overloads cannot created, the whole group that contains the parameters will
2862// be erased from `FixItsForVariable`.
2864 std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
2865 const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
2866 const FixitStrategy &S, ASTContext &Ctx,
2867 UnsafeBufferUsageHandler &Handler) {
2868 FixItList FixItsSharedByParms{};
2869
2870 std::optional<FixItList> OverloadFixes =
2871 createOverloadsForFixedParams(S, FD, Ctx, Handler);
2872
2873 if (OverloadFixes) {
2874 FixItsSharedByParms.append(*OverloadFixes);
2875 } else {
2876 // Something wrong in generating `OverloadFixes`, need to remove the
2877 // whole group, where parameters are in, from `FixItsForVariable` (Note
2878 // that all parameters should be in the same group):
2879 for (auto *Member : VarGrpMgr.getGroupOfParms())
2880 FixItsForVariable.erase(Member);
2881 }
2882 return FixItsSharedByParms;
2883}
2884
2885// Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
2886static std::map<const VarDecl *, FixItList>
2887getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S,
2888 ASTContext &Ctx,
2889 /* The function decl under analysis */ const Decl *D,
2890 const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
2891 const VariableGroupsManager &VarGrpMgr) {
2892 // `FixItsForVariable` will map each variable to a set of fix-its directly
2893 // associated to the variable itself. Fix-its of distinct variables in
2894 // `FixItsForVariable` are disjoint.
2895 std::map<const VarDecl *, FixItList> FixItsForVariable;
2896
2897 // Populate `FixItsForVariable` with fix-its directly associated with each
2898 // variable. Fix-its directly associated to a variable 'v' are the ones
2899 // produced by the `FixableGadget`s whose claimed variable is 'v'.
2900 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
2901 FixItsForVariable[VD] =
2902 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
2903 // If we fail to produce Fix-It for the declaration we have to skip the
2904 // variable entirely.
2905 if (FixItsForVariable[VD].empty()) {
2906 FixItsForVariable.erase(VD);
2907 continue;
2908 }
2909 for (const auto &F : Fixables) {
2910 std::optional<FixItList> Fixits = F->getFixits(S);
2911
2912 if (Fixits) {
2913 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
2914 Fixits->begin(), Fixits->end());
2915 continue;
2916 }
2917#ifndef NDEBUG
2918 Handler.addDebugNoteForVar(
2919 VD, F->getSourceLoc(),
2920 ("gadget '" + F->getDebugName() + "' refused to produce a fix")
2921 .str());
2922#endif
2923 FixItsForVariable.erase(VD);
2924 break;
2925 }
2926 }
2927
2928 // `FixItsForVariable` now contains only variables that can be
2929 // fixed. A variable can be fixed if its' declaration and all Fixables
2930 // associated to it can all be fixed.
2931
2932 // To further remove from `FixItsForVariable` variables whose group mates
2933 // cannot be fixed...
2934 eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
2935 // Now `FixItsForVariable` gets further reduced: a variable is in
2936 // `FixItsForVariable` iff it can be fixed and all its group mates can be
2937 // fixed.
2938
2939 // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
2940 // That is, when fixing multiple parameters in one step, these fix-its will
2941 // be applied only once (instead of being applied per parameter).
2942 FixItList FixItsSharedByParms{};
2943
2944 if (auto *FD = dyn_cast<FunctionDecl>(D))
2945 FixItsSharedByParms = createFunctionOverloadsForParms(
2946 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
2947
2948 // The map that maps each variable `v` to fix-its for the whole group where
2949 // `v` is in:
2950 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
2951 FixItsForVariable};
2952
2953 for (auto &[Var, Ignore] : FixItsForVariable) {
2954 bool AnyParm = false;
2955 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
2956
2957 for (const VarDecl *GrpMate : VarGroupForVD) {
2958 if (Var == GrpMate)
2959 continue;
2960 if (FixItsForVariable.count(GrpMate))
2961 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
2962 }
2963 if (AnyParm) {
2964 // This assertion should never fail. Otherwise we have a bug.
2965 assert(!FixItsSharedByParms.empty() &&
2966 "Should not try to fix a parameter that does not belong to a "
2967 "FunctionDecl");
2968 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
2969 }
2970 }
2971 // Fix-its that will be applied in one step shall NOT:
2972 // 1. overlap with macros or/and templates; or
2973 // 2. conflict with each other.
2974 // Otherwise, the fix-its will be dropped.
2975 for (auto Iter = FinalFixItsForVariable.begin();
2976 Iter != FinalFixItsForVariable.end();)
2977 if (overlapWithMacro(Iter->second) ||
2979 Iter = FinalFixItsForVariable.erase(Iter);
2980 } else
2981 Iter++;
2982 return FinalFixItsForVariable;
2983}
2984
2985template <typename VarDeclIterTy>
2986static FixitStrategy
2987getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
2988 FixitStrategy S;
2989 for (const VarDecl *VD : UnsafeVars) {
2990 if (isa<ConstantArrayType>(VD->getType().getCanonicalType()))
2991 S.set(VD, FixitStrategy::Kind::Array);
2992 else
2993 S.set(VD, FixitStrategy::Kind::Span);
2994 }
2995 return S;
2996}
2997
2998// Manages variable groups:
3000 const std::vector<VarGrpTy> Groups;
3001 const std::map<const VarDecl *, unsigned> &VarGrpMap;
3002 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
3003
3004public:
3006 const std::vector<VarGrpTy> &Groups,
3007 const std::map<const VarDecl *, unsigned> &VarGrpMap,
3008 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
3009 : Groups(Groups), VarGrpMap(VarGrpMap),
3010 GrpsUnionForParms(GrpsUnionForParms) {}
3011
3012 VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
3013 if (GrpsUnionForParms.contains(Var)) {
3014 if (HasParm)
3015 *HasParm = true;
3016 return GrpsUnionForParms.getArrayRef();
3017 }
3018 if (HasParm)
3019 *HasParm = false;
3020
3021 auto It = VarGrpMap.find(Var);
3022
3023 if (It == VarGrpMap.end())
3024 return std::nullopt;
3025 return Groups[It->second];
3026 }
3027
3028 VarGrpRef getGroupOfParms() const override {
3029 return GrpsUnionForParms.getArrayRef();
3030 }
3031};
3032
3034 UnsafeBufferUsageHandler &Handler,
3035 bool EmitSuggestions) {
3036#ifndef NDEBUG
3037 Handler.clearDebugNotes();
3038#endif
3039
3040 assert(D && D->getBody());
3041 // We do not want to visit a Lambda expression defined inside a method
3042 // independently. Instead, it should be visited along with the outer method.
3043 // FIXME: do we want to do the same thing for `BlockDecl`s?
3044 if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) {
3045 if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass())
3046 return;
3047 }
3048
3049 // Do not emit fixit suggestions for functions declared in an
3050 // extern "C" block.
3051 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
3052 for (FunctionDecl *FReDecl : FD->redecls()) {
3053 if (FReDecl->isExternC()) {
3054 EmitSuggestions = false;
3055 break;
3056 }
3057 }
3058 }
3059
3060 WarningGadgetSets UnsafeOps;
3061 FixableGadgetSets FixablesForAllVars;
3062
3063 auto [FixableGadgets, WarningGadgets, Tracker] =
3064 findGadgets(D, Handler, EmitSuggestions);
3065
3066 if (!EmitSuggestions) {
3067 // Our job is very easy without suggestions. Just warn about
3068 // every problematic operation and consider it done. No need to deal
3069 // with fixable gadgets, no need to group operations by variable.
3070 for (const auto &G : WarningGadgets) {
3071 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
3072 D->getASTContext());
3073 }
3074
3075 // This return guarantees that most of the machine doesn't run when
3076 // suggestions aren't requested.
3077 assert(FixableGadgets.size() == 0 &&
3078 "Fixable gadgets found but suggestions not requested!");
3079 return;
3080 }
3081
3082 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
3083 // function under the analysis. No need to fix any Fixables.
3084 if (!WarningGadgets.empty()) {
3085 // Gadgets "claim" variables they're responsible for. Once this loop
3086 // finishes, the tracker will only track DREs that weren't claimed by any
3087 // gadgets, i.e. not understood by the analysis.
3088 for (const auto &G : FixableGadgets) {
3089 for (const auto *DRE : G->getClaimedVarUseSites()) {
3090 Tracker.claimUse(DRE);
3091 }
3092 }
3093 }
3094
3095 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
3096 // function under the analysis. Thus, it early returns here as there is
3097 // nothing needs to be fixed.
3098 //
3099 // Note this claim is based on the assumption that there is no unsafe
3100 // variable whose declaration is invisible from the analyzing function.
3101 // Otherwise, we need to consider if the uses of those unsafe varuables needs
3102 // fix.
3103 // So far, we are not fixing any global variables or class members. And,
3104 // lambdas will be analyzed along with the enclosing function. So this early
3105 // return is correct for now.
3106 if (WarningGadgets.empty())
3107 return;
3108
3109 UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
3110 FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets));
3111
3112 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
3113
3114 // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
3115 for (auto it = FixablesForAllVars.byVar.cbegin();
3116 it != FixablesForAllVars.byVar.cend();) {
3117 // FIXME: need to deal with global variables later
3118 if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
3119#ifndef NDEBUG
3120 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3121 ("failed to produce fixit for '" +
3122 it->first->getNameAsString() +
3123 "' : neither local nor a parameter"));
3124#endif
3125 it = FixablesForAllVars.byVar.erase(it);
3126 } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
3127#ifndef NDEBUG
3128 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3129 ("failed to produce fixit for '" +
3130 it->first->getNameAsString() +
3131 "' : has a reference type"));
3132#endif
3133 it = FixablesForAllVars.byVar.erase(it);
3134 } else if (Tracker.hasUnclaimedUses(it->first)) {
3135 it = FixablesForAllVars.byVar.erase(it);
3136 } else if (it->first->isInitCapture()) {
3137#ifndef NDEBUG
3138 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3139 ("failed to produce fixit for '" +
3140 it->first->getNameAsString() +
3141 "' : init capture"));
3142#endif
3143 it = FixablesForAllVars.byVar.erase(it);
3144 } else {
3145 ++it;
3146 }
3147 }
3148
3149#ifndef NDEBUG
3150 for (const auto &it : UnsafeOps.byVar) {
3151 const VarDecl *const UnsafeVD = it.first;
3152 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
3153 if (UnclaimedDREs.empty())
3154 continue;
3155 const auto UnfixedVDName = UnsafeVD->getNameAsString();
3156 for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
3157 std::string UnclaimedUseTrace =
3158 getDREAncestorString(UnclaimedDRE, D->getASTContext());
3159
3160 Handler.addDebugNoteForVar(
3161 UnsafeVD, UnclaimedDRE->getBeginLoc(),
3162 ("failed to produce fixit for '" + UnfixedVDName +
3163 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
3164 UnclaimedUseTrace));
3165 }
3166 }
3167#endif
3168
3169 // Fixpoint iteration for pointer assignments
3170 using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
3171 DepMapTy DependenciesMap{};
3172 DepMapTy PtrAssignmentGraph{};
3173
3174 for (auto it : FixablesForAllVars.byVar) {
3175 for (const FixableGadget *fixable : it.second) {
3176 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
3177 fixable->getStrategyImplications();
3178 if (ImplPair) {
3179 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
3180 PtrAssignmentGraph[Impl.first].insert(Impl.second);
3181 }
3182 }
3183 }
3184
3185 /*
3186 The following code does a BFS traversal of the `PtrAssignmentGraph`
3187 considering all unsafe vars as starting nodes and constructs an undirected
3188 graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
3189 elimiates all variables that are unreachable from any unsafe var. In other
3190 words, this removes all dependencies that don't include any unsafe variable
3191 and consequently don't need any fixit generation.
3192 Note: A careful reader would observe that the code traverses
3193 `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
3194 `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
3195 achieve the same result but the one used here dramatically cuts the
3196 amount of hoops the second part of the algorithm needs to jump, given that
3197 a lot of these connections become "direct". The reader is advised not to
3198 imagine how the graph is transformed because of using `Var` instead of
3199 `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
3200 and think about why it's equivalent later.
3201 */
3202 std::set<const VarDecl *> VisitedVarsDirected{};
3203 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
3204 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
3205
3206 std::queue<const VarDecl *> QueueDirected{};
3207 QueueDirected.push(Var);
3208 while (!QueueDirected.empty()) {
3209 const VarDecl *CurrentVar = QueueDirected.front();
3210 QueueDirected.pop();
3211 VisitedVarsDirected.insert(CurrentVar);
3212 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
3213 for (const VarDecl *Adj : AdjacentNodes) {
3214 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
3215 QueueDirected.push(Adj);
3216 }
3217 DependenciesMap[Var].insert(Adj);
3218 DependenciesMap[Adj].insert(Var);
3219 }
3220 }
3221 }
3222 }
3223
3224 // `Groups` stores the set of Connected Components in the graph.
3225 std::vector<VarGrpTy> Groups;
3226 // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
3227 // variables belong to. Group indexes refer to the elements in `Groups`.
3228 // `VarGrpMap` is complete in that every variable that needs fix is in it.
3229 std::map<const VarDecl *, unsigned> VarGrpMap;
3230 // The union group over the ones in "Groups" that contain parameters of `D`:
3231 llvm::SetVector<const VarDecl *>
3232 GrpsUnionForParms; // these variables need to be fixed in one step
3233
3234 // Group Connected Components for Unsafe Vars
3235 // (Dependencies based on pointer assignments)
3236 std::set<const VarDecl *> VisitedVars{};
3237 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
3238 if (VisitedVars.find(Var) == VisitedVars.end()) {
3239 VarGrpTy &VarGroup = Groups.emplace_back();
3240 std::queue<const VarDecl *> Queue{};
3241
3242 Queue.push(Var);
3243 while (!Queue.empty()) {
3244 const VarDecl *CurrentVar = Queue.front();
3245 Queue.pop();
3246 VisitedVars.insert(CurrentVar);
3247 VarGroup.push_back(CurrentVar);
3248 auto AdjacentNodes = DependenciesMap[CurrentVar];
3249 for (const VarDecl *Adj : AdjacentNodes) {
3250 if (VisitedVars.find(Adj) == VisitedVars.end()) {
3251 Queue.push(Adj);
3252 }
3253 }
3254 }
3255
3256 bool HasParm = false;
3257 unsigned GrpIdx = Groups.size() - 1;
3258
3259 for (const VarDecl *V : VarGroup) {
3260 VarGrpMap[V] = GrpIdx;
3261 if (!HasParm && isParameterOf(V, D))
3262 HasParm = true;
3263 }
3264 if (HasParm)
3265 GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end());
3266 }
3267 }
3268
3269 // Remove a `FixableGadget` if the associated variable is not in the graph
3270 // computed above. We do not want to generate fix-its for such variables,
3271 // since they are neither warned nor reachable from a warned one.
3272 //
3273 // Note a variable is not warned if it is not directly used in any unsafe
3274 // operation. A variable `v` is NOT reachable from an unsafe variable, if it
3275 // does not exist another variable `u` such that `u` is warned and fixing `u`
3276 // (transitively) implicates fixing `v`.
3277 //
3278 // For example,
3279 // ```
3280 // void f(int * p) {
3281 // int * a = p; *p = 0;
3282 // }
3283 // ```
3284 // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
3285 // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of
3286 // the function above, `p` becomes reachable from a warned variable.
3287 for (auto I = FixablesForAllVars.byVar.begin();
3288 I != FixablesForAllVars.byVar.end();) {
3289 // Note `VisitedVars` contain all the variables in the graph:
3290 if (!VisitedVars.count((*I).first)) {
3291 // no such var in graph:
3292 I = FixablesForAllVars.byVar.erase(I);
3293 } else
3294 ++I;
3295 }
3296
3297 // We assign strategies to variables that are 1) in the graph and 2) can be
3298 // fixed. Other variables have the default "Won't fix" strategy.
3299 FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
3300 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
3301 // If a warned variable has no "Fixable", it is considered unfixable:
3302 return FixablesForAllVars.byVar.count(V);
3303 }));
3304 VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
3305
3306 if (isa<NamedDecl>(D))
3307 // The only case where `D` is not a `NamedDecl` is when `D` is a
3308 // `BlockDecl`. Let's not fix variables in blocks for now
3309 FixItsForVariableGroup =
3310 getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
3311 Tracker, Handler, VarGrpMgr);
3312
3313 for (const auto &G : UnsafeOps.noVar) {
3314 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
3315 D->getASTContext());
3316 }
3317
3318 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
3319 auto FixItsIt = FixItsForVariableGroup.find(VD);
3320 Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
3321 FixItsIt != FixItsForVariableGroup.end()
3322 ? std::move(FixItsIt->second)
3323 : FixItList{},
3324 D, NaiveStrategy);
3325 for (const auto &G : WarningGadgets) {
3326 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
3327 D->getASTContext());
3328 }
3329 }
3330}
Defines the clang::ASTContext interface.
#define V(N, I)
Definition: ASTContext.h:3285
BoundNodesTreeBuilder Nodes
DynTypedNode Node
#define AST_MATCHER(Type, DefineMatcher)
AST_MATCHER(Type, DefineMatcher) { ... } defines a zero parameter function named DefineMatcher() that...
#define AST_MATCHER_P(Type, DefineMatcher, ParamType, Param)
AST_MATCHER_P(Type, DefineMatcher, ParamType, Param) { ... } defines a single-parameter function name...
#define SM(sm)
Definition: Cuda.cpp:83
llvm::APSInt APSInt
static Decl::Kind getKind(const Decl *D)
Definition: DeclBase.cpp:1125
StringRef Text
Definition: Format.cpp:2976
unsigned Iter
Definition: HTMLLogger.cpp:154
static const Decl * getCanonicalDecl(const Decl *D)
llvm::MachO::Target Target
Definition: MachO.h:50
Defines the clang::Preprocessor interface.
static bool hasAttr(const Decl *D, bool IgnoreImplicitAttr)
Definition: SemaCUDA.cpp:111
SourceRange Range
Definition: SemaObjC.cpp:754
SourceLocation Loc
Definition: SemaObjC.cpp:755
Defines the clang::SourceLocation class and associated facilities.
SourceLocation Begin
static FixItList fixVariableWithSpan(const VarDecl *VD, const DeclUseTracker &Tracker, ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
static std::optional< FixItList > fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator *Node)
static std::optional< StringRef > getExprText(const Expr *E, const SourceManager &SM, const LangOptions &LangOpts)
static WarningGadgetSets groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations)
static StringRef getEndOfLine()
static std::optional< FixItList > FixVarInitializerWithSpan(const Expr *Init, ASTContext &Ctx, const StringRef UserFillPlaceHolder)
#define NEXT
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 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 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)
#define FIXABLE_GADGET(name)
static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx, UnsafeBufferUsageHandler &Handler)
#define WARNING_GADGET(name)
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 std::optional< StringRef > getRangeText(SourceRange SR, const SourceManager &SM, const LangOptions &LangOpts)
static FixitStrategy getNaiveStrategy(llvm::iterator_range< VarDeclIterTy > UnsafeVars)
static std::optional< std::string > createSpanTypeForVarDecl(const VarDecl *VD, const ASTContext &Ctx)
static SourceLocation getVarDeclIdentifierLoc(const VarDecl *VD)
static std::optional< SourceLocation > getPastLoc(const NodeTy *Node, const SourceManager &SM, const LangOptions &LangOpts)
std::string getUserFillPlaceHolder(StringRef HintTextToUser="placeholder")
static bool hasConflictingOverload(const FunctionDecl *FD)
static std::optional< StringRef > getVarDeclIdentifierText(const VarDecl *VD, const SourceManager &SM, const LangOptions &LangOpts)
static std::optional< std::string > getPointeeTypeText(const VarDecl *VD, const SourceManager &SM, const LangOptions &LangOpts, std::optional< Qualifiers > *QualifiersToAppend)
static std::tuple< FixableGadgetList, WarningGadgetList, DeclUseTracker > findGadgets(const Decl *D, const UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
Scan the function and return a list of gadgets found with provided kits.
static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD, const ASTContext &Ctx)
static bool overlapWithMacro(const FixItList &FixIts)
static bool hasUnsupportedSpecifiers(const VarDecl *VD, const SourceManager &SM)
#define DEBUG_NOTE_DECL_FAIL(D, Msg)
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 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)
static std::optional< StringRef > getFunNameText(const FunctionDecl *FD, const SourceManager &SM, const LangOptions &LangOpts)
#define WARNING_CONTAINER_GADGET(x)
__device__ __2f16 float __ockl_bool s
virtual std::optional< FixItList > getFixits(const FixitStrategy &s) const final
DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult &Result)
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const final
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
SourceLocation getSourceLoc() const override
virtual DeclUseList getClaimedVarUseSites() const override
static bool classof(const Gadget *G)
UPCPreIncrementGadget(const MatchFinder::MatchResult &Result)
static bool classof(const Gadget *G)
static Matcher matcher()
UUCAddAssignGadget(const MatchFinder::MatchResult &Result)
virtual std::optional< FixItList > getFixits(const FixitStrategy &S) const override
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,...
Holds long-lived AST nodes (such as types and decls) that can be referred to throughout the semantic ...
Definition: ASTContext.h:182
SourceManager & getSourceManager()
Definition: ASTContext.h:705
const ConstantArrayType * getAsConstantArrayType(QualType T) const
Definition: ASTContext.h:2768
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
const LangOptions & getLangOpts() const
Definition: ASTContext.h:775
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition: Expr.h:2664
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:2707
Attr - This represents one attribute.
Definition: Attr.h:42
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3840
Expr * getLHS() const
Definition: Expr.h:3889
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:3894
static StringRef getOpcodeStr(Opcode Op)
getOpcodeStr - Turn an Opcode enum value into the punctuation char it corresponds to,...
Definition: Expr.cpp:2134
Expr * getRHS() const
Definition: Expr.h:3891
Represents a call to a C++ constructor.
Definition: ExprCXX.h:1542
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition: ExprCXX.h:1685
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: ExprCXX.cpp:513
Represents a C++11 noexcept expression (C++ [expr.unary.noexcept]).
Definition: ExprCXX.h:4119
A C++ typeid expression (C++ [expr.typeid]), which gets the type_info that corresponds to the supplie...
Definition: ExprCXX.h:845
CallExpr - Represents a function call (C99 6.5.2.2, C++ [expr.call]).
Definition: Expr.h:2820
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.cpp:1638
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition: Expr.h:3483
static const char * getCastKindName(CastKind CK)
Definition: Expr.cpp:1952
Represents a character-granular source range.
static CharSourceRange getCharRange(SourceRange R)
SourceLocation getEnd() const
ConstStmtVisitor - This class implements a simple visitor for Stmt subclasses.
Definition: StmtVisitor.h:195
lookup_result lookup(DeclarationName Name) const
lookup - Find the declarations (if any) with the given Name in this context.
Definition: DeclBase.cpp:1802
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1260
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.cpp:551
ValueDecl * getDecl()
Definition: Expr.h:1328
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition: Stmt.h:1497
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Definition: Stmt.h:1510
decl_range decls()
Definition: Stmt.h:1545
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
SourceLocation getEndLoc() const LLVM_READONLY
Definition: DeclBase.h:441
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:501
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition: DeclBase.h:599
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:1077
SourceLocation getLocation() const
Definition: DeclBase.h:445
DeclContext * getDeclContext()
Definition: DeclBase.h:454
attr_range attrs() const
Definition: DeclBase.h:541
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: DeclBase.h:437
NestedNameSpecifier * getQualifier() const
Retrieve the nested-name-specifier that qualifies the name of this declaration, if it was present in ...
Definition: Decl.h:828
SourceLocation getTypeSpecEndLoc() const
Definition: Decl.cpp:1991
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Decl.h:822
NestedNameSpecifierLoc getQualifierLoc() const
Retrieve the nested-name-specifier (with source-location information) that qualifies the name of this...
Definition: Decl.h:836
TypeSourceInfo * getTypeSourceInfo() const
Definition: Decl.h:799
Container for either a single DynTypedNode or for an ArrayRef to DynTypedNode.
const DynTypedNode * begin() const
A dynamically typed AST node container.
SourceRange getSourceRange() const
For nodes which represent textual entities in the source code, return their SourceRange.
const T * get() const
Retrieve the stored node as type T.
static DynTypedNode create(const T &Node)
Creates a DynTypedNode from Node.
ExplicitCastExpr - An explicit cast written in the source code.
Definition: Expr.h:3730
This represents one expression.
Definition: Expr.h:110
Expr * IgnoreParenImpCasts() LLVM_READONLY
Skip past any parentheses and implicit casts which might surround this expression until reaching a fi...
Definition: Expr.cpp:3059
Expr * IgnoreImplicit() LLVM_READONLY
Skip past any implicit AST nodes which might surround this expression until reaching a fixed point.
Definition: Expr.cpp:3047
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition: Expr.cpp:3055
NullPointerConstantValueDependence
Enumeration used to describe how isNullPointerConstant() should cope with value-dependent expressions...
Definition: Expr.h:815
std::optional< llvm::APSInt > getIntegerConstantExpr(const ASTContext &Ctx, SourceLocation *Loc=nullptr) const
isIntegerConstantExpr - Return the value if this expression is a valid integer constant expression.
QualType getType() const
Definition: Expr.h:142
Annotates a diagnostic with some code that should be inserted, removed, or replaced to fix the proble...
Definition: Diagnostic.h:71
CharSourceRange RemoveRange
Code that should be replaced to correct the error.
Definition: Diagnostic.h:75
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:134
static FixItHint CreateRemoval(CharSourceRange RemoveRange)
Create a code modification hint that removes the given source range.
Definition: Diagnostic.h:123
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:97
Represents a function declaration or definition.
Definition: Decl.h:1971
const ParmVarDecl * getParamDecl(unsigned i) const
Definition: Decl.h:2706
Stmt * getBody(const FunctionDecl *&Definition) const
Retrieve the body (definition) of the function.
Definition: Decl.cpp:3236
param_iterator param_begin()
Definition: Decl.h:2695
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition: Decl.cpp:3692
DeclarationNameInfo getNameInfo() const
Definition: Decl.h:2182
Represents a C11 generic selection.
Definition: Expr.h:5725
One of these records is kept for each identifier that is lexed.
StringRef getName() const
Return the actual identifier string.
ImplicitCastExpr - Allows us to explicitly represent implicit type conversions, which have no direct ...
Definition: Expr.h:3655
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:461
static StringRef getSourceText(CharSourceRange Range, const SourceManager &SM, const LangOptions &LangOpts, bool *Invalid=nullptr)
Returns a string for the source that the range encompasses.
Definition: Lexer.cpp:1024
static bool isAtEndOfMacroExpansion(SourceLocation loc, const SourceManager &SM, const LangOptions &LangOpts, SourceLocation *MacroEnd=nullptr)
Returns true if the given MacroID location points at the last token of the macro expansion.
Definition: Lexer.cpp:894
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:499
static std::optional< Token > findNextToken(SourceLocation Loc, const SourceManager &SM, const LangOptions &LangOpts)
Finds the token that comes right after the given location.
Definition: Lexer.cpp:1325
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:850
IdentifierInfo * getIdentifier() const
Get the identifier that names this declaration, if there is one.
Definition: Decl.h:270
StringRef getName() const
Get the name of identifier for this declaration as a StringRef.
Definition: Decl.h:276
DeclarationName getDeclName() const
Get the actual, stored name of the declaration, which may be a special name.
Definition: Decl.h:315
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:292
SourceLocation getBeginLoc() const
Retrieve the location of the beginning of this nested-name-specifier.
Represents a parameter to a function.
Definition: Decl.h:1761
bool hasDefaultArg() const
Determines whether this parameter has a default argument, either parsed or not.
Definition: Decl.cpp:3016
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition: Decl.cpp:2938
Wrapper for source info for pointers.
Definition: TypeLoc.h:1301
A (possibly-)qualified type.
Definition: Type.h:940
bool hasQualifiers() const
Determine whether this type has any qualifiers.
Definition: Type.h:7448
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition: Type.h:1007
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition: Type.h:7399
QualType getCanonicalType() const
Definition: Type.h:7411
std::string getAsString() const
A class that does preorder or postorder depth-first traversal on the entire Clang AST and visits each...
bool TraverseStmt(Stmt *S, DataRecursionQueue *Queue=nullptr)
Recursively visit a statement or expression, by dispatching to Traverse*() based on the argument's dy...
bool TraverseDecl(Decl *D)
Recursively visit a declaration, by dispatching to Traverse*Decl() based on the argument's dynamic ty...
redecl_range redecls() const
Returns an iterator range for all the redeclarations of the same decl.
Definition: Redeclarable.h:296
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
SourceLocation getBegin() const
bool isValid() const
Stmt - This represents one statement.
Definition: Stmt.h:84
SourceLocation getEndLoc() const LLVM_READONLY
Definition: Stmt.cpp:350
StmtClass getStmtClass() const
Definition: Stmt.h:1358
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Stmt.cpp:338
Base wrapper for a particular "section" of type source info.
Definition: TypeLoc.h:59
UnqualTypeLoc getUnqualifiedLoc() const
Skips past any qualifiers, if this is qualified.
Definition: TypeLoc.h:338
TypeLoc getNextTypeLoc() const
Get the next TypeLoc pointed by this TypeLoc, e.g for "int*" the TypeLoc is a PointerLoc and next Typ...
Definition: TypeLoc.h:170
T castAs() const
Convert to the specified TypeLoc type, asserting that this TypeLoc is of the desired type.
Definition: TypeLoc.h:78
SourceRange getSourceRange() const LLVM_READONLY
Get the full source range.
Definition: TypeLoc.h:153
TypeLocClass getTypeLocClass() const
Definition: TypeLoc.h:116
bool isNull() const
Definition: TypeLoc.h:121
SourceLocation getEndLoc() const
Get the end source location.
Definition: TypeLoc.cpp:235
SourceLocation getBeginLoc() const
Get the begin source location.
Definition: TypeLoc.cpp:192
TypeLoc getTypeLoc() const
Return the TypeLoc wrapper for the type source info.
Definition: TypeLoc.h:256
bool isConstantArrayType() const
Definition: Type.h:7682
bool isFunctionPointerType() const
Definition: Type.h:7646
bool isPointerType() const
Definition: Type.h:7612
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:695
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:2185
UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand.
Definition: Expr.h:2568
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition: Expr.h:2183
SourceLocation getOperatorLoc() const
getOperatorLoc - Return the location of the operator.
Definition: Expr.h:2232
Expr * getSubExpr() const
Definition: Expr.h:2228
Opcode getOpcode() const
Definition: Expr.h:2223
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:2305
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...
void addDebugNoteForVar(const VarDecl *VD, SourceLocation Loc, std::string Text)
virtual std::string getUnsafeBufferUsageAttributeTextAt(SourceLocation Loc, StringRef WSSuffix="") 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.
QualType getType() const
Definition: Decl.h:717
Represents a variable declaration or definition.
Definition: Decl.h:918
bool isConstexpr() const
Whether this variable is (C++11) constexpr.
Definition: Decl.h:1549
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition: Decl.cpp:2187
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition: Decl.cpp:2254
bool isInlineSpecified() const
Definition: Decl.h:1534
bool hasConstantInitialization() const
Determine whether this variable has constant initialization.
Definition: Decl.cpp:2624
const Expr * getInit() const
Definition: Decl.h:1355
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
Definition: Decl.h:1171
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition: Decl.h:1240
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,...
bool findMatch(const DynTypedNode &DynNode)
bool TraverseCXXNoexceptExpr(CXXNoexceptExpr *Node)
RecursiveASTVisitor< MatchDescendantVisitor > VisitorBase
bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node)
bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr *Node)
MatchDescendantVisitor(const internal::DynTypedMatcher *Matcher, internal::ASTMatchFinder *Finder, internal::BoundNodesTreeBuilder *Builder, internal::ASTMatchFinder::BindKind Bind, const bool ignoreUnevaluatedContext)
bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node)
bool TraverseStmt(Stmt *Node, DataRecursionQueue *Queue=nullptr)
bool TraverseGenericSelectionExpr(GenericSelectionExpr *Node)
Called when the Match registered for it was successfully found in the AST.
virtual void run(const MatchResult &Result)=0
Called on every match by the MatchFinder.
A class to allow finding matches over the Clang AST.
void addMatcher(const DeclarationMatcher &NodeMatch, MatchCallback *Action)
Adds a matcher to execute when running over the AST.
void match(const T &Node, ASTContext &Context)
Calls the registered callbacks on all matches on the given Node.
const internal::VariadicDynCastAllOfMatcher< Decl, VarDecl > varDecl
Matches variable declarations.
const internal::VariadicDynCastAllOfMatcher< Stmt, DeclRefExpr > declRefExpr
Matches expressions that refer to declarations.
const AstTypeMatcher< EnumType > enumType
Matches enum types.
static auto isInUnspecifiedLvalueContext(internal::Matcher< Expr > innerMatcher)
const internal::VariadicOperatorMatcherFunc< 1, 1 > unless
Matches if the provided matcher does not match.
const internal::VariadicDynCastAllOfMatcher< Stmt, ImplicitCastExpr > implicitCastExpr
Matches the implicit cast nodes of Clang's AST.
const AstTypeMatcher< PointerType > pointerType
Matches pointer types, but does not match Objective-C object pointer types.
const internal::VariadicDynCastAllOfMatcher< Decl, BindingDecl > bindingDecl
Matches binding declarations Example matches foo and bar (matcher = bindingDecl()
internal::Matcher< NamedDecl > hasName(StringRef Name)
Matches NamedDecl nodes that have the specified name.
Definition: ASTMatchers.h:3079
const internal::VariadicDynCastAllOfMatcher< Stmt, CallExpr > callExpr
Matches call expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, CompoundStmt > compoundStmt
Matches compound statements.
const internal::ArgumentAdaptingMatcherFunc< internal::ForEachMatcher > forEach
Matches AST nodes that have child AST nodes that match the provided matcher.
const AstTypeMatcher< ArrayType > arrayType
Matches all kinds of arrays.
const internal::VariadicDynCastAllOfMatcher< Stmt, UnaryOperator > unaryOperator
Matches unary operator expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, ArraySubscriptExpr > arraySubscriptExpr
Matches array subscript expressions.
static internal::Matcher< Stmt > isInUnspecifiedUntypedContext(internal::Matcher< Stmt > InnerMatcher)
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXMemberCallExpr > cxxMemberCallExpr
Matches member call expressions.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXConstructorDecl > cxxConstructorDecl
Matches C++ constructor declarations.
const internal::VariadicDynCastAllOfMatcher< Stmt, ArrayInitIndexExpr > arrayInitIndexExpr
The arrayInitIndexExpr consists of two subexpressions: a common expression (the source array) that is...
const internal::VariadicDynCastAllOfMatcher< Stmt, BinaryOperator > binaryOperator
Matches binary operator expressions.
const internal::VariadicDynCastAllOfMatcher< Stmt, ParenExpr > parenExpr
Matches parentheses used in expressions.
const internal::ArgumentAdaptingMatcherFunc< internal::HasMatcher > has
Matches AST nodes that have child AST nodes that match the provided matcher.
const internal::VariadicDynCastAllOfMatcher< Stmt, ExplicitCastExpr > explicitCastExpr
Matches explicit cast expressions.
internal::PolymorphicMatcher< internal::ValueEqualsMatcher, void(internal::AllNodeBaseTypes), ValueT > equals(const ValueT &Value)
Matches literals that are equal to the given value of type ValueT.
Definition: ASTMatchers.h:5848
const AstTypeMatcher< ConstantArrayType > constantArrayType
Matches C arrays with a specified constant size.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> eachOf
Matches if any of the given matchers matches.
const internal::VariadicDynCastAllOfMatcher< Stmt, CXXConstructExpr > cxxConstructExpr
Matches constructor call expressions (including implicit ones).
static internal::Matcher< Stmt > isInUnspecifiedPointerContext(internal::Matcher< Stmt > InnerMatcher)
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> allOf
Matches if all given matchers match.
const internal::VariadicDynCastAllOfMatcher< Decl, FunctionDecl > functionDecl
Matches function declarations.
static auto hasPointerType()
const internal::VariadicDynCastAllOfMatcher< Stmt, IntegerLiteral > integerLiteral
Matches integer literals of all sizes / encodings, e.g.
internal::PolymorphicMatcher< internal::HasDeclarationMatcher, void(internal::HasDeclarationSupportedTypes), internal::Matcher< Decl > > hasDeclaration(const internal::Matcher< Decl > &InnerMatcher)
Matches a node if the declaration associated with that node matches the given matcher.
Definition: ASTMatchers.h:3653
const internal::VariadicDynCastAllOfMatcher< Stmt, DeclStmt > declStmt
Matches declaration statements.
const internal::VariadicAllOfMatcher< Stmt > stmt
Matches statements.
const internal::VariadicDynCastAllOfMatcher< Stmt, Expr > expr
Matches expressions.
const internal::VariadicOperatorMatcherFunc< 2, std::numeric_limits< unsigned >::max()> anyOf
Matches if any of the given matchers matches.
const internal::VariadicFunction< internal::PolymorphicMatcher< internal::HasAnyOperatorNameMatcher, AST_POLYMORPHIC_SUPPORTED_TYPES(BinaryOperator, CXXOperatorCallExpr, CXXRewrittenBinaryOperator, UnaryOperator), std::vector< std::string > >, StringRef, internal::hasAnyOperatorNameFunc > hasAnyOperatorName
Matches operator expressions (binary or unary) that have any of the specified names.
const internal::VariadicDynCastAllOfMatcher< Decl, CXXMethodDecl > cxxMethodDecl
Matches method declarations.
const internal::VariadicDynCastAllOfMatcher< Stmt, CastExpr > castExpr
Matches any cast nodes of Clang's AST.
const internal::VariadicDynCastAllOfMatcher< Stmt, IfStmt > ifStmt
Matches if statements.
bool anyConflict(const llvm::SmallVectorImpl< FixItHint > &FixIts, const SourceManager &SM)
The JSON file list parser is used to communicate input to InstallAPI.
void checkUnsafeBufferUsage(const Decl *D, UnsafeBufferUsageHandler &Handler, bool EmitSuggestions)
std::vector< const VarDecl * > VarGrpTy
const FunctionProtoType * T
Diagnostic wrappers for TextAPI types for error reporting.
Definition: Dominators.h:30
#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
Wraps an identifier and optional source location for the identifier.
Definition: ParsedAttr.h:103
Contains all information for a given match.