clang 20.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 (auto *ConstArrTy =
406 Finder->getASTContext().getAsConstantArrayType(Arg0Ty)) {
407 const APSInt ConstArrSize = APSInt(ConstArrTy->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 = "attr_expr";
930 const Expr *Op;
931
932public:
933 UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult &Result)
934 : WarningGadget(Kind::UnsafeBufferUsageAttr),
935 Op(Result.Nodes.getNodeAs<Expr>(OpTag)) {}
936
937 static bool classof(const Gadget *G) {
938 return G->getKind() == Kind::UnsafeBufferUsageAttr;
939 }
940
941 static Matcher matcher() {
942 auto HasUnsafeFieldDecl =
943 member(fieldDecl(hasAttr(attr::UnsafeBufferUsage)));
944
945 auto HasUnsafeFnDecl =
946 callee(functionDecl(hasAttr(attr::UnsafeBufferUsage)));
947
948 return stmt(anyOf(callExpr(HasUnsafeFnDecl).bind(OpTag),
949 memberExpr(HasUnsafeFieldDecl).bind(OpTag)));
950 }
951
952 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
953 bool IsRelatedToDecl,
954 ASTContext &Ctx) const override {
955 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
956 }
957 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
958
959 DeclUseList getClaimedVarUseSites() const override { return {}; }
960};
961
962/// A call of a constructor that performs unchecked buffer operations
963/// over one of its pointer parameters, or constructs a class object that will
964/// perform buffer operations that depend on the correctness of the parameters.
965class UnsafeBufferUsageCtorAttrGadget : public WarningGadget {
966 constexpr static const char *const OpTag = "cxx_construct_expr";
967 const CXXConstructExpr *Op;
968
969public:
970 UnsafeBufferUsageCtorAttrGadget(const MatchFinder::MatchResult &Result)
971 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr),
972 Op(Result.Nodes.getNodeAs<CXXConstructExpr>(OpTag)) {}
973
974 static bool classof(const Gadget *G) {
975 return G->getKind() == Kind::UnsafeBufferUsageCtorAttr;
976 }
977
978 static Matcher matcher() {
979 auto HasUnsafeCtorDecl =
980 hasDeclaration(cxxConstructorDecl(hasAttr(attr::UnsafeBufferUsage)));
981 // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget.
982 auto HasTwoParamSpanCtorDecl = SpanTwoParamConstructorGadget::matcher();
983 return stmt(
984 cxxConstructExpr(HasUnsafeCtorDecl, unless(HasTwoParamSpanCtorDecl))
985 .bind(OpTag));
986 }
987
988 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
989 bool IsRelatedToDecl,
990 ASTContext &Ctx) const override {
991 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
992 }
993 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
994
995 DeclUseList getClaimedVarUseSites() const override { return {}; }
996};
997
998// Warning gadget for unsafe invocation of span::data method.
999// Triggers when the pointer returned by the invocation is immediately
1000// cast to a larger type.
1001
1002class DataInvocationGadget : public WarningGadget {
1003 constexpr static const char *const OpTag = "data_invocation_expr";
1004 const ExplicitCastExpr *Op;
1005
1006public:
1007 DataInvocationGadget(const MatchFinder::MatchResult &Result)
1008 : WarningGadget(Kind::DataInvocation),
1009 Op(Result.Nodes.getNodeAs<ExplicitCastExpr>(OpTag)) {}
1010
1011 static bool classof(const Gadget *G) {
1012 return G->getKind() == Kind::DataInvocation;
1013 }
1014
1015 static Matcher matcher() {
1016 Matcher callExpr = cxxMemberCallExpr(
1017 callee(cxxMethodDecl(hasName("data"), ofClass(hasName("std::span")))));
1018 return stmt(
1020 .bind(OpTag));
1021 }
1022
1023 void handleUnsafeOperation(UnsafeBufferUsageHandler &Handler,
1024 bool IsRelatedToDecl,
1025 ASTContext &Ctx) const override {
1026 Handler.handleUnsafeOperation(Op, IsRelatedToDecl, Ctx);
1027 }
1028 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1029
1030 DeclUseList getClaimedVarUseSites() const override { return {}; }
1031};
1032
1033// Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
1034// Context (see `isInUnspecifiedLvalueContext`).
1035// Note here `[]` is the built-in subscript operator.
1036class ULCArraySubscriptGadget : public FixableGadget {
1037private:
1038 static constexpr const char *const ULCArraySubscriptTag =
1039 "ArraySubscriptUnderULC";
1040 const ArraySubscriptExpr *Node;
1041
1042public:
1043 ULCArraySubscriptGadget(const MatchFinder::MatchResult &Result)
1044 : FixableGadget(Kind::ULCArraySubscript),
1045 Node(Result.Nodes.getNodeAs<ArraySubscriptExpr>(ULCArraySubscriptTag)) {
1046 assert(Node != nullptr && "Expecting a non-null matching result");
1047 }
1048
1049 static bool classof(const Gadget *G) {
1050 return G->getKind() == Kind::ULCArraySubscript;
1051 }
1052
1053 static Matcher matcher() {
1054 auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
1055 auto BaseIsArrayOrPtrDRE = hasBase(
1056 ignoringParenImpCasts(declRefExpr(ArrayOrPtr, toSupportedVariable())));
1057 auto Target =
1058 arraySubscriptExpr(BaseIsArrayOrPtrDRE).bind(ULCArraySubscriptTag);
1059
1061 }
1062
1063 virtual std::optional<FixItList>
1064 getFixits(const FixitStrategy &S) const override;
1065 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1066
1067 virtual DeclUseList getClaimedVarUseSites() const override {
1068 if (const auto *DRE =
1069 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts())) {
1070 return {DRE};
1071 }
1072 return {};
1073 }
1074};
1075
1076// Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
1077// unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits
1078// fixit of the form `UPC(DRE.data())`.
1079class UPCStandalonePointerGadget : public FixableGadget {
1080private:
1081 static constexpr const char *const DeclRefExprTag = "StandalonePointer";
1082 const DeclRefExpr *Node;
1083
1084public:
1085 UPCStandalonePointerGadget(const MatchFinder::MatchResult &Result)
1086 : FixableGadget(Kind::UPCStandalonePointer),
1087 Node(Result.Nodes.getNodeAs<DeclRefExpr>(DeclRefExprTag)) {
1088 assert(Node != nullptr && "Expecting a non-null matching result");
1089 }
1090
1091 static bool classof(const Gadget *G) {
1092 return G->getKind() == Kind::UPCStandalonePointer;
1093 }
1094
1095 static Matcher matcher() {
1096 auto ArrayOrPtr = anyOf(hasPointerType(), hasArrayType());
1097 auto target = expr(ignoringParenImpCasts(
1098 declRefExpr(allOf(ArrayOrPtr, toSupportedVariable()))
1099 .bind(DeclRefExprTag)));
1100 return stmt(isInUnspecifiedPointerContext(target));
1101 }
1102
1103 virtual std::optional<FixItList>
1104 getFixits(const FixitStrategy &S) const override;
1105 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1106
1107 virtual DeclUseList getClaimedVarUseSites() const override { return {Node}; }
1108};
1109
1110class PointerDereferenceGadget : public FixableGadget {
1111 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
1112 static constexpr const char *const OperatorTag = "op";
1113
1114 const DeclRefExpr *BaseDeclRefExpr = nullptr;
1115 const UnaryOperator *Op = nullptr;
1116
1117public:
1118 PointerDereferenceGadget(const MatchFinder::MatchResult &Result)
1119 : FixableGadget(Kind::PointerDereference),
1120 BaseDeclRefExpr(
1121 Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
1122 Op(Result.Nodes.getNodeAs<UnaryOperator>(OperatorTag)) {}
1123
1124 static bool classof(const Gadget *G) {
1125 return G->getKind() == Kind::PointerDereference;
1126 }
1127
1128 static Matcher matcher() {
1129 auto Target =
1131 hasOperatorName("*"),
1132 has(expr(ignoringParenImpCasts(
1133 declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag)))))
1134 .bind(OperatorTag);
1135
1137 }
1138
1139 DeclUseList getClaimedVarUseSites() const override {
1140 return {BaseDeclRefExpr};
1141 }
1142
1143 virtual std::optional<FixItList>
1144 getFixits(const FixitStrategy &S) const override;
1145 SourceLocation getSourceLoc() const override { return Op->getBeginLoc(); }
1146};
1147
1148// Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
1149// Context (see `isInUnspecifiedPointerContext`).
1150// Note here `[]` is the built-in subscript operator.
1151class UPCAddressofArraySubscriptGadget : public FixableGadget {
1152private:
1153 static constexpr const char *const UPCAddressofArraySubscriptTag =
1154 "AddressofArraySubscriptUnderUPC";
1155 const UnaryOperator *Node; // the `&DRE[any]` node
1156
1157public:
1158 UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult &Result)
1159 : FixableGadget(Kind::ULCArraySubscript),
1160 Node(Result.Nodes.getNodeAs<UnaryOperator>(
1161 UPCAddressofArraySubscriptTag)) {
1162 assert(Node != nullptr && "Expecting a non-null matching result");
1163 }
1164
1165 static bool classof(const Gadget *G) {
1166 return G->getKind() == Kind::UPCAddressofArraySubscript;
1167 }
1168
1169 static Matcher matcher() {
1170 return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
1172 hasOperatorName("&"),
1173 hasUnaryOperand(arraySubscriptExpr(hasBase(
1174 ignoringParenImpCasts(declRefExpr(toSupportedVariable()))))))
1175 .bind(UPCAddressofArraySubscriptTag)))));
1176 }
1177
1178 virtual std::optional<FixItList>
1179 getFixits(const FixitStrategy &) const override;
1180 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1181
1182 virtual DeclUseList getClaimedVarUseSites() const override {
1183 const auto *ArraySubst = cast<ArraySubscriptExpr>(Node->getSubExpr());
1184 const auto *DRE =
1185 cast<DeclRefExpr>(ArraySubst->getBase()->IgnoreParenImpCasts());
1186 return {DRE};
1187 }
1188};
1189} // namespace
1190
1191namespace {
1192// An auxiliary tracking facility for the fixit analysis. It helps connect
1193// declarations to its uses and make sure we've covered all uses with our
1194// analysis before we try to fix the declaration.
1195class DeclUseTracker {
1196 using UseSetTy = SmallSet<const DeclRefExpr *, 16>;
1197 using DefMapTy = DenseMap<const VarDecl *, const DeclStmt *>;
1198
1199 // Allocate on the heap for easier move.
1200 std::unique_ptr<UseSetTy> Uses{std::make_unique<UseSetTy>()};
1201 DefMapTy Defs{};
1202
1203public:
1204 DeclUseTracker() = default;
1205 DeclUseTracker(const DeclUseTracker &) = delete; // Let's avoid copies.
1206 DeclUseTracker &operator=(const DeclUseTracker &) = delete;
1207 DeclUseTracker(DeclUseTracker &&) = default;
1208 DeclUseTracker &operator=(DeclUseTracker &&) = default;
1209
1210 // Start tracking a freshly discovered DRE.
1211 void discoverUse(const DeclRefExpr *DRE) { Uses->insert(DRE); }
1212
1213 // Stop tracking the DRE as it's been fully figured out.
1214 void claimUse(const DeclRefExpr *DRE) {
1215 assert(Uses->count(DRE) &&
1216 "DRE not found or claimed by multiple matchers!");
1217 Uses->erase(DRE);
1218 }
1219
1220 // A variable is unclaimed if at least one use is unclaimed.
1221 bool hasUnclaimedUses(const VarDecl *VD) const {
1222 // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
1223 return any_of(*Uses, [VD](const DeclRefExpr *DRE) {
1224 return DRE->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl();
1225 });
1226 }
1227
1228 UseSetTy getUnclaimedUses(const VarDecl *VD) const {
1229 UseSetTy ReturnSet;
1230 for (auto use : *Uses) {
1231 if (use->getDecl()->getCanonicalDecl() == VD->getCanonicalDecl()) {
1232 ReturnSet.insert(use);
1233 }
1234 }
1235 return ReturnSet;
1236 }
1237
1238 void discoverDecl(const DeclStmt *DS) {
1239 for (const Decl *D : DS->decls()) {
1240 if (const auto *VD = dyn_cast<VarDecl>(D)) {
1241 // FIXME: Assertion temporarily disabled due to a bug in
1242 // ASTMatcher internal behavior in presence of GNU
1243 // statement-expressions. We need to properly investigate this
1244 // because it can screw up our algorithm in other ways.
1245 // assert(Defs.count(VD) == 0 && "Definition already discovered!");
1246 Defs[VD] = DS;
1247 }
1248 }
1249 }
1250
1251 const DeclStmt *lookupDecl(const VarDecl *VD) const {
1252 return Defs.lookup(VD);
1253 }
1254};
1255} // namespace
1256
1257// Representing a pointer type expression of the form `++Ptr` in an Unspecified
1258// Pointer Context (UPC):
1259class UPCPreIncrementGadget : public FixableGadget {
1260private:
1261 static constexpr const char *const UPCPreIncrementTag =
1262 "PointerPreIncrementUnderUPC";
1263 const UnaryOperator *Node; // the `++Ptr` node
1264
1265public:
1267 : FixableGadget(Kind::UPCPreIncrement),
1268 Node(Result.Nodes.getNodeAs<UnaryOperator>(UPCPreIncrementTag)) {
1269 assert(Node != nullptr && "Expecting a non-null matching result");
1270 }
1271
1272 static bool classof(const Gadget *G) {
1273 return G->getKind() == Kind::UPCPreIncrement;
1274 }
1275
1276 static Matcher matcher() {
1277 // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
1278 // Although currently we can only provide fix-its when `Ptr` is a DRE, we
1279 // can have the matcher be general, so long as `getClaimedVarUseSites` does
1280 // things right.
1281 return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
1282 unaryOperator(isPreInc(),
1283 hasUnaryOperand(declRefExpr(toSupportedVariable())))
1284 .bind(UPCPreIncrementTag)))));
1285 }
1286
1287 virtual std::optional<FixItList>
1288 getFixits(const FixitStrategy &S) const override;
1289 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1290
1291 virtual DeclUseList getClaimedVarUseSites() const override {
1292 return {dyn_cast<DeclRefExpr>(Node->getSubExpr())};
1293 }
1294};
1295
1296// Representing a pointer type expression of the form `Ptr += n` in an
1297// Unspecified Untyped Context (UUC):
1298class UUCAddAssignGadget : public FixableGadget {
1299private:
1300 static constexpr const char *const UUCAddAssignTag =
1301 "PointerAddAssignUnderUUC";
1302 static constexpr const char *const OffsetTag = "Offset";
1303
1304 const BinaryOperator *Node; // the `Ptr += n` node
1305 const Expr *Offset = nullptr;
1306
1307public:
1309 : FixableGadget(Kind::UUCAddAssign),
1310 Node(Result.Nodes.getNodeAs<BinaryOperator>(UUCAddAssignTag)),
1311 Offset(Result.Nodes.getNodeAs<Expr>(OffsetTag)) {
1312 assert(Node != nullptr && "Expecting a non-null matching result");
1313 }
1314
1315 static bool classof(const Gadget *G) {
1316 return G->getKind() == Kind::UUCAddAssign;
1317 }
1318
1319 static Matcher matcher() {
1320 // clang-format off
1321 return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts(
1322 binaryOperator(hasOperatorName("+="),
1323 hasLHS(
1326 toSupportedVariable())),
1327 hasRHS(expr().bind(OffsetTag)))
1328 .bind(UUCAddAssignTag)))));
1329 // clang-format on
1330 }
1331
1332 virtual std::optional<FixItList>
1333 getFixits(const FixitStrategy &S) const override;
1334 SourceLocation getSourceLoc() const override { return Node->getBeginLoc(); }
1335
1336 virtual DeclUseList getClaimedVarUseSites() const override {
1337 return {dyn_cast<DeclRefExpr>(Node->getLHS())};
1338 }
1339};
1340
1341// Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
1342// ptr)`:
1343class DerefSimplePtrArithFixableGadget : public FixableGadget {
1344 static constexpr const char *const BaseDeclRefExprTag = "BaseDRE";
1345 static constexpr const char *const DerefOpTag = "DerefOp";
1346 static constexpr const char *const AddOpTag = "AddOp";
1347 static constexpr const char *const OffsetTag = "Offset";
1348
1349 const DeclRefExpr *BaseDeclRefExpr = nullptr;
1350 const UnaryOperator *DerefOp = nullptr;
1351 const BinaryOperator *AddOp = nullptr;
1352 const IntegerLiteral *Offset = nullptr;
1353
1354public:
1356 : FixableGadget(Kind::DerefSimplePtrArithFixable),
1357 BaseDeclRefExpr(
1358 Result.Nodes.getNodeAs<DeclRefExpr>(BaseDeclRefExprTag)),
1359 DerefOp(Result.Nodes.getNodeAs<UnaryOperator>(DerefOpTag)),
1360 AddOp(Result.Nodes.getNodeAs<BinaryOperator>(AddOpTag)),
1361 Offset(Result.Nodes.getNodeAs<IntegerLiteral>(OffsetTag)) {}
1362
1363 static Matcher matcher() {
1364 // clang-format off
1365 auto ThePtr = expr(hasPointerType(),
1366 ignoringImpCasts(declRefExpr(toSupportedVariable()).
1367 bind(BaseDeclRefExprTag)));
1368 auto PlusOverPtrAndInteger = expr(anyOf(
1369 binaryOperator(hasOperatorName("+"), hasLHS(ThePtr),
1370 hasRHS(integerLiteral().bind(OffsetTag)))
1371 .bind(AddOpTag),
1372 binaryOperator(hasOperatorName("+"), hasRHS(ThePtr),
1373 hasLHS(integerLiteral().bind(OffsetTag)))
1374 .bind(AddOpTag)));
1376 hasOperatorName("*"),
1377 hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger)))
1378 .bind(DerefOpTag));
1379 // clang-format on
1380 }
1381
1382 virtual std::optional<FixItList>
1383 getFixits(const FixitStrategy &s) const final;
1384 SourceLocation getSourceLoc() const override {
1385 return DerefOp->getBeginLoc();
1386 }
1387
1388 virtual DeclUseList getClaimedVarUseSites() const final {
1389 return {BaseDeclRefExpr};
1390 }
1391};
1392
1393/// Scan the function and return a list of gadgets found with provided kits.
1394static std::tuple<FixableGadgetList, WarningGadgetList, DeclUseTracker>
1396 bool EmitSuggestions) {
1397
1398 struct GadgetFinderCallback : MatchFinder::MatchCallback {
1399 FixableGadgetList FixableGadgets;
1400 WarningGadgetList WarningGadgets;
1401 DeclUseTracker Tracker;
1402
1403 void run(const MatchFinder::MatchResult &Result) override {
1404 // In debug mode, assert that we've found exactly one gadget.
1405 // This helps us avoid conflicts in .bind() tags.
1406#if NDEBUG
1407#define NEXT return
1408#else
1409 [[maybe_unused]] int numFound = 0;
1410#define NEXT ++numFound
1411#endif
1412
1413 if (const auto *DRE = Result.Nodes.getNodeAs<DeclRefExpr>("any_dre")) {
1414 Tracker.discoverUse(DRE);
1415 NEXT;
1416 }
1417
1418 if (const auto *DS = Result.Nodes.getNodeAs<DeclStmt>("any_ds")) {
1419 Tracker.discoverDecl(DS);
1420 NEXT;
1421 }
1422
1423 // Figure out which matcher we've found, and call the appropriate
1424 // subclass constructor.
1425 // FIXME: Can we do this more logarithmically?
1426#define FIXABLE_GADGET(name) \
1427 if (Result.Nodes.getNodeAs<Stmt>(#name)) { \
1428 FixableGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
1429 NEXT; \
1430 }
1431#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1432#define WARNING_GADGET(name) \
1433 if (Result.Nodes.getNodeAs<Stmt>(#name)) { \
1434 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
1435 NEXT; \
1436 }
1437#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1438
1439 assert(numFound >= 1 && "Gadgets not found in match result!");
1440 assert(numFound <= 1 && "Conflicting bind tags in gadgets!");
1441 }
1442 };
1443
1444 MatchFinder M;
1445 GadgetFinderCallback CB;
1446
1447 // clang-format off
1448 M.addMatcher(
1449 stmt(
1450 forEachDescendantEvaluatedStmt(stmt(anyOf(
1451 // Add Gadget::matcher() for every gadget in the registry.
1452#define WARNING_GADGET(x) \
1453 allOf(x ## Gadget::matcher().bind(#x), \
1454 notInSafeBufferOptOut(&Handler)),
1455#define WARNING_CONTAINER_GADGET(x) \
1456 allOf(x ## Gadget::matcher().bind(#x), \
1457 notInSafeBufferOptOut(&Handler), \
1458 unless(ignoreUnsafeBufferInContainer(&Handler))),
1459#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1460 // Avoid a hanging comma.
1461 unless(stmt())
1462 )))
1463 ),
1464 &CB
1465 );
1466 // clang-format on
1467
1468 if (EmitSuggestions) {
1469 // clang-format off
1470 M.addMatcher(
1471 stmt(
1472 forEachDescendantStmt(stmt(eachOf(
1473#define FIXABLE_GADGET(x) \
1474 x ## Gadget::matcher().bind(#x),
1475#include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1476 // In parallel, match all DeclRefExprs so that to find out
1477 // whether there are any uncovered by gadgets.
1479 to(anyOf(varDecl(), bindingDecl()))).bind("any_dre"),
1480 // Also match DeclStmts because we'll need them when fixing
1481 // their underlying VarDecls that otherwise don't have
1482 // any backreferences to DeclStmts.
1483 declStmt().bind("any_ds")
1484 )))
1485 ),
1486 &CB
1487 );
1488 // clang-format on
1489 }
1490
1491 M.match(*D->getBody(), D->getASTContext());
1492 return {std::move(CB.FixableGadgets), std::move(CB.WarningGadgets),
1493 std::move(CB.Tracker)};
1494}
1495
1496// Compares AST nodes by source locations.
1497template <typename NodeTy> struct CompareNode {
1498 bool operator()(const NodeTy *N1, const NodeTy *N2) const {
1499 return N1->getBeginLoc().getRawEncoding() <
1500 N2->getBeginLoc().getRawEncoding();
1501 }
1502};
1503
1505 std::map<const VarDecl *, std::set<const WarningGadget *>,
1506 // To keep keys sorted by their locations in the map so that the
1507 // order is deterministic:
1510 // These Gadgets are not related to pointer variables (e. g. temporaries).
1512};
1513
1514static WarningGadgetSets
1515groupWarningGadgetsByVar(const WarningGadgetList &AllUnsafeOperations) {
1516 WarningGadgetSets result;
1517 // If some gadgets cover more than one
1518 // variable, they'll appear more than once in the map.
1519 for (auto &G : AllUnsafeOperations) {
1520 DeclUseList ClaimedVarUseSites = G->getClaimedVarUseSites();
1521
1522 bool AssociatedWithVarDecl = false;
1523 for (const DeclRefExpr *DRE : ClaimedVarUseSites) {
1524 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1525 result.byVar[VD].insert(G.get());
1526 AssociatedWithVarDecl = true;
1527 }
1528 }
1529
1530 if (!AssociatedWithVarDecl) {
1531 result.noVar.push_back(G.get());
1532 continue;
1533 }
1534 }
1535 return result;
1536}
1537
1539 std::map<const VarDecl *, std::set<const FixableGadget *>,
1540 // To keep keys sorted by their locations in the map so that the
1541 // order is deterministic:
1544};
1545
1546static FixableGadgetSets
1547groupFixablesByVar(FixableGadgetList &&AllFixableOperations) {
1548 FixableGadgetSets FixablesForUnsafeVars;
1549 for (auto &F : AllFixableOperations) {
1550 DeclUseList DREs = F->getClaimedVarUseSites();
1551
1552 for (const DeclRefExpr *DRE : DREs) {
1553 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1554 FixablesForUnsafeVars.byVar[VD].insert(F.get());
1555 }
1556 }
1557 }
1558 return FixablesForUnsafeVars;
1559}
1560
1562 const SourceManager &SM) {
1563 // A simple interval overlap detection algorithm. Sorts all ranges by their
1564 // begin location then finds the first overlap in one pass.
1565 std::vector<const FixItHint *> All; // a copy of `FixIts`
1566
1567 for (const FixItHint &H : FixIts)
1568 All.push_back(&H);
1569 std::sort(All.begin(), All.end(),
1570 [&SM](const FixItHint *H1, const FixItHint *H2) {
1571 return SM.isBeforeInTranslationUnit(H1->RemoveRange.getBegin(),
1572 H2->RemoveRange.getBegin());
1573 });
1574
1575 const FixItHint *CurrHint = nullptr;
1576
1577 for (const FixItHint *Hint : All) {
1578 if (!CurrHint ||
1579 SM.isBeforeInTranslationUnit(CurrHint->RemoveRange.getEnd(),
1580 Hint->RemoveRange.getBegin())) {
1581 // Either to initialize `CurrHint` or `CurrHint` does not
1582 // overlap with `Hint`:
1583 CurrHint = Hint;
1584 } else
1585 // In case `Hint` overlaps the `CurrHint`, we found at least one
1586 // conflict:
1587 return true;
1588 }
1589 return false;
1590}
1591
1592std::optional<FixItList>
1593PtrToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
1594 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
1595 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
1596 switch (S.lookup(LeftVD)) {
1597 case FixitStrategy::Kind::Span:
1598 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
1599 return FixItList{};
1600 return std::nullopt;
1601 case FixitStrategy::Kind::Wontfix:
1602 return std::nullopt;
1603 case FixitStrategy::Kind::Iterator:
1604 case FixitStrategy::Kind::Array:
1605 return std::nullopt;
1606 case FixitStrategy::Kind::Vector:
1607 llvm_unreachable("unsupported strategies for FixableGadgets");
1608 }
1609 return std::nullopt;
1610}
1611
1612/// \returns fixit that adds .data() call after \DRE.
1613static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
1614 const DeclRefExpr *DRE);
1615
1616std::optional<FixItList>
1617CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy &S) const {
1618 const auto *LeftVD = cast<VarDecl>(PtrLHS->getDecl());
1619 const auto *RightVD = cast<VarDecl>(PtrRHS->getDecl());
1620 // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
1621 // non-trivial.
1622 //
1623 // CArrayToPtrAssignmentGadget doesn't have strategy implications because
1624 // constant size array propagates its bounds. Because of that LHS and RHS are
1625 // addressed by two different fixits.
1626 //
1627 // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
1628 // to and can't be generally relied on in multi-variable Fixables!
1629 //
1630 // E. g. If an instance of this gadget is fixing variable on LHS then the
1631 // variable on RHS is fixed by a different fixit and its strategy for LHS
1632 // fixit is as if Wontfix.
1633 //
1634 // The only exception is Wontfix strategy for a given variable as that is
1635 // valid for any fixit produced for the given input source code.
1636 if (S.lookup(LeftVD) == FixitStrategy::Kind::Span) {
1637 if (S.lookup(RightVD) == FixitStrategy::Kind::Wontfix) {
1638 return FixItList{};
1639 }
1640 } else if (S.lookup(LeftVD) == FixitStrategy::Kind::Wontfix) {
1641 if (S.lookup(RightVD) == FixitStrategy::Kind::Array) {
1642 return createDataFixit(RightVD->getASTContext(), PtrRHS);
1643 }
1644 }
1645 return std::nullopt;
1646}
1647
1648std::optional<FixItList>
1649PointerInitGadget::getFixits(const FixitStrategy &S) const {
1650 const auto *LeftVD = PtrInitLHS;
1651 const auto *RightVD = cast<VarDecl>(PtrInitRHS->getDecl());
1652 switch (S.lookup(LeftVD)) {
1653 case FixitStrategy::Kind::Span:
1654 if (S.lookup(RightVD) == FixitStrategy::Kind::Span)
1655 return FixItList{};
1656 return std::nullopt;
1657 case FixitStrategy::Kind::Wontfix:
1658 return std::nullopt;
1659 case FixitStrategy::Kind::Iterator:
1660 case FixitStrategy::Kind::Array:
1661 return std::nullopt;
1662 case FixitStrategy::Kind::Vector:
1663 llvm_unreachable("unsupported strategies for FixableGadgets");
1664 }
1665 return std::nullopt;
1666}
1667
1668static bool isNonNegativeIntegerExpr(const Expr *Expr, const VarDecl *VD,
1669 const ASTContext &Ctx) {
1670 if (auto ConstVal = Expr->getIntegerConstantExpr(Ctx)) {
1671 if (ConstVal->isNegative())
1672 return false;
1673 } else if (!Expr->getType()->isUnsignedIntegerType())
1674 return false;
1675 return true;
1676}
1677
1678std::optional<FixItList>
1679ULCArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
1680 if (const auto *DRE =
1681 dyn_cast<DeclRefExpr>(Node->getBase()->IgnoreImpCasts()))
1682 if (const auto *VD = dyn_cast<VarDecl>(DRE->getDecl())) {
1683 switch (S.lookup(VD)) {
1684 case FixitStrategy::Kind::Span: {
1685
1686 // If the index has a negative constant value, we give up as no valid
1687 // fix-it can be generated:
1688 const ASTContext &Ctx = // FIXME: we need ASTContext to be passed in!
1689 VD->getASTContext();
1690 if (!isNonNegativeIntegerExpr(Node->getIdx(), VD, Ctx))
1691 return std::nullopt;
1692 // no-op is a good fix-it, otherwise
1693 return FixItList{};
1694 }
1695 case FixitStrategy::Kind::Array:
1696 return FixItList{};
1697 case FixitStrategy::Kind::Wontfix:
1698 case FixitStrategy::Kind::Iterator:
1699 case FixitStrategy::Kind::Vector:
1700 llvm_unreachable("unsupported strategies for FixableGadgets");
1701 }
1702 }
1703 return std::nullopt;
1704}
1705
1706static std::optional<FixItList> // forward declaration
1708
1709std::optional<FixItList>
1710UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy &S) const {
1711 auto DREs = getClaimedVarUseSites();
1712 const auto *VD = cast<VarDecl>(DREs.front()->getDecl());
1713
1714 switch (S.lookup(VD)) {
1715 case FixitStrategy::Kind::Span:
1717 case FixitStrategy::Kind::Wontfix:
1718 case FixitStrategy::Kind::Iterator:
1719 case FixitStrategy::Kind::Array:
1720 return std::nullopt;
1721 case FixitStrategy::Kind::Vector:
1722 llvm_unreachable("unsupported strategies for FixableGadgets");
1723 }
1724 return std::nullopt; // something went wrong, no fix-it
1725}
1726
1727// FIXME: this function should be customizable through format
1728static StringRef getEndOfLine() {
1729 static const char *const EOL = "\n";
1730 return EOL;
1731}
1732
1733// Returns the text indicating that the user needs to provide input there:
1734std::string getUserFillPlaceHolder(StringRef HintTextToUser = "placeholder") {
1735 std::string s = std::string("<# ");
1736 s += HintTextToUser;
1737 s += " #>";
1738 return s;
1739}
1740
1741// Return the source location of the last character of the AST `Node`.
1742template <typename NodeTy>
1743static std::optional<SourceLocation>
1744getEndCharLoc(const NodeTy *Node, const SourceManager &SM,
1745 const LangOptions &LangOpts) {
1746 unsigned TkLen = Lexer::MeasureTokenLength(Node->getEndLoc(), SM, LangOpts);
1747 SourceLocation Loc = Node->getEndLoc().getLocWithOffset(TkLen - 1);
1748
1749 if (Loc.isValid())
1750 return Loc;
1751
1752 return std::nullopt;
1753}
1754
1755// Return the source location just past the last character of the AST `Node`.
1756template <typename NodeTy>
1757static std::optional<SourceLocation> getPastLoc(const NodeTy *Node,
1758 const SourceManager &SM,
1759 const LangOptions &LangOpts) {
1761 Lexer::getLocForEndOfToken(Node->getEndLoc(), 0, SM, LangOpts);
1762 if (Loc.isValid())
1763 return Loc;
1764 return std::nullopt;
1765}
1766
1767// Return text representation of an `Expr`.
1768static std::optional<StringRef> getExprText(const Expr *E,
1769 const SourceManager &SM,
1770 const LangOptions &LangOpts) {
1771 std::optional<SourceLocation> LastCharLoc = getPastLoc(E, SM, LangOpts);
1772
1773 if (LastCharLoc)
1774 return Lexer::getSourceText(
1775 CharSourceRange::getCharRange(E->getBeginLoc(), *LastCharLoc), SM,
1776 LangOpts);
1777
1778 return std::nullopt;
1779}
1780
1781// Returns the literal text in `SourceRange SR`, if `SR` is a valid range.
1782static std::optional<StringRef> getRangeText(SourceRange SR,
1783 const SourceManager &SM,
1784 const LangOptions &LangOpts) {
1785 bool Invalid = false;
1787 StringRef Text = Lexer::getSourceText(CSR, SM, LangOpts, &Invalid);
1788
1789 if (!Invalid)
1790 return Text;
1791 return std::nullopt;
1792}
1793
1794// Returns the begin location of the identifier of the given variable
1795// declaration.
1797 // According to the implementation of `VarDecl`, `VD->getLocation()` actually
1798 // returns the begin location of the identifier of the declaration:
1799 return VD->getLocation();
1800}
1801
1802// Returns the literal text of the identifier of the given variable declaration.
1803static std::optional<StringRef>
1805 const LangOptions &LangOpts) {
1806 SourceLocation ParmIdentBeginLoc = getVarDeclIdentifierLoc(VD);
1807 SourceLocation ParmIdentEndLoc =
1808 Lexer::getLocForEndOfToken(ParmIdentBeginLoc, 0, SM, LangOpts);
1809
1810 if (ParmIdentEndLoc.isMacroID() &&
1811 !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc, SM, LangOpts))
1812 return std::nullopt;
1813 return getRangeText({ParmIdentBeginLoc, ParmIdentEndLoc}, SM, LangOpts);
1814}
1815
1816// We cannot fix a variable declaration if it has some other specifiers than the
1817// type specifier. Because the source ranges of those specifiers could overlap
1818// with the source range that is being replaced using fix-its. Especially when
1819// we often cannot obtain accurate source ranges of cv-qualified type
1820// specifiers.
1821// FIXME: also deal with type attributes
1822static bool hasUnsupportedSpecifiers(const VarDecl *VD,
1823 const SourceManager &SM) {
1824 // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
1825 // source range of `VD`:
1826 bool AttrRangeOverlapping = llvm::any_of(VD->attrs(), [&](Attr *At) -> bool {
1827 return !(SM.isBeforeInTranslationUnit(At->getRange().getEnd(),
1828 VD->getBeginLoc())) &&
1829 !(SM.isBeforeInTranslationUnit(VD->getEndLoc(),
1830 At->getRange().getBegin()));
1831 });
1832 return VD->isInlineSpecified() || VD->isConstexpr() ||
1834 AttrRangeOverlapping;
1835}
1836
1837// Returns the `SourceRange` of `D`. The reason why this function exists is
1838// that `D->getSourceRange()` may return a range where the end location is the
1839// starting location of the last token. The end location of the source range
1840// returned by this function is the last location of the last token.
1842 const SourceManager &SM,
1843 const LangOptions &LangOpts) {
1846 End = // `D->getEndLoc` should always return the starting location of the
1847 // last token, so we should get the end of the token
1848 Lexer::getLocForEndOfToken(D->getEndLoc(), 0, SM, LangOpts);
1849
1850 return SourceRange(Begin, End);
1851}
1852
1853// Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
1854// type. The text is obtained through from `TypeLoc`s. Since `TypeLoc` does not
1855// have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
1856// :( ), `Qualifiers` of the pointee type is returned separately through the
1857// output parameter `QualifiersToAppend`.
1858static std::optional<std::string>
1860 const LangOptions &LangOpts,
1861 std::optional<Qualifiers> *QualifiersToAppend) {
1862 QualType Ty = VD->getType();
1863 QualType PteTy;
1864
1865 assert(Ty->isPointerType() && !Ty->isFunctionPointerType() &&
1866 "Expecting a VarDecl of type of pointer to object type");
1867 PteTy = Ty->getPointeeType();
1868
1870 TypeLoc PteTyLoc;
1871
1872 // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
1873 // the `TypeLoc` of the pointee type:
1874 switch (TyLoc.getTypeLocClass()) {
1875 case TypeLoc::ConstantArray:
1876 case TypeLoc::IncompleteArray:
1877 case TypeLoc::VariableArray:
1878 case TypeLoc::DependentSizedArray:
1879 case TypeLoc::Decayed:
1880 assert(isa<ParmVarDecl>(VD) && "An array type shall not be treated as a "
1881 "pointer type unless it decays.");
1882 PteTyLoc = TyLoc.getNextTypeLoc();
1883 break;
1884 case TypeLoc::Pointer:
1885 PteTyLoc = TyLoc.castAs<PointerTypeLoc>().getPointeeLoc();
1886 break;
1887 default:
1888 return std::nullopt;
1889 }
1890 if (PteTyLoc.isNull())
1891 // Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
1892 // when the pointer type is `auto`.
1893 return std::nullopt;
1894
1896
1897 if (!(IdentLoc.isValid() && PteTyLoc.getSourceRange().isValid())) {
1898 // We are expecting these locations to be valid. But in some cases, they are
1899 // not all valid. It is a Clang bug to me and we are not responsible for
1900 // fixing it. So we will just give up for now when it happens.
1901 return std::nullopt;
1902 }
1903
1904 // Note that TypeLoc.getEndLoc() returns the begin location of the last token:
1905 SourceLocation PteEndOfTokenLoc =
1906 Lexer::getLocForEndOfToken(PteTyLoc.getEndLoc(), 0, SM, LangOpts);
1907
1908 if (!PteEndOfTokenLoc.isValid())
1909 // Sometimes we cannot get the end location of the pointee type, e.g., when
1910 // there are macros involved.
1911 return std::nullopt;
1912 if (!SM.isBeforeInTranslationUnit(PteEndOfTokenLoc, IdentLoc)) {
1913 // We only deal with the cases where the source text of the pointee type
1914 // appears on the left-hand side of the variable identifier completely,
1915 // including the following forms:
1916 // `T ident`,
1917 // `T ident[]`, where `T` is any type.
1918 // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`.
1919 return std::nullopt;
1920 }
1921 if (PteTy.hasQualifiers()) {
1922 // TypeLoc does not provide source ranges for qualifiers (it says it's
1923 // intentional but seems fishy to me), so we cannot get the full text
1924 // `PteTy` via source ranges.
1925 *QualifiersToAppend = PteTy.getQualifiers();
1926 }
1927 return getRangeText({PteTyLoc.getBeginLoc(), PteEndOfTokenLoc}, SM, LangOpts)
1928 ->str();
1929}
1930
1931// Returns the text of the name (with qualifiers) of a `FunctionDecl`.
1932static std::optional<StringRef> getFunNameText(const FunctionDecl *FD,
1933 const SourceManager &SM,
1934 const LangOptions &LangOpts) {
1935 SourceLocation BeginLoc = FD->getQualifier()
1937 : FD->getNameInfo().getBeginLoc();
1938 // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
1939 // last token:
1941 FD->getNameInfo().getEndLoc(), 0, SM, LangOpts);
1942 SourceRange NameRange{BeginLoc, EndLoc};
1943
1944 return getRangeText(NameRange, SM, LangOpts);
1945}
1946
1947// Returns the text representing a `std::span` type where the element type is
1948// represented by `EltTyText`.
1949//
1950// Note the optional parameter `Qualifiers`: one needs to pass qualifiers
1951// explicitly if the element type needs to be qualified.
1952static std::string
1953getSpanTypeText(StringRef EltTyText,
1954 std::optional<Qualifiers> Quals = std::nullopt) {
1955 const char *const SpanOpen = "std::span<";
1956
1957 if (Quals)
1958 return SpanOpen + EltTyText.str() + ' ' + Quals->getAsString() + '>';
1959 return SpanOpen + EltTyText.str() + '>';
1960}
1961
1962std::optional<FixItList>
1964 const VarDecl *VD = dyn_cast<VarDecl>(BaseDeclRefExpr->getDecl());
1965
1966 if (VD && s.lookup(VD) == FixitStrategy::Kind::Span) {
1967 ASTContext &Ctx = VD->getASTContext();
1968 // std::span can't represent elements before its begin()
1969 if (auto ConstVal = Offset->getIntegerConstantExpr(Ctx))
1970 if (ConstVal->isNegative())
1971 return std::nullopt;
1972
1973 // note that the expr may (oddly) has multiple layers of parens
1974 // example:
1975 // *((..(pointer + 123)..))
1976 // goal:
1977 // pointer[123]
1978 // Fix-It:
1979 // remove '*('
1980 // replace ' + ' with '['
1981 // replace ')' with ']'
1982
1983 // example:
1984 // *((..(123 + pointer)..))
1985 // goal:
1986 // 123[pointer]
1987 // Fix-It:
1988 // remove '*('
1989 // replace ' + ' with '['
1990 // replace ')' with ']'
1991
1992 const Expr *LHS = AddOp->getLHS(), *RHS = AddOp->getRHS();
1993 const SourceManager &SM = Ctx.getSourceManager();
1994 const LangOptions &LangOpts = Ctx.getLangOpts();
1995 CharSourceRange StarWithTrailWhitespace =
1997 LHS->getBeginLoc());
1998
1999 std::optional<SourceLocation> LHSLocation = getPastLoc(LHS, SM, LangOpts);
2000 if (!LHSLocation)
2001 return std::nullopt;
2002
2003 CharSourceRange PlusWithSurroundingWhitespace =
2004 clang::CharSourceRange::getCharRange(*LHSLocation, RHS->getBeginLoc());
2005
2006 std::optional<SourceLocation> AddOpLocation =
2007 getPastLoc(AddOp, SM, LangOpts);
2008 std::optional<SourceLocation> DerefOpLocation =
2009 getPastLoc(DerefOp, SM, LangOpts);
2010
2011 if (!AddOpLocation || !DerefOpLocation)
2012 return std::nullopt;
2013
2014 CharSourceRange ClosingParenWithPrecWhitespace =
2015 clang::CharSourceRange::getCharRange(*AddOpLocation, *DerefOpLocation);
2016
2017 return FixItList{
2018 {FixItHint::CreateRemoval(StarWithTrailWhitespace),
2019 FixItHint::CreateReplacement(PlusWithSurroundingWhitespace, "["),
2020 FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace, "]")}};
2021 }
2022 return std::nullopt; // something wrong or unsupported, give up
2023}
2024
2025std::optional<FixItList>
2026PointerDereferenceGadget::getFixits(const FixitStrategy &S) const {
2027 const VarDecl *VD = cast<VarDecl>(BaseDeclRefExpr->getDecl());
2028 switch (S.lookup(VD)) {
2029 case FixitStrategy::Kind::Span: {
2030 ASTContext &Ctx = VD->getASTContext();
2032 // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
2033 // Deletes the *operand
2035 Op->getBeginLoc(), Op->getBeginLoc().getLocWithOffset(1));
2036 // Inserts the [0]
2037 if (auto LocPastOperand =
2038 getPastLoc(BaseDeclRefExpr, SM, Ctx.getLangOpts())) {
2039 return FixItList{{FixItHint::CreateRemoval(derefRange),
2040 FixItHint::CreateInsertion(*LocPastOperand, "[0]")}};
2041 }
2042 break;
2043 }
2044 case FixitStrategy::Kind::Iterator:
2045 case FixitStrategy::Kind::Array:
2046 return std::nullopt;
2047 case FixitStrategy::Kind::Vector:
2048 llvm_unreachable("FixitStrategy not implemented yet!");
2049 case FixitStrategy::Kind::Wontfix:
2050 llvm_unreachable("Invalid strategy!");
2051 }
2052
2053 return std::nullopt;
2054}
2055
2056static inline std::optional<FixItList> createDataFixit(const ASTContext &Ctx,
2057 const DeclRefExpr *DRE) {
2058 const SourceManager &SM = Ctx.getSourceManager();
2059 // Inserts the .data() after the DRE
2060 std::optional<SourceLocation> EndOfOperand =
2061 getPastLoc(DRE, SM, Ctx.getLangOpts());
2062
2063 if (EndOfOperand)
2064 return FixItList{{FixItHint::CreateInsertion(*EndOfOperand, ".data()")}};
2065
2066 return std::nullopt;
2067}
2068
2069// Generates fix-its replacing an expression of the form UPC(DRE) with
2070// `DRE.data()`
2071std::optional<FixItList>
2072UPCStandalonePointerGadget::getFixits(const FixitStrategy &S) const {
2073 const auto VD = cast<VarDecl>(Node->getDecl());
2074 switch (S.lookup(VD)) {
2075 case FixitStrategy::Kind::Array:
2076 case FixitStrategy::Kind::Span: {
2077 return createDataFixit(VD->getASTContext(), Node);
2078 // FIXME: Points inside a macro expansion.
2079 break;
2080 }
2081 case FixitStrategy::Kind::Wontfix:
2082 case FixitStrategy::Kind::Iterator:
2083 return std::nullopt;
2084 case FixitStrategy::Kind::Vector:
2085 llvm_unreachable("unsupported strategies for FixableGadgets");
2086 }
2087
2088 return std::nullopt;
2089}
2090
2091// Generates fix-its replacing an expression of the form `&DRE[e]` with
2092// `&DRE.data()[e]`:
2093static std::optional<FixItList>
2095 const auto *ArraySub = cast<ArraySubscriptExpr>(Node->getSubExpr());
2096 const auto *DRE = cast<DeclRefExpr>(ArraySub->getBase()->IgnoreImpCasts());
2097 // FIXME: this `getASTContext` call is costly, we should pass the
2098 // ASTContext in:
2099 const ASTContext &Ctx = DRE->getDecl()->getASTContext();
2100 const Expr *Idx = ArraySub->getIdx();
2101 const SourceManager &SM = Ctx.getSourceManager();
2102 const LangOptions &LangOpts = Ctx.getLangOpts();
2103 std::stringstream SS;
2104 bool IdxIsLitZero = false;
2105
2106 if (auto ICE = Idx->getIntegerConstantExpr(Ctx))
2107 if ((*ICE).isZero())
2108 IdxIsLitZero = true;
2109 std::optional<StringRef> DreString = getExprText(DRE, SM, LangOpts);
2110 if (!DreString)
2111 return std::nullopt;
2112
2113 if (IdxIsLitZero) {
2114 // If the index is literal zero, we produce the most concise fix-it:
2115 SS << (*DreString).str() << ".data()";
2116 } else {
2117 std::optional<StringRef> IndexString = getExprText(Idx, SM, LangOpts);
2118 if (!IndexString)
2119 return std::nullopt;
2120
2121 SS << "&" << (*DreString).str() << ".data()"
2122 << "[" << (*IndexString).str() << "]";
2123 }
2124 return FixItList{
2126}
2127
2128std::optional<FixItList>
2130 DeclUseList DREs = getClaimedVarUseSites();
2131
2132 if (DREs.size() != 1)
2133 return std::nullopt; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
2134 // give up
2135 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
2136 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
2137 FixItList Fixes;
2138
2139 const Stmt *AddAssignNode = Node;
2140 StringRef varName = VD->getName();
2141 const ASTContext &Ctx = VD->getASTContext();
2142
2143 if (!isNonNegativeIntegerExpr(Offset, VD, Ctx))
2144 return std::nullopt;
2145
2146 // To transform UUC(p += n) to UUC(p = p.subspan(..)):
2147 bool NotParenExpr =
2148 (Offset->IgnoreParens()->getBeginLoc() == Offset->getBeginLoc());
2149 std::string SS = varName.str() + " = " + varName.str() + ".subspan";
2150 if (NotParenExpr)
2151 SS += "(";
2152
2153 std::optional<SourceLocation> AddAssignLocation = getEndCharLoc(
2154 AddAssignNode, Ctx.getSourceManager(), Ctx.getLangOpts());
2155 if (!AddAssignLocation)
2156 return std::nullopt;
2157
2158 Fixes.push_back(FixItHint::CreateReplacement(
2159 SourceRange(AddAssignNode->getBeginLoc(), Node->getOperatorLoc()),
2160 SS));
2161 if (NotParenExpr)
2162 Fixes.push_back(FixItHint::CreateInsertion(
2163 Offset->getEndLoc().getLocWithOffset(1), ")"));
2164 return Fixes;
2165 }
2166 }
2167 return std::nullopt; // Not in the cases that we can handle for now, give up.
2168}
2169
2170std::optional<FixItList>
2172 DeclUseList DREs = getClaimedVarUseSites();
2173
2174 if (DREs.size() != 1)
2175 return std::nullopt; // In cases of `++Ptr` where `Ptr` is not a DRE, we
2176 // give up
2177 if (const VarDecl *VD = dyn_cast<VarDecl>(DREs.front()->getDecl())) {
2178 if (S.lookup(VD) == FixitStrategy::Kind::Span) {
2179 FixItList Fixes;
2180 std::stringstream SS;
2181 StringRef varName = VD->getName();
2182 const ASTContext &Ctx = VD->getASTContext();
2183
2184 // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
2185 SS << "(" << varName.data() << " = " << varName.data()
2186 << ".subspan(1)).data()";
2187 std::optional<SourceLocation> PreIncLocation =
2189 if (!PreIncLocation)
2190 return std::nullopt;
2191
2192 Fixes.push_back(FixItHint::CreateReplacement(
2193 SourceRange(Node->getBeginLoc(), *PreIncLocation), SS.str()));
2194 return Fixes;
2195 }
2196 }
2197 return std::nullopt; // Not in the cases that we can handle for now, give up.
2198}
2199
2200// For a non-null initializer `Init` of `T *` type, this function returns
2201// `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
2202// to output stream.
2203// In many cases, this function cannot figure out the actual extent `S`. It
2204// then will use a place holder to replace `S` to ask users to fill `S` in. The
2205// initializer shall be used to initialize a variable of type `std::span<T>`.
2206// In some cases (e. g. constant size array) the initializer should remain
2207// unchanged and the function returns empty list. In case the function can't
2208// provide the right fixit it will return nullopt.
2209//
2210// FIXME: Support multi-level pointers
2211//
2212// Parameters:
2213// `Init` a pointer to the initializer expression
2214// `Ctx` a reference to the ASTContext
2215static std::optional<FixItList>
2217 const StringRef UserFillPlaceHolder) {
2218 const SourceManager &SM = Ctx.getSourceManager();
2219 const LangOptions &LangOpts = Ctx.getLangOpts();
2220
2221 // If `Init` has a constant value that is (or equivalent to) a
2222 // NULL pointer, we use the default constructor to initialize the span
2223 // object, i.e., a `std:span` variable declaration with no initializer.
2224 // So the fix-it is just to remove the initializer.
2225 if (Init->isNullPointerConstant(
2226 Ctx,
2227 // FIXME: Why does this function not ask for `const ASTContext
2228 // &`? It should. Maybe worth an NFC patch later.
2230 NPC_ValueDependentIsNotNull)) {
2231 std::optional<SourceLocation> InitLocation =
2232 getEndCharLoc(Init, SM, LangOpts);
2233 if (!InitLocation)
2234 return std::nullopt;
2235
2236 SourceRange SR(Init->getBeginLoc(), *InitLocation);
2237
2238 return FixItList{FixItHint::CreateRemoval(SR)};
2239 }
2240
2241 FixItList FixIts{};
2242 std::string ExtentText = UserFillPlaceHolder.data();
2243 StringRef One = "1";
2244
2245 // Insert `{` before `Init`:
2246 FixIts.push_back(FixItHint::CreateInsertion(Init->getBeginLoc(), "{"));
2247 // Try to get the data extent. Break into different cases:
2248 if (auto CxxNew = dyn_cast<CXXNewExpr>(Init->IgnoreImpCasts())) {
2249 // In cases `Init` is `new T[n]` and there is no explicit cast over
2250 // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
2251 // of `T`. So the extent is `n` unless `n` has side effects. Similar but
2252 // simpler for the case where `Init` is `new T`.
2253 if (const Expr *Ext = CxxNew->getArraySize().value_or(nullptr)) {
2254 if (!Ext->HasSideEffects(Ctx)) {
2255 std::optional<StringRef> ExtentString = getExprText(Ext, SM, LangOpts);
2256 if (!ExtentString)
2257 return std::nullopt;
2258 ExtentText = *ExtentString;
2259 }
2260 } else if (!CxxNew->isArray())
2261 // Although the initializer is not allocating a buffer, the pointer
2262 // variable could still be used in buffer access operations.
2263 ExtentText = One;
2264 } else if (Ctx.getAsConstantArrayType(Init->IgnoreImpCasts()->getType())) {
2265 // std::span has a single parameter constructor for initialization with
2266 // constant size array. The size is auto-deduced as the constructor is a
2267 // function template. The correct fixit is empty - no changes should happen.
2268 return FixItList{};
2269 } else {
2270 // In cases `Init` is of the form `&Var` after stripping of implicit
2271 // casts, where `&` is the built-in operator, the extent is 1.
2272 if (auto AddrOfExpr = dyn_cast<UnaryOperator>(Init->IgnoreImpCasts()))
2273 if (AddrOfExpr->getOpcode() == UnaryOperatorKind::UO_AddrOf &&
2274 isa_and_present<DeclRefExpr>(AddrOfExpr->getSubExpr()))
2275 ExtentText = One;
2276 // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
2277 // and explicit casting, etc. etc.
2278 }
2279
2280 SmallString<32> StrBuffer{};
2281 std::optional<SourceLocation> LocPassInit = getPastLoc(Init, SM, LangOpts);
2282
2283 if (!LocPassInit)
2284 return std::nullopt;
2285
2286 StrBuffer.append(", ");
2287 StrBuffer.append(ExtentText);
2288 StrBuffer.append("}");
2289 FixIts.push_back(FixItHint::CreateInsertion(*LocPassInit, StrBuffer.str()));
2290 return FixIts;
2291}
2292
2293#ifndef NDEBUG
2294#define DEBUG_NOTE_DECL_FAIL(D, Msg) \
2295 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
2296 "failed to produce fixit for declaration '" + \
2297 (D)->getNameAsString() + "'" + (Msg))
2298#else
2299#define DEBUG_NOTE_DECL_FAIL(D, Msg)
2300#endif
2301
2302// For the given variable declaration with a pointer-to-T type, returns the text
2303// `std::span<T>`. If it is unable to generate the text, returns
2304// `std::nullopt`.
2305static std::optional<std::string>
2307 assert(VD->getType()->isPointerType());
2308
2309 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
2310 std::optional<std::string> PteTyText = getPointeeTypeText(
2311 VD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
2312
2313 if (!PteTyText)
2314 return std::nullopt;
2315
2316 std::string SpanTyText = "std::span<";
2317
2318 SpanTyText.append(*PteTyText);
2319 // Append qualifiers to span element type if any:
2320 if (PteTyQualifiers) {
2321 SpanTyText.append(" ");
2322 SpanTyText.append(PteTyQualifiers->getAsString());
2323 }
2324 SpanTyText.append(">");
2325 return SpanTyText;
2326}
2327
2328// For a `VarDecl` of the form `T * var (= Init)?`, this
2329// function generates fix-its that
2330// 1) replace `T * var` with `std::span<T> var`; and
2331// 2) change `Init` accordingly to a span constructor, if it exists.
2332//
2333// FIXME: support Multi-level pointers
2334//
2335// Parameters:
2336// `D` a pointer the variable declaration node
2337// `Ctx` a reference to the ASTContext
2338// `UserFillPlaceHolder` the user-input placeholder text
2339// Returns:
2340// the non-empty fix-it list, if fix-its are successfuly generated; empty
2341// list otherwise.
2342static FixItList fixLocalVarDeclWithSpan(const VarDecl *D, ASTContext &Ctx,
2343 const StringRef UserFillPlaceHolder,
2344 UnsafeBufferUsageHandler &Handler) {
2346 return {};
2347
2348 FixItList FixIts{};
2349 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(D, Ctx);
2350
2351 if (!SpanTyText) {
2352 DEBUG_NOTE_DECL_FAIL(D, " : failed to generate 'std::span' type");
2353 return {};
2354 }
2355
2356 // Will hold the text for `std::span<T> Ident`:
2357 std::stringstream SS;
2358
2359 SS << *SpanTyText;
2360 // Fix the initializer if it exists:
2361 if (const Expr *Init = D->getInit()) {
2362 std::optional<FixItList> InitFixIts =
2363 FixVarInitializerWithSpan(Init, Ctx, UserFillPlaceHolder);
2364 if (!InitFixIts)
2365 return {};
2366 FixIts.insert(FixIts.end(), std::make_move_iterator(InitFixIts->begin()),
2367 std::make_move_iterator(InitFixIts->end()));
2368 }
2369 // For declaration of the form `T * ident = init;`, we want to replace
2370 // `T * ` with `std::span<T>`.
2371 // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
2372 // just `T *` with `std::span<T>`.
2373 const SourceLocation EndLocForReplacement = D->getTypeSpecEndLoc();
2374 if (!EndLocForReplacement.isValid()) {
2375 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the end of the declaration");
2376 return {};
2377 }
2378 // The only exception is that for `T *ident` we'll add a single space between
2379 // "std::span<T>" and "ident".
2380 // FIXME: The condition is false for identifiers expended from macros.
2381 if (EndLocForReplacement.getLocWithOffset(1) == getVarDeclIdentifierLoc(D))
2382 SS << " ";
2383
2384 FixIts.push_back(FixItHint::CreateReplacement(
2385 SourceRange(D->getBeginLoc(), EndLocForReplacement), SS.str()));
2386 return FixIts;
2387}
2388
2389static bool hasConflictingOverload(const FunctionDecl *FD) {
2390 return !FD->getDeclContext()->lookup(FD->getDeclName()).isSingleResult();
2391}
2392
2393// For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
2394// types, this function produces fix-its to make the change self-contained. Let
2395// 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
2396// entity defined by the `FunctionDecl` after the change to the parameters.
2397// Fix-its produced by this function are
2398// 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
2399// of 'F';
2400// 2. Create a declaration of "NewF" next to each declaration of `F`;
2401// 3. Create a definition of "F" (as its' original definition is now belongs
2402// to "NewF") next to its original definition. The body of the creating
2403// definition calls to "NewF".
2404//
2405// Example:
2406//
2407// void f(int *p); // original declaration
2408// void f(int *p) { // original definition
2409// p[5];
2410// }
2411//
2412// To change the parameter `p` to be of `std::span<int>` type, we
2413// also add overloads:
2414//
2415// [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
2416// void f(std::span<int> p); // added overload decl
2417// void f(std::span<int> p) { // original def where param is changed
2418// p[5];
2419// }
2420// [[clang::unsafe_buffer_usage]] void f(int *p) { // added def
2421// return f(std::span(p, <# size #>));
2422// }
2423//
2424static std::optional<FixItList>
2426 const ASTContext &Ctx,
2427 UnsafeBufferUsageHandler &Handler) {
2428 // FIXME: need to make this conflict checking better:
2429 if (hasConflictingOverload(FD))
2430 return std::nullopt;
2431
2432 const SourceManager &SM = Ctx.getSourceManager();
2433 const LangOptions &LangOpts = Ctx.getLangOpts();
2434 const unsigned NumParms = FD->getNumParams();
2435 std::vector<std::string> NewTysTexts(NumParms);
2436 std::vector<bool> ParmsMask(NumParms, false);
2437 bool AtLeastOneParmToFix = false;
2438
2439 for (unsigned i = 0; i < NumParms; i++) {
2440 const ParmVarDecl *PVD = FD->getParamDecl(i);
2441
2442 if (S.lookup(PVD) == FixitStrategy::Kind::Wontfix)
2443 continue;
2444 if (S.lookup(PVD) != FixitStrategy::Kind::Span)
2445 // Not supported, not suppose to happen:
2446 return std::nullopt;
2447
2448 std::optional<Qualifiers> PteTyQuals = std::nullopt;
2449 std::optional<std::string> PteTyText =
2450 getPointeeTypeText(PVD, SM, LangOpts, &PteTyQuals);
2451
2452 if (!PteTyText)
2453 // something wrong in obtaining the text of the pointee type, give up
2454 return std::nullopt;
2455 // FIXME: whether we should create std::span type depends on the
2456 // FixitStrategy.
2457 NewTysTexts[i] = getSpanTypeText(*PteTyText, PteTyQuals);
2458 ParmsMask[i] = true;
2459 AtLeastOneParmToFix = true;
2460 }
2461 if (!AtLeastOneParmToFix)
2462 // No need to create function overloads:
2463 return {};
2464 // FIXME Respect indentation of the original code.
2465
2466 // A lambda that creates the text representation of a function declaration
2467 // with the new type signatures:
2468 const auto NewOverloadSignatureCreator =
2469 [&SM, &LangOpts, &NewTysTexts,
2470 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
2471 std::stringstream SS;
2472
2473 SS << ";";
2474 SS << getEndOfLine().str();
2475 // Append: ret-type func-name "("
2476 if (auto Prefix = getRangeText(
2477 SourceRange(FD->getBeginLoc(), (*FD->param_begin())->getBeginLoc()),
2478 SM, LangOpts))
2479 SS << Prefix->str();
2480 else
2481 return std::nullopt; // give up
2482 // Append: parameter-type-list
2483 const unsigned NumParms = FD->getNumParams();
2484
2485 for (unsigned i = 0; i < NumParms; i++) {
2486 const ParmVarDecl *Parm = FD->getParamDecl(i);
2487
2488 if (Parm->isImplicit())
2489 continue;
2490 if (ParmsMask[i]) {
2491 // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
2492 // new type:
2493 SS << NewTysTexts[i];
2494 // print parameter name if provided:
2495 if (IdentifierInfo *II = Parm->getIdentifier())
2496 SS << ' ' << II->getName().str();
2497 } else if (auto ParmTypeText =
2498 getRangeText(getSourceRangeToTokenEnd(Parm, SM, LangOpts),
2499 SM, LangOpts)) {
2500 // print the whole `Parm` without modification:
2501 SS << ParmTypeText->str();
2502 } else
2503 return std::nullopt; // something wrong, give up
2504 if (i != NumParms - 1)
2505 SS << ", ";
2506 }
2507 SS << ")";
2508 return SS.str();
2509 };
2510
2511 // A lambda that creates the text representation of a function definition with
2512 // the original signature:
2513 const auto OldOverloadDefCreator =
2514 [&Handler, &SM, &LangOpts, &NewTysTexts,
2515 &ParmsMask](const FunctionDecl *FD) -> std::optional<std::string> {
2516 std::stringstream SS;
2517
2518 SS << getEndOfLine().str();
2519 // Append: attr-name ret-type func-name "(" param-list ")" "{"
2520 if (auto FDPrefix = getRangeText(
2521 SourceRange(FD->getBeginLoc(), FD->getBody()->getBeginLoc()), SM,
2522 LangOpts))
2523 SS << Handler.getUnsafeBufferUsageAttributeTextAt(FD->getBeginLoc(), " ")
2524 << FDPrefix->str() << "{";
2525 else
2526 return std::nullopt;
2527 // Append: "return" func-name "("
2528 if (auto FunQualName = getFunNameText(FD, SM, LangOpts))
2529 SS << "return " << FunQualName->str() << "(";
2530 else
2531 return std::nullopt;
2532
2533 // Append: arg-list
2534 const unsigned NumParms = FD->getNumParams();
2535 for (unsigned i = 0; i < NumParms; i++) {
2536 const ParmVarDecl *Parm = FD->getParamDecl(i);
2537
2538 if (Parm->isImplicit())
2539 continue;
2540 // FIXME: If a parameter has no name, it is unused in the
2541 // definition. So we could just leave it as it is.
2542 if (!Parm->getIdentifier())
2543 // If a parameter of a function definition has no name:
2544 return std::nullopt;
2545 if (ParmsMask[i])
2546 // This is our spanified paramter!
2547 SS << NewTysTexts[i] << "(" << Parm->getIdentifier()->getName().str()
2548 << ", " << getUserFillPlaceHolder("size") << ")";
2549 else
2550 SS << Parm->getIdentifier()->getName().str();
2551 if (i != NumParms - 1)
2552 SS << ", ";
2553 }
2554 // finish call and the body
2555 SS << ");}" << getEndOfLine().str();
2556 // FIXME: 80-char line formatting?
2557 return SS.str();
2558 };
2559
2560 FixItList FixIts{};
2561 for (FunctionDecl *FReDecl : FD->redecls()) {
2562 std::optional<SourceLocation> Loc = getPastLoc(FReDecl, SM, LangOpts);
2563
2564 if (!Loc)
2565 return {};
2566 if (FReDecl->isThisDeclarationADefinition()) {
2567 assert(FReDecl == FD && "inconsistent function definition");
2568 // Inserts a definition with the old signature to the end of
2569 // `FReDecl`:
2570 if (auto OldOverloadDef = OldOverloadDefCreator(FReDecl))
2571 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *OldOverloadDef));
2572 else
2573 return {}; // give up
2574 } else {
2575 // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
2576 if (!FReDecl->hasAttr<UnsafeBufferUsageAttr>()) {
2577 FixIts.emplace_back(FixItHint::CreateInsertion(
2578 FReDecl->getBeginLoc(), Handler.getUnsafeBufferUsageAttributeTextAt(
2579 FReDecl->getBeginLoc(), " ")));
2580 }
2581 // Inserts a declaration with the new signature to the end of `FReDecl`:
2582 if (auto NewOverloadDecl = NewOverloadSignatureCreator(FReDecl))
2583 FixIts.emplace_back(FixItHint::CreateInsertion(*Loc, *NewOverloadDecl));
2584 else
2585 return {};
2586 }
2587 }
2588 return FixIts;
2589}
2590
2591// To fix a `ParmVarDecl` to be of `std::span` type.
2592static FixItList fixParamWithSpan(const ParmVarDecl *PVD, const ASTContext &Ctx,
2593 UnsafeBufferUsageHandler &Handler) {
2595 DEBUG_NOTE_DECL_FAIL(PVD, " : has unsupport specifier(s)");
2596 return {};
2597 }
2598 if (PVD->hasDefaultArg()) {
2599 // FIXME: generate fix-its for default values:
2600 DEBUG_NOTE_DECL_FAIL(PVD, " : has default arg");
2601 return {};
2602 }
2603
2604 std::optional<Qualifiers> PteTyQualifiers = std::nullopt;
2605 std::optional<std::string> PteTyText = getPointeeTypeText(
2606 PVD, Ctx.getSourceManager(), Ctx.getLangOpts(), &PteTyQualifiers);
2607
2608 if (!PteTyText) {
2609 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid pointee type");
2610 return {};
2611 }
2612
2613 std::optional<StringRef> PVDNameText = PVD->getIdentifier()->getName();
2614
2615 if (!PVDNameText) {
2616 DEBUG_NOTE_DECL_FAIL(PVD, " : invalid identifier name");
2617 return {};
2618 }
2619
2620 std::stringstream SS;
2621 std::optional<std::string> SpanTyText = createSpanTypeForVarDecl(PVD, Ctx);
2622
2623 if (PteTyQualifiers)
2624 // Append qualifiers if they exist:
2625 SS << getSpanTypeText(*PteTyText, PteTyQualifiers);
2626 else
2627 SS << getSpanTypeText(*PteTyText);
2628 // Append qualifiers to the type of the parameter:
2629 if (PVD->getType().hasQualifiers())
2630 SS << ' ' << PVD->getType().getQualifiers().getAsString();
2631 // Append parameter's name:
2632 SS << ' ' << PVDNameText->str();
2633 // Add replacement fix-it:
2634 return {FixItHint::CreateReplacement(PVD->getSourceRange(), SS.str())};
2635}
2636
2637static FixItList fixVariableWithSpan(const VarDecl *VD,
2638 const DeclUseTracker &Tracker,
2639 ASTContext &Ctx,
2640 UnsafeBufferUsageHandler &Handler) {
2641 const DeclStmt *DS = Tracker.lookupDecl(VD);
2642 if (!DS) {
2644 " : variables declared this way not implemented yet");
2645 return {};
2646 }
2647 if (!DS->isSingleDecl()) {
2648 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
2649 DEBUG_NOTE_DECL_FAIL(VD, " : multiple VarDecls");
2650 return {};
2651 }
2652 // Currently DS is an unused variable but we'll need it when
2653 // non-single decls are implemented, where the pointee type name
2654 // and the '*' are spread around the place.
2655 (void)DS;
2656
2657 // FIXME: handle cases where DS has multiple declarations
2658 return fixLocalVarDeclWithSpan(VD, Ctx, getUserFillPlaceHolder(), Handler);
2659}
2660
2661static FixItList fixVarDeclWithArray(const VarDecl *D, const ASTContext &Ctx,
2662 UnsafeBufferUsageHandler &Handler) {
2663 FixItList FixIts{};
2664
2665 // Note: the code below expects the declaration to not use any type sugar like
2666 // typedef.
2667 if (auto CAT = Ctx.getAsConstantArrayType(D->getType())) {
2668 const QualType &ArrayEltT = CAT->getElementType();
2669 assert(!ArrayEltT.isNull() && "Trying to fix a non-array type variable!");
2670 // FIXME: support multi-dimensional arrays
2671 if (isa<clang::ArrayType>(ArrayEltT.getCanonicalType()))
2672 return {};
2673
2675
2676 // Get the spelling of the element type as written in the source file
2677 // (including macros, etc.).
2678 auto MaybeElemTypeTxt =
2680 Ctx.getLangOpts());
2681 if (!MaybeElemTypeTxt)
2682 return {};
2683 const llvm::StringRef ElemTypeTxt = MaybeElemTypeTxt->trim();
2684
2685 // Find the '[' token.
2686 std::optional<Token> NextTok = Lexer::findNextToken(
2688 while (NextTok && !NextTok->is(tok::l_square) &&
2689 NextTok->getLocation() <= D->getSourceRange().getEnd())
2690 NextTok = Lexer::findNextToken(NextTok->getLocation(),
2691 Ctx.getSourceManager(), Ctx.getLangOpts());
2692 if (!NextTok)
2693 return {};
2694 const SourceLocation LSqBracketLoc = NextTok->getLocation();
2695
2696 // Get the spelling of the array size as written in the source file
2697 // (including macros, etc.).
2698 auto MaybeArraySizeTxt = getRangeText(
2699 {LSqBracketLoc.getLocWithOffset(1), D->getTypeSpecEndLoc()},
2700 Ctx.getSourceManager(), Ctx.getLangOpts());
2701 if (!MaybeArraySizeTxt)
2702 return {};
2703 const llvm::StringRef ArraySizeTxt = MaybeArraySizeTxt->trim();
2704 if (ArraySizeTxt.empty()) {
2705 // FIXME: Support array size getting determined from the initializer.
2706 // Examples:
2707 // int arr1[] = {0, 1, 2};
2708 // int arr2{3, 4, 5};
2709 // We might be able to preserve the non-specified size with `auto` and
2710 // `std::to_array`:
2711 // auto arr1 = std::to_array<int>({0, 1, 2});
2712 return {};
2713 }
2714
2715 std::optional<StringRef> IdentText =
2717
2718 if (!IdentText) {
2719 DEBUG_NOTE_DECL_FAIL(D, " : failed to locate the identifier");
2720 return {};
2721 }
2722
2723 SmallString<32> Replacement;
2724 raw_svector_ostream OS(Replacement);
2725 OS << "std::array<" << ElemTypeTxt << ", " << ArraySizeTxt << "> "
2726 << IdentText->str();
2727
2728 FixIts.push_back(FixItHint::CreateReplacement(
2729 SourceRange{D->getBeginLoc(), D->getTypeSpecEndLoc()}, OS.str()));
2730 }
2731
2732 return FixIts;
2733}
2734
2735static FixItList fixVariableWithArray(const VarDecl *VD,
2736 const DeclUseTracker &Tracker,
2737 const ASTContext &Ctx,
2738 UnsafeBufferUsageHandler &Handler) {
2739 const DeclStmt *DS = Tracker.lookupDecl(VD);
2740 assert(DS && "Fixing non-local variables not implemented yet!");
2741 if (!DS->isSingleDecl()) {
2742 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
2743 return {};
2744 }
2745 // Currently DS is an unused variable but we'll need it when
2746 // non-single decls are implemented, where the pointee type name
2747 // and the '*' are spread around the place.
2748 (void)DS;
2749
2750 // FIXME: handle cases where DS has multiple declarations
2751 return fixVarDeclWithArray(VD, Ctx, Handler);
2752}
2753
2754// TODO: we should be consistent to use `std::nullopt` to represent no-fix due
2755// to any unexpected problem.
2756static FixItList
2758 /* The function decl under analysis */ const Decl *D,
2759 const DeclUseTracker &Tracker, ASTContext &Ctx,
2760 UnsafeBufferUsageHandler &Handler) {
2761 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD)) {
2762 auto *FD = dyn_cast<clang::FunctionDecl>(PVD->getDeclContext());
2763 if (!FD || FD != D) {
2764 // `FD != D` means that `PVD` belongs to a function that is not being
2765 // analyzed currently. Thus `FD` may not be complete.
2766 DEBUG_NOTE_DECL_FAIL(VD, " : function not currently analyzed");
2767 return {};
2768 }
2769
2770 // TODO If function has a try block we can't change params unless we check
2771 // also its catch block for their use.
2772 // FIXME We might support static class methods, some select methods,
2773 // operators and possibly lamdas.
2774 if (FD->isMain() || FD->isConstexpr() ||
2775 FD->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate ||
2776 FD->isVariadic() ||
2777 // also covers call-operator of lamdas
2778 isa<CXXMethodDecl>(FD) ||
2779 // skip when the function body is a try-block
2780 (FD->hasBody() && isa<CXXTryStmt>(FD->getBody())) ||
2781 FD->isOverloadedOperator()) {
2782 DEBUG_NOTE_DECL_FAIL(VD, " : unsupported function decl");
2783 return {}; // TODO test all these cases
2784 }
2785 }
2786
2787 switch (K) {
2788 case FixitStrategy::Kind::Span: {
2789 if (VD->getType()->isPointerType()) {
2790 if (const auto *PVD = dyn_cast<ParmVarDecl>(VD))
2791 return fixParamWithSpan(PVD, Ctx, Handler);
2792
2793 if (VD->isLocalVarDecl())
2794 return fixVariableWithSpan(VD, Tracker, Ctx, Handler);
2795 }
2796 DEBUG_NOTE_DECL_FAIL(VD, " : not a pointer");
2797 return {};
2798 }
2799 case FixitStrategy::Kind::Array: {
2800 if (VD->isLocalVarDecl() && Ctx.getAsConstantArrayType(VD->getType()))
2801 return fixVariableWithArray(VD, Tracker, Ctx, Handler);
2802
2803 DEBUG_NOTE_DECL_FAIL(VD, " : not a local const-size array");
2804 return {};
2805 }
2806 case FixitStrategy::Kind::Iterator:
2807 case FixitStrategy::Kind::Vector:
2808 llvm_unreachable("FixitStrategy not implemented yet!");
2809 case FixitStrategy::Kind::Wontfix:
2810 llvm_unreachable("Invalid strategy!");
2811 }
2812 llvm_unreachable("Unknown strategy!");
2813}
2814
2815// Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
2816// `RemoveRange` of 'h' overlaps with a macro use.
2817static bool overlapWithMacro(const FixItList &FixIts) {
2818 // FIXME: For now we only check if the range (or the first token) is (part of)
2819 // a macro expansion. Ideally, we want to check for all tokens in the range.
2820 return llvm::any_of(FixIts, [](const FixItHint &Hint) {
2821 auto Range = Hint.RemoveRange;
2823 // If the range (or the first token) is (part of) a macro expansion:
2824 return true;
2825 return false;
2826 });
2827}
2828
2829// Returns true iff `VD` is a parameter of the declaration `D`:
2830static bool isParameterOf(const VarDecl *VD, const Decl *D) {
2831 return isa<ParmVarDecl>(VD) &&
2832 VD->getDeclContext() == dyn_cast<DeclContext>(D);
2833}
2834
2835// Erases variables in `FixItsForVariable`, if such a variable has an unfixable
2836// group mate. A variable `v` is unfixable iff `FixItsForVariable` does not
2837// contain `v`.
2839 std::map<const VarDecl *, FixItList> &FixItsForVariable,
2840 const VariableGroupsManager &VarGrpMgr) {
2841 // Variables will be removed from `FixItsForVariable`:
2843
2844 for (const auto &[VD, Ignore] : FixItsForVariable) {
2845 VarGrpRef Grp = VarGrpMgr.getGroupOfVar(VD);
2846 if (llvm::any_of(Grp,
2847 [&FixItsForVariable](const VarDecl *GrpMember) -> bool {
2848 return !FixItsForVariable.count(GrpMember);
2849 })) {
2850 // At least one group member cannot be fixed, so we have to erase the
2851 // whole group:
2852 for (const VarDecl *Member : Grp)
2853 ToErase.push_back(Member);
2854 }
2855 }
2856 for (auto *VarToErase : ToErase)
2857 FixItsForVariable.erase(VarToErase);
2858}
2859
2860// Returns the fix-its that create bounds-safe function overloads for the
2861// function `D`, if `D`'s parameters will be changed to safe-types through
2862// fix-its in `FixItsForVariable`.
2863//
2864// NOTE: In case `D`'s parameters will be changed but bounds-safe function
2865// overloads cannot created, the whole group that contains the parameters will
2866// be erased from `FixItsForVariable`.
2868 std::map<const VarDecl *, FixItList> &FixItsForVariable /* mutable */,
2869 const VariableGroupsManager &VarGrpMgr, const FunctionDecl *FD,
2870 const FixitStrategy &S, ASTContext &Ctx,
2871 UnsafeBufferUsageHandler &Handler) {
2872 FixItList FixItsSharedByParms{};
2873
2874 std::optional<FixItList> OverloadFixes =
2875 createOverloadsForFixedParams(S, FD, Ctx, Handler);
2876
2877 if (OverloadFixes) {
2878 FixItsSharedByParms.append(*OverloadFixes);
2879 } else {
2880 // Something wrong in generating `OverloadFixes`, need to remove the
2881 // whole group, where parameters are in, from `FixItsForVariable` (Note
2882 // that all parameters should be in the same group):
2883 for (auto *Member : VarGrpMgr.getGroupOfParms())
2884 FixItsForVariable.erase(Member);
2885 }
2886 return FixItsSharedByParms;
2887}
2888
2889// Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
2890static std::map<const VarDecl *, FixItList>
2891getFixIts(FixableGadgetSets &FixablesForAllVars, const FixitStrategy &S,
2892 ASTContext &Ctx,
2893 /* The function decl under analysis */ const Decl *D,
2894 const DeclUseTracker &Tracker, UnsafeBufferUsageHandler &Handler,
2895 const VariableGroupsManager &VarGrpMgr) {
2896 // `FixItsForVariable` will map each variable to a set of fix-its directly
2897 // associated to the variable itself. Fix-its of distinct variables in
2898 // `FixItsForVariable` are disjoint.
2899 std::map<const VarDecl *, FixItList> FixItsForVariable;
2900
2901 // Populate `FixItsForVariable` with fix-its directly associated with each
2902 // variable. Fix-its directly associated to a variable 'v' are the ones
2903 // produced by the `FixableGadget`s whose claimed variable is 'v'.
2904 for (const auto &[VD, Fixables] : FixablesForAllVars.byVar) {
2905 FixItsForVariable[VD] =
2906 fixVariable(VD, S.lookup(VD), D, Tracker, Ctx, Handler);
2907 // If we fail to produce Fix-It for the declaration we have to skip the
2908 // variable entirely.
2909 if (FixItsForVariable[VD].empty()) {
2910 FixItsForVariable.erase(VD);
2911 continue;
2912 }
2913 for (const auto &F : Fixables) {
2914 std::optional<FixItList> Fixits = F->getFixits(S);
2915
2916 if (Fixits) {
2917 FixItsForVariable[VD].insert(FixItsForVariable[VD].end(),
2918 Fixits->begin(), Fixits->end());
2919 continue;
2920 }
2921#ifndef NDEBUG
2922 Handler.addDebugNoteForVar(
2923 VD, F->getSourceLoc(),
2924 ("gadget '" + F->getDebugName() + "' refused to produce a fix")
2925 .str());
2926#endif
2927 FixItsForVariable.erase(VD);
2928 break;
2929 }
2930 }
2931
2932 // `FixItsForVariable` now contains only variables that can be
2933 // fixed. A variable can be fixed if its' declaration and all Fixables
2934 // associated to it can all be fixed.
2935
2936 // To further remove from `FixItsForVariable` variables whose group mates
2937 // cannot be fixed...
2938 eraseVarsForUnfixableGroupMates(FixItsForVariable, VarGrpMgr);
2939 // Now `FixItsForVariable` gets further reduced: a variable is in
2940 // `FixItsForVariable` iff it can be fixed and all its group mates can be
2941 // fixed.
2942
2943 // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
2944 // That is, when fixing multiple parameters in one step, these fix-its will
2945 // be applied only once (instead of being applied per parameter).
2946 FixItList FixItsSharedByParms{};
2947
2948 if (auto *FD = dyn_cast<FunctionDecl>(D))
2949 FixItsSharedByParms = createFunctionOverloadsForParms(
2950 FixItsForVariable, VarGrpMgr, FD, S, Ctx, Handler);
2951
2952 // The map that maps each variable `v` to fix-its for the whole group where
2953 // `v` is in:
2954 std::map<const VarDecl *, FixItList> FinalFixItsForVariable{
2955 FixItsForVariable};
2956
2957 for (auto &[Var, Ignore] : FixItsForVariable) {
2958 bool AnyParm = false;
2959 const auto VarGroupForVD = VarGrpMgr.getGroupOfVar(Var, &AnyParm);
2960
2961 for (const VarDecl *GrpMate : VarGroupForVD) {
2962 if (Var == GrpMate)
2963 continue;
2964 if (FixItsForVariable.count(GrpMate))
2965 FinalFixItsForVariable[Var].append(FixItsForVariable[GrpMate]);
2966 }
2967 if (AnyParm) {
2968 // This assertion should never fail. Otherwise we have a bug.
2969 assert(!FixItsSharedByParms.empty() &&
2970 "Should not try to fix a parameter that does not belong to a "
2971 "FunctionDecl");
2972 FinalFixItsForVariable[Var].append(FixItsSharedByParms);
2973 }
2974 }
2975 // Fix-its that will be applied in one step shall NOT:
2976 // 1. overlap with macros or/and templates; or
2977 // 2. conflict with each other.
2978 // Otherwise, the fix-its will be dropped.
2979 for (auto Iter = FinalFixItsForVariable.begin();
2980 Iter != FinalFixItsForVariable.end();)
2981 if (overlapWithMacro(Iter->second) ||
2983 Iter = FinalFixItsForVariable.erase(Iter);
2984 } else
2985 Iter++;
2986 return FinalFixItsForVariable;
2987}
2988
2989template <typename VarDeclIterTy>
2990static FixitStrategy
2991getNaiveStrategy(llvm::iterator_range<VarDeclIterTy> UnsafeVars) {
2992 FixitStrategy S;
2993 for (const VarDecl *VD : UnsafeVars) {
2994 if (isa<ConstantArrayType>(VD->getType().getCanonicalType()))
2995 S.set(VD, FixitStrategy::Kind::Array);
2996 else
2997 S.set(VD, FixitStrategy::Kind::Span);
2998 }
2999 return S;
3000}
3001
3002// Manages variable groups:
3004 const std::vector<VarGrpTy> Groups;
3005 const std::map<const VarDecl *, unsigned> &VarGrpMap;
3006 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms;
3007
3008public:
3010 const std::vector<VarGrpTy> &Groups,
3011 const std::map<const VarDecl *, unsigned> &VarGrpMap,
3012 const llvm::SetVector<const VarDecl *> &GrpsUnionForParms)
3013 : Groups(Groups), VarGrpMap(VarGrpMap),
3014 GrpsUnionForParms(GrpsUnionForParms) {}
3015
3016 VarGrpRef getGroupOfVar(const VarDecl *Var, bool *HasParm) const override {
3017 if (GrpsUnionForParms.contains(Var)) {
3018 if (HasParm)
3019 *HasParm = true;
3020 return GrpsUnionForParms.getArrayRef();
3021 }
3022 if (HasParm)
3023 *HasParm = false;
3024
3025 auto It = VarGrpMap.find(Var);
3026
3027 if (It == VarGrpMap.end())
3028 return std::nullopt;
3029 return Groups[It->second];
3030 }
3031
3032 VarGrpRef getGroupOfParms() const override {
3033 return GrpsUnionForParms.getArrayRef();
3034 }
3035};
3036
3038 UnsafeBufferUsageHandler &Handler,
3039 bool EmitSuggestions) {
3040#ifndef NDEBUG
3041 Handler.clearDebugNotes();
3042#endif
3043
3044 assert(D && D->getBody());
3045 // We do not want to visit a Lambda expression defined inside a method
3046 // independently. Instead, it should be visited along with the outer method.
3047 // FIXME: do we want to do the same thing for `BlockDecl`s?
3048 if (const auto *fd = dyn_cast<CXXMethodDecl>(D)) {
3049 if (fd->getParent()->isLambda() && fd->getParent()->isLocalClass())
3050 return;
3051 }
3052
3053 // Do not emit fixit suggestions for functions declared in an
3054 // extern "C" block.
3055 if (const auto *FD = dyn_cast<FunctionDecl>(D)) {
3056 for (FunctionDecl *FReDecl : FD->redecls()) {
3057 if (FReDecl->isExternC()) {
3058 EmitSuggestions = false;
3059 break;
3060 }
3061 }
3062 }
3063
3064 WarningGadgetSets UnsafeOps;
3065 FixableGadgetSets FixablesForAllVars;
3066
3067 auto [FixableGadgets, WarningGadgets, Tracker] =
3068 findGadgets(D, Handler, EmitSuggestions);
3069
3070 if (!EmitSuggestions) {
3071 // Our job is very easy without suggestions. Just warn about
3072 // every problematic operation and consider it done. No need to deal
3073 // with fixable gadgets, no need to group operations by variable.
3074 for (const auto &G : WarningGadgets) {
3075 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
3076 D->getASTContext());
3077 }
3078
3079 // This return guarantees that most of the machine doesn't run when
3080 // suggestions aren't requested.
3081 assert(FixableGadgets.size() == 0 &&
3082 "Fixable gadgets found but suggestions not requested!");
3083 return;
3084 }
3085
3086 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
3087 // function under the analysis. No need to fix any Fixables.
3088 if (!WarningGadgets.empty()) {
3089 // Gadgets "claim" variables they're responsible for. Once this loop
3090 // finishes, the tracker will only track DREs that weren't claimed by any
3091 // gadgets, i.e. not understood by the analysis.
3092 for (const auto &G : FixableGadgets) {
3093 for (const auto *DRE : G->getClaimedVarUseSites()) {
3094 Tracker.claimUse(DRE);
3095 }
3096 }
3097 }
3098
3099 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
3100 // function under the analysis. Thus, it early returns here as there is
3101 // nothing needs to be fixed.
3102 //
3103 // Note this claim is based on the assumption that there is no unsafe
3104 // variable whose declaration is invisible from the analyzing function.
3105 // Otherwise, we need to consider if the uses of those unsafe varuables needs
3106 // fix.
3107 // So far, we are not fixing any global variables or class members. And,
3108 // lambdas will be analyzed along with the enclosing function. So this early
3109 // return is correct for now.
3110 if (WarningGadgets.empty())
3111 return;
3112
3113 UnsafeOps = groupWarningGadgetsByVar(std::move(WarningGadgets));
3114 FixablesForAllVars = groupFixablesByVar(std::move(FixableGadgets));
3115
3116 std::map<const VarDecl *, FixItList> FixItsForVariableGroup;
3117
3118 // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
3119 for (auto it = FixablesForAllVars.byVar.cbegin();
3120 it != FixablesForAllVars.byVar.cend();) {
3121 // FIXME: need to deal with global variables later
3122 if ((!it->first->isLocalVarDecl() && !isa<ParmVarDecl>(it->first))) {
3123#ifndef NDEBUG
3124 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3125 ("failed to produce fixit for '" +
3126 it->first->getNameAsString() +
3127 "' : neither local nor a parameter"));
3128#endif
3129 it = FixablesForAllVars.byVar.erase(it);
3130 } else if (it->first->getType().getCanonicalType()->isReferenceType()) {
3131#ifndef NDEBUG
3132 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3133 ("failed to produce fixit for '" +
3134 it->first->getNameAsString() +
3135 "' : has a reference type"));
3136#endif
3137 it = FixablesForAllVars.byVar.erase(it);
3138 } else if (Tracker.hasUnclaimedUses(it->first)) {
3139 it = FixablesForAllVars.byVar.erase(it);
3140 } else if (it->first->isInitCapture()) {
3141#ifndef NDEBUG
3142 Handler.addDebugNoteForVar(it->first, it->first->getBeginLoc(),
3143 ("failed to produce fixit for '" +
3144 it->first->getNameAsString() +
3145 "' : init capture"));
3146#endif
3147 it = FixablesForAllVars.byVar.erase(it);
3148 } else {
3149 ++it;
3150 }
3151 }
3152
3153#ifndef NDEBUG
3154 for (const auto &it : UnsafeOps.byVar) {
3155 const VarDecl *const UnsafeVD = it.first;
3156 auto UnclaimedDREs = Tracker.getUnclaimedUses(UnsafeVD);
3157 if (UnclaimedDREs.empty())
3158 continue;
3159 const auto UnfixedVDName = UnsafeVD->getNameAsString();
3160 for (const clang::DeclRefExpr *UnclaimedDRE : UnclaimedDREs) {
3161 std::string UnclaimedUseTrace =
3162 getDREAncestorString(UnclaimedDRE, D->getASTContext());
3163
3164 Handler.addDebugNoteForVar(
3165 UnsafeVD, UnclaimedDRE->getBeginLoc(),
3166 ("failed to produce fixit for '" + UnfixedVDName +
3167 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
3168 UnclaimedUseTrace));
3169 }
3170 }
3171#endif
3172
3173 // Fixpoint iteration for pointer assignments
3174 using DepMapTy = DenseMap<const VarDecl *, llvm::SetVector<const VarDecl *>>;
3175 DepMapTy DependenciesMap{};
3176 DepMapTy PtrAssignmentGraph{};
3177
3178 for (auto it : FixablesForAllVars.byVar) {
3179 for (const FixableGadget *fixable : it.second) {
3180 std::optional<std::pair<const VarDecl *, const VarDecl *>> ImplPair =
3181 fixable->getStrategyImplications();
3182 if (ImplPair) {
3183 std::pair<const VarDecl *, const VarDecl *> Impl = std::move(*ImplPair);
3184 PtrAssignmentGraph[Impl.first].insert(Impl.second);
3185 }
3186 }
3187 }
3188
3189 /*
3190 The following code does a BFS traversal of the `PtrAssignmentGraph`
3191 considering all unsafe vars as starting nodes and constructs an undirected
3192 graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
3193 elimiates all variables that are unreachable from any unsafe var. In other
3194 words, this removes all dependencies that don't include any unsafe variable
3195 and consequently don't need any fixit generation.
3196 Note: A careful reader would observe that the code traverses
3197 `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
3198 `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
3199 achieve the same result but the one used here dramatically cuts the
3200 amount of hoops the second part of the algorithm needs to jump, given that
3201 a lot of these connections become "direct". The reader is advised not to
3202 imagine how the graph is transformed because of using `Var` instead of
3203 `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
3204 and think about why it's equivalent later.
3205 */
3206 std::set<const VarDecl *> VisitedVarsDirected{};
3207 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
3208 if (VisitedVarsDirected.find(Var) == VisitedVarsDirected.end()) {
3209
3210 std::queue<const VarDecl *> QueueDirected{};
3211 QueueDirected.push(Var);
3212 while (!QueueDirected.empty()) {
3213 const VarDecl *CurrentVar = QueueDirected.front();
3214 QueueDirected.pop();
3215 VisitedVarsDirected.insert(CurrentVar);
3216 auto AdjacentNodes = PtrAssignmentGraph[CurrentVar];
3217 for (const VarDecl *Adj : AdjacentNodes) {
3218 if (VisitedVarsDirected.find(Adj) == VisitedVarsDirected.end()) {
3219 QueueDirected.push(Adj);
3220 }
3221 DependenciesMap[Var].insert(Adj);
3222 DependenciesMap[Adj].insert(Var);
3223 }
3224 }
3225 }
3226 }
3227
3228 // `Groups` stores the set of Connected Components in the graph.
3229 std::vector<VarGrpTy> Groups;
3230 // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
3231 // variables belong to. Group indexes refer to the elements in `Groups`.
3232 // `VarGrpMap` is complete in that every variable that needs fix is in it.
3233 std::map<const VarDecl *, unsigned> VarGrpMap;
3234 // The union group over the ones in "Groups" that contain parameters of `D`:
3235 llvm::SetVector<const VarDecl *>
3236 GrpsUnionForParms; // these variables need to be fixed in one step
3237
3238 // Group Connected Components for Unsafe Vars
3239 // (Dependencies based on pointer assignments)
3240 std::set<const VarDecl *> VisitedVars{};
3241 for (const auto &[Var, ignore] : UnsafeOps.byVar) {
3242 if (VisitedVars.find(Var) == VisitedVars.end()) {
3243 VarGrpTy &VarGroup = Groups.emplace_back();
3244 std::queue<const VarDecl *> Queue{};
3245
3246 Queue.push(Var);
3247 while (!Queue.empty()) {
3248 const VarDecl *CurrentVar = Queue.front();
3249 Queue.pop();
3250 VisitedVars.insert(CurrentVar);
3251 VarGroup.push_back(CurrentVar);
3252 auto AdjacentNodes = DependenciesMap[CurrentVar];
3253 for (const VarDecl *Adj : AdjacentNodes) {
3254 if (VisitedVars.find(Adj) == VisitedVars.end()) {
3255 Queue.push(Adj);
3256 }
3257 }
3258 }
3259
3260 bool HasParm = false;
3261 unsigned GrpIdx = Groups.size() - 1;
3262
3263 for (const VarDecl *V : VarGroup) {
3264 VarGrpMap[V] = GrpIdx;
3265 if (!HasParm && isParameterOf(V, D))
3266 HasParm = true;
3267 }
3268 if (HasParm)
3269 GrpsUnionForParms.insert(VarGroup.begin(), VarGroup.end());
3270 }
3271 }
3272
3273 // Remove a `FixableGadget` if the associated variable is not in the graph
3274 // computed above. We do not want to generate fix-its for such variables,
3275 // since they are neither warned nor reachable from a warned one.
3276 //
3277 // Note a variable is not warned if it is not directly used in any unsafe
3278 // operation. A variable `v` is NOT reachable from an unsafe variable, if it
3279 // does not exist another variable `u` such that `u` is warned and fixing `u`
3280 // (transitively) implicates fixing `v`.
3281 //
3282 // For example,
3283 // ```
3284 // void f(int * p) {
3285 // int * a = p; *p = 0;
3286 // }
3287 // ```
3288 // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
3289 // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of
3290 // the function above, `p` becomes reachable from a warned variable.
3291 for (auto I = FixablesForAllVars.byVar.begin();
3292 I != FixablesForAllVars.byVar.end();) {
3293 // Note `VisitedVars` contain all the variables in the graph:
3294 if (!VisitedVars.count((*I).first)) {
3295 // no such var in graph:
3296 I = FixablesForAllVars.byVar.erase(I);
3297 } else
3298 ++I;
3299 }
3300
3301 // We assign strategies to variables that are 1) in the graph and 2) can be
3302 // fixed. Other variables have the default "Won't fix" strategy.
3303 FixitStrategy NaiveStrategy = getNaiveStrategy(llvm::make_filter_range(
3304 VisitedVars, [&FixablesForAllVars](const VarDecl *V) {
3305 // If a warned variable has no "Fixable", it is considered unfixable:
3306 return FixablesForAllVars.byVar.count(V);
3307 }));
3308 VariableGroupsManagerImpl VarGrpMgr(Groups, VarGrpMap, GrpsUnionForParms);
3309
3310 if (isa<NamedDecl>(D))
3311 // The only case where `D` is not a `NamedDecl` is when `D` is a
3312 // `BlockDecl`. Let's not fix variables in blocks for now
3313 FixItsForVariableGroup =
3314 getFixIts(FixablesForAllVars, NaiveStrategy, D->getASTContext(), D,
3315 Tracker, Handler, VarGrpMgr);
3316
3317 for (const auto &G : UnsafeOps.noVar) {
3318 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/false,
3319 D->getASTContext());
3320 }
3321
3322 for (const auto &[VD, WarningGadgets] : UnsafeOps.byVar) {
3323 auto FixItsIt = FixItsForVariableGroup.find(VD);
3324 Handler.handleUnsafeVariableGroup(VD, VarGrpMgr,
3325 FixItsIt != FixItsForVariableGroup.end()
3326 ? std::move(FixItsIt->second)
3327 : FixItList{},
3328 D, NaiveStrategy);
3329 for (const auto &G : WarningGadgets) {
3330 G->handleUnsafeOperation(Handler, /*IsRelatedToDecl=*/true,
3331 D->getASTContext());
3332 }
3333 }
3334}
Defines the clang::ASTContext interface.
#define V(N, I)
Definition: ASTContext.h:3341
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
const Decl * D
Expr * E
llvm::APSInt APSInt
Definition: Compiler.cpp:22
static Decl::Kind getKind(const Decl *D)
Definition: DeclBase.cpp:1171
StringRef Text
Definition: Format.cpp:3002
unsigned Iter
Definition: HTMLLogger.cpp:154
static const Decl * getCanonicalDecl(const Decl *D)
llvm::MachO::Target Target
Definition: MachO.h:51
Defines the clang::Preprocessor interface.
static bool hasAttr(const Decl *D, bool IgnoreImplicitAttr)
Definition: SemaCUDA.cpp:111
SourceRange Range
Definition: SemaObjC.cpp:758
SourceLocation Loc
Definition: SemaObjC.cpp:759
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:187
SourceManager & getSourceManager()
Definition: ASTContext.h:721
const ConstantArrayType * getAsConstantArrayType(QualType T) const
Definition: ASTContext.h:2825
DynTypedNodeList getParents(const NodeT &Node)
Forwards to get node parents from the ParentMapContext.
const LangOptions & getLangOpts() const
Definition: ASTContext.h:797
ArraySubscriptExpr - [C99 6.5.2.1] Array Subscripting.
Definition: Expr.h:2674
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:2717
Attr - This represents one attribute.
Definition: Attr.h:42
A builtin binary operation expression such as "x + y" or "x <= y".
Definition: Expr.h:3860
Expr * getLHS() const
Definition: Expr.h:3910
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:3915
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:3912
Represents a call to a C++ constructor.
Definition: ExprCXX.h:1546
Expr * getArg(unsigned Arg)
Return the specified argument.
Definition: ExprCXX.h:1689
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: ExprCXX.cpp:563
Represents a C++11 noexcept expression (C++ [expr.unary.noexcept]).
Definition: ExprCXX.h:4126
A C++ typeid expression (C++ [expr.typeid]), which gets the type_info that corresponds to the supplie...
Definition: ExprCXX.h:845
CastExpr - Base class for type casts, including both implicit casts (ImplicitCastExpr) and explicit c...
Definition: Expr.h:3498
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:1852
A reference to a declared variable, function, enum, etc.
Definition: Expr.h:1265
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.cpp:551
ValueDecl * getDecl()
Definition: Expr.h:1333
DeclStmt - Adaptor class for mixing declarations with statements and expressions.
Definition: Stmt.h:1502
bool isSingleDecl() const
isSingleDecl - This method returns true if this DeclStmt refers to a single Decl.
Definition: Stmt.h:1515
decl_range decls()
Definition: Stmt.h:1550
Decl - This represents one declaration (or definition), e.g.
Definition: DeclBase.h:86
SourceLocation getEndLoc() const LLVM_READONLY
Definition: DeclBase.h:442
ASTContext & getASTContext() const LLVM_READONLY
Definition: DeclBase.cpp:523
bool isImplicit() const
isImplicit - Indicates whether the declaration was implicitly generated by the implementation.
Definition: DeclBase.h:600
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:446
DeclContext * getDeclContext()
Definition: DeclBase.h:455
attr_range attrs() const
Definition: DeclBase.h:542
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: DeclBase.h:438
virtual SourceRange getSourceRange() const LLVM_READONLY
Source range that this declaration covers.
Definition: DeclBase.h:434
NestedNameSpecifier * getQualifier() const
Retrieve the nested-name-specifier that qualifies the name of this declaration, if it was present in ...
Definition: Decl.h:789
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Decl.h:783
NestedNameSpecifierLoc getQualifierLoc() const
Retrieve the nested-name-specifier (with source-location information) that qualifies the name of this...
Definition: Decl.h:797
TypeSourceInfo * getTypeSourceInfo() const
Definition: Decl.h:760
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:3750
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:3070
Expr * IgnoreImplicit() LLVM_READONLY
Skip past any implicit AST nodes which might surround this expression until reaching a fixed point.
Definition: Expr.cpp:3058
Expr * IgnoreParens() LLVM_READONLY
Skip past any parentheses which might surround this expression until reaching a fixed point.
Definition: Expr.cpp:3066
NullPointerConstantValueDependence
Enumeration used to describe how isNullPointerConstant() should cope with value-dependent expressions...
Definition: Expr.h:820
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:1932
const ParmVarDecl * getParamDecl(unsigned i) const
Definition: Decl.h:2669
Stmt * getBody(const FunctionDecl *&Definition) const
Retrieve the body (definition) of the function.
Definition: Decl.cpp:3224
param_iterator param_begin()
Definition: Decl.h:2658
unsigned getNumParams() const
Return the number of parameters this function must have based on its FunctionType.
Definition: Decl.cpp:3678
DeclarationNameInfo getNameInfo() const
Definition: Decl.h:2143
Represents a C11 generic selection.
Definition: Expr.h:5917
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:3675
Keeps track of the various options that can be enabled, which controls the dialect of C or C++ that i...
Definition: LangOptions.h:476
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:1722
bool hasDefaultArg() const
Determines whether this parameter has a default argument, either parsed or not.
Definition: Decl.cpp:3004
SourceRange getSourceRange() const override LLVM_READONLY
Source range that this declaration covers.
Definition: Decl.cpp:2926
Wrapper for source info for pointers.
Definition: TypeLoc.h:1301
A (possibly-)qualified type.
Definition: Type.h:941
bool hasQualifiers() const
Determine whether this type has any qualifiers.
Definition: Type.h:7839
bool isNull() const
Return true if this QualType doesn't point to a type yet.
Definition: Type.h:1008
Qualifiers getQualifiers() const
Retrieve the set of qualifiers applied to this type.
Definition: Type.h:7790
QualType getCanonicalType() const
Definition: Type.h:7802
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:297
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:1363
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 isFunctionPointerType() const
Definition: Type.h:8043
bool isPointerType() const
Definition: Type.h:8003
QualType getPointeeType() const
If this is a pointer, ObjC object pointer, or block pointer, this returns the respective pointee.
Definition: Type.cpp:705
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:2196
UnaryExprOrTypeTraitExpr - expression with either a type or (unevaluated) expression operand.
Definition: Expr.h:2578
UnaryOperator - This represents the unary-expression's (except sizeof and alignof),...
Definition: Expr.h:2188
SourceLocation getOperatorLoc() const
getOperatorLoc - Return the location of the operator.
Definition: Expr.h:2237
Expr * getSubExpr() const
Definition: Expr.h:2233
Opcode getOpcode() const
Definition: Expr.h:2228
SourceLocation getBeginLoc() const LLVM_READONLY
Definition: Expr.h:2310
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:678
Represents a variable declaration or definition.
Definition: Decl.h:879
bool isConstexpr() const
Whether this variable is (C++11) constexpr.
Definition: Decl.h:1510
VarDecl * getCanonicalDecl() override
Retrieves the "canonical" declaration of the given declaration.
Definition: Decl.cpp:2239
bool isInlineSpecified() const
Definition: Decl.h:1495
bool hasConstantInitialization() const
Determine whether this variable has constant initialization.
Definition: Decl.cpp:2612
bool hasLocalStorage() const
Returns true if a variable with function scope is a non-static local variable.
Definition: Decl.h:1132
bool isLocalVarDecl() const
Returns true for local variable declarations other than parameters.
Definition: Decl.h:1201
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).
const internal::VariadicDynCastAllOfMatcher< Decl, FieldDecl > fieldDecl
Matches field declarations.
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.
const internal::VariadicDynCastAllOfMatcher< Stmt, MemberExpr > memberExpr
Matches member expressions.
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)
RangeSelector member(std::string ID)
Given a MemberExpr, selects the member token.
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.