Skip to content

Commit 72610ac

Browse files
committed
feedback
1 parent cd2fce3 commit 72610ac

File tree

2 files changed

+72
-25
lines changed

2 files changed

+72
-25
lines changed

clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp

Lines changed: 45 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
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

clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,3 +69,30 @@ void test() {
6969
// CHECK-FIXES: auto sub8 = s.last(2);
7070

7171
}
72+
73+
template <typename T>
74+
void testTemplate() {
75+
T arr[] = {1, 2, 3, 4, 5};
76+
std::span<T> s(arr, 5);
77+
78+
auto sub1 = s.subspan(0, 3);
79+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::first()' over 'subspan()'
80+
// CHECK-FIXES: auto sub1 = s.first(3);
81+
82+
auto sub2 = s.subspan(s.size() - 2);
83+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over 'subspan()'
84+
// CHECK-FIXES: auto sub2 = s.last(2);
85+
86+
__SIZE_TYPE__ n = 2;
87+
auto sub3 = s.subspan(0, n);
88+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::first()' over 'subspan()'
89+
// CHECK-FIXES: auto sub3 = s.first(n);
90+
91+
auto sub4 = s.subspan(1, 2); // No warning
92+
auto sub5 = s.subspan(2); // No warning
93+
}
94+
95+
// Test instantiation
96+
void testInt() {
97+
testTemplate<int>();
98+
}

0 commit comments

Comments
 (0)