1 //===- UnsafeBufferUsage.cpp - Replace pointers with modern C++ -----------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "clang/Analysis/Analyses/UnsafeBufferUsage.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/AST/Decl.h"
12 #include "clang/AST/Expr.h"
13 #include "clang/AST/RecursiveASTVisitor.h"
14 #include "clang/AST/Stmt.h"
15 #include "clang/AST/StmtVisitor.h"
16 #include "clang/ASTMatchers/ASTMatchFinder.h"
17 #include "clang/ASTMatchers/ASTMatchers.h"
18 #include "clang/Basic/CharInfo.h"
19 #include "clang/Basic/SourceLocation.h"
20 #include "clang/Lex/Lexer.h"
21 #include "clang/Lex/Preprocessor.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"
32 using namespace clang
;
33 using namespace ast_matchers
;
37 class StmtDebugPrinter
38 : public ConstStmtVisitor
<StmtDebugPrinter
, std::string
> {
40 std::string
VisitStmt(const Stmt
*S
) { return S
->getStmtClassName(); }
42 std::string
VisitBinaryOperator(const BinaryOperator
*BO
) {
43 return "BinaryOperator(" + BO
->getOpcodeStr().str() + ")";
46 std::string
VisitUnaryOperator(const UnaryOperator
*UO
) {
47 return "UnaryOperator(" + UO
->getOpcodeStr(UO
->getOpcode()).str() + ")";
50 std::string
VisitImplicitCastExpr(const ImplicitCastExpr
*ICE
) {
51 return "ImplicitCastExpr(" + std::string(ICE
->getCastKindName()) + ")";
55 // Returns a string of ancestor `Stmt`s of the given `DRE` in such a form:
56 // "DRE ==> parent-of-DRE ==> grandparent-of-DRE ==> ...".
57 static std::string
getDREAncestorString(const DeclRefExpr
*DRE
,
61 StmtDebugPrinter StmtPriner
;
64 SS
<< StmtPriner
.Visit(St
);
66 DynTypedNodeList StParents
= Ctx
.getParents(*St
);
68 if (StParents
.size() > 1)
69 return "unavailable due to multiple parents";
70 if (StParents
.size() == 0)
72 St
= StParents
.begin()->get
<Stmt
>();
81 namespace 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".
84 class MatchDescendantVisitor
85 : public RecursiveASTVisitor
<MatchDescendantVisitor
> {
87 typedef RecursiveASTVisitor
<MatchDescendantVisitor
> VisitorBase
;
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
) {}
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
) {
104 if (const Stmt
*StmtNode
= DynNode
.get
<Stmt
>()) {
105 TraverseStmt(const_cast<Stmt
*>(StmtNode
));
106 *Builder
= ResultBindings
;
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.
116 // For the matchers so far used in safe buffers, we only need to match
117 // `Stmt`s. To override more as needed.
119 bool TraverseDecl(Decl
*Node
) {
124 // To skip callables:
125 if (isa
<FunctionDecl
, BlockDecl
, ObjCMethodDecl
>(Node
))
127 // Traverse descendants
128 return VisitorBase::TraverseDecl(Node
);
131 bool TraverseGenericSelectionExpr(GenericSelectionExpr
*Node
) {
132 // These are unevaluated, except the result expression.
133 if (ignoreUnevaluatedContext
)
134 return TraverseStmt(Node
->getResultExpr());
135 return VisitorBase::TraverseGenericSelectionExpr(Node
);
138 bool TraverseUnaryExprOrTypeTraitExpr(UnaryExprOrTypeTraitExpr
*Node
) {
139 // Unevaluated context.
140 if (ignoreUnevaluatedContext
)
142 return VisitorBase::TraverseUnaryExprOrTypeTraitExpr(Node
);
145 bool TraverseTypeOfExprTypeLoc(TypeOfExprTypeLoc Node
) {
146 // Unevaluated context.
147 if (ignoreUnevaluatedContext
)
149 return VisitorBase::TraverseTypeOfExprTypeLoc(Node
);
152 bool TraverseDecltypeTypeLoc(DecltypeTypeLoc Node
) {
153 // Unevaluated context.
154 if (ignoreUnevaluatedContext
)
156 return VisitorBase::TraverseDecltypeTypeLoc(Node
);
159 bool TraverseCXXNoexceptExpr(CXXNoexceptExpr
*Node
) {
160 // Unevaluated context.
161 if (ignoreUnevaluatedContext
)
163 return VisitorBase::TraverseCXXNoexceptExpr(Node
);
166 bool TraverseCXXTypeidExpr(CXXTypeidExpr
*Node
) {
167 // Unevaluated context.
168 if (ignoreUnevaluatedContext
)
170 return VisitorBase::TraverseCXXTypeidExpr(Node
);
173 bool TraverseStmt(Stmt
*Node
, DataRecursionQueue
*Queue
= nullptr) {
178 return VisitorBase::TraverseStmt(Node
);
181 bool shouldVisitTemplateInstantiations() const { return true; }
182 bool shouldVisitImplicitCode() const {
183 // TODO: let's ignore implicit code for now
188 // Sets 'Matched' to true if 'Matcher' matches 'Node'
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
);
195 if (Matcher
->matches(DynTypedNode::create(Node
), Finder
,
196 &RecursiveBuilder
)) {
197 ResultBindings
.addMatch(RecursiveBuilder
);
199 if (Bind
!= internal::ASTMatchFinder::BK_All
)
200 return false; // Abort as soon as a match is found.
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
;
211 bool ignoreUnevaluatedContext
;
214 // Because we're dealing with raw pointers, let's define what we mean by that.
215 static auto hasPointerType() {
216 return hasType(hasCanonicalType(pointerType()));
219 static auto hasArrayType() { return hasType(hasCanonicalType(arrayType())); }
221 AST_MATCHER_P(Stmt
, forEachDescendantEvaluatedStmt
, internal::Matcher
<Stmt
>,
223 const DynTypedMatcher
&DTM
= static_cast<DynTypedMatcher
>(innerMatcher
);
225 MatchDescendantVisitor
Visitor(&DTM
, Finder
, Builder
, ASTMatchFinder::BK_All
,
227 return Visitor
.findMatch(DynTypedNode::create(Node
));
230 AST_MATCHER_P(Stmt
, forEachDescendantStmt
, internal::Matcher
<Stmt
>,
232 const DynTypedMatcher
&DTM
= static_cast<DynTypedMatcher
>(innerMatcher
);
234 MatchDescendantVisitor
Visitor(&DTM
, Finder
, Builder
, ASTMatchFinder::BK_All
,
236 return Visitor
.findMatch(DynTypedNode::create(Node
));
239 // Matches a `Stmt` node iff the node is in a safe-buffer opt-out region
240 AST_MATCHER_P(Stmt
, notInSafeBufferOptOut
, const UnsafeBufferUsageHandler
*,
242 return !Handler
->isSafeBufferOptOut(Node
.getBeginLoc());
245 AST_MATCHER_P(Stmt
, ignoreUnsafeBufferInContainer
,
246 const UnsafeBufferUsageHandler
*, Handler
) {
247 return Handler
->ignoreUnsafeBufferInContainer(Node
.getBeginLoc());
250 AST_MATCHER_P(CastExpr
, castSubExpr
, internal::Matcher
<Expr
>, innerMatcher
) {
251 return innerMatcher
.matches(*Node
.getSubExpr(), Finder
, Builder
);
254 // Matches a `UnaryOperator` whose operator is pre-increment:
255 AST_MATCHER(UnaryOperator
, isPreInc
) {
256 return Node
.getOpcode() == UnaryOperator::Opcode::UO_PreInc
;
259 // Returns a matcher that matches any expression 'e' such that `innerMatcher`
260 // matches 'e' and 'e' is in an Unspecified Lvalue Context.
261 static auto isInUnspecifiedLvalueContext(internal::Matcher
<Expr
> innerMatcher
) {
266 hasCastKind(CastKind::CK_LValueToRValue
),
267 castSubExpr(innerMatcher
)),
269 hasAnyOperatorName("="),
276 // Returns a matcher that matches any expression `e` such that `InnerMatcher`
277 // matches `e` and `e` is in an Unspecified Pointer Context (UPC).
278 static internal::Matcher
<Stmt
>
279 isInUnspecifiedPointerContext(internal::Matcher
<Stmt
> InnerMatcher
) {
281 // 1. an argument of a function call (except the callee has [[unsafe_...]]
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 ...
289 auto CallArgMatcher
= callExpr(
290 forEachArgumentWithParamType(
292 isAnyPointer() /* array also decays to pointer type*/),
294 functionDecl(hasAttr(attr::UnsafeBufferUsage
)))));
296 auto CastOperandMatcher
=
297 castExpr(anyOf(hasCastKind(CastKind::CK_PointerToIntegral
),
298 hasCastKind(CastKind::CK_PointerToBoolean
)),
299 castSubExpr(allOf(hasPointerType(), InnerMatcher
)));
301 auto CompOperandMatcher
=
302 binaryOperator(hasAnyOperatorName("!=", "==", "<", "<=", ">", ">="),
303 eachOf(hasLHS(allOf(hasPointerType(), InnerMatcher
)),
304 hasRHS(allOf(hasPointerType(), InnerMatcher
))));
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
312 allOf(hasLHS(hasPointerType()),
313 hasRHS(hasPointerType())),
314 eachOf(hasLHS(InnerMatcher
),
315 hasRHS(InnerMatcher
)));
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.)
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];
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.
334 static internal::Matcher
<Stmt
>
335 isInUnspecifiedUntypedContext(internal::Matcher
<Stmt
> InnerMatcher
) {
336 // An unspecified context can be
337 // 1. A compound statement,
338 // 2. The body of an if statement
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
));
347 // Given a two-param std::span construct call, matches iff the call has the
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
354 // 5. `std::span<T>{any, 0}`
355 AST_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;
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();
374 std::optional
<APSInt
> Arg1CV
=
375 Arg1
->getIntegerConstantExpr(Finder
->getASTContext());
377 if (Arg1CV
&& Arg1CV
->isZero())
380 switch (Arg0
->IgnoreImplicit()->getStmtClass()) {
381 case Stmt::CXXNewExprClass
:
382 if (auto Size
= cast
<CXXNewExpr
>(Arg0
)->getArraySize()) {
384 return AreSameDRE((*Size
)->IgnoreImplicit(), Arg1
) ||
385 HaveEqualConstantValues(*Size
, Arg1
);
387 // TODO: what's placeholder type? avoid it for now.
388 if (!cast
<CXXNewExpr
>(Arg0
)->hasPlaceholderType()) {
390 return Arg1CV
&& Arg1CV
->isOne();
393 case Stmt::UnaryOperatorClass
:
394 if (cast
<UnaryOperator
>(Arg0
)->getOpcode() ==
395 UnaryOperator::Opcode::UO_AddrOf
)
397 return Arg1CV
&& Arg1CV
->isOne();
403 QualType Arg0Ty
= Arg0
->IgnoreImplicit()->getType();
405 if (Arg0Ty
->isConstantArrayType()) {
406 const APSInt ConstArrSize
=
407 APSInt(cast
<ConstantArrayType
>(Arg0Ty
)->getSize());
410 return Arg1CV
&& APSInt::compareValues(ConstArrSize
, *Arg1CV
) == 0;
415 AST_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
423 const auto *BaseDRE
=
424 dyn_cast
<DeclRefExpr
>(Node
.getBase()->IgnoreParenImpCasts());
427 if (!BaseDRE
->getDecl())
429 const auto *CATy
= Finder
->getASTContext().getAsConstantArrayType(
430 BaseDRE
->getDecl()->getType());
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
438 if (ArrIdx
.isNonNegative() &&
439 ArrIdx
.getLimitedValue() < CATy
->getLimitedSize())
446 } // namespace clang::ast_matchers
449 // Because the analysis revolves around variables and their types, we'll need to
450 // track uses of variables (aka DeclRefExprs).
451 using DeclUseList
= SmallVector
<const DeclRefExpr
*, 1>;
453 // Convenience typedef.
454 using FixItList
= SmallVector
<FixItHint
, 4>;
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).
470 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
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());
478 Gadget(Kind K
) : K(K
) {}
480 Kind
getKind() const { return K
; }
483 StringRef
getDebugName() const {
488 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
490 llvm_unreachable("Unhandled Gadget::Kind enum");
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;
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;
504 virtual ~Gadget() = default;
510 /// Warning gadgets correspond to unsafe code patterns that warrants
511 /// an immediate warning.
512 class WarningGadget
: public Gadget
{
514 WarningGadget(Kind K
) : Gadget(K
) {}
516 static bool classof(const Gadget
*G
) { return G
->isWarningGadget(); }
517 bool isWarningGadget() const final
{ return true; }
519 virtual void handleUnsafeOperation(UnsafeBufferUsageHandler
&Handler
,
520 bool IsRelatedToDecl
,
521 ASTContext
&Ctx
) const = 0;
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.
528 class FixableGadget
: public Gadget
{
530 FixableGadget(Kind K
) : Gadget(K
) {}
532 static bool classof(const Gadget
*G
) { return !G
->isWarningGadget(); }
533 bool isWarningGadget() const final
{ return false; }
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 {
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
548 virtual std::optional
<std::pair
<const VarDecl
*, const VarDecl
*>>
549 getStrategyImplications() const {
554 static auto toSupportedVariable() { return to(varDecl()); }
556 using FixableGadgetList
= std::vector
<std::unique_ptr
<FixableGadget
>>;
557 using WarningGadgetList
= std::vector
<std::unique_ptr
<WarningGadget
>>;
559 /// An increment of a pointer-type value is unsafe as it may run the pointer
561 class IncrementGadget
: public WarningGadget
{
562 static constexpr const char *const OpTag
= "op";
563 const UnaryOperator
*Op
;
566 IncrementGadget(const MatchFinder::MatchResult
&Result
)
567 : WarningGadget(Kind::Increment
),
568 Op(Result
.Nodes
.getNodeAs
<UnaryOperator
>(OpTag
)) {}
570 static bool classof(const Gadget
*G
) {
571 return G
->getKind() == Kind::Increment
;
574 static Matcher
matcher() {
576 unaryOperator(hasOperatorName("++"),
577 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
581 void handleUnsafeOperation(UnsafeBufferUsageHandler
&Handler
,
582 bool IsRelatedToDecl
,
583 ASTContext
&Ctx
) const override
{
584 Handler
.handleUnsafeOperation(Op
, IsRelatedToDecl
, Ctx
);
586 SourceLocation
getSourceLoc() const override
{ return Op
->getBeginLoc(); }
588 DeclUseList
getClaimedVarUseSites() const override
{
589 SmallVector
<const DeclRefExpr
*, 2> Uses
;
590 if (const auto *DRE
=
591 dyn_cast
<DeclRefExpr
>(Op
->getSubExpr()->IgnoreParenImpCasts())) {
595 return std::move(Uses
);
599 /// A decrement of a pointer-type value is unsafe as it may run the pointer
601 class DecrementGadget
: public WarningGadget
{
602 static constexpr const char *const OpTag
= "op";
603 const UnaryOperator
*Op
;
606 DecrementGadget(const MatchFinder::MatchResult
&Result
)
607 : WarningGadget(Kind::Decrement
),
608 Op(Result
.Nodes
.getNodeAs
<UnaryOperator
>(OpTag
)) {}
610 static bool classof(const Gadget
*G
) {
611 return G
->getKind() == Kind::Decrement
;
614 static Matcher
matcher() {
616 unaryOperator(hasOperatorName("--"),
617 hasUnaryOperand(ignoringParenImpCasts(hasPointerType())))
621 void handleUnsafeOperation(UnsafeBufferUsageHandler
&Handler
,
622 bool IsRelatedToDecl
,
623 ASTContext
&Ctx
) const override
{
624 Handler
.handleUnsafeOperation(Op
, IsRelatedToDecl
, Ctx
);
626 SourceLocation
getSourceLoc() const override
{ return Op
->getBeginLoc(); }
628 DeclUseList
getClaimedVarUseSites() const override
{
629 if (const auto *DRE
=
630 dyn_cast
<DeclRefExpr
>(Op
->getSubExpr()->IgnoreParenImpCasts())) {
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.
640 class ArraySubscriptGadget
: public WarningGadget
{
641 static constexpr const char *const ArraySubscrTag
= "ArraySubscript";
642 const ArraySubscriptExpr
*ASE
;
645 ArraySubscriptGadget(const MatchFinder::MatchResult
&Result
)
646 : WarningGadget(Kind::ArraySubscript
),
647 ASE(Result
.Nodes
.getNodeAs
<ArraySubscriptExpr
>(ArraySubscrTag
)) {}
649 static bool classof(const Gadget
*G
) {
650 return G
->getKind() == Kind::ArraySubscript
;
653 static Matcher
matcher() {
655 return stmt(arraySubscriptExpr(
656 hasBase(ignoringParenImpCasts(
657 anyOf(hasPointerType(), hasArrayType()))),
659 isSafeArraySubscript(),
661 anyOf(integerLiteral(equals(0)), arrayInitIndexExpr())
663 ))).bind(ArraySubscrTag
));
667 void handleUnsafeOperation(UnsafeBufferUsageHandler
&Handler
,
668 bool IsRelatedToDecl
,
669 ASTContext
&Ctx
) const override
{
670 Handler
.handleUnsafeOperation(ASE
, IsRelatedToDecl
, Ctx
);
672 SourceLocation
getSourceLoc() const override
{ return ASE
->getBeginLoc(); }
674 DeclUseList
getClaimedVarUseSites() const override
{
675 if (const auto *DRE
=
676 dyn_cast
<DeclRefExpr
>(ASE
->getBase()->IgnoreParenImpCasts())) {
684 /// A pointer arithmetic expression of one of the forms:
686 /// ptr + n | n + ptr | ptr - n | ptr += n | ptr -= n
688 class 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`
695 PointerArithmeticGadget(const MatchFinder::MatchResult
&Result
)
696 : WarningGadget(Kind::PointerArithmetic
),
697 PA(Result
.Nodes
.getNodeAs
<BinaryOperator
>(PointerArithmeticTag
)),
698 Ptr(Result
.Nodes
.getNodeAs
<Expr
>(PointerArithmeticPointerTag
)) {}
700 static bool classof(const Gadget
*G
) {
701 return G
->getKind() == Kind::PointerArithmetic
;
704 static Matcher
matcher() {
705 auto HasIntegerType
= anyOf(hasType(isInteger()), hasType(enumType()));
707 allOf(hasOperatorName("+"),
708 hasRHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag
)),
709 hasLHS(HasIntegerType
));
711 allOf(anyOf(hasOperatorName("+"), hasOperatorName("-"),
712 hasOperatorName("+="), hasOperatorName("-=")),
713 hasLHS(expr(hasPointerType()).bind(PointerArithmeticPointerTag
)),
714 hasRHS(HasIntegerType
));
716 return stmt(binaryOperator(anyOf(PtrAtLeft
, PtrAtRight
))
717 .bind(PointerArithmeticTag
));
720 void handleUnsafeOperation(UnsafeBufferUsageHandler
&Handler
,
721 bool IsRelatedToDecl
,
722 ASTContext
&Ctx
) const override
{
723 Handler
.handleUnsafeOperation(PA
, IsRelatedToDecl
, Ctx
);
725 SourceLocation
getSourceLoc() const override
{ return PA
->getBeginLoc(); }
727 DeclUseList
getClaimedVarUseSites() const override
{
728 if (const auto *DRE
= dyn_cast
<DeclRefExpr
>(Ptr
->IgnoreParenImpCasts())) {
734 // FIXME: pointer adding zero should be fine
735 // FIXME: this gadge will need a fix-it
738 class SpanTwoParamConstructorGadget
: public WarningGadget
{
739 static constexpr const char *const SpanTwoParamConstructorTag
=
740 "spanTwoParamConstructor";
741 const CXXConstructExpr
*Ctor
; // the span constructor expression
744 SpanTwoParamConstructorGadget(const MatchFinder::MatchResult
&Result
)
745 : WarningGadget(Kind::SpanTwoParamConstructor
),
746 Ctor(Result
.Nodes
.getNodeAs
<CXXConstructExpr
>(
747 SpanTwoParamConstructorTag
)) {}
749 static bool classof(const Gadget
*G
) {
750 return G
->getKind() == Kind::SpanTwoParamConstructor
;
753 static Matcher
matcher() {
754 auto HasTwoParamSpanCtorDecl
= hasDeclaration(
755 cxxConstructorDecl(hasDeclContext(isInStdNamespace()), hasName("span"),
756 parameterCountIs(2)));
758 return stmt(cxxConstructExpr(HasTwoParamSpanCtorDecl
,
759 unless(isSafeSpanTwoParamConstruct()))
760 .bind(SpanTwoParamConstructorTag
));
763 void handleUnsafeOperation(UnsafeBufferUsageHandler
&Handler
,
764 bool IsRelatedToDecl
,
765 ASTContext
&Ctx
) const override
{
766 Handler
.handleUnsafeOperationInContainer(Ctor
, IsRelatedToDecl
, Ctx
);
768 SourceLocation
getSourceLoc() const override
{ return Ctor
->getBeginLoc(); }
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()))
781 /// A pointer initialization expression of the form:
785 class PointerInitGadget
: public FixableGadget
{
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`
793 PointerInitGadget(const MatchFinder::MatchResult
&Result
)
794 : FixableGadget(Kind::PointerInit
),
795 PtrInitLHS(Result
.Nodes
.getNodeAs
<VarDecl
>(PointerInitLHSTag
)),
796 PtrInitRHS(Result
.Nodes
.getNodeAs
<DeclRefExpr
>(PointerInitRHSTag
)) {}
798 static bool classof(const Gadget
*G
) {
799 return G
->getKind() == Kind::PointerInit
;
802 static Matcher
matcher() {
803 auto PtrInitStmt
= declStmt(hasSingleDecl(
804 varDecl(hasInitializer(ignoringImpCasts(
805 declRefExpr(hasPointerType(), toSupportedVariable())
806 .bind(PointerInitRHSTag
))))
807 .bind(PointerInitLHSTag
)));
809 return stmt(PtrInitStmt
);
812 virtual std::optional
<FixItList
>
813 getFixits(const FixitStrategy
&S
) const override
;
814 SourceLocation
getSourceLoc() const override
{
815 return PtrInitRHS
->getBeginLoc();
818 virtual DeclUseList
getClaimedVarUseSites() const override
{
819 return DeclUseList
{PtrInitRHS
};
822 virtual std::optional
<std::pair
<const VarDecl
*, const VarDecl
*>>
823 getStrategyImplications() const override
{
824 return std::make_pair(PtrInitLHS
, cast
<VarDecl
>(PtrInitRHS
->getDecl()));
828 /// A pointer assignment expression of the form:
832 /// where both `p` and `q` are pointers.
833 class PtrToPtrAssignmentGadget
: public FixableGadget
{
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`
841 PtrToPtrAssignmentGadget(const MatchFinder::MatchResult
&Result
)
842 : FixableGadget(Kind::PtrToPtrAssignment
),
843 PtrLHS(Result
.Nodes
.getNodeAs
<DeclRefExpr
>(PointerAssignLHSTag
)),
844 PtrRHS(Result
.Nodes
.getNodeAs
<DeclRefExpr
>(PointerAssignRHSTag
)) {}
846 static bool classof(const Gadget
*G
) {
847 return G
->getKind() == Kind::PtrToPtrAssignment
;
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
))));
859 return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr
));
862 virtual std::optional
<FixItList
>
863 getFixits(const FixitStrategy
&S
) const override
;
864 SourceLocation
getSourceLoc() const override
{ return PtrLHS
->getBeginLoc(); }
866 virtual DeclUseList
getClaimedVarUseSites() const override
{
867 return DeclUseList
{PtrLHS
, PtrRHS
};
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()));
877 /// An assignment expression of the form:
881 /// where `p` is a pointer and `array` is a constant size array.
882 class CArrayToPtrAssignmentGadget
: public FixableGadget
{
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`
890 CArrayToPtrAssignmentGadget(const MatchFinder::MatchResult
&Result
)
891 : FixableGadget(Kind::CArrayToPtrAssignment
),
892 PtrLHS(Result
.Nodes
.getNodeAs
<DeclRefExpr
>(PointerAssignLHSTag
)),
893 PtrRHS(Result
.Nodes
.getNodeAs
<DeclRefExpr
>(PointerAssignRHSTag
)) {}
895 static bool classof(const Gadget
*G
) {
896 return G
->getKind() == Kind::CArrayToPtrAssignment
;
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
))));
909 return stmt(isInUnspecifiedUntypedContext(PtrAssignExpr
));
912 virtual std::optional
<FixItList
>
913 getFixits(const FixitStrategy
&S
) const override
;
914 SourceLocation
getSourceLoc() const override
{ return PtrLHS
->getBeginLoc(); }
916 virtual DeclUseList
getClaimedVarUseSites() const override
{
917 return DeclUseList
{PtrLHS
, PtrRHS
};
920 virtual std::optional
<std::pair
<const VarDecl
*, const VarDecl
*>>
921 getStrategyImplications() const override
{
926 /// A call of a function or method that performs unchecked buffer operations
927 /// over one of its pointer parameters.
928 class UnsafeBufferUsageAttrGadget
: public WarningGadget
{
929 constexpr static const char *const OpTag
= "call_expr";
933 UnsafeBufferUsageAttrGadget(const MatchFinder::MatchResult
&Result
)
934 : WarningGadget(Kind::UnsafeBufferUsageAttr
),
935 Op(Result
.Nodes
.getNodeAs
<CallExpr
>(OpTag
)) {}
937 static bool classof(const Gadget
*G
) {
938 return G
->getKind() == Kind::UnsafeBufferUsageAttr
;
941 static Matcher
matcher() {
942 auto HasUnsafeFnDecl
=
943 callee(functionDecl(hasAttr(attr::UnsafeBufferUsage
)));
944 return stmt(callExpr(HasUnsafeFnDecl
).bind(OpTag
));
947 void handleUnsafeOperation(UnsafeBufferUsageHandler
&Handler
,
948 bool IsRelatedToDecl
,
949 ASTContext
&Ctx
) const override
{
950 Handler
.handleUnsafeOperation(Op
, IsRelatedToDecl
, Ctx
);
952 SourceLocation
getSourceLoc() const override
{ return Op
->getBeginLoc(); }
954 DeclUseList
getClaimedVarUseSites() const override
{ return {}; }
957 /// A call of a constructor that performs unchecked buffer operations
958 /// over one of its pointer parameters, or constructs a class object that will
959 /// perform buffer operations that depend on the correctness of the parameters.
960 class UnsafeBufferUsageCtorAttrGadget
: public WarningGadget
{
961 constexpr static const char *const OpTag
= "cxx_construct_expr";
962 const CXXConstructExpr
*Op
;
965 UnsafeBufferUsageCtorAttrGadget(const MatchFinder::MatchResult
&Result
)
966 : WarningGadget(Kind::UnsafeBufferUsageCtorAttr
),
967 Op(Result
.Nodes
.getNodeAs
<CXXConstructExpr
>(OpTag
)) {}
969 static bool classof(const Gadget
*G
) {
970 return G
->getKind() == Kind::UnsafeBufferUsageCtorAttr
;
973 static Matcher
matcher() {
974 auto HasUnsafeCtorDecl
=
975 hasDeclaration(cxxConstructorDecl(hasAttr(attr::UnsafeBufferUsage
)));
976 // std::span(ptr, size) ctor is handled by SpanTwoParamConstructorGadget.
977 auto HasTwoParamSpanCtorDecl
= SpanTwoParamConstructorGadget::matcher();
979 cxxConstructExpr(HasUnsafeCtorDecl
, unless(HasTwoParamSpanCtorDecl
))
983 void handleUnsafeOperation(UnsafeBufferUsageHandler
&Handler
,
984 bool IsRelatedToDecl
,
985 ASTContext
&Ctx
) const override
{
986 Handler
.handleUnsafeOperation(Op
, IsRelatedToDecl
, Ctx
);
988 SourceLocation
getSourceLoc() const override
{ return Op
->getBeginLoc(); }
990 DeclUseList
getClaimedVarUseSites() const override
{ return {}; }
993 // Warning gadget for unsafe invocation of span::data method.
994 // Triggers when the pointer returned by the invocation is immediately
995 // cast to a larger type.
997 class DataInvocationGadget
: public WarningGadget
{
998 constexpr static const char *const OpTag
= "data_invocation_expr";
999 const ExplicitCastExpr
*Op
;
1002 DataInvocationGadget(const MatchFinder::MatchResult
&Result
)
1003 : WarningGadget(Kind::DataInvocation
),
1004 Op(Result
.Nodes
.getNodeAs
<ExplicitCastExpr
>(OpTag
)) {}
1006 static bool classof(const Gadget
*G
) {
1007 return G
->getKind() == Kind::DataInvocation
;
1010 static Matcher
matcher() {
1011 Matcher callExpr
= cxxMemberCallExpr(
1012 callee(cxxMethodDecl(hasName("data"), ofClass(hasName("std::span")))));
1014 explicitCastExpr(anyOf(has(callExpr
), has(parenExpr(has(callExpr
)))))
1018 void handleUnsafeOperation(UnsafeBufferUsageHandler
&Handler
,
1019 bool IsRelatedToDecl
,
1020 ASTContext
&Ctx
) const override
{
1021 Handler
.handleUnsafeOperation(Op
, IsRelatedToDecl
, Ctx
);
1023 SourceLocation
getSourceLoc() const override
{ return Op
->getBeginLoc(); }
1025 DeclUseList
getClaimedVarUseSites() const override
{ return {}; }
1028 // Represents expressions of the form `DRE[*]` in the Unspecified Lvalue
1029 // Context (see `isInUnspecifiedLvalueContext`).
1030 // Note here `[]` is the built-in subscript operator.
1031 class ULCArraySubscriptGadget
: public FixableGadget
{
1033 static constexpr const char *const ULCArraySubscriptTag
=
1034 "ArraySubscriptUnderULC";
1035 const ArraySubscriptExpr
*Node
;
1038 ULCArraySubscriptGadget(const MatchFinder::MatchResult
&Result
)
1039 : FixableGadget(Kind::ULCArraySubscript
),
1040 Node(Result
.Nodes
.getNodeAs
<ArraySubscriptExpr
>(ULCArraySubscriptTag
)) {
1041 assert(Node
!= nullptr && "Expecting a non-null matching result");
1044 static bool classof(const Gadget
*G
) {
1045 return G
->getKind() == Kind::ULCArraySubscript
;
1048 static Matcher
matcher() {
1049 auto ArrayOrPtr
= anyOf(hasPointerType(), hasArrayType());
1050 auto BaseIsArrayOrPtrDRE
= hasBase(
1051 ignoringParenImpCasts(declRefExpr(ArrayOrPtr
, toSupportedVariable())));
1053 arraySubscriptExpr(BaseIsArrayOrPtrDRE
).bind(ULCArraySubscriptTag
);
1055 return expr(isInUnspecifiedLvalueContext(Target
));
1058 virtual std::optional
<FixItList
>
1059 getFixits(const FixitStrategy
&S
) const override
;
1060 SourceLocation
getSourceLoc() const override
{ return Node
->getBeginLoc(); }
1062 virtual DeclUseList
getClaimedVarUseSites() const override
{
1063 if (const auto *DRE
=
1064 dyn_cast
<DeclRefExpr
>(Node
->getBase()->IgnoreImpCasts())) {
1071 // Fixable gadget to handle stand alone pointers of the form `UPC(DRE)` in the
1072 // unspecified pointer context (isInUnspecifiedPointerContext). The gadget emits
1073 // fixit of the form `UPC(DRE.data())`.
1074 class UPCStandalonePointerGadget
: public FixableGadget
{
1076 static constexpr const char *const DeclRefExprTag
= "StandalonePointer";
1077 const DeclRefExpr
*Node
;
1080 UPCStandalonePointerGadget(const MatchFinder::MatchResult
&Result
)
1081 : FixableGadget(Kind::UPCStandalonePointer
),
1082 Node(Result
.Nodes
.getNodeAs
<DeclRefExpr
>(DeclRefExprTag
)) {
1083 assert(Node
!= nullptr && "Expecting a non-null matching result");
1086 static bool classof(const Gadget
*G
) {
1087 return G
->getKind() == Kind::UPCStandalonePointer
;
1090 static Matcher
matcher() {
1091 auto ArrayOrPtr
= anyOf(hasPointerType(), hasArrayType());
1092 auto target
= expr(ignoringParenImpCasts(
1093 declRefExpr(allOf(ArrayOrPtr
, toSupportedVariable()))
1094 .bind(DeclRefExprTag
)));
1095 return stmt(isInUnspecifiedPointerContext(target
));
1098 virtual std::optional
<FixItList
>
1099 getFixits(const FixitStrategy
&S
) const override
;
1100 SourceLocation
getSourceLoc() const override
{ return Node
->getBeginLoc(); }
1102 virtual DeclUseList
getClaimedVarUseSites() const override
{ return {Node
}; }
1105 class PointerDereferenceGadget
: public FixableGadget
{
1106 static constexpr const char *const BaseDeclRefExprTag
= "BaseDRE";
1107 static constexpr const char *const OperatorTag
= "op";
1109 const DeclRefExpr
*BaseDeclRefExpr
= nullptr;
1110 const UnaryOperator
*Op
= nullptr;
1113 PointerDereferenceGadget(const MatchFinder::MatchResult
&Result
)
1114 : FixableGadget(Kind::PointerDereference
),
1116 Result
.Nodes
.getNodeAs
<DeclRefExpr
>(BaseDeclRefExprTag
)),
1117 Op(Result
.Nodes
.getNodeAs
<UnaryOperator
>(OperatorTag
)) {}
1119 static bool classof(const Gadget
*G
) {
1120 return G
->getKind() == Kind::PointerDereference
;
1123 static Matcher
matcher() {
1126 hasOperatorName("*"),
1127 has(expr(ignoringParenImpCasts(
1128 declRefExpr(toSupportedVariable()).bind(BaseDeclRefExprTag
)))))
1131 return expr(isInUnspecifiedLvalueContext(Target
));
1134 DeclUseList
getClaimedVarUseSites() const override
{
1135 return {BaseDeclRefExpr
};
1138 virtual std::optional
<FixItList
>
1139 getFixits(const FixitStrategy
&S
) const override
;
1140 SourceLocation
getSourceLoc() const override
{ return Op
->getBeginLoc(); }
1143 // Represents expressions of the form `&DRE[any]` in the Unspecified Pointer
1144 // Context (see `isInUnspecifiedPointerContext`).
1145 // Note here `[]` is the built-in subscript operator.
1146 class UPCAddressofArraySubscriptGadget
: public FixableGadget
{
1148 static constexpr const char *const UPCAddressofArraySubscriptTag
=
1149 "AddressofArraySubscriptUnderUPC";
1150 const UnaryOperator
*Node
; // the `&DRE[any]` node
1153 UPCAddressofArraySubscriptGadget(const MatchFinder::MatchResult
&Result
)
1154 : FixableGadget(Kind::ULCArraySubscript
),
1155 Node(Result
.Nodes
.getNodeAs
<UnaryOperator
>(
1156 UPCAddressofArraySubscriptTag
)) {
1157 assert(Node
!= nullptr && "Expecting a non-null matching result");
1160 static bool classof(const Gadget
*G
) {
1161 return G
->getKind() == Kind::UPCAddressofArraySubscript
;
1164 static Matcher
matcher() {
1165 return expr(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
1167 hasOperatorName("&"),
1168 hasUnaryOperand(arraySubscriptExpr(hasBase(
1169 ignoringParenImpCasts(declRefExpr(toSupportedVariable()))))))
1170 .bind(UPCAddressofArraySubscriptTag
)))));
1173 virtual std::optional
<FixItList
>
1174 getFixits(const FixitStrategy
&) const override
;
1175 SourceLocation
getSourceLoc() const override
{ return Node
->getBeginLoc(); }
1177 virtual DeclUseList
getClaimedVarUseSites() const override
{
1178 const auto *ArraySubst
= cast
<ArraySubscriptExpr
>(Node
->getSubExpr());
1180 cast
<DeclRefExpr
>(ArraySubst
->getBase()->IgnoreParenImpCasts());
1187 // An auxiliary tracking facility for the fixit analysis. It helps connect
1188 // declarations to its uses and make sure we've covered all uses with our
1189 // analysis before we try to fix the declaration.
1190 class DeclUseTracker
{
1191 using UseSetTy
= SmallSet
<const DeclRefExpr
*, 16>;
1192 using DefMapTy
= DenseMap
<const VarDecl
*, const DeclStmt
*>;
1194 // Allocate on the heap for easier move.
1195 std::unique_ptr
<UseSetTy
> Uses
{std::make_unique
<UseSetTy
>()};
1199 DeclUseTracker() = default;
1200 DeclUseTracker(const DeclUseTracker
&) = delete; // Let's avoid copies.
1201 DeclUseTracker
&operator=(const DeclUseTracker
&) = delete;
1202 DeclUseTracker(DeclUseTracker
&&) = default;
1203 DeclUseTracker
&operator=(DeclUseTracker
&&) = default;
1205 // Start tracking a freshly discovered DRE.
1206 void discoverUse(const DeclRefExpr
*DRE
) { Uses
->insert(DRE
); }
1208 // Stop tracking the DRE as it's been fully figured out.
1209 void claimUse(const DeclRefExpr
*DRE
) {
1210 assert(Uses
->count(DRE
) &&
1211 "DRE not found or claimed by multiple matchers!");
1215 // A variable is unclaimed if at least one use is unclaimed.
1216 bool hasUnclaimedUses(const VarDecl
*VD
) const {
1217 // FIXME: Can this be less linear? Maybe maintain a map from VDs to DREs?
1218 return any_of(*Uses
, [VD
](const DeclRefExpr
*DRE
) {
1219 return DRE
->getDecl()->getCanonicalDecl() == VD
->getCanonicalDecl();
1223 UseSetTy
getUnclaimedUses(const VarDecl
*VD
) const {
1225 for (auto use
: *Uses
) {
1226 if (use
->getDecl()->getCanonicalDecl() == VD
->getCanonicalDecl()) {
1227 ReturnSet
.insert(use
);
1233 void discoverDecl(const DeclStmt
*DS
) {
1234 for (const Decl
*D
: DS
->decls()) {
1235 if (const auto *VD
= dyn_cast
<VarDecl
>(D
)) {
1236 // FIXME: Assertion temporarily disabled due to a bug in
1237 // ASTMatcher internal behavior in presence of GNU
1238 // statement-expressions. We need to properly investigate this
1239 // because it can screw up our algorithm in other ways.
1240 // assert(Defs.count(VD) == 0 && "Definition already discovered!");
1246 const DeclStmt
*lookupDecl(const VarDecl
*VD
) const {
1247 return Defs
.lookup(VD
);
1252 // Representing a pointer type expression of the form `++Ptr` in an Unspecified
1253 // Pointer Context (UPC):
1254 class UPCPreIncrementGadget
: public FixableGadget
{
1256 static constexpr const char *const UPCPreIncrementTag
=
1257 "PointerPreIncrementUnderUPC";
1258 const UnaryOperator
*Node
; // the `++Ptr` node
1261 UPCPreIncrementGadget(const MatchFinder::MatchResult
&Result
)
1262 : FixableGadget(Kind::UPCPreIncrement
),
1263 Node(Result
.Nodes
.getNodeAs
<UnaryOperator
>(UPCPreIncrementTag
)) {
1264 assert(Node
!= nullptr && "Expecting a non-null matching result");
1267 static bool classof(const Gadget
*G
) {
1268 return G
->getKind() == Kind::UPCPreIncrement
;
1271 static Matcher
matcher() {
1272 // Note here we match `++Ptr` for any expression `Ptr` of pointer type.
1273 // Although currently we can only provide fix-its when `Ptr` is a DRE, we
1274 // can have the matcher be general, so long as `getClaimedVarUseSites` does
1276 return stmt(isInUnspecifiedPointerContext(expr(ignoringImpCasts(
1277 unaryOperator(isPreInc(),
1278 hasUnaryOperand(declRefExpr(toSupportedVariable())))
1279 .bind(UPCPreIncrementTag
)))));
1282 virtual std::optional
<FixItList
>
1283 getFixits(const FixitStrategy
&S
) const override
;
1284 SourceLocation
getSourceLoc() const override
{ return Node
->getBeginLoc(); }
1286 virtual DeclUseList
getClaimedVarUseSites() const override
{
1287 return {dyn_cast
<DeclRefExpr
>(Node
->getSubExpr())};
1291 // Representing a pointer type expression of the form `Ptr += n` in an
1292 // Unspecified Untyped Context (UUC):
1293 class UUCAddAssignGadget
: public FixableGadget
{
1295 static constexpr const char *const UUCAddAssignTag
=
1296 "PointerAddAssignUnderUUC";
1297 static constexpr const char *const OffsetTag
= "Offset";
1299 const BinaryOperator
*Node
; // the `Ptr += n` node
1300 const Expr
*Offset
= nullptr;
1303 UUCAddAssignGadget(const MatchFinder::MatchResult
&Result
)
1304 : FixableGadget(Kind::UUCAddAssign
),
1305 Node(Result
.Nodes
.getNodeAs
<BinaryOperator
>(UUCAddAssignTag
)),
1306 Offset(Result
.Nodes
.getNodeAs
<Expr
>(OffsetTag
)) {
1307 assert(Node
!= nullptr && "Expecting a non-null matching result");
1310 static bool classof(const Gadget
*G
) {
1311 return G
->getKind() == Kind::UUCAddAssign
;
1314 static Matcher
matcher() {
1316 return stmt(isInUnspecifiedUntypedContext(expr(ignoringImpCasts(
1317 binaryOperator(hasOperatorName("+="),
1321 toSupportedVariable())),
1322 hasRHS(expr().bind(OffsetTag
)))
1323 .bind(UUCAddAssignTag
)))));
1327 virtual std::optional
<FixItList
>
1328 getFixits(const FixitStrategy
&S
) const override
;
1329 SourceLocation
getSourceLoc() const override
{ return Node
->getBeginLoc(); }
1331 virtual DeclUseList
getClaimedVarUseSites() const override
{
1332 return {dyn_cast
<DeclRefExpr
>(Node
->getLHS())};
1336 // Representing a fixable expression of the form `*(ptr + 123)` or `*(123 +
1338 class DerefSimplePtrArithFixableGadget
: public FixableGadget
{
1339 static constexpr const char *const BaseDeclRefExprTag
= "BaseDRE";
1340 static constexpr const char *const DerefOpTag
= "DerefOp";
1341 static constexpr const char *const AddOpTag
= "AddOp";
1342 static constexpr const char *const OffsetTag
= "Offset";
1344 const DeclRefExpr
*BaseDeclRefExpr
= nullptr;
1345 const UnaryOperator
*DerefOp
= nullptr;
1346 const BinaryOperator
*AddOp
= nullptr;
1347 const IntegerLiteral
*Offset
= nullptr;
1350 DerefSimplePtrArithFixableGadget(const MatchFinder::MatchResult
&Result
)
1351 : FixableGadget(Kind::DerefSimplePtrArithFixable
),
1353 Result
.Nodes
.getNodeAs
<DeclRefExpr
>(BaseDeclRefExprTag
)),
1354 DerefOp(Result
.Nodes
.getNodeAs
<UnaryOperator
>(DerefOpTag
)),
1355 AddOp(Result
.Nodes
.getNodeAs
<BinaryOperator
>(AddOpTag
)),
1356 Offset(Result
.Nodes
.getNodeAs
<IntegerLiteral
>(OffsetTag
)) {}
1358 static Matcher
matcher() {
1360 auto ThePtr
= expr(hasPointerType(),
1361 ignoringImpCasts(declRefExpr(toSupportedVariable()).
1362 bind(BaseDeclRefExprTag
)));
1363 auto PlusOverPtrAndInteger
= expr(anyOf(
1364 binaryOperator(hasOperatorName("+"), hasLHS(ThePtr
),
1365 hasRHS(integerLiteral().bind(OffsetTag
)))
1367 binaryOperator(hasOperatorName("+"), hasRHS(ThePtr
),
1368 hasLHS(integerLiteral().bind(OffsetTag
)))
1370 return isInUnspecifiedLvalueContext(unaryOperator(
1371 hasOperatorName("*"),
1372 hasUnaryOperand(ignoringParens(PlusOverPtrAndInteger
)))
1377 virtual std::optional
<FixItList
>
1378 getFixits(const FixitStrategy
&s
) const final
;
1379 SourceLocation
getSourceLoc() const override
{
1380 return DerefOp
->getBeginLoc();
1383 virtual DeclUseList
getClaimedVarUseSites() const final
{
1384 return {BaseDeclRefExpr
};
1388 /// Scan the function and return a list of gadgets found with provided kits.
1389 static std::tuple
<FixableGadgetList
, WarningGadgetList
, DeclUseTracker
>
1390 findGadgets(const Decl
*D
, const UnsafeBufferUsageHandler
&Handler
,
1391 bool EmitSuggestions
) {
1393 struct GadgetFinderCallback
: MatchFinder::MatchCallback
{
1394 FixableGadgetList FixableGadgets
;
1395 WarningGadgetList WarningGadgets
;
1396 DeclUseTracker Tracker
;
1398 void run(const MatchFinder::MatchResult
&Result
) override
{
1399 // In debug mode, assert that we've found exactly one gadget.
1400 // This helps us avoid conflicts in .bind() tags.
1404 [[maybe_unused
]] int numFound
= 0;
1405 #define NEXT ++numFound
1408 if (const auto *DRE
= Result
.Nodes
.getNodeAs
<DeclRefExpr
>("any_dre")) {
1409 Tracker
.discoverUse(DRE
);
1413 if (const auto *DS
= Result
.Nodes
.getNodeAs
<DeclStmt
>("any_ds")) {
1414 Tracker
.discoverDecl(DS
);
1418 // Figure out which matcher we've found, and call the appropriate
1419 // subclass constructor.
1420 // FIXME: Can we do this more logarithmically?
1421 #define FIXABLE_GADGET(name) \
1422 if (Result.Nodes.getNodeAs<Stmt>(#name)) { \
1423 FixableGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
1426 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1427 #define WARNING_GADGET(name) \
1428 if (Result.Nodes.getNodeAs<Stmt>(#name)) { \
1429 WarningGadgets.push_back(std::make_unique<name##Gadget>(Result)); \
1432 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1434 assert(numFound
>= 1 && "Gadgets not found in match result!");
1435 assert(numFound
<= 1 && "Conflicting bind tags in gadgets!");
1440 GadgetFinderCallback CB
;
1445 forEachDescendantEvaluatedStmt(stmt(anyOf(
1446 // Add Gadget::matcher() for every gadget in the registry.
1447 #define WARNING_GADGET(x) \
1448 allOf(x ## Gadget::matcher().bind(#x), \
1449 notInSafeBufferOptOut(&Handler)),
1450 #define WARNING_CONTAINER_GADGET(x) \
1451 allOf(x ## Gadget::matcher().bind(#x), \
1452 notInSafeBufferOptOut(&Handler), \
1453 unless(ignoreUnsafeBufferInContainer(&Handler))),
1454 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1455 // Avoid a hanging comma.
1463 if (EmitSuggestions
) {
1467 forEachDescendantStmt(stmt(eachOf(
1468 #define FIXABLE_GADGET(x) \
1469 x ## Gadget::matcher().bind(#x),
1470 #include "clang/Analysis/Analyses/UnsafeBufferUsageGadgets.def"
1471 // In parallel, match all DeclRefExprs so that to find out
1472 // whether there are any uncovered by gadgets.
1473 declRefExpr(anyOf(hasPointerType(), hasArrayType()),
1474 to(anyOf(varDecl(), bindingDecl()))).bind("any_dre"),
1475 // Also match DeclStmts because we'll need them when fixing
1476 // their underlying VarDecls that otherwise don't have
1477 // any backreferences to DeclStmts.
1478 declStmt().bind("any_ds")
1486 M
.match(*D
->getBody(), D
->getASTContext());
1487 return {std::move(CB
.FixableGadgets
), std::move(CB
.WarningGadgets
),
1488 std::move(CB
.Tracker
)};
1491 // Compares AST nodes by source locations.
1492 template <typename NodeTy
> struct CompareNode
{
1493 bool operator()(const NodeTy
*N1
, const NodeTy
*N2
) const {
1494 return N1
->getBeginLoc().getRawEncoding() <
1495 N2
->getBeginLoc().getRawEncoding();
1499 struct WarningGadgetSets
{
1500 std::map
<const VarDecl
*, std::set
<const WarningGadget
*>,
1501 // To keep keys sorted by their locations in the map so that the
1502 // order is deterministic:
1503 CompareNode
<VarDecl
>>
1505 // These Gadgets are not related to pointer variables (e. g. temporaries).
1506 llvm::SmallVector
<const WarningGadget
*, 16> noVar
;
1509 static WarningGadgetSets
1510 groupWarningGadgetsByVar(const WarningGadgetList
&AllUnsafeOperations
) {
1511 WarningGadgetSets result
;
1512 // If some gadgets cover more than one
1513 // variable, they'll appear more than once in the map.
1514 for (auto &G
: AllUnsafeOperations
) {
1515 DeclUseList ClaimedVarUseSites
= G
->getClaimedVarUseSites();
1517 bool AssociatedWithVarDecl
= false;
1518 for (const DeclRefExpr
*DRE
: ClaimedVarUseSites
) {
1519 if (const auto *VD
= dyn_cast
<VarDecl
>(DRE
->getDecl())) {
1520 result
.byVar
[VD
].insert(G
.get());
1521 AssociatedWithVarDecl
= true;
1525 if (!AssociatedWithVarDecl
) {
1526 result
.noVar
.push_back(G
.get());
1533 struct FixableGadgetSets
{
1534 std::map
<const VarDecl
*, std::set
<const FixableGadget
*>,
1535 // To keep keys sorted by their locations in the map so that the
1536 // order is deterministic:
1537 CompareNode
<VarDecl
>>
1541 static FixableGadgetSets
1542 groupFixablesByVar(FixableGadgetList
&&AllFixableOperations
) {
1543 FixableGadgetSets FixablesForUnsafeVars
;
1544 for (auto &F
: AllFixableOperations
) {
1545 DeclUseList DREs
= F
->getClaimedVarUseSites();
1547 for (const DeclRefExpr
*DRE
: DREs
) {
1548 if (const auto *VD
= dyn_cast
<VarDecl
>(DRE
->getDecl())) {
1549 FixablesForUnsafeVars
.byVar
[VD
].insert(F
.get());
1553 return FixablesForUnsafeVars
;
1556 bool clang::internal::anyConflict(const SmallVectorImpl
<FixItHint
> &FixIts
,
1557 const SourceManager
&SM
) {
1558 // A simple interval overlap detection algorithm. Sorts all ranges by their
1559 // begin location then finds the first overlap in one pass.
1560 std::vector
<const FixItHint
*> All
; // a copy of `FixIts`
1562 for (const FixItHint
&H
: FixIts
)
1564 std::sort(All
.begin(), All
.end(),
1565 [&SM
](const FixItHint
*H1
, const FixItHint
*H2
) {
1566 return SM
.isBeforeInTranslationUnit(H1
->RemoveRange
.getBegin(),
1567 H2
->RemoveRange
.getBegin());
1570 const FixItHint
*CurrHint
= nullptr;
1572 for (const FixItHint
*Hint
: All
) {
1574 SM
.isBeforeInTranslationUnit(CurrHint
->RemoveRange
.getEnd(),
1575 Hint
->RemoveRange
.getBegin())) {
1576 // Either to initialize `CurrHint` or `CurrHint` does not
1577 // overlap with `Hint`:
1580 // In case `Hint` overlaps the `CurrHint`, we found at least one
1587 std::optional
<FixItList
>
1588 PtrToPtrAssignmentGadget::getFixits(const FixitStrategy
&S
) const {
1589 const auto *LeftVD
= cast
<VarDecl
>(PtrLHS
->getDecl());
1590 const auto *RightVD
= cast
<VarDecl
>(PtrRHS
->getDecl());
1591 switch (S
.lookup(LeftVD
)) {
1592 case FixitStrategy::Kind::Span
:
1593 if (S
.lookup(RightVD
) == FixitStrategy::Kind::Span
)
1595 return std::nullopt
;
1596 case FixitStrategy::Kind::Wontfix
:
1597 return std::nullopt
;
1598 case FixitStrategy::Kind::Iterator
:
1599 case FixitStrategy::Kind::Array
:
1600 return std::nullopt
;
1601 case FixitStrategy::Kind::Vector
:
1602 llvm_unreachable("unsupported strategies for FixableGadgets");
1604 return std::nullopt
;
1607 /// \returns fixit that adds .data() call after \DRE.
1608 static inline std::optional
<FixItList
> createDataFixit(const ASTContext
&Ctx
,
1609 const DeclRefExpr
*DRE
);
1611 std::optional
<FixItList
>
1612 CArrayToPtrAssignmentGadget::getFixits(const FixitStrategy
&S
) const {
1613 const auto *LeftVD
= cast
<VarDecl
>(PtrLHS
->getDecl());
1614 const auto *RightVD
= cast
<VarDecl
>(PtrRHS
->getDecl());
1615 // TLDR: Implementing fixits for non-Wontfix strategy on both LHS and RHS is
1618 // CArrayToPtrAssignmentGadget doesn't have strategy implications because
1619 // constant size array propagates its bounds. Because of that LHS and RHS are
1620 // addressed by two different fixits.
1622 // At the same time FixitStrategy S doesn't reflect what group a fixit belongs
1623 // to and can't be generally relied on in multi-variable Fixables!
1625 // E. g. If an instance of this gadget is fixing variable on LHS then the
1626 // variable on RHS is fixed by a different fixit and its strategy for LHS
1627 // fixit is as if Wontfix.
1629 // The only exception is Wontfix strategy for a given variable as that is
1630 // valid for any fixit produced for the given input source code.
1631 if (S
.lookup(LeftVD
) == FixitStrategy::Kind::Span
) {
1632 if (S
.lookup(RightVD
) == FixitStrategy::Kind::Wontfix
) {
1635 } else if (S
.lookup(LeftVD
) == FixitStrategy::Kind::Wontfix
) {
1636 if (S
.lookup(RightVD
) == FixitStrategy::Kind::Array
) {
1637 return createDataFixit(RightVD
->getASTContext(), PtrRHS
);
1640 return std::nullopt
;
1643 std::optional
<FixItList
>
1644 PointerInitGadget::getFixits(const FixitStrategy
&S
) const {
1645 const auto *LeftVD
= PtrInitLHS
;
1646 const auto *RightVD
= cast
<VarDecl
>(PtrInitRHS
->getDecl());
1647 switch (S
.lookup(LeftVD
)) {
1648 case FixitStrategy::Kind::Span
:
1649 if (S
.lookup(RightVD
) == FixitStrategy::Kind::Span
)
1651 return std::nullopt
;
1652 case FixitStrategy::Kind::Wontfix
:
1653 return std::nullopt
;
1654 case FixitStrategy::Kind::Iterator
:
1655 case FixitStrategy::Kind::Array
:
1656 return std::nullopt
;
1657 case FixitStrategy::Kind::Vector
:
1658 llvm_unreachable("unsupported strategies for FixableGadgets");
1660 return std::nullopt
;
1663 static bool isNonNegativeIntegerExpr(const Expr
*Expr
, const VarDecl
*VD
,
1664 const ASTContext
&Ctx
) {
1665 if (auto ConstVal
= Expr
->getIntegerConstantExpr(Ctx
)) {
1666 if (ConstVal
->isNegative())
1668 } else if (!Expr
->getType()->isUnsignedIntegerType())
1673 std::optional
<FixItList
>
1674 ULCArraySubscriptGadget::getFixits(const FixitStrategy
&S
) const {
1675 if (const auto *DRE
=
1676 dyn_cast
<DeclRefExpr
>(Node
->getBase()->IgnoreImpCasts()))
1677 if (const auto *VD
= dyn_cast
<VarDecl
>(DRE
->getDecl())) {
1678 switch (S
.lookup(VD
)) {
1679 case FixitStrategy::Kind::Span
: {
1681 // If the index has a negative constant value, we give up as no valid
1682 // fix-it can be generated:
1683 const ASTContext
&Ctx
= // FIXME: we need ASTContext to be passed in!
1684 VD
->getASTContext();
1685 if (!isNonNegativeIntegerExpr(Node
->getIdx(), VD
, Ctx
))
1686 return std::nullopt
;
1687 // no-op is a good fix-it, otherwise
1690 case FixitStrategy::Kind::Array
:
1692 case FixitStrategy::Kind::Wontfix
:
1693 case FixitStrategy::Kind::Iterator
:
1694 case FixitStrategy::Kind::Vector
:
1695 llvm_unreachable("unsupported strategies for FixableGadgets");
1698 return std::nullopt
;
1701 static std::optional
<FixItList
> // forward declaration
1702 fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator
*Node
);
1704 std::optional
<FixItList
>
1705 UPCAddressofArraySubscriptGadget::getFixits(const FixitStrategy
&S
) const {
1706 auto DREs
= getClaimedVarUseSites();
1707 const auto *VD
= cast
<VarDecl
>(DREs
.front()->getDecl());
1709 switch (S
.lookup(VD
)) {
1710 case FixitStrategy::Kind::Span
:
1711 return fixUPCAddressofArraySubscriptWithSpan(Node
);
1712 case FixitStrategy::Kind::Wontfix
:
1713 case FixitStrategy::Kind::Iterator
:
1714 case FixitStrategy::Kind::Array
:
1715 return std::nullopt
;
1716 case FixitStrategy::Kind::Vector
:
1717 llvm_unreachable("unsupported strategies for FixableGadgets");
1719 return std::nullopt
; // something went wrong, no fix-it
1722 // FIXME: this function should be customizable through format
1723 static StringRef
getEndOfLine() {
1724 static const char *const EOL
= "\n";
1728 // Returns the text indicating that the user needs to provide input there:
1729 std::string
getUserFillPlaceHolder(StringRef HintTextToUser
= "placeholder") {
1730 std::string s
= std::string("<# ");
1731 s
+= HintTextToUser
;
1736 // Return the source location of the last character of the AST `Node`.
1737 template <typename NodeTy
>
1738 static std::optional
<SourceLocation
>
1739 getEndCharLoc(const NodeTy
*Node
, const SourceManager
&SM
,
1740 const LangOptions
&LangOpts
) {
1741 unsigned TkLen
= Lexer::MeasureTokenLength(Node
->getEndLoc(), SM
, LangOpts
);
1742 SourceLocation Loc
= Node
->getEndLoc().getLocWithOffset(TkLen
- 1);
1747 return std::nullopt
;
1750 // Return the source location just past the last character of the AST `Node`.
1751 template <typename NodeTy
>
1752 static std::optional
<SourceLocation
> getPastLoc(const NodeTy
*Node
,
1753 const SourceManager
&SM
,
1754 const LangOptions
&LangOpts
) {
1755 SourceLocation Loc
=
1756 Lexer::getLocForEndOfToken(Node
->getEndLoc(), 0, SM
, LangOpts
);
1759 return std::nullopt
;
1762 // Return text representation of an `Expr`.
1763 static std::optional
<StringRef
> getExprText(const Expr
*E
,
1764 const SourceManager
&SM
,
1765 const LangOptions
&LangOpts
) {
1766 std::optional
<SourceLocation
> LastCharLoc
= getPastLoc(E
, SM
, LangOpts
);
1769 return Lexer::getSourceText(
1770 CharSourceRange::getCharRange(E
->getBeginLoc(), *LastCharLoc
), SM
,
1773 return std::nullopt
;
1776 // Returns the literal text in `SourceRange SR`, if `SR` is a valid range.
1777 static std::optional
<StringRef
> getRangeText(SourceRange SR
,
1778 const SourceManager
&SM
,
1779 const LangOptions
&LangOpts
) {
1780 bool Invalid
= false;
1781 CharSourceRange CSR
= CharSourceRange::getCharRange(SR
);
1782 StringRef Text
= Lexer::getSourceText(CSR
, SM
, LangOpts
, &Invalid
);
1786 return std::nullopt
;
1789 // Returns the begin location of the identifier of the given variable
1791 static SourceLocation
getVarDeclIdentifierLoc(const VarDecl
*VD
) {
1792 // According to the implementation of `VarDecl`, `VD->getLocation()` actually
1793 // returns the begin location of the identifier of the declaration:
1794 return VD
->getLocation();
1797 // Returns the literal text of the identifier of the given variable declaration.
1798 static std::optional
<StringRef
>
1799 getVarDeclIdentifierText(const VarDecl
*VD
, const SourceManager
&SM
,
1800 const LangOptions
&LangOpts
) {
1801 SourceLocation ParmIdentBeginLoc
= getVarDeclIdentifierLoc(VD
);
1802 SourceLocation ParmIdentEndLoc
=
1803 Lexer::getLocForEndOfToken(ParmIdentBeginLoc
, 0, SM
, LangOpts
);
1805 if (ParmIdentEndLoc
.isMacroID() &&
1806 !Lexer::isAtEndOfMacroExpansion(ParmIdentEndLoc
, SM
, LangOpts
))
1807 return std::nullopt
;
1808 return getRangeText({ParmIdentBeginLoc
, ParmIdentEndLoc
}, SM
, LangOpts
);
1811 // We cannot fix a variable declaration if it has some other specifiers than the
1812 // type specifier. Because the source ranges of those specifiers could overlap
1813 // with the source range that is being replaced using fix-its. Especially when
1814 // we often cannot obtain accurate source ranges of cv-qualified type
1816 // FIXME: also deal with type attributes
1817 static bool hasUnsupportedSpecifiers(const VarDecl
*VD
,
1818 const SourceManager
&SM
) {
1819 // AttrRangeOverlapping: true if at least one attribute of `VD` overlaps the
1820 // source range of `VD`:
1821 bool AttrRangeOverlapping
= llvm::any_of(VD
->attrs(), [&](Attr
*At
) -> bool {
1822 return !(SM
.isBeforeInTranslationUnit(At
->getRange().getEnd(),
1823 VD
->getBeginLoc())) &&
1824 !(SM
.isBeforeInTranslationUnit(VD
->getEndLoc(),
1825 At
->getRange().getBegin()));
1827 return VD
->isInlineSpecified() || VD
->isConstexpr() ||
1828 VD
->hasConstantInitialization() || !VD
->hasLocalStorage() ||
1829 AttrRangeOverlapping
;
1832 // Returns the `SourceRange` of `D`. The reason why this function exists is
1833 // that `D->getSourceRange()` may return a range where the end location is the
1834 // starting location of the last token. The end location of the source range
1835 // returned by this function is the last location of the last token.
1836 static SourceRange
getSourceRangeToTokenEnd(const Decl
*D
,
1837 const SourceManager
&SM
,
1838 const LangOptions
&LangOpts
) {
1839 SourceLocation Begin
= D
->getBeginLoc();
1841 End
= // `D->getEndLoc` should always return the starting location of the
1842 // last token, so we should get the end of the token
1843 Lexer::getLocForEndOfToken(D
->getEndLoc(), 0, SM
, LangOpts
);
1845 return SourceRange(Begin
, End
);
1848 // Returns the text of the pointee type of `T` from a `VarDecl` of a pointer
1849 // type. The text is obtained through from `TypeLoc`s. Since `TypeLoc` does not
1850 // have source ranges of qualifiers ( The `QualifiedTypeLoc` looks hacky too me
1851 // :( ), `Qualifiers` of the pointee type is returned separately through the
1852 // output parameter `QualifiersToAppend`.
1853 static std::optional
<std::string
>
1854 getPointeeTypeText(const VarDecl
*VD
, const SourceManager
&SM
,
1855 const LangOptions
&LangOpts
,
1856 std::optional
<Qualifiers
> *QualifiersToAppend
) {
1857 QualType Ty
= VD
->getType();
1860 assert(Ty
->isPointerType() && !Ty
->isFunctionPointerType() &&
1861 "Expecting a VarDecl of type of pointer to object type");
1862 PteTy
= Ty
->getPointeeType();
1864 TypeLoc TyLoc
= VD
->getTypeSourceInfo()->getTypeLoc().getUnqualifiedLoc();
1867 // We only deal with the cases that we know `TypeLoc::getNextTypeLoc` returns
1868 // the `TypeLoc` of the pointee type:
1869 switch (TyLoc
.getTypeLocClass()) {
1870 case TypeLoc::ConstantArray
:
1871 case TypeLoc::IncompleteArray
:
1872 case TypeLoc::VariableArray
:
1873 case TypeLoc::DependentSizedArray
:
1874 case TypeLoc::Decayed
:
1875 assert(isa
<ParmVarDecl
>(VD
) && "An array type shall not be treated as a "
1876 "pointer type unless it decays.");
1877 PteTyLoc
= TyLoc
.getNextTypeLoc();
1879 case TypeLoc::Pointer
:
1880 PteTyLoc
= TyLoc
.castAs
<PointerTypeLoc
>().getPointeeLoc();
1883 return std::nullopt
;
1885 if (PteTyLoc
.isNull())
1886 // Sometimes we cannot get a useful `TypeLoc` for the pointee type, e.g.,
1887 // when the pointer type is `auto`.
1888 return std::nullopt
;
1890 SourceLocation IdentLoc
= getVarDeclIdentifierLoc(VD
);
1892 if (!(IdentLoc
.isValid() && PteTyLoc
.getSourceRange().isValid())) {
1893 // We are expecting these locations to be valid. But in some cases, they are
1894 // not all valid. It is a Clang bug to me and we are not responsible for
1895 // fixing it. So we will just give up for now when it happens.
1896 return std::nullopt
;
1899 // Note that TypeLoc.getEndLoc() returns the begin location of the last token:
1900 SourceLocation PteEndOfTokenLoc
=
1901 Lexer::getLocForEndOfToken(PteTyLoc
.getEndLoc(), 0, SM
, LangOpts
);
1903 if (!PteEndOfTokenLoc
.isValid())
1904 // Sometimes we cannot get the end location of the pointee type, e.g., when
1905 // there are macros involved.
1906 return std::nullopt
;
1907 if (!SM
.isBeforeInTranslationUnit(PteEndOfTokenLoc
, IdentLoc
)) {
1908 // We only deal with the cases where the source text of the pointee type
1909 // appears on the left-hand side of the variable identifier completely,
1910 // including the following forms:
1912 // `T ident[]`, where `T` is any type.
1913 // Examples of excluded cases are `T (*ident)[]` or `T ident[][n]`.
1914 return std::nullopt
;
1916 if (PteTy
.hasQualifiers()) {
1917 // TypeLoc does not provide source ranges for qualifiers (it says it's
1918 // intentional but seems fishy to me), so we cannot get the full text
1919 // `PteTy` via source ranges.
1920 *QualifiersToAppend
= PteTy
.getQualifiers();
1922 return getRangeText({PteTyLoc
.getBeginLoc(), PteEndOfTokenLoc
}, SM
, LangOpts
)
1926 // Returns the text of the name (with qualifiers) of a `FunctionDecl`.
1927 static std::optional
<StringRef
> getFunNameText(const FunctionDecl
*FD
,
1928 const SourceManager
&SM
,
1929 const LangOptions
&LangOpts
) {
1930 SourceLocation BeginLoc
= FD
->getQualifier()
1931 ? FD
->getQualifierLoc().getBeginLoc()
1932 : FD
->getNameInfo().getBeginLoc();
1933 // Note that `FD->getNameInfo().getEndLoc()` returns the begin location of the
1935 SourceLocation EndLoc
= Lexer::getLocForEndOfToken(
1936 FD
->getNameInfo().getEndLoc(), 0, SM
, LangOpts
);
1937 SourceRange NameRange
{BeginLoc
, EndLoc
};
1939 return getRangeText(NameRange
, SM
, LangOpts
);
1942 // Returns the text representing a `std::span` type where the element type is
1943 // represented by `EltTyText`.
1945 // Note the optional parameter `Qualifiers`: one needs to pass qualifiers
1946 // explicitly if the element type needs to be qualified.
1948 getSpanTypeText(StringRef EltTyText
,
1949 std::optional
<Qualifiers
> Quals
= std::nullopt
) {
1950 const char *const SpanOpen
= "std::span<";
1953 return SpanOpen
+ EltTyText
.str() + ' ' + Quals
->getAsString() + '>';
1954 return SpanOpen
+ EltTyText
.str() + '>';
1957 std::optional
<FixItList
>
1958 DerefSimplePtrArithFixableGadget::getFixits(const FixitStrategy
&s
) const {
1959 const VarDecl
*VD
= dyn_cast
<VarDecl
>(BaseDeclRefExpr
->getDecl());
1961 if (VD
&& s
.lookup(VD
) == FixitStrategy::Kind::Span
) {
1962 ASTContext
&Ctx
= VD
->getASTContext();
1963 // std::span can't represent elements before its begin()
1964 if (auto ConstVal
= Offset
->getIntegerConstantExpr(Ctx
))
1965 if (ConstVal
->isNegative())
1966 return std::nullopt
;
1968 // note that the expr may (oddly) has multiple layers of parens
1970 // *((..(pointer + 123)..))
1975 // replace ' + ' with '['
1976 // replace ')' with ']'
1979 // *((..(123 + pointer)..))
1984 // replace ' + ' with '['
1985 // replace ')' with ']'
1987 const Expr
*LHS
= AddOp
->getLHS(), *RHS
= AddOp
->getRHS();
1988 const SourceManager
&SM
= Ctx
.getSourceManager();
1989 const LangOptions
&LangOpts
= Ctx
.getLangOpts();
1990 CharSourceRange StarWithTrailWhitespace
=
1991 clang::CharSourceRange::getCharRange(DerefOp
->getOperatorLoc(),
1992 LHS
->getBeginLoc());
1994 std::optional
<SourceLocation
> LHSLocation
= getPastLoc(LHS
, SM
, LangOpts
);
1996 return std::nullopt
;
1998 CharSourceRange PlusWithSurroundingWhitespace
=
1999 clang::CharSourceRange::getCharRange(*LHSLocation
, RHS
->getBeginLoc());
2001 std::optional
<SourceLocation
> AddOpLocation
=
2002 getPastLoc(AddOp
, SM
, LangOpts
);
2003 std::optional
<SourceLocation
> DerefOpLocation
=
2004 getPastLoc(DerefOp
, SM
, LangOpts
);
2006 if (!AddOpLocation
|| !DerefOpLocation
)
2007 return std::nullopt
;
2009 CharSourceRange ClosingParenWithPrecWhitespace
=
2010 clang::CharSourceRange::getCharRange(*AddOpLocation
, *DerefOpLocation
);
2013 {FixItHint::CreateRemoval(StarWithTrailWhitespace
),
2014 FixItHint::CreateReplacement(PlusWithSurroundingWhitespace
, "["),
2015 FixItHint::CreateReplacement(ClosingParenWithPrecWhitespace
, "]")}};
2017 return std::nullopt
; // something wrong or unsupported, give up
2020 std::optional
<FixItList
>
2021 PointerDereferenceGadget::getFixits(const FixitStrategy
&S
) const {
2022 const VarDecl
*VD
= cast
<VarDecl
>(BaseDeclRefExpr
->getDecl());
2023 switch (S
.lookup(VD
)) {
2024 case FixitStrategy::Kind::Span
: {
2025 ASTContext
&Ctx
= VD
->getASTContext();
2026 SourceManager
&SM
= Ctx
.getSourceManager();
2027 // Required changes: *(ptr); => (ptr[0]); and *ptr; => ptr[0]
2028 // Deletes the *operand
2029 CharSourceRange derefRange
= clang::CharSourceRange::getCharRange(
2030 Op
->getBeginLoc(), Op
->getBeginLoc().getLocWithOffset(1));
2032 if (auto LocPastOperand
=
2033 getPastLoc(BaseDeclRefExpr
, SM
, Ctx
.getLangOpts())) {
2034 return FixItList
{{FixItHint::CreateRemoval(derefRange
),
2035 FixItHint::CreateInsertion(*LocPastOperand
, "[0]")}};
2039 case FixitStrategy::Kind::Iterator
:
2040 case FixitStrategy::Kind::Array
:
2041 return std::nullopt
;
2042 case FixitStrategy::Kind::Vector
:
2043 llvm_unreachable("FixitStrategy not implemented yet!");
2044 case FixitStrategy::Kind::Wontfix
:
2045 llvm_unreachable("Invalid strategy!");
2048 return std::nullopt
;
2051 static inline std::optional
<FixItList
> createDataFixit(const ASTContext
&Ctx
,
2052 const DeclRefExpr
*DRE
) {
2053 const SourceManager
&SM
= Ctx
.getSourceManager();
2054 // Inserts the .data() after the DRE
2055 std::optional
<SourceLocation
> EndOfOperand
=
2056 getPastLoc(DRE
, SM
, Ctx
.getLangOpts());
2059 return FixItList
{{FixItHint::CreateInsertion(*EndOfOperand
, ".data()")}};
2061 return std::nullopt
;
2064 // Generates fix-its replacing an expression of the form UPC(DRE) with
2066 std::optional
<FixItList
>
2067 UPCStandalonePointerGadget::getFixits(const FixitStrategy
&S
) const {
2068 const auto VD
= cast
<VarDecl
>(Node
->getDecl());
2069 switch (S
.lookup(VD
)) {
2070 case FixitStrategy::Kind::Array
:
2071 case FixitStrategy::Kind::Span
: {
2072 return createDataFixit(VD
->getASTContext(), Node
);
2073 // FIXME: Points inside a macro expansion.
2076 case FixitStrategy::Kind::Wontfix
:
2077 case FixitStrategy::Kind::Iterator
:
2078 return std::nullopt
;
2079 case FixitStrategy::Kind::Vector
:
2080 llvm_unreachable("unsupported strategies for FixableGadgets");
2083 return std::nullopt
;
2086 // Generates fix-its replacing an expression of the form `&DRE[e]` with
2087 // `&DRE.data()[e]`:
2088 static std::optional
<FixItList
>
2089 fixUPCAddressofArraySubscriptWithSpan(const UnaryOperator
*Node
) {
2090 const auto *ArraySub
= cast
<ArraySubscriptExpr
>(Node
->getSubExpr());
2091 const auto *DRE
= cast
<DeclRefExpr
>(ArraySub
->getBase()->IgnoreImpCasts());
2092 // FIXME: this `getASTContext` call is costly, we should pass the
2094 const ASTContext
&Ctx
= DRE
->getDecl()->getASTContext();
2095 const Expr
*Idx
= ArraySub
->getIdx();
2096 const SourceManager
&SM
= Ctx
.getSourceManager();
2097 const LangOptions
&LangOpts
= Ctx
.getLangOpts();
2098 std::stringstream SS
;
2099 bool IdxIsLitZero
= false;
2101 if (auto ICE
= Idx
->getIntegerConstantExpr(Ctx
))
2102 if ((*ICE
).isZero())
2103 IdxIsLitZero
= true;
2104 std::optional
<StringRef
> DreString
= getExprText(DRE
, SM
, LangOpts
);
2106 return std::nullopt
;
2109 // If the index is literal zero, we produce the most concise fix-it:
2110 SS
<< (*DreString
).str() << ".data()";
2112 std::optional
<StringRef
> IndexString
= getExprText(Idx
, SM
, LangOpts
);
2114 return std::nullopt
;
2116 SS
<< "&" << (*DreString
).str() << ".data()"
2117 << "[" << (*IndexString
).str() << "]";
2120 FixItHint::CreateReplacement(Node
->getSourceRange(), SS
.str())};
2123 std::optional
<FixItList
>
2124 UUCAddAssignGadget::getFixits(const FixitStrategy
&S
) const {
2125 DeclUseList DREs
= getClaimedVarUseSites();
2127 if (DREs
.size() != 1)
2128 return std::nullopt
; // In cases of `Ptr += n` where `Ptr` is not a DRE, we
2130 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(DREs
.front()->getDecl())) {
2131 if (S
.lookup(VD
) == FixitStrategy::Kind::Span
) {
2134 const Stmt
*AddAssignNode
= Node
;
2135 StringRef varName
= VD
->getName();
2136 const ASTContext
&Ctx
= VD
->getASTContext();
2138 if (!isNonNegativeIntegerExpr(Offset
, VD
, Ctx
))
2139 return std::nullopt
;
2141 // To transform UUC(p += n) to UUC(p = p.subspan(..)):
2143 (Offset
->IgnoreParens()->getBeginLoc() == Offset
->getBeginLoc());
2144 std::string SS
= varName
.str() + " = " + varName
.str() + ".subspan";
2148 std::optional
<SourceLocation
> AddAssignLocation
= getEndCharLoc(
2149 AddAssignNode
, Ctx
.getSourceManager(), Ctx
.getLangOpts());
2150 if (!AddAssignLocation
)
2151 return std::nullopt
;
2153 Fixes
.push_back(FixItHint::CreateReplacement(
2154 SourceRange(AddAssignNode
->getBeginLoc(), Node
->getOperatorLoc()),
2157 Fixes
.push_back(FixItHint::CreateInsertion(
2158 Offset
->getEndLoc().getLocWithOffset(1), ")"));
2162 return std::nullopt
; // Not in the cases that we can handle for now, give up.
2165 std::optional
<FixItList
>
2166 UPCPreIncrementGadget::getFixits(const FixitStrategy
&S
) const {
2167 DeclUseList DREs
= getClaimedVarUseSites();
2169 if (DREs
.size() != 1)
2170 return std::nullopt
; // In cases of `++Ptr` where `Ptr` is not a DRE, we
2172 if (const VarDecl
*VD
= dyn_cast
<VarDecl
>(DREs
.front()->getDecl())) {
2173 if (S
.lookup(VD
) == FixitStrategy::Kind::Span
) {
2175 std::stringstream SS
;
2176 StringRef varName
= VD
->getName();
2177 const ASTContext
&Ctx
= VD
->getASTContext();
2179 // To transform UPC(++p) to UPC((p = p.subspan(1)).data()):
2180 SS
<< "(" << varName
.data() << " = " << varName
.data()
2181 << ".subspan(1)).data()";
2182 std::optional
<SourceLocation
> PreIncLocation
=
2183 getEndCharLoc(Node
, Ctx
.getSourceManager(), Ctx
.getLangOpts());
2184 if (!PreIncLocation
)
2185 return std::nullopt
;
2187 Fixes
.push_back(FixItHint::CreateReplacement(
2188 SourceRange(Node
->getBeginLoc(), *PreIncLocation
), SS
.str()));
2192 return std::nullopt
; // Not in the cases that we can handle for now, give up.
2195 // For a non-null initializer `Init` of `T *` type, this function returns
2196 // `FixItHint`s producing a list initializer `{Init, S}` as a part of a fix-it
2197 // to output stream.
2198 // In many cases, this function cannot figure out the actual extent `S`. It
2199 // then will use a place holder to replace `S` to ask users to fill `S` in. The
2200 // initializer shall be used to initialize a variable of type `std::span<T>`.
2201 // In some cases (e. g. constant size array) the initializer should remain
2202 // unchanged and the function returns empty list. In case the function can't
2203 // provide the right fixit it will return nullopt.
2205 // FIXME: Support multi-level pointers
2208 // `Init` a pointer to the initializer expression
2209 // `Ctx` a reference to the ASTContext
2210 static std::optional
<FixItList
>
2211 FixVarInitializerWithSpan(const Expr
*Init
, ASTContext
&Ctx
,
2212 const StringRef UserFillPlaceHolder
) {
2213 const SourceManager
&SM
= Ctx
.getSourceManager();
2214 const LangOptions
&LangOpts
= Ctx
.getLangOpts();
2216 // If `Init` has a constant value that is (or equivalent to) a
2217 // NULL pointer, we use the default constructor to initialize the span
2218 // object, i.e., a `std:span` variable declaration with no initializer.
2219 // So the fix-it is just to remove the initializer.
2220 if (Init
->isNullPointerConstant(
2222 // FIXME: Why does this function not ask for `const ASTContext
2223 // &`? It should. Maybe worth an NFC patch later.
2224 Expr::NullPointerConstantValueDependence::
2225 NPC_ValueDependentIsNotNull
)) {
2226 std::optional
<SourceLocation
> InitLocation
=
2227 getEndCharLoc(Init
, SM
, LangOpts
);
2229 return std::nullopt
;
2231 SourceRange
SR(Init
->getBeginLoc(), *InitLocation
);
2233 return FixItList
{FixItHint::CreateRemoval(SR
)};
2237 std::string ExtentText
= UserFillPlaceHolder
.data();
2238 StringRef One
= "1";
2240 // Insert `{` before `Init`:
2241 FixIts
.push_back(FixItHint::CreateInsertion(Init
->getBeginLoc(), "{"));
2242 // Try to get the data extent. Break into different cases:
2243 if (auto CxxNew
= dyn_cast
<CXXNewExpr
>(Init
->IgnoreImpCasts())) {
2244 // In cases `Init` is `new T[n]` and there is no explicit cast over
2245 // `Init`, we know that `Init` must evaluates to a pointer to `n` objects
2246 // of `T`. So the extent is `n` unless `n` has side effects. Similar but
2247 // simpler for the case where `Init` is `new T`.
2248 if (const Expr
*Ext
= CxxNew
->getArraySize().value_or(nullptr)) {
2249 if (!Ext
->HasSideEffects(Ctx
)) {
2250 std::optional
<StringRef
> ExtentString
= getExprText(Ext
, SM
, LangOpts
);
2252 return std::nullopt
;
2253 ExtentText
= *ExtentString
;
2255 } else if (!CxxNew
->isArray())
2256 // Although the initializer is not allocating a buffer, the pointer
2257 // variable could still be used in buffer access operations.
2259 } else if (Ctx
.getAsConstantArrayType(Init
->IgnoreImpCasts()->getType())) {
2260 // std::span has a single parameter constructor for initialization with
2261 // constant size array. The size is auto-deduced as the constructor is a
2262 // function template. The correct fixit is empty - no changes should happen.
2265 // In cases `Init` is of the form `&Var` after stripping of implicit
2266 // casts, where `&` is the built-in operator, the extent is 1.
2267 if (auto AddrOfExpr
= dyn_cast
<UnaryOperator
>(Init
->IgnoreImpCasts()))
2268 if (AddrOfExpr
->getOpcode() == UnaryOperatorKind::UO_AddrOf
&&
2269 isa_and_present
<DeclRefExpr
>(AddrOfExpr
->getSubExpr()))
2271 // TODO: we can handle more cases, e.g., `&a[0]`, `&a`, `std::addressof`,
2272 // and explicit casting, etc. etc.
2275 SmallString
<32> StrBuffer
{};
2276 std::optional
<SourceLocation
> LocPassInit
= getPastLoc(Init
, SM
, LangOpts
);
2279 return std::nullopt
;
2281 StrBuffer
.append(", ");
2282 StrBuffer
.append(ExtentText
);
2283 StrBuffer
.append("}");
2284 FixIts
.push_back(FixItHint::CreateInsertion(*LocPassInit
, StrBuffer
.str()));
2289 #define DEBUG_NOTE_DECL_FAIL(D, Msg) \
2290 Handler.addDebugNoteForVar((D), (D)->getBeginLoc(), \
2291 "failed to produce fixit for declaration '" + \
2292 (D)->getNameAsString() + "'" + (Msg))
2294 #define DEBUG_NOTE_DECL_FAIL(D, Msg)
2297 // For the given variable declaration with a pointer-to-T type, returns the text
2298 // `std::span<T>`. If it is unable to generate the text, returns
2300 static std::optional
<std::string
>
2301 createSpanTypeForVarDecl(const VarDecl
*VD
, const ASTContext
&Ctx
) {
2302 assert(VD
->getType()->isPointerType());
2304 std::optional
<Qualifiers
> PteTyQualifiers
= std::nullopt
;
2305 std::optional
<std::string
> PteTyText
= getPointeeTypeText(
2306 VD
, Ctx
.getSourceManager(), Ctx
.getLangOpts(), &PteTyQualifiers
);
2309 return std::nullopt
;
2311 std::string SpanTyText
= "std::span<";
2313 SpanTyText
.append(*PteTyText
);
2314 // Append qualifiers to span element type if any:
2315 if (PteTyQualifiers
) {
2316 SpanTyText
.append(" ");
2317 SpanTyText
.append(PteTyQualifiers
->getAsString());
2319 SpanTyText
.append(">");
2323 // For a `VarDecl` of the form `T * var (= Init)?`, this
2324 // function generates fix-its that
2325 // 1) replace `T * var` with `std::span<T> var`; and
2326 // 2) change `Init` accordingly to a span constructor, if it exists.
2328 // FIXME: support Multi-level pointers
2331 // `D` a pointer the variable declaration node
2332 // `Ctx` a reference to the ASTContext
2333 // `UserFillPlaceHolder` the user-input placeholder text
2335 // the non-empty fix-it list, if fix-its are successfuly generated; empty
2337 static FixItList
fixLocalVarDeclWithSpan(const VarDecl
*D
, ASTContext
&Ctx
,
2338 const StringRef UserFillPlaceHolder
,
2339 UnsafeBufferUsageHandler
&Handler
) {
2340 if (hasUnsupportedSpecifiers(D
, Ctx
.getSourceManager()))
2344 std::optional
<std::string
> SpanTyText
= createSpanTypeForVarDecl(D
, Ctx
);
2347 DEBUG_NOTE_DECL_FAIL(D
, " : failed to generate 'std::span' type");
2351 // Will hold the text for `std::span<T> Ident`:
2352 std::stringstream SS
;
2355 // Fix the initializer if it exists:
2356 if (const Expr
*Init
= D
->getInit()) {
2357 std::optional
<FixItList
> InitFixIts
=
2358 FixVarInitializerWithSpan(Init
, Ctx
, UserFillPlaceHolder
);
2361 FixIts
.insert(FixIts
.end(), std::make_move_iterator(InitFixIts
->begin()),
2362 std::make_move_iterator(InitFixIts
->end()));
2364 // For declaration of the form `T * ident = init;`, we want to replace
2365 // `T * ` with `std::span<T>`.
2366 // We ignore CV-qualifiers so for `T * const ident;` we also want to replace
2367 // just `T *` with `std::span<T>`.
2368 const SourceLocation EndLocForReplacement
= D
->getTypeSpecEndLoc();
2369 if (!EndLocForReplacement
.isValid()) {
2370 DEBUG_NOTE_DECL_FAIL(D
, " : failed to locate the end of the declaration");
2373 // The only exception is that for `T *ident` we'll add a single space between
2374 // "std::span<T>" and "ident".
2375 // FIXME: The condition is false for identifiers expended from macros.
2376 if (EndLocForReplacement
.getLocWithOffset(1) == getVarDeclIdentifierLoc(D
))
2379 FixIts
.push_back(FixItHint::CreateReplacement(
2380 SourceRange(D
->getBeginLoc(), EndLocForReplacement
), SS
.str()));
2384 static bool hasConflictingOverload(const FunctionDecl
*FD
) {
2385 return !FD
->getDeclContext()->lookup(FD
->getDeclName()).isSingleResult();
2388 // For a `FunctionDecl`, whose `ParmVarDecl`s are being changed to have new
2389 // types, this function produces fix-its to make the change self-contained. Let
2390 // 'F' be the entity defined by the original `FunctionDecl` and "NewF" be the
2391 // entity defined by the `FunctionDecl` after the change to the parameters.
2392 // Fix-its produced by this function are
2393 // 1. Add the `[[clang::unsafe_buffer_usage]]` attribute to each declaration
2395 // 2. Create a declaration of "NewF" next to each declaration of `F`;
2396 // 3. Create a definition of "F" (as its' original definition is now belongs
2397 // to "NewF") next to its original definition. The body of the creating
2398 // definition calls to "NewF".
2402 // void f(int *p); // original declaration
2403 // void f(int *p) { // original definition
2407 // To change the parameter `p` to be of `std::span<int>` type, we
2408 // also add overloads:
2410 // [[clang::unsafe_buffer_usage]] void f(int *p); // original decl
2411 // void f(std::span<int> p); // added overload decl
2412 // void f(std::span<int> p) { // original def where param is changed
2415 // [[clang::unsafe_buffer_usage]] void f(int *p) { // added def
2416 // return f(std::span(p, <# size #>));
2419 static std::optional
<FixItList
>
2420 createOverloadsForFixedParams(const FixitStrategy
&S
, const FunctionDecl
*FD
,
2421 const ASTContext
&Ctx
,
2422 UnsafeBufferUsageHandler
&Handler
) {
2423 // FIXME: need to make this conflict checking better:
2424 if (hasConflictingOverload(FD
))
2425 return std::nullopt
;
2427 const SourceManager
&SM
= Ctx
.getSourceManager();
2428 const LangOptions
&LangOpts
= Ctx
.getLangOpts();
2429 const unsigned NumParms
= FD
->getNumParams();
2430 std::vector
<std::string
> NewTysTexts(NumParms
);
2431 std::vector
<bool> ParmsMask(NumParms
, false);
2432 bool AtLeastOneParmToFix
= false;
2434 for (unsigned i
= 0; i
< NumParms
; i
++) {
2435 const ParmVarDecl
*PVD
= FD
->getParamDecl(i
);
2437 if (S
.lookup(PVD
) == FixitStrategy::Kind::Wontfix
)
2439 if (S
.lookup(PVD
) != FixitStrategy::Kind::Span
)
2440 // Not supported, not suppose to happen:
2441 return std::nullopt
;
2443 std::optional
<Qualifiers
> PteTyQuals
= std::nullopt
;
2444 std::optional
<std::string
> PteTyText
=
2445 getPointeeTypeText(PVD
, SM
, LangOpts
, &PteTyQuals
);
2448 // something wrong in obtaining the text of the pointee type, give up
2449 return std::nullopt
;
2450 // FIXME: whether we should create std::span type depends on the
2452 NewTysTexts
[i
] = getSpanTypeText(*PteTyText
, PteTyQuals
);
2453 ParmsMask
[i
] = true;
2454 AtLeastOneParmToFix
= true;
2456 if (!AtLeastOneParmToFix
)
2457 // No need to create function overloads:
2459 // FIXME Respect indentation of the original code.
2461 // A lambda that creates the text representation of a function declaration
2462 // with the new type signatures:
2463 const auto NewOverloadSignatureCreator
=
2464 [&SM
, &LangOpts
, &NewTysTexts
,
2465 &ParmsMask
](const FunctionDecl
*FD
) -> std::optional
<std::string
> {
2466 std::stringstream SS
;
2469 SS
<< getEndOfLine().str();
2470 // Append: ret-type func-name "("
2471 if (auto Prefix
= getRangeText(
2472 SourceRange(FD
->getBeginLoc(), (*FD
->param_begin())->getBeginLoc()),
2474 SS
<< Prefix
->str();
2476 return std::nullopt
; // give up
2477 // Append: parameter-type-list
2478 const unsigned NumParms
= FD
->getNumParams();
2480 for (unsigned i
= 0; i
< NumParms
; i
++) {
2481 const ParmVarDecl
*Parm
= FD
->getParamDecl(i
);
2483 if (Parm
->isImplicit())
2486 // This `i`-th parameter will be fixed with `NewTysTexts[i]` being its
2488 SS
<< NewTysTexts
[i
];
2489 // print parameter name if provided:
2490 if (IdentifierInfo
*II
= Parm
->getIdentifier())
2491 SS
<< ' ' << II
->getName().str();
2492 } else if (auto ParmTypeText
=
2493 getRangeText(getSourceRangeToTokenEnd(Parm
, SM
, LangOpts
),
2495 // print the whole `Parm` without modification:
2496 SS
<< ParmTypeText
->str();
2498 return std::nullopt
; // something wrong, give up
2499 if (i
!= NumParms
- 1)
2506 // A lambda that creates the text representation of a function definition with
2507 // the original signature:
2508 const auto OldOverloadDefCreator
=
2509 [&Handler
, &SM
, &LangOpts
, &NewTysTexts
,
2510 &ParmsMask
](const FunctionDecl
*FD
) -> std::optional
<std::string
> {
2511 std::stringstream SS
;
2513 SS
<< getEndOfLine().str();
2514 // Append: attr-name ret-type func-name "(" param-list ")" "{"
2515 if (auto FDPrefix
= getRangeText(
2516 SourceRange(FD
->getBeginLoc(), FD
->getBody()->getBeginLoc()), SM
,
2518 SS
<< Handler
.getUnsafeBufferUsageAttributeTextAt(FD
->getBeginLoc(), " ")
2519 << FDPrefix
->str() << "{";
2521 return std::nullopt
;
2522 // Append: "return" func-name "("
2523 if (auto FunQualName
= getFunNameText(FD
, SM
, LangOpts
))
2524 SS
<< "return " << FunQualName
->str() << "(";
2526 return std::nullopt
;
2529 const unsigned NumParms
= FD
->getNumParams();
2530 for (unsigned i
= 0; i
< NumParms
; i
++) {
2531 const ParmVarDecl
*Parm
= FD
->getParamDecl(i
);
2533 if (Parm
->isImplicit())
2535 // FIXME: If a parameter has no name, it is unused in the
2536 // definition. So we could just leave it as it is.
2537 if (!Parm
->getIdentifier())
2538 // If a parameter of a function definition has no name:
2539 return std::nullopt
;
2541 // This is our spanified paramter!
2542 SS
<< NewTysTexts
[i
] << "(" << Parm
->getIdentifier()->getName().str()
2543 << ", " << getUserFillPlaceHolder("size") << ")";
2545 SS
<< Parm
->getIdentifier()->getName().str();
2546 if (i
!= NumParms
- 1)
2549 // finish call and the body
2550 SS
<< ");}" << getEndOfLine().str();
2551 // FIXME: 80-char line formatting?
2556 for (FunctionDecl
*FReDecl
: FD
->redecls()) {
2557 std::optional
<SourceLocation
> Loc
= getPastLoc(FReDecl
, SM
, LangOpts
);
2561 if (FReDecl
->isThisDeclarationADefinition()) {
2562 assert(FReDecl
== FD
&& "inconsistent function definition");
2563 // Inserts a definition with the old signature to the end of
2565 if (auto OldOverloadDef
= OldOverloadDefCreator(FReDecl
))
2566 FixIts
.emplace_back(FixItHint::CreateInsertion(*Loc
, *OldOverloadDef
));
2568 return {}; // give up
2570 // Adds the unsafe-buffer attribute (if not already there) to `FReDecl`:
2571 if (!FReDecl
->hasAttr
<UnsafeBufferUsageAttr
>()) {
2572 FixIts
.emplace_back(FixItHint::CreateInsertion(
2573 FReDecl
->getBeginLoc(), Handler
.getUnsafeBufferUsageAttributeTextAt(
2574 FReDecl
->getBeginLoc(), " ")));
2576 // Inserts a declaration with the new signature to the end of `FReDecl`:
2577 if (auto NewOverloadDecl
= NewOverloadSignatureCreator(FReDecl
))
2578 FixIts
.emplace_back(FixItHint::CreateInsertion(*Loc
, *NewOverloadDecl
));
2586 // To fix a `ParmVarDecl` to be of `std::span` type.
2587 static FixItList
fixParamWithSpan(const ParmVarDecl
*PVD
, const ASTContext
&Ctx
,
2588 UnsafeBufferUsageHandler
&Handler
) {
2589 if (hasUnsupportedSpecifiers(PVD
, Ctx
.getSourceManager())) {
2590 DEBUG_NOTE_DECL_FAIL(PVD
, " : has unsupport specifier(s)");
2593 if (PVD
->hasDefaultArg()) {
2594 // FIXME: generate fix-its for default values:
2595 DEBUG_NOTE_DECL_FAIL(PVD
, " : has default arg");
2599 std::optional
<Qualifiers
> PteTyQualifiers
= std::nullopt
;
2600 std::optional
<std::string
> PteTyText
= getPointeeTypeText(
2601 PVD
, Ctx
.getSourceManager(), Ctx
.getLangOpts(), &PteTyQualifiers
);
2604 DEBUG_NOTE_DECL_FAIL(PVD
, " : invalid pointee type");
2608 std::optional
<StringRef
> PVDNameText
= PVD
->getIdentifier()->getName();
2611 DEBUG_NOTE_DECL_FAIL(PVD
, " : invalid identifier name");
2615 std::stringstream SS
;
2616 std::optional
<std::string
> SpanTyText
= createSpanTypeForVarDecl(PVD
, Ctx
);
2618 if (PteTyQualifiers
)
2619 // Append qualifiers if they exist:
2620 SS
<< getSpanTypeText(*PteTyText
, PteTyQualifiers
);
2622 SS
<< getSpanTypeText(*PteTyText
);
2623 // Append qualifiers to the type of the parameter:
2624 if (PVD
->getType().hasQualifiers())
2625 SS
<< ' ' << PVD
->getType().getQualifiers().getAsString();
2626 // Append parameter's name:
2627 SS
<< ' ' << PVDNameText
->str();
2628 // Add replacement fix-it:
2629 return {FixItHint::CreateReplacement(PVD
->getSourceRange(), SS
.str())};
2632 static FixItList
fixVariableWithSpan(const VarDecl
*VD
,
2633 const DeclUseTracker
&Tracker
,
2635 UnsafeBufferUsageHandler
&Handler
) {
2636 const DeclStmt
*DS
= Tracker
.lookupDecl(VD
);
2638 DEBUG_NOTE_DECL_FAIL(VD
,
2639 " : variables declared this way not implemented yet");
2642 if (!DS
->isSingleDecl()) {
2643 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
2644 DEBUG_NOTE_DECL_FAIL(VD
, " : multiple VarDecls");
2647 // Currently DS is an unused variable but we'll need it when
2648 // non-single decls are implemented, where the pointee type name
2649 // and the '*' are spread around the place.
2652 // FIXME: handle cases where DS has multiple declarations
2653 return fixLocalVarDeclWithSpan(VD
, Ctx
, getUserFillPlaceHolder(), Handler
);
2656 static FixItList
fixVarDeclWithArray(const VarDecl
*D
, const ASTContext
&Ctx
,
2657 UnsafeBufferUsageHandler
&Handler
) {
2660 // Note: the code below expects the declaration to not use any type sugar like
2662 if (auto CAT
= dyn_cast
<clang::ConstantArrayType
>(D
->getType())) {
2663 const QualType
&ArrayEltT
= CAT
->getElementType();
2664 assert(!ArrayEltT
.isNull() && "Trying to fix a non-array type variable!");
2665 // FIXME: support multi-dimensional arrays
2666 if (isa
<clang::ArrayType
>(ArrayEltT
.getCanonicalType()))
2669 const SourceLocation IdentifierLoc
= getVarDeclIdentifierLoc(D
);
2671 // Get the spelling of the element type as written in the source file
2672 // (including macros, etc.).
2673 auto MaybeElemTypeTxt
=
2674 getRangeText({D
->getBeginLoc(), IdentifierLoc
}, Ctx
.getSourceManager(),
2676 if (!MaybeElemTypeTxt
)
2678 const llvm::StringRef ElemTypeTxt
= MaybeElemTypeTxt
->trim();
2680 // Find the '[' token.
2681 std::optional
<Token
> NextTok
= Lexer::findNextToken(
2682 IdentifierLoc
, Ctx
.getSourceManager(), Ctx
.getLangOpts());
2683 while (NextTok
&& !NextTok
->is(tok::l_square
) &&
2684 NextTok
->getLocation() <= D
->getSourceRange().getEnd())
2685 NextTok
= Lexer::findNextToken(NextTok
->getLocation(),
2686 Ctx
.getSourceManager(), Ctx
.getLangOpts());
2689 const SourceLocation LSqBracketLoc
= NextTok
->getLocation();
2691 // Get the spelling of the array size as written in the source file
2692 // (including macros, etc.).
2693 auto MaybeArraySizeTxt
= getRangeText(
2694 {LSqBracketLoc
.getLocWithOffset(1), D
->getTypeSpecEndLoc()},
2695 Ctx
.getSourceManager(), Ctx
.getLangOpts());
2696 if (!MaybeArraySizeTxt
)
2698 const llvm::StringRef ArraySizeTxt
= MaybeArraySizeTxt
->trim();
2699 if (ArraySizeTxt
.empty()) {
2700 // FIXME: Support array size getting determined from the initializer.
2702 // int arr1[] = {0, 1, 2};
2703 // int arr2{3, 4, 5};
2704 // We might be able to preserve the non-specified size with `auto` and
2706 // auto arr1 = std::to_array<int>({0, 1, 2});
2710 std::optional
<StringRef
> IdentText
=
2711 getVarDeclIdentifierText(D
, Ctx
.getSourceManager(), Ctx
.getLangOpts());
2714 DEBUG_NOTE_DECL_FAIL(D
, " : failed to locate the identifier");
2718 SmallString
<32> Replacement
;
2719 raw_svector_ostream
OS(Replacement
);
2720 OS
<< "std::array<" << ElemTypeTxt
<< ", " << ArraySizeTxt
<< "> "
2721 << IdentText
->str();
2723 FixIts
.push_back(FixItHint::CreateReplacement(
2724 SourceRange
{D
->getBeginLoc(), D
->getTypeSpecEndLoc()}, OS
.str()));
2730 static FixItList
fixVariableWithArray(const VarDecl
*VD
,
2731 const DeclUseTracker
&Tracker
,
2732 const ASTContext
&Ctx
,
2733 UnsafeBufferUsageHandler
&Handler
) {
2734 const DeclStmt
*DS
= Tracker
.lookupDecl(VD
);
2735 assert(DS
&& "Fixing non-local variables not implemented yet!");
2736 if (!DS
->isSingleDecl()) {
2737 // FIXME: to support handling multiple `VarDecl`s in a single `DeclStmt`
2740 // Currently DS is an unused variable but we'll need it when
2741 // non-single decls are implemented, where the pointee type name
2742 // and the '*' are spread around the place.
2745 // FIXME: handle cases where DS has multiple declarations
2746 return fixVarDeclWithArray(VD
, Ctx
, Handler
);
2749 // TODO: we should be consistent to use `std::nullopt` to represent no-fix due
2750 // to any unexpected problem.
2752 fixVariable(const VarDecl
*VD
, FixitStrategy::Kind K
,
2753 /* The function decl under analysis */ const Decl
*D
,
2754 const DeclUseTracker
&Tracker
, ASTContext
&Ctx
,
2755 UnsafeBufferUsageHandler
&Handler
) {
2756 if (const auto *PVD
= dyn_cast
<ParmVarDecl
>(VD
)) {
2757 auto *FD
= dyn_cast
<clang::FunctionDecl
>(PVD
->getDeclContext());
2758 if (!FD
|| FD
!= D
) {
2759 // `FD != D` means that `PVD` belongs to a function that is not being
2760 // analyzed currently. Thus `FD` may not be complete.
2761 DEBUG_NOTE_DECL_FAIL(VD
, " : function not currently analyzed");
2765 // TODO If function has a try block we can't change params unless we check
2766 // also its catch block for their use.
2767 // FIXME We might support static class methods, some select methods,
2768 // operators and possibly lamdas.
2769 if (FD
->isMain() || FD
->isConstexpr() ||
2770 FD
->getTemplatedKind() != FunctionDecl::TemplatedKind::TK_NonTemplate
||
2772 // also covers call-operator of lamdas
2773 isa
<CXXMethodDecl
>(FD
) ||
2774 // skip when the function body is a try-block
2775 (FD
->hasBody() && isa
<CXXTryStmt
>(FD
->getBody())) ||
2776 FD
->isOverloadedOperator()) {
2777 DEBUG_NOTE_DECL_FAIL(VD
, " : unsupported function decl");
2778 return {}; // TODO test all these cases
2783 case FixitStrategy::Kind::Span
: {
2784 if (VD
->getType()->isPointerType()) {
2785 if (const auto *PVD
= dyn_cast
<ParmVarDecl
>(VD
))
2786 return fixParamWithSpan(PVD
, Ctx
, Handler
);
2788 if (VD
->isLocalVarDecl())
2789 return fixVariableWithSpan(VD
, Tracker
, Ctx
, Handler
);
2791 DEBUG_NOTE_DECL_FAIL(VD
, " : not a pointer");
2794 case FixitStrategy::Kind::Array
: {
2795 if (VD
->isLocalVarDecl() &&
2796 isa
<clang::ConstantArrayType
>(VD
->getType().getCanonicalType()))
2797 return fixVariableWithArray(VD
, Tracker
, Ctx
, Handler
);
2799 DEBUG_NOTE_DECL_FAIL(VD
, " : not a local const-size array");
2802 case FixitStrategy::Kind::Iterator
:
2803 case FixitStrategy::Kind::Vector
:
2804 llvm_unreachable("FixitStrategy not implemented yet!");
2805 case FixitStrategy::Kind::Wontfix
:
2806 llvm_unreachable("Invalid strategy!");
2808 llvm_unreachable("Unknown strategy!");
2811 // Returns true iff there exists a `FixItHint` 'h' in `FixIts` such that the
2812 // `RemoveRange` of 'h' overlaps with a macro use.
2813 static bool overlapWithMacro(const FixItList
&FixIts
) {
2814 // FIXME: For now we only check if the range (or the first token) is (part of)
2815 // a macro expansion. Ideally, we want to check for all tokens in the range.
2816 return llvm::any_of(FixIts
, [](const FixItHint
&Hint
) {
2817 auto Range
= Hint
.RemoveRange
;
2818 if (Range
.getBegin().isMacroID() || Range
.getEnd().isMacroID())
2819 // If the range (or the first token) is (part of) a macro expansion:
2825 // Returns true iff `VD` is a parameter of the declaration `D`:
2826 static bool isParameterOf(const VarDecl
*VD
, const Decl
*D
) {
2827 return isa
<ParmVarDecl
>(VD
) &&
2828 VD
->getDeclContext() == dyn_cast
<DeclContext
>(D
);
2831 // Erases variables in `FixItsForVariable`, if such a variable has an unfixable
2832 // group mate. A variable `v` is unfixable iff `FixItsForVariable` does not
2834 static void eraseVarsForUnfixableGroupMates(
2835 std::map
<const VarDecl
*, FixItList
> &FixItsForVariable
,
2836 const VariableGroupsManager
&VarGrpMgr
) {
2837 // Variables will be removed from `FixItsForVariable`:
2838 SmallVector
<const VarDecl
*, 8> ToErase
;
2840 for (const auto &[VD
, Ignore
] : FixItsForVariable
) {
2841 VarGrpRef Grp
= VarGrpMgr
.getGroupOfVar(VD
);
2842 if (llvm::any_of(Grp
,
2843 [&FixItsForVariable
](const VarDecl
*GrpMember
) -> bool {
2844 return !FixItsForVariable
.count(GrpMember
);
2846 // At least one group member cannot be fixed, so we have to erase the
2848 for (const VarDecl
*Member
: Grp
)
2849 ToErase
.push_back(Member
);
2852 for (auto *VarToErase
: ToErase
)
2853 FixItsForVariable
.erase(VarToErase
);
2856 // Returns the fix-its that create bounds-safe function overloads for the
2857 // function `D`, if `D`'s parameters will be changed to safe-types through
2858 // fix-its in `FixItsForVariable`.
2860 // NOTE: In case `D`'s parameters will be changed but bounds-safe function
2861 // overloads cannot created, the whole group that contains the parameters will
2862 // be erased from `FixItsForVariable`.
2863 static FixItList
createFunctionOverloadsForParms(
2864 std::map
<const VarDecl
*, FixItList
> &FixItsForVariable
/* mutable */,
2865 const VariableGroupsManager
&VarGrpMgr
, const FunctionDecl
*FD
,
2866 const FixitStrategy
&S
, ASTContext
&Ctx
,
2867 UnsafeBufferUsageHandler
&Handler
) {
2868 FixItList FixItsSharedByParms
{};
2870 std::optional
<FixItList
> OverloadFixes
=
2871 createOverloadsForFixedParams(S
, FD
, Ctx
, Handler
);
2873 if (OverloadFixes
) {
2874 FixItsSharedByParms
.append(*OverloadFixes
);
2876 // Something wrong in generating `OverloadFixes`, need to remove the
2877 // whole group, where parameters are in, from `FixItsForVariable` (Note
2878 // that all parameters should be in the same group):
2879 for (auto *Member
: VarGrpMgr
.getGroupOfParms())
2880 FixItsForVariable
.erase(Member
);
2882 return FixItsSharedByParms
;
2885 // Constructs self-contained fix-its for each variable in `FixablesForAllVars`.
2886 static std::map
<const VarDecl
*, FixItList
>
2887 getFixIts(FixableGadgetSets
&FixablesForAllVars
, const FixitStrategy
&S
,
2889 /* The function decl under analysis */ const Decl
*D
,
2890 const DeclUseTracker
&Tracker
, UnsafeBufferUsageHandler
&Handler
,
2891 const VariableGroupsManager
&VarGrpMgr
) {
2892 // `FixItsForVariable` will map each variable to a set of fix-its directly
2893 // associated to the variable itself. Fix-its of distinct variables in
2894 // `FixItsForVariable` are disjoint.
2895 std::map
<const VarDecl
*, FixItList
> FixItsForVariable
;
2897 // Populate `FixItsForVariable` with fix-its directly associated with each
2898 // variable. Fix-its directly associated to a variable 'v' are the ones
2899 // produced by the `FixableGadget`s whose claimed variable is 'v'.
2900 for (const auto &[VD
, Fixables
] : FixablesForAllVars
.byVar
) {
2901 FixItsForVariable
[VD
] =
2902 fixVariable(VD
, S
.lookup(VD
), D
, Tracker
, Ctx
, Handler
);
2903 // If we fail to produce Fix-It for the declaration we have to skip the
2904 // variable entirely.
2905 if (FixItsForVariable
[VD
].empty()) {
2906 FixItsForVariable
.erase(VD
);
2909 for (const auto &F
: Fixables
) {
2910 std::optional
<FixItList
> Fixits
= F
->getFixits(S
);
2913 FixItsForVariable
[VD
].insert(FixItsForVariable
[VD
].end(),
2914 Fixits
->begin(), Fixits
->end());
2918 Handler
.addDebugNoteForVar(
2919 VD
, F
->getSourceLoc(),
2920 ("gadget '" + F
->getDebugName() + "' refused to produce a fix")
2923 FixItsForVariable
.erase(VD
);
2928 // `FixItsForVariable` now contains only variables that can be
2929 // fixed. A variable can be fixed if its' declaration and all Fixables
2930 // associated to it can all be fixed.
2932 // To further remove from `FixItsForVariable` variables whose group mates
2933 // cannot be fixed...
2934 eraseVarsForUnfixableGroupMates(FixItsForVariable
, VarGrpMgr
);
2935 // Now `FixItsForVariable` gets further reduced: a variable is in
2936 // `FixItsForVariable` iff it can be fixed and all its group mates can be
2939 // Fix-its of bounds-safe overloads of `D` are shared by parameters of `D`.
2940 // That is, when fixing multiple parameters in one step, these fix-its will
2941 // be applied only once (instead of being applied per parameter).
2942 FixItList FixItsSharedByParms
{};
2944 if (auto *FD
= dyn_cast
<FunctionDecl
>(D
))
2945 FixItsSharedByParms
= createFunctionOverloadsForParms(
2946 FixItsForVariable
, VarGrpMgr
, FD
, S
, Ctx
, Handler
);
2948 // The map that maps each variable `v` to fix-its for the whole group where
2950 std::map
<const VarDecl
*, FixItList
> FinalFixItsForVariable
{
2953 for (auto &[Var
, Ignore
] : FixItsForVariable
) {
2954 bool AnyParm
= false;
2955 const auto VarGroupForVD
= VarGrpMgr
.getGroupOfVar(Var
, &AnyParm
);
2957 for (const VarDecl
*GrpMate
: VarGroupForVD
) {
2960 if (FixItsForVariable
.count(GrpMate
))
2961 FinalFixItsForVariable
[Var
].append(FixItsForVariable
[GrpMate
]);
2964 // This assertion should never fail. Otherwise we have a bug.
2965 assert(!FixItsSharedByParms
.empty() &&
2966 "Should not try to fix a parameter that does not belong to a "
2968 FinalFixItsForVariable
[Var
].append(FixItsSharedByParms
);
2971 // Fix-its that will be applied in one step shall NOT:
2972 // 1. overlap with macros or/and templates; or
2973 // 2. conflict with each other.
2974 // Otherwise, the fix-its will be dropped.
2975 for (auto Iter
= FinalFixItsForVariable
.begin();
2976 Iter
!= FinalFixItsForVariable
.end();)
2977 if (overlapWithMacro(Iter
->second
) ||
2978 clang::internal::anyConflict(Iter
->second
, Ctx
.getSourceManager())) {
2979 Iter
= FinalFixItsForVariable
.erase(Iter
);
2982 return FinalFixItsForVariable
;
2985 template <typename VarDeclIterTy
>
2986 static FixitStrategy
2987 getNaiveStrategy(llvm::iterator_range
<VarDeclIterTy
> UnsafeVars
) {
2989 for (const VarDecl
*VD
: UnsafeVars
) {
2990 if (isa
<ConstantArrayType
>(VD
->getType().getCanonicalType()))
2991 S
.set(VD
, FixitStrategy::Kind::Array
);
2993 S
.set(VD
, FixitStrategy::Kind::Span
);
2998 // Manages variable groups:
2999 class VariableGroupsManagerImpl
: public VariableGroupsManager
{
3000 const std::vector
<VarGrpTy
> Groups
;
3001 const std::map
<const VarDecl
*, unsigned> &VarGrpMap
;
3002 const llvm::SetVector
<const VarDecl
*> &GrpsUnionForParms
;
3005 VariableGroupsManagerImpl(
3006 const std::vector
<VarGrpTy
> &Groups
,
3007 const std::map
<const VarDecl
*, unsigned> &VarGrpMap
,
3008 const llvm::SetVector
<const VarDecl
*> &GrpsUnionForParms
)
3009 : Groups(Groups
), VarGrpMap(VarGrpMap
),
3010 GrpsUnionForParms(GrpsUnionForParms
) {}
3012 VarGrpRef
getGroupOfVar(const VarDecl
*Var
, bool *HasParm
) const override
{
3013 if (GrpsUnionForParms
.contains(Var
)) {
3016 return GrpsUnionForParms
.getArrayRef();
3021 auto It
= VarGrpMap
.find(Var
);
3023 if (It
== VarGrpMap
.end())
3024 return std::nullopt
;
3025 return Groups
[It
->second
];
3028 VarGrpRef
getGroupOfParms() const override
{
3029 return GrpsUnionForParms
.getArrayRef();
3033 void clang::checkUnsafeBufferUsage(const Decl
*D
,
3034 UnsafeBufferUsageHandler
&Handler
,
3035 bool EmitSuggestions
) {
3037 Handler
.clearDebugNotes();
3040 assert(D
&& D
->getBody());
3041 // We do not want to visit a Lambda expression defined inside a method
3042 // independently. Instead, it should be visited along with the outer method.
3043 // FIXME: do we want to do the same thing for `BlockDecl`s?
3044 if (const auto *fd
= dyn_cast
<CXXMethodDecl
>(D
)) {
3045 if (fd
->getParent()->isLambda() && fd
->getParent()->isLocalClass())
3049 // Do not emit fixit suggestions for functions declared in an
3050 // extern "C" block.
3051 if (const auto *FD
= dyn_cast
<FunctionDecl
>(D
)) {
3052 for (FunctionDecl
*FReDecl
: FD
->redecls()) {
3053 if (FReDecl
->isExternC()) {
3054 EmitSuggestions
= false;
3060 WarningGadgetSets UnsafeOps
;
3061 FixableGadgetSets FixablesForAllVars
;
3063 auto [FixableGadgets
, WarningGadgets
, Tracker
] =
3064 findGadgets(D
, Handler
, EmitSuggestions
);
3066 if (!EmitSuggestions
) {
3067 // Our job is very easy without suggestions. Just warn about
3068 // every problematic operation and consider it done. No need to deal
3069 // with fixable gadgets, no need to group operations by variable.
3070 for (const auto &G
: WarningGadgets
) {
3071 G
->handleUnsafeOperation(Handler
, /*IsRelatedToDecl=*/false,
3072 D
->getASTContext());
3075 // This return guarantees that most of the machine doesn't run when
3076 // suggestions aren't requested.
3077 assert(FixableGadgets
.size() == 0 &&
3078 "Fixable gadgets found but suggestions not requested!");
3082 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
3083 // function under the analysis. No need to fix any Fixables.
3084 if (!WarningGadgets
.empty()) {
3085 // Gadgets "claim" variables they're responsible for. Once this loop
3086 // finishes, the tracker will only track DREs that weren't claimed by any
3087 // gadgets, i.e. not understood by the analysis.
3088 for (const auto &G
: FixableGadgets
) {
3089 for (const auto *DRE
: G
->getClaimedVarUseSites()) {
3090 Tracker
.claimUse(DRE
);
3095 // If no `WarningGadget`s ever matched, there is no unsafe operations in the
3096 // function under the analysis. Thus, it early returns here as there is
3097 // nothing needs to be fixed.
3099 // Note this claim is based on the assumption that there is no unsafe
3100 // variable whose declaration is invisible from the analyzing function.
3101 // Otherwise, we need to consider if the uses of those unsafe varuables needs
3103 // So far, we are not fixing any global variables or class members. And,
3104 // lambdas will be analyzed along with the enclosing function. So this early
3105 // return is correct for now.
3106 if (WarningGadgets
.empty())
3109 UnsafeOps
= groupWarningGadgetsByVar(std::move(WarningGadgets
));
3110 FixablesForAllVars
= groupFixablesByVar(std::move(FixableGadgets
));
3112 std::map
<const VarDecl
*, FixItList
> FixItsForVariableGroup
;
3114 // Filter out non-local vars and vars with unclaimed DeclRefExpr-s.
3115 for (auto it
= FixablesForAllVars
.byVar
.cbegin();
3116 it
!= FixablesForAllVars
.byVar
.cend();) {
3117 // FIXME: need to deal with global variables later
3118 if ((!it
->first
->isLocalVarDecl() && !isa
<ParmVarDecl
>(it
->first
))) {
3120 Handler
.addDebugNoteForVar(it
->first
, it
->first
->getBeginLoc(),
3121 ("failed to produce fixit for '" +
3122 it
->first
->getNameAsString() +
3123 "' : neither local nor a parameter"));
3125 it
= FixablesForAllVars
.byVar
.erase(it
);
3126 } else if (it
->first
->getType().getCanonicalType()->isReferenceType()) {
3128 Handler
.addDebugNoteForVar(it
->first
, it
->first
->getBeginLoc(),
3129 ("failed to produce fixit for '" +
3130 it
->first
->getNameAsString() +
3131 "' : has a reference type"));
3133 it
= FixablesForAllVars
.byVar
.erase(it
);
3134 } else if (Tracker
.hasUnclaimedUses(it
->first
)) {
3135 it
= FixablesForAllVars
.byVar
.erase(it
);
3136 } else if (it
->first
->isInitCapture()) {
3138 Handler
.addDebugNoteForVar(it
->first
, it
->first
->getBeginLoc(),
3139 ("failed to produce fixit for '" +
3140 it
->first
->getNameAsString() +
3141 "' : init capture"));
3143 it
= FixablesForAllVars
.byVar
.erase(it
);
3150 for (const auto &it
: UnsafeOps
.byVar
) {
3151 const VarDecl
*const UnsafeVD
= it
.first
;
3152 auto UnclaimedDREs
= Tracker
.getUnclaimedUses(UnsafeVD
);
3153 if (UnclaimedDREs
.empty())
3155 const auto UnfixedVDName
= UnsafeVD
->getNameAsString();
3156 for (const clang::DeclRefExpr
*UnclaimedDRE
: UnclaimedDREs
) {
3157 std::string UnclaimedUseTrace
=
3158 getDREAncestorString(UnclaimedDRE
, D
->getASTContext());
3160 Handler
.addDebugNoteForVar(
3161 UnsafeVD
, UnclaimedDRE
->getBeginLoc(),
3162 ("failed to produce fixit for '" + UnfixedVDName
+
3163 "' : has an unclaimed use\nThe unclaimed DRE trace: " +
3164 UnclaimedUseTrace
));
3169 // Fixpoint iteration for pointer assignments
3170 using DepMapTy
= DenseMap
<const VarDecl
*, llvm::SetVector
<const VarDecl
*>>;
3171 DepMapTy DependenciesMap
{};
3172 DepMapTy PtrAssignmentGraph
{};
3174 for (auto it
: FixablesForAllVars
.byVar
) {
3175 for (const FixableGadget
*fixable
: it
.second
) {
3176 std::optional
<std::pair
<const VarDecl
*, const VarDecl
*>> ImplPair
=
3177 fixable
->getStrategyImplications();
3179 std::pair
<const VarDecl
*, const VarDecl
*> Impl
= std::move(*ImplPair
);
3180 PtrAssignmentGraph
[Impl
.first
].insert(Impl
.second
);
3186 The following code does a BFS traversal of the `PtrAssignmentGraph`
3187 considering all unsafe vars as starting nodes and constructs an undirected
3188 graph `DependenciesMap`. Constructing the `DependenciesMap` in this manner
3189 elimiates all variables that are unreachable from any unsafe var. In other
3190 words, this removes all dependencies that don't include any unsafe variable
3191 and consequently don't need any fixit generation.
3192 Note: A careful reader would observe that the code traverses
3193 `PtrAssignmentGraph` using `CurrentVar` but adds edges between `Var` and
3194 `Adj` and not between `CurrentVar` and `Adj`. Both approaches would
3195 achieve the same result but the one used here dramatically cuts the
3196 amount of hoops the second part of the algorithm needs to jump, given that
3197 a lot of these connections become "direct". The reader is advised not to
3198 imagine how the graph is transformed because of using `Var` instead of
3199 `CurrentVar`. The reader can continue reading as if `CurrentVar` was used,
3200 and think about why it's equivalent later.
3202 std::set
<const VarDecl
*> VisitedVarsDirected
{};
3203 for (const auto &[Var
, ignore
] : UnsafeOps
.byVar
) {
3204 if (VisitedVarsDirected
.find(Var
) == VisitedVarsDirected
.end()) {
3206 std::queue
<const VarDecl
*> QueueDirected
{};
3207 QueueDirected
.push(Var
);
3208 while (!QueueDirected
.empty()) {
3209 const VarDecl
*CurrentVar
= QueueDirected
.front();
3210 QueueDirected
.pop();
3211 VisitedVarsDirected
.insert(CurrentVar
);
3212 auto AdjacentNodes
= PtrAssignmentGraph
[CurrentVar
];
3213 for (const VarDecl
*Adj
: AdjacentNodes
) {
3214 if (VisitedVarsDirected
.find(Adj
) == VisitedVarsDirected
.end()) {
3215 QueueDirected
.push(Adj
);
3217 DependenciesMap
[Var
].insert(Adj
);
3218 DependenciesMap
[Adj
].insert(Var
);
3224 // `Groups` stores the set of Connected Components in the graph.
3225 std::vector
<VarGrpTy
> Groups
;
3226 // `VarGrpMap` maps variables that need fix to the groups (indexes) that the
3227 // variables belong to. Group indexes refer to the elements in `Groups`.
3228 // `VarGrpMap` is complete in that every variable that needs fix is in it.
3229 std::map
<const VarDecl
*, unsigned> VarGrpMap
;
3230 // The union group over the ones in "Groups" that contain parameters of `D`:
3231 llvm::SetVector
<const VarDecl
*>
3232 GrpsUnionForParms
; // these variables need to be fixed in one step
3234 // Group Connected Components for Unsafe Vars
3235 // (Dependencies based on pointer assignments)
3236 std::set
<const VarDecl
*> VisitedVars
{};
3237 for (const auto &[Var
, ignore
] : UnsafeOps
.byVar
) {
3238 if (VisitedVars
.find(Var
) == VisitedVars
.end()) {
3239 VarGrpTy
&VarGroup
= Groups
.emplace_back();
3240 std::queue
<const VarDecl
*> Queue
{};
3243 while (!Queue
.empty()) {
3244 const VarDecl
*CurrentVar
= Queue
.front();
3246 VisitedVars
.insert(CurrentVar
);
3247 VarGroup
.push_back(CurrentVar
);
3248 auto AdjacentNodes
= DependenciesMap
[CurrentVar
];
3249 for (const VarDecl
*Adj
: AdjacentNodes
) {
3250 if (VisitedVars
.find(Adj
) == VisitedVars
.end()) {
3256 bool HasParm
= false;
3257 unsigned GrpIdx
= Groups
.size() - 1;
3259 for (const VarDecl
*V
: VarGroup
) {
3260 VarGrpMap
[V
] = GrpIdx
;
3261 if (!HasParm
&& isParameterOf(V
, D
))
3265 GrpsUnionForParms
.insert(VarGroup
.begin(), VarGroup
.end());
3269 // Remove a `FixableGadget` if the associated variable is not in the graph
3270 // computed above. We do not want to generate fix-its for such variables,
3271 // since they are neither warned nor reachable from a warned one.
3273 // Note a variable is not warned if it is not directly used in any unsafe
3274 // operation. A variable `v` is NOT reachable from an unsafe variable, if it
3275 // does not exist another variable `u` such that `u` is warned and fixing `u`
3276 // (transitively) implicates fixing `v`.
3280 // void f(int * p) {
3281 // int * a = p; *p = 0;
3284 // `*p = 0` is a fixable gadget associated with a variable `p` that is neither
3285 // warned nor reachable from a warned one. If we add `a[5] = 0` to the end of
3286 // the function above, `p` becomes reachable from a warned variable.
3287 for (auto I
= FixablesForAllVars
.byVar
.begin();
3288 I
!= FixablesForAllVars
.byVar
.end();) {
3289 // Note `VisitedVars` contain all the variables in the graph:
3290 if (!VisitedVars
.count((*I
).first
)) {
3291 // no such var in graph:
3292 I
= FixablesForAllVars
.byVar
.erase(I
);
3297 // We assign strategies to variables that are 1) in the graph and 2) can be
3298 // fixed. Other variables have the default "Won't fix" strategy.
3299 FixitStrategy NaiveStrategy
= getNaiveStrategy(llvm::make_filter_range(
3300 VisitedVars
, [&FixablesForAllVars
](const VarDecl
*V
) {
3301 // If a warned variable has no "Fixable", it is considered unfixable:
3302 return FixablesForAllVars
.byVar
.count(V
);
3304 VariableGroupsManagerImpl
VarGrpMgr(Groups
, VarGrpMap
, GrpsUnionForParms
);
3306 if (isa
<NamedDecl
>(D
))
3307 // The only case where `D` is not a `NamedDecl` is when `D` is a
3308 // `BlockDecl`. Let's not fix variables in blocks for now
3309 FixItsForVariableGroup
=
3310 getFixIts(FixablesForAllVars
, NaiveStrategy
, D
->getASTContext(), D
,
3311 Tracker
, Handler
, VarGrpMgr
);
3313 for (const auto &G
: UnsafeOps
.noVar
) {
3314 G
->handleUnsafeOperation(Handler
, /*IsRelatedToDecl=*/false,
3315 D
->getASTContext());
3318 for (const auto &[VD
, WarningGadgets
] : UnsafeOps
.byVar
) {
3319 auto FixItsIt
= FixItsForVariableGroup
.find(VD
);
3320 Handler
.handleUnsafeVariableGroup(VD
, VarGrpMgr
,
3321 FixItsIt
!= FixItsForVariableGroup
.end()
3322 ? std::move(FixItsIt
->second
)
3325 for (const auto &G
: WarningGadgets
) {
3326 G
->handleUnsafeOperation(Handler
, /*IsRelatedToDecl=*/true,
3327 D
->getASTContext());