[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
Helmut Januschka via cfe-commits
cfe-commits at lists.llvm.org
Fri Nov 29 01:59:33 PST 2024
https://github.com/hjanuschka updated https://github.com/llvm/llvm-project/pull/118074
>From cb748c34d35b8c0c9ca93a67b111dcf5d7665b34 Mon Sep 17 00:00:00 2001
From: Helmut Januschka <helmut at januschka.com>
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/10] [clang-tidy] Add modernize-use-span-first-last check
Add new check that modernizes std::span::subspan() calls to use the more
expressive first() and last() member functions where applicable.
---
.../clang-tidy/modernize/CMakeLists.txt | 1 +
.../modernize/ModernizeTidyModule.cpp | 3 +
.../modernize/UseSpanFirstLastCheck.cpp | 97 +++++++++++++++++++
.../modernize/UseSpanFirstLastCheck.h | 40 ++++++++
clang-tools-extra/docs/ReleaseNotes.rst | 4 +
.../checks/modernize/use-span-first-last.rst | 19 ++++
.../modernize-subspan-conversion.cpp | 50 ++++++++++
7 files changed, 214 insertions(+)
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp
create mode 100644 clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst
create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/modernize-subspan-conversion.cpp
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index c919d49b42873a..47dd12a2640b6c 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -49,6 +49,7 @@ add_clang_library(clangTidyModernizeModule STATIC
UseTransparentFunctorsCheck.cpp
UseUncaughtExceptionsCheck.cpp
UseUsingCheck.cpp
+ UseSpanFirstLastCheck.cpp
LINK_LIBS
clangTidy
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index 18607593320635..6fc5de5aad20b7 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -42,6 +42,7 @@
#include "UseNullptrCheck.h"
#include "UseOverrideCheck.h"
#include "UseRangesCheck.h"
+#include "UseSpanFirstLastCheck.h"
#include "UseStartsEndsWithCheck.h"
#include "UseStdFormatCheck.h"
#include "UseStdNumbersCheck.h"
@@ -122,6 +123,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseUncaughtExceptionsCheck>(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
+ CheckFactories.registerCheck<UseSpanFirstLastCheck>("modernize-use-span-first-last");
+
}
};
diff --git a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp
new file mode 100644
index 00000000000000..f57571f2aa7c86
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp
@@ -0,0 +1,97 @@
+//===--- UseSpanFirstLastCheck.cpp - clang-tidy-----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseSpanFirstLastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ // Match span::subspan calls
+ const auto HasSpanType = hasType(hasUnqualifiedDesugaredType(
+ recordType(hasDeclaration(classTemplateSpecializationDecl(
+ hasName("::std::span"))))));
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(
+ cxxMethodDecl(hasName("subspan"))))),
+ on(expr(HasSpanType)))
+ .bind("subspan"),
+ this);
+}
+
+void UseSpanFirstLastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("subspan");
+ if (!Call)
+ return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+ const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ // Get arguments
+ unsigned NumArgs = Call->getNumArgs();
+ if (NumArgs == 0 || NumArgs > 2)
+ return;
+
+ const Expr *Offset = Call->getArg(0);
+ const Expr *Count = NumArgs > 1 ? Call->getArg(1) : nullptr;
+ auto &Context = *Result.Context;
+ bool IsZeroOffset = false;
+
+ // Check if offset is zero through any implicit casts
+ const Expr* OffsetE = Offset->IgnoreImpCasts();
+ if (const auto *IL = dyn_cast<IntegerLiteral>(OffsetE)) {
+ IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+ // subspan(0, count) -> first(count)
+ auto CountStr = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Count->getSourceRange()),
+ Context.getSourceManager(), Context.getLangOpts());
+ const auto *Base = cast<CXXMemberCallExpr>(Call)->getImplicitObjectArgument();
+ auto BaseStr = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Base->getSourceRange()),
+ Context.getSourceManager(), Context.getLangOpts());
+ Replacement = BaseStr.str() + ".first(" + CountStr.str() + ")";
+ } else if (NumArgs == 1) {
+ // subspan(n) -> last(size() - n)
+ auto OffsetStr = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Offset->getSourceRange()),
+ Context.getSourceManager(), Context.getLangOpts());
+
+ const auto *Base = cast<CXXMemberCallExpr>(Call)->getImplicitObjectArgument();
+ auto BaseStr = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Base->getSourceRange()),
+ Context.getSourceManager(), Context.getLangOpts());
+
+ Replacement = BaseStr.str() + ".last(" + BaseStr.str() + ".size() - " + OffsetStr.str() + ")";
+ }
+
+ if (!Replacement.empty()) {
+ if (IsZeroOffset && Count) {
+ diag(Call->getBeginLoc(), "prefer span::first() over subspan()")
+ << FixItHint::CreateReplacement(Call->getSourceRange(), Replacement);
+ } else {
+ diag(Call->getBeginLoc(), "prefer span::last() over subspan()")
+ << FixItHint::CreateReplacement(Call->getSourceRange(), Replacement);
+ }
+ }
+}
+
+} // namespace clang::tidy::modernize
\ No newline at end of file
diff --git a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h
new file mode 100644
index 00000000000000..141b848be9abbb
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h
@@ -0,0 +1,40 @@
+//===--- UseSpanFirstLastCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANFIRSTLASTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANFIRSTLASTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::modernize {
+
+/// Converts std::span::subspan() calls to the more modern first()/last() methods
+/// where applicable.
+///
+/// For example:
+/// \code
+/// std::span<int> s = ...;
+/// auto sub = s.subspan(0, n); // -> auto sub = s.first(n);
+/// auto sub2 = s.subspan(n); // -> auto sub2 = s.last(s.size() - n);
+/// \endcode
+class UseSpanFirstLastCheck : public ClangTidyCheck {
+public:
+ UseSpanFirstLastCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void handleSubspanCall(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CXXMemberCallExpr *Call);
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANFIRSTLASTCHECK_H
\ No newline at end of file
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index f050391110385e..04a45d002c0d1d 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -145,6 +145,10 @@ New checks
New check aliases
^^^^^^^^^^^^^^^^^
+- New check `modernize-use-span-first-last` has been added that suggests using
+ ``std::span::first()`` and ``std::span::last()`` member functions instead of
+ equivalent ``subspan()``.
+
- New alias :doc:`cert-arr39-c <clang-tidy/checks/cert/arr39-c>` to
:doc:`bugprone-sizeof-expression
<clang-tidy/checks/bugprone/sizeof-expression>` was added.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst
new file mode 100644
index 00000000000000..e8aad59bb2264f
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst
@@ -0,0 +1,19 @@
+.. title:: clang-tidy - modernize-use-span-first-last
+
+modernize-use-span-first-last
+============================
+
+Checks for uses of ``std::span::subspan()`` that can be replaced with clearer
+``first()`` or ``last()`` member functions.
+
+Covered scenarios:
+
+==================================== ==================================
+Expression Replacement
+------------------------------------ ----------------------------------
+``s.subspan(0, n)`` ``s.first(n)``
+``s.subspan(n)`` ``s.last(s.size() - n)``
+==================================== ==================================
+
+Non-zero offset with count (like ``subspan(1, n)``) has no direct equivalent
+using ``first()`` or ``last()``, so these cases are not transformed.
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/modernize-subspan-conversion.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/modernize-subspan-conversion.cpp
new file mode 100644
index 00000000000000..cb78bc02f22d4f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/modernize-subspan-conversion.cpp
@@ -0,0 +1,50 @@
+// RUN: %check_clang_tidy %s modernize-use-span-first-last %t
+
+namespace std {
+template <typename T>
+class span {
+ T* ptr;
+ __SIZE_TYPE__ len;
+
+public:
+ span(T* p, __SIZE_TYPE__ l) : ptr(p), len(l) {}
+
+ span<T> subspan(__SIZE_TYPE__ offset) const {
+ return span(ptr + offset, len - offset);
+ }
+
+ span<T> subspan(__SIZE_TYPE__ offset, __SIZE_TYPE__ count) const {
+ return span(ptr + offset, count);
+ }
+
+ span<T> first(__SIZE_TYPE__ count) const {
+ return span(ptr, count);
+ }
+
+ span<T> last(__SIZE_TYPE__ count) const {
+ return span(ptr + (len - count), count);
+ }
+
+ __SIZE_TYPE__ size() const { return len; }
+};
+} // namespace std
+
+void test() {
+ int arr[] = {1, 2, 3, 4, 5};
+ std::span<int> s(arr, 5);
+
+ auto sub1 = s.subspan(0, 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer span::first() over subspan()
+ // CHECK-FIXES: auto sub1 = s.first(3);
+
+ auto sub2 = s.subspan(2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer span::last() over subspan()
+ // CHECK-FIXES: auto sub2 = s.last(s.size() - 2);
+
+ __SIZE_TYPE__ n = 2;
+ auto sub3 = s.subspan(0, n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer span::first() over subspan()
+ // CHECK-FIXES: auto sub3 = s.first(n);
+
+ auto sub4 = s.subspan(1, 2); // No warning
+}
\ No newline at end of file
>From ec5a6b0ceb0fbbf03ea36a38fb627b25ab4e62de Mon Sep 17 00:00:00 2001
From: Helmut Januschka <helmut at januschka.com>
Date: Fri, 29 Nov 2024 10:19:21 +0100
Subject: [PATCH 02/10] format
---
.../modernize/UseSpanFirstLastCheck.cpp | 41 ++++++++++---------
.../modernize/UseSpanFirstLastCheck.h | 6 +--
2 files changed, 24 insertions(+), 23 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp
index f57571f2aa7c86..6cf01386b0c3fb 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp
@@ -18,17 +18,15 @@ namespace clang::tidy::modernize {
void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
// Match span::subspan calls
- const auto HasSpanType = hasType(hasUnqualifiedDesugaredType(
- recordType(hasDeclaration(classTemplateSpecializationDecl(
- hasName("::std::span"))))));
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"))))));
- Finder->addMatcher(
- cxxMemberCallExpr(
- callee(memberExpr(hasDeclaration(
- cxxMethodDecl(hasName("subspan"))))),
- on(expr(HasSpanType)))
- .bind("subspan"),
- this);
+ Finder->addMatcher(cxxMemberCallExpr(callee(memberExpr(hasDeclaration(
+ cxxMethodDecl(hasName("subspan"))))),
+ on(expr(HasSpanType)))
+ .bind("subspan"),
+ this);
}
void UseSpanFirstLastCheck::check(const MatchFinder::MatchResult &Result) {
@@ -52,7 +50,7 @@ void UseSpanFirstLastCheck::handleSubspanCall(
bool IsZeroOffset = false;
// Check if offset is zero through any implicit casts
- const Expr* OffsetE = Offset->IgnoreImpCasts();
+ const Expr *OffsetE = Offset->IgnoreImpCasts();
if (const auto *IL = dyn_cast<IntegerLiteral>(OffsetE)) {
IsZeroOffset = IL->getValue() == 0;
}
@@ -64,7 +62,8 @@ void UseSpanFirstLastCheck::handleSubspanCall(
auto CountStr = Lexer::getSourceText(
CharSourceRange::getTokenRange(Count->getSourceRange()),
Context.getSourceManager(), Context.getLangOpts());
- const auto *Base = cast<CXXMemberCallExpr>(Call)->getImplicitObjectArgument();
+ const auto *Base =
+ cast<CXXMemberCallExpr>(Call)->getImplicitObjectArgument();
auto BaseStr = Lexer::getSourceText(
CharSourceRange::getTokenRange(Base->getSourceRange()),
Context.getSourceManager(), Context.getLangOpts());
@@ -74,22 +73,24 @@ void UseSpanFirstLastCheck::handleSubspanCall(
auto OffsetStr = Lexer::getSourceText(
CharSourceRange::getTokenRange(Offset->getSourceRange()),
Context.getSourceManager(), Context.getLangOpts());
-
- const auto *Base = cast<CXXMemberCallExpr>(Call)->getImplicitObjectArgument();
+
+ const auto *Base =
+ cast<CXXMemberCallExpr>(Call)->getImplicitObjectArgument();
auto BaseStr = Lexer::getSourceText(
CharSourceRange::getTokenRange(Base->getSourceRange()),
Context.getSourceManager(), Context.getLangOpts());
-
- Replacement = BaseStr.str() + ".last(" + BaseStr.str() + ".size() - " + OffsetStr.str() + ")";
+
+ Replacement = BaseStr.str() + ".last(" + BaseStr.str() + ".size() - " +
+ OffsetStr.str() + ")";
}
if (!Replacement.empty()) {
if (IsZeroOffset && Count) {
- diag(Call->getBeginLoc(), "prefer span::first() over subspan()")
- << FixItHint::CreateReplacement(Call->getSourceRange(), Replacement);
+ diag(Call->getBeginLoc(), "prefer span::first() over subspan()")
+ << FixItHint::CreateReplacement(Call->getSourceRange(), Replacement);
} else {
- diag(Call->getBeginLoc(), "prefer span::last() over subspan()")
- << FixItHint::CreateReplacement(Call->getSourceRange(), Replacement);
+ diag(Call->getBeginLoc(), "prefer span::last() over subspan()")
+ << FixItHint::CreateReplacement(Call->getSourceRange(), Replacement);
}
}
}
diff --git a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h
index 141b848be9abbb..8d4c6035f7ec76 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h
+++ b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h
@@ -13,8 +13,8 @@
namespace clang::tidy::modernize {
-/// Converts std::span::subspan() calls to the more modern first()/last() methods
-/// where applicable.
+/// Converts std::span::subspan() calls to the more modern first()/last()
+/// methods where applicable.
///
/// For example:
/// \code
@@ -32,7 +32,7 @@ class UseSpanFirstLastCheck : public ClangTidyCheck {
private:
void handleSubspanCall(const ast_matchers::MatchFinder::MatchResult &Result,
- const CXXMemberCallExpr *Call);
+ const CXXMemberCallExpr *Call);
};
} // namespace clang::tidy::modernize
>From 5cf1b7ce1fcaf87a26f1ad1ff0d265a47613c144 Mon Sep 17 00:00:00 2001
From: Helmut Januschka <helmut at januschka.com>
Date: Fri, 29 Nov 2024 10:28:42 +0100
Subject: [PATCH 03/10] format
---
clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index 6fc5de5aad20b7..c473c80e3cd0eb 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -123,7 +123,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseUncaughtExceptionsCheck>(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
- CheckFactories.registerCheck<UseSpanFirstLastCheck>("modernize-use-span-first-last");
+ CheckFactories.registerCheck<UseSpanFirstLastCheck>(
+ "modernize-use-span-first-last");
}
};
>From b357f8c8fe607924ba6c5537f42ebfffaa09c011 Mon Sep 17 00:00:00 2001
From: Helmut Januschka <helmut at januschka.com>
Date: Fri, 29 Nov 2024 10:36:31 +0100
Subject: [PATCH 04/10] format
---
clang-tools-extra/clang-tidy/modernize/CMakeLists.txt | 1 -
.../clang-tidy/modernize/ModernizeTidyModule.cpp | 3 ---
.../clang-tidy/readability/CMakeLists.txt | 1 +
.../clang-tidy/readability/ReadabilityTidyModule.cpp | 3 +++
.../UseSpanFirstLastCheck.cpp | 4 ++--
.../{modernize => readability}/UseSpanFirstLastCheck.h | 10 +++++-----
clang-tools-extra/docs/ReleaseNotes.rst | 2 +-
.../use-starts-ends-with.rst | 4 ++--
.../use-span-first-last.cpp} | 2 +-
9 files changed, 15 insertions(+), 15 deletions(-)
rename clang-tools-extra/clang-tidy/{modernize => readability}/UseSpanFirstLastCheck.cpp (97%)
rename clang-tools-extra/clang-tidy/{modernize => readability}/UseSpanFirstLastCheck.h (79%)
rename clang-tools-extra/docs/clang-tidy/checks/{modernize => readability}/use-starts-ends-with.rst (92%)
rename clang-tools-extra/test/clang-tidy/checkers/{modernize/modernize-subspan-conversion.cpp => readability/use-span-first-last.cpp} (95%)
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index 47dd12a2640b6c..c919d49b42873a 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -49,7 +49,6 @@ add_clang_library(clangTidyModernizeModule STATIC
UseTransparentFunctorsCheck.cpp
UseUncaughtExceptionsCheck.cpp
UseUsingCheck.cpp
- UseSpanFirstLastCheck.cpp
LINK_LIBS
clangTidy
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index c473c80e3cd0eb..b2a8f9dd20adba 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -42,7 +42,6 @@
#include "UseNullptrCheck.h"
#include "UseOverrideCheck.h"
#include "UseRangesCheck.h"
-#include "UseSpanFirstLastCheck.h"
#include "UseStartsEndsWithCheck.h"
#include "UseStdFormatCheck.h"
#include "UseStdNumbersCheck.h"
@@ -123,8 +122,6 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseUncaughtExceptionsCheck>(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
- CheckFactories.registerCheck<UseSpanFirstLastCheck>(
- "modernize-use-span-first-last");
}
};
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index 8f303c51e1b0da..f9f9e8e7f19685 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -58,6 +58,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
UppercaseLiteralSuffixCheck.cpp
UseAnyOfAllOfCheck.cpp
UseStdMinMaxCheck.cpp
+ UseSpanFirstLastCheck.cpp
LINK_LIBS
clangTidy
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index d61c0ba39658e5..9729d080f63a84 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -60,6 +60,7 @@
#include "UniqueptrDeleteReleaseCheck.h"
#include "UppercaseLiteralSuffixCheck.h"
#include "UseAnyOfAllOfCheck.h"
+#include "UseSpanFirstLastCheck.h"
#include "UseStdMinMaxCheck.h"
namespace clang::tidy {
@@ -172,6 +173,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-use-anyofallof");
CheckFactories.registerCheck<UseStdMinMaxCheck>(
"readability-use-std-min-max");
+ CheckFactories.registerCheck<UseSpanFirstLastCheck>(
+ "readability-use-span-first-last");
}
};
diff --git a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
similarity index 97%
rename from clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp
rename to clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
index 6cf01386b0c3fb..da7d147565fbdf 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
@@ -14,7 +14,7 @@
using namespace clang::ast_matchers;
-namespace clang::tidy::modernize {
+namespace clang::tidy::readability {
void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
// Match span::subspan calls
@@ -95,4 +95,4 @@ void UseSpanFirstLastCheck::handleSubspanCall(
}
}
-} // namespace clang::tidy::modernize
\ No newline at end of file
+} // namespace clang::tidy::readability
\ No newline at end of file
diff --git a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
similarity index 79%
rename from clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h
rename to clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
index 8d4c6035f7ec76..271730ed4985a3 100644
--- a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
@@ -6,12 +6,12 @@
//
//===----------------------------------------------------------------------===//
-#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANFIRSTLASTCHECK_H
-#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANFIRSTLASTCHECK_H
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESPANFIRSTLASTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESPANFIRSTLASTCHECK_H
#include "../ClangTidyCheck.h"
-namespace clang::tidy::modernize {
+namespace clang::tidy::readability {
/// Converts std::span::subspan() calls to the more modern first()/last()
/// methods where applicable.
@@ -35,6 +35,6 @@ class UseSpanFirstLastCheck : public ClangTidyCheck {
const CXXMemberCallExpr *Call);
};
-} // namespace clang::tidy::modernize
+} // namespace clang::tidy::readability
-#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANFIRSTLASTCHECK_H
\ No newline at end of file
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESPANFIRSTLASTCHECK_H
\ No newline at end of file
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index 04a45d002c0d1d..a52b778a27b5f4 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -145,7 +145,7 @@ New checks
New check aliases
^^^^^^^^^^^^^^^^^
-- New check `modernize-use-span-first-last` has been added that suggests using
+- New check `readability-use-span-first-last` has been added that suggests using
``std::span::first()`` and ``std::span::last()`` member functions instead of
equivalent ``subspan()``.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-starts-ends-with.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/use-starts-ends-with.rst
similarity index 92%
rename from clang-tools-extra/docs/clang-tidy/checks/modernize/use-starts-ends-with.rst
rename to clang-tools-extra/docs/clang-tidy/checks/readability/use-starts-ends-with.rst
index 78cd900885ac3f..ff5c0d53feeb15 100644
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-starts-ends-with.rst
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/use-starts-ends-with.rst
@@ -1,6 +1,6 @@
-.. title:: clang-tidy - modernize-use-starts-ends-with
+.. title:: clang-tidy - readability-use-starts-ends-with
-modernize-use-starts-ends-with
+readability-use-starts-ends-with
==============================
Checks for common roundabout ways to express ``starts_with`` and ``ends_with``
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/modernize-subspan-conversion.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
similarity index 95%
rename from clang-tools-extra/test/clang-tidy/checkers/modernize/modernize-subspan-conversion.cpp
rename to clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
index cb78bc02f22d4f..93ae95bdd8b6b4 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/modernize/modernize-subspan-conversion.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
@@ -1,4 +1,4 @@
-// RUN: %check_clang_tidy %s modernize-use-span-first-last %t
+// RUN: %check_clang_tidy %s readability-use-span-first-last %t
namespace std {
template <typename T>
>From 42d1df2f5f6b0527d5066a9fbd8ff2f445d2d79a Mon Sep 17 00:00:00 2001
From: Helmut Januschka <helmut at januschka.com>
Date: Fri, 29 Nov 2024 10:37:32 +0100
Subject: [PATCH 05/10] format
---
clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp | 1 -
1 file changed, 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index b2a8f9dd20adba..18607593320635 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -122,7 +122,6 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseUncaughtExceptionsCheck>(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
-
}
};
>From 04f3edc9edb5148652bea8a8b5066f32d321ed2b Mon Sep 17 00:00:00 2001
From: Helmut Januschka <helmut at januschka.com>
Date: Fri, 29 Nov 2024 10:37:47 +0100
Subject: [PATCH 06/10] format
---
.../checks/modernize/use-span-first-last.rst | 19 -------------------
1 file changed, 19 deletions(-)
delete mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst
deleted file mode 100644
index e8aad59bb2264f..00000000000000
--- a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst
+++ /dev/null
@@ -1,19 +0,0 @@
-.. title:: clang-tidy - modernize-use-span-first-last
-
-modernize-use-span-first-last
-============================
-
-Checks for uses of ``std::span::subspan()`` that can be replaced with clearer
-``first()`` or ``last()`` member functions.
-
-Covered scenarios:
-
-==================================== ==================================
-Expression Replacement
------------------------------------- ----------------------------------
-``s.subspan(0, n)`` ``s.first(n)``
-``s.subspan(n)`` ``s.last(s.size() - n)``
-==================================== ==================================
-
-Non-zero offset with count (like ``subspan(1, n)``) has no direct equivalent
-using ``first()`` or ``last()``, so these cases are not transformed.
\ No newline at end of file
>From 2f9cc3e878a3c053240a7e6f06b304f89c0d6085 Mon Sep 17 00:00:00 2001
From: Helmut Januschka <helmut at januschka.com>
Date: Fri, 29 Nov 2024 10:43:56 +0100
Subject: [PATCH 07/10] format
---
.../clang-tidy/readability/UseSpanFirstLastCheck.cpp | 2 ++
.../clang-tidy/checkers/readability/use-span-first-last.cpp | 4 +++-
2 files changed, 5 insertions(+), 1 deletion(-)
diff --git a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
index da7d147565fbdf..345a197551d618 100644
--- a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
@@ -17,6 +17,8 @@ using namespace clang::ast_matchers;
namespace clang::tidy::readability {
void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus20)
+ return;
// Match span::subspan calls
const auto HasSpanType =
hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
index 93ae95bdd8b6b4..d99a535e012fa3 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
@@ -1,4 +1,6 @@
-// RUN: %check_clang_tidy %s readability-use-span-first-last %t
+// RUN: %check_clang_tidy -std=c++20 %s readability-use-span-first-last %t
+
+
namespace std {
template <typename T>
>From 24690e3a1155b222436b082c4236c933766a8707 Mon Sep 17 00:00:00 2001
From: Helmut Januschka <helmut at januschka.com>
Date: Fri, 29 Nov 2024 10:53:12 +0100
Subject: [PATCH 08/10] up
---
.../readability/UseSpanFirstLastCheck.cpp | 34 ++++++++++++-------
.../readability/use-span-first-last.cpp | 7 ++--
2 files changed, 25 insertions(+), 16 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
index 345a197551d618..5cd0cd0756e3ba 100644
--- a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
+++ b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
@@ -19,6 +19,7 @@ namespace clang::tidy::readability {
void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
if (!getLangOpts().CPlusPlus20)
return;
+
// Match span::subspan calls
const auto HasSpanType =
hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
@@ -41,7 +42,6 @@ void UseSpanFirstLastCheck::check(const MatchFinder::MatchResult &Result) {
void UseSpanFirstLastCheck::handleSubspanCall(
const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
- // Get arguments
unsigned NumArgs = Call->getNumArgs();
if (NumArgs == 0 || NumArgs > 2)
return;
@@ -49,14 +49,28 @@ void UseSpanFirstLastCheck::handleSubspanCall(
const Expr *Offset = Call->getArg(0);
const Expr *Count = NumArgs > 1 ? Call->getArg(1) : nullptr;
auto &Context = *Result.Context;
- bool IsZeroOffset = false;
- // Check if offset is zero through any implicit casts
+ // Check if this is subspan(0, n) -> first(n)
+ bool IsZeroOffset = false;
const Expr *OffsetE = Offset->IgnoreImpCasts();
if (const auto *IL = dyn_cast<IntegerLiteral>(OffsetE)) {
IsZeroOffset = IL->getValue() == 0;
}
+ // Check if this is subspan(size() - n) -> last(n)
+ bool IsSizeMinusN = false;
+ const Expr *SizeMinusArg = nullptr;
+ if (const auto *BO = dyn_cast<BinaryOperator>(OffsetE)) {
+ if (BO->getOpcode() == BO_Sub) {
+ if (const auto *SizeCall = dyn_cast<CXXMemberCallExpr>(BO->getLHS())) {
+ if (SizeCall->getMethodDecl()->getName() == "size") {
+ IsSizeMinusN = true;
+ SizeMinusArg = BO->getRHS();
+ }
+ }
+ }
+ }
+
// Build replacement text
std::string Replacement;
if (IsZeroOffset && Count) {
@@ -70,20 +84,17 @@ void UseSpanFirstLastCheck::handleSubspanCall(
CharSourceRange::getTokenRange(Base->getSourceRange()),
Context.getSourceManager(), Context.getLangOpts());
Replacement = BaseStr.str() + ".first(" + CountStr.str() + ")";
- } else if (NumArgs == 1) {
- // subspan(n) -> last(size() - n)
- auto OffsetStr = Lexer::getSourceText(
- CharSourceRange::getTokenRange(Offset->getSourceRange()),
+ } else if (IsSizeMinusN && SizeMinusArg) {
+ // subspan(size() - n) -> last(n)
+ auto ArgStr = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(SizeMinusArg->getSourceRange()),
Context.getSourceManager(), Context.getLangOpts());
-
const auto *Base =
cast<CXXMemberCallExpr>(Call)->getImplicitObjectArgument();
auto BaseStr = Lexer::getSourceText(
CharSourceRange::getTokenRange(Base->getSourceRange()),
Context.getSourceManager(), Context.getLangOpts());
-
- Replacement = BaseStr.str() + ".last(" + BaseStr.str() + ".size() - " +
- OffsetStr.str() + ")";
+ Replacement = BaseStr.str() + ".last(" + ArgStr.str() + ")";
}
if (!Replacement.empty()) {
@@ -96,5 +107,4 @@ void UseSpanFirstLastCheck::handleSubspanCall(
}
}
}
-
} // namespace clang::tidy::readability
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp b/clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
index d99a535e012fa3..e775de8b806ad8 100644
--- a/clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
+++ b/clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
@@ -1,7 +1,5 @@
// RUN: %check_clang_tidy -std=c++20 %s readability-use-span-first-last %t
-
-
namespace std {
template <typename T>
class span {
@@ -39,9 +37,9 @@ void test() {
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer span::first() over subspan()
// CHECK-FIXES: auto sub1 = s.first(3);
- auto sub2 = s.subspan(2);
+ auto sub2 = s.subspan(s.size() - 2);
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer span::last() over subspan()
- // CHECK-FIXES: auto sub2 = s.last(s.size() - 2);
+ // CHECK-FIXES: auto sub2 = s.last(2);
__SIZE_TYPE__ n = 2;
auto sub3 = s.subspan(0, n);
@@ -49,4 +47,5 @@ void test() {
// CHECK-FIXES: auto sub3 = s.first(n);
auto sub4 = s.subspan(1, 2); // No warning
+ auto sub5 = s.subspan(2); // No warning
}
\ No newline at end of file
>From 39833d470eea5a0f446b01dba8bbc8271ec69366 Mon Sep 17 00:00:00 2001
From: Helmut Januschka <helmut at januschka.com>
Date: Fri, 29 Nov 2024 10:56:54 +0100
Subject: [PATCH 09/10] up
---
.../readability/use-span-first-last.rst | 23 +++++++++++++++++++
.../readability/use-starts-ends-with.rst | 22 ------------------
2 files changed, 23 insertions(+), 22 deletions(-)
create mode 100644 clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
delete mode 100644 clang-tools-extra/docs/clang-tidy/checks/readability/use-starts-ends-with.rst
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
new file mode 100644
index 00000000000000..1872ed56583acb
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
@@ -0,0 +1,23 @@
+.. title:: clang-tidy - readability-use-span-first-last
+
+readability-use-span-first-last
+=============================
+
+Checks for uses of ``std::span::subspan()`` that can be replaced with clearer
+``first()`` or ``last()`` member functions. These dedicated methods were added
+to C++20 to provide more expressive alternatives to common subspan operations.
+
+Covered scenarios:
+
+==================================== ==================================
+Expression Replacement
+------------------------------------ ----------------------------------
+``s.subspan(0, n)`` ``s.first(n)``
+``s.subspan(s.size() - n)`` ``s.last(n)``
+==================================== ==================================
+
+Non-zero offset with count (like ``subspan(1, n)``) or offset-only calls
+(like ``subspan(n)``) have no clearer equivalent using ``first()`` or
+``last()``, so these cases are not transformed.
+
+This check is only active when C++20 or later is used.
\ No newline at end of file
diff --git a/clang-tools-extra/docs/clang-tidy/checks/readability/use-starts-ends-with.rst b/clang-tools-extra/docs/clang-tidy/checks/readability/use-starts-ends-with.rst
deleted file mode 100644
index ff5c0d53feeb15..00000000000000
--- a/clang-tools-extra/docs/clang-tidy/checks/readability/use-starts-ends-with.rst
+++ /dev/null
@@ -1,22 +0,0 @@
-.. title:: clang-tidy - readability-use-starts-ends-with
-
-readability-use-starts-ends-with
-==============================
-
-Checks for common roundabout ways to express ``starts_with`` and ``ends_with``
-and suggests replacing with the simpler method when it is available. Notably,
-this will work with ``std::string`` and ``std::string_view``.
-
-Covered scenarios:
-
-==================================================== =====================
-Expression Replacement
----------------------------------------------------- ---------------------
-``u.find(v) == 0`` ``u.starts_with(v)``
-``u.rfind(v, 0) != 0`` ``!u.starts_with(v)``
-``u.compare(0, v.size(), v) == 0`` ``u.starts_with(v)``
-``u.substr(0, v.size()) == v`` ``u.starts_with(v)``
-``v != u.substr(0, v.size())`` ``!u.starts_with(v)``
-``u.compare(u.size() - v.size(), v.size(), v) == 0`` ``u.ends_with(v)``
-``u.rfind(v) == u.size() - v.size()`` ``u.ends_with(v)``
-==================================================== =====================
>From 964fadc022c33e0e09217b3f8d9eea25b1e7a215 Mon Sep 17 00:00:00 2001
From: Helmut Januschka <helmut at januschka.com>
Date: Fri, 29 Nov 2024 10:59:15 +0100
Subject: [PATCH 10/10] format
---
.../clang-tidy/readability/UseSpanFirstLastCheck.h | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
index 271730ed4985a3..354ff6edb83dec 100644
--- a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
+++ b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
@@ -13,15 +13,19 @@
namespace clang::tidy::readability {
-/// Converts std::span::subspan() calls to the more modern first()/last()
-/// methods where applicable.
+/// Suggests using clearer std::span member functions first()/last() instead of
+/// equivalent subspan() calls where applicable.
///
/// For example:
/// \code
/// std::span<int> s = ...;
-/// auto sub = s.subspan(0, n); // -> auto sub = s.first(n);
-/// auto sub2 = s.subspan(n); // -> auto sub2 = s.last(s.size() - n);
+/// auto sub1 = s.subspan(0, n); // -> auto sub1 = s.first(n);
+/// auto sub2 = s.subspan(s.size() - n); // -> auto sub2 = s.last(n);
+/// auto sub3 = s.subspan(1, n); // not changed
+/// auto sub4 = s.subspan(n); // not changed
/// \endcode
+///
+/// The check is only active in C++20 mode.
class UseSpanFirstLastCheck : public ClangTidyCheck {
public:
UseSpanFirstLastCheck(StringRef Name, ClangTidyContext *Context)
More information about the cfe-commits
mailing list