88
99#include " UseSpanFirstLastCheck.h"
1010#include " clang/AST/ASTContext.h"
11+ #include " clang/AST/RecursiveASTVisitor.h"
1112#include " clang/ASTMatchers/ASTMatchFinder.h"
1213#include " clang/ASTMatchers/ASTMatchers.h"
1314#include " clang/Lex/Lexer.h"
@@ -47,53 +48,71 @@ void UseSpanFirstLastCheck::handleSubspanCall(
4748 const Expr *Count = NumArgs > 1 ? Call->getArg (1 ) : nullptr ;
4849 auto &Context = *Result.Context ;
4950
50- // Check if this is subspan(0, n) -> first(n)
51- bool IsZeroOffset = false ;
52- const Expr *OffsetE = Offset->IgnoreImpCasts ();
53- if (const auto *IL = dyn_cast<IntegerLiteral>(OffsetE))
54- IsZeroOffset = IL->getValue () == 0 ;
55-
56- // Check if this is subspan(size() - n) -> last(n)
57- bool IsSizeMinusN = false ;
58- const Expr *SizeMinusArg = nullptr ;
59-
60- const auto *BO = dyn_cast<BinaryOperator>(OffsetE);
61- if (BO && BO->getOpcode () == BO_Sub) {
62- const auto *SizeCall = dyn_cast<CXXMemberCallExpr>(BO->getLHS ());
63- if (SizeCall && SizeCall->getMethodDecl ()->getName () == " size" ) {
64- IsSizeMinusN = true ;
65- SizeMinusArg = BO->getRHS ();
51+ class SubspanVisitor : public RecursiveASTVisitor <SubspanVisitor> {
52+ public:
53+ SubspanVisitor (const ASTContext &Context) : Context(Context) {}
54+
55+ TraversalKind getTraversalKind () const {
56+ return TK_IgnoreUnlessSpelledInSource;
6657 }
67- }
58+
59+ bool VisitIntegerLiteral (IntegerLiteral *IL) {
60+ if (IL->getValue () == 0 )
61+ IsZeroOffset = true ;
62+ return true ;
63+ }
64+
65+ bool VisitBinaryOperator (BinaryOperator *BO) {
66+ if (BO->getOpcode () == BO_Sub) {
67+ if (const auto *SizeCall = dyn_cast<CXXMemberCallExpr>(BO->getLHS ())) {
68+ if (SizeCall->getMethodDecl ()->getName () == " size" ) {
69+ IsSizeMinusN = true ;
70+ SizeMinusArg = BO->getRHS ();
71+ }
72+ }
73+ }
74+ return true ;
75+ }
76+
77+ bool IsZeroOffset = false ;
78+ bool IsSizeMinusN = false ;
79+ const Expr *SizeMinusArg = nullptr ;
80+
81+ private:
82+ const ASTContext &Context;
83+ };
84+
85+ SubspanVisitor Visitor (Context);
86+ Visitor.TraverseStmt (const_cast <Expr *>(Offset->IgnoreImpCasts ()));
6887
6988 // Build replacement text
7089 std::string Replacement;
71- if (IsZeroOffset && Count) {
90+ if (Visitor. IsZeroOffset && Count) {
7291 // subspan(0, count) -> first(count)
73- const auto CountStr = Lexer::getSourceText (
92+ auto CountStr = Lexer::getSourceText (
7493 CharSourceRange::getTokenRange (Count->getSourceRange ()),
7594 Context.getSourceManager (), Context.getLangOpts ());
7695 const auto *Base =
7796 cast<CXXMemberCallExpr>(Call)->getImplicitObjectArgument ();
78- const StringRef BaseStr = Lexer::getSourceText (
97+ auto BaseStr = Lexer::getSourceText (
7998 CharSourceRange::getTokenRange (Base->getSourceRange ()),
8099 Context.getSourceManager (), Context.getLangOpts ());
81100 Replacement = BaseStr.str () + " .first(" + CountStr.str () + " )" ;
82- } else if (IsSizeMinusN && SizeMinusArg) {
101+ } else if (Visitor. IsSizeMinusN && Visitor. SizeMinusArg ) {
83102 // subspan(size() - n) -> last(n)
84- const StringRef ArgStr = Lexer::getSourceText (
85- CharSourceRange::getTokenRange (SizeMinusArg->getSourceRange ()),
103+ auto ArgStr = Lexer::getSourceText (
104+ CharSourceRange::getTokenRange (Visitor. SizeMinusArg ->getSourceRange ()),
86105 Context.getSourceManager (), Context.getLangOpts ());
87106 const auto *Base =
88107 cast<CXXMemberCallExpr>(Call)->getImplicitObjectArgument ();
89- const StringRef BaseStr = Lexer::getSourceText (
108+ auto BaseStr = Lexer::getSourceText (
90109 CharSourceRange::getTokenRange (Base->getSourceRange ()),
91110 Context.getSourceManager (), Context.getLangOpts ());
92111 Replacement = BaseStr.str () + " .last(" + ArgStr.str () + " )" ;
93112 }
94113
95114 if (!Replacement.empty ()) {
96- if (IsZeroOffset && Count) {
115+ if (Visitor. IsZeroOffset && Count) {
97116 diag (Call->getBeginLoc (), " prefer 'span::first()' over 'subspan()'" )
98117 << FixItHint::CreateReplacement (Call->getSourceRange (), Replacement);
99118 } else {
@@ -102,4 +121,5 @@ void UseSpanFirstLastCheck::handleSubspanCall(
102121 }
103122 }
104123}
124+
105125} // namespace clang::tidy::readability
0 commit comments