[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,117 @@
+//===--===//
+//
+// 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 "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ // Type matcher for concrete std::span types.
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Type matcher for dependent std::span types (in templates).
+ const auto HasDependentSpanType = hasType(
+ hasCanonicalType(hasDeclaration(namedDecl(hasName("::std::span");
+
+ // --- Non-dependent matchers (concrete types) ---
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ argumentCountIs(2),
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")))
+ .bind("subspan_call"),
+ this);
+
+ // Match span.subspan(span.size() - n) -> last(n)
+ const auto SizeCall = anyOf(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size"),
+ on(expr(isStatementIdenticalToBoundNode("span_object",
+ callExpr(callee(functionDecl(
+ hasAnyName("::std::size", "::std::ranges::size"))),
+ hasArgument(
+ 0, expr(isStatementIdenticalToBoundNode("span_object");
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ argumentCountIs(1),
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, binaryOperator(hasOperatorName("-"), hasLHS(SizeCall),
+hasRHS(expr().bind("count")
+ .bind("subspan_call"),
+ this);
+
+ // --- Dependent matchers (template definitions) ---
+
+ const auto DependentSubspanCallee = callee(cxxDependentScopeMemberExpr(
+ hasMemberName("subspan"),
+ hasObjectExpression(
+ expr(anyOf(HasDependentSpanType,
HasSpanType)).bind("span_object";
+
+ // Match span.subspan(0, n) -> first(n) in dependent context
+ Finder->addMatcher(callExpr(argumentCountIs(2), DependentSubspanCallee,
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")))
+ .bind("subspan_call"),
+ this);
+
+ // Match span.subspan(span.size() - n) -> last(n) in dependent context
+ const auto DependentSizeCall = callExpr(callee(cxxDependentScopeMemberExpr(
+ hasMemberName("size"),
+ hasObjectExpression(
+ expr(isStatementIdenticalToBoundNode("span_object"));
+
+ Finder->addMatcher(
+ callExpr(argumentCountIs(1), DependentSubspanCallee,
+ hasArgument(0, binaryOperator(hasOperatorName("-"),
+ hasLHS(DependentSizeCall),
+ hasRHS(expr().bind("count")
+ .bind("subspan_call"),
+ this);
+}
+
+void UseSpanFirstLastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *SpanObj = Result.Nodes.getNodeAs("span_object");
+ if (!SpanObj)
+return;
vbvictor wrote:
use assert()
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,194 @@ +// RUN: %check_clang_tidy -std=c++20 %s readability-use-span-first-last %t vbvictor wrote: c++20-or-later https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,46 @@
+//===--===//
+//
+// 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_READABILITY_USESPANFIRSTLASTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESPANFIRSTLASTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Suggests using clearer 'std::span' member functions 'first()'/'last()'
+/// instead of equivalent 'subspan()' calls where applicable.
+///
+/// For the user-facing documentation see:
+///
https://clang.llvm.org/extra/clang-tidy/checks/readability/use-span-first-last.html
vbvictor wrote:
Only this is needed, everything else is just .rst docs and could be removed
from here
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,194 @@
+// RUN: %check_clang_tidy -std=c++20 %s readability-use-span-first-last %t
+
+namespace std {
+
+enum class byte : unsigned char {};
+
+template
+class span {
+ T* ptr;
+ __SIZE_TYPE__ len;
+
+public:
+ span(T* p, __SIZE_TYPE__ l) : ptr(p), len(l) {}
+
+ span subspan(__SIZE_TYPE__ offset) const {
+return span(ptr + offset, len - offset);
+ }
+
+ span subspan(__SIZE_TYPE__ offset, __SIZE_TYPE__ count) const {
+return span(ptr + offset, count);
+ }
+
+ span first(__SIZE_TYPE__ count) const {
+return span(ptr, count);
+ }
+
+ span last(__SIZE_TYPE__ count) const {
+return span(ptr + (len - count), count);
+ }
+
+ __SIZE_TYPE__ size() const { return len; }
+ __SIZE_TYPE__ size_bytes() const { return len * sizeof(T); }
+};
+} // namespace std
+
+// Add here, right after the std namespace closes:
+namespace std::ranges {
+ template
+ __SIZE_TYPE__ size(const span& s) { return s.size(); }
+}
vbvictor wrote:
This should be a separate header under
`clang-tools-extra/test/clang-tidy/checkers/Inputs/Headers/std` after
https://github.com/llvm/llvm-project/pull/185533 is merged. And you don't need
to do any manual `-I`
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,194 @@
+// RUN: %check_clang_tidy -std=c++20 %s readability-use-span-first-last %t
+
+namespace std {
+
+enum class byte : unsigned char {};
+
+template
+class span {
+ T* ptr;
+ __SIZE_TYPE__ len;
+
+public:
+ span(T* p, __SIZE_TYPE__ l) : ptr(p), len(l) {}
+
+ span subspan(__SIZE_TYPE__ offset) const {
+return span(ptr + offset, len - offset);
+ }
+
+ span subspan(__SIZE_TYPE__ offset, __SIZE_TYPE__ count) const {
+return span(ptr + offset, count);
+ }
+
+ span first(__SIZE_TYPE__ count) const {
+return span(ptr, count);
+ }
+
+ span last(__SIZE_TYPE__ count) const {
+return span(ptr + (len - count), count);
+ }
+
+ __SIZE_TYPE__ size() const { return len; }
+ __SIZE_TYPE__ size_bytes() const { return len * sizeof(T); }
+};
+} // namespace std
+
+// Add here, right after the std namespace closes:
+namespace std::ranges {
+ template
+ __SIZE_TYPE__ size(const span& s) { return s.size(); }
+}
+
+void test() {
+ int arr[] = {1, 2, 3, 4, 5};
+ std::span 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(s.size() - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub2 = s.last(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
+ auto sub5 = s.subspan(2); // No warning
+
+
+#define ZERO 0
+#define TWO 2
+#define SIZE_MINUS(s, n) s.size() - n
+#define MAKE_SUBSPAN(obj, n) obj.subspan(0, n)
+#define MAKE_LAST_N(obj, n) obj.subspan(obj.size() - n)
+
+ auto sub_zero = s.subspan(ZERO, 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:19: warning: prefer 'span::first()' over
'subspan()'
+ // CHECK-FIXES: auto sub_zero = s.first(3);
+
+ auto sub_two = s.subspan(s.size() - TWO);
+ // CHECK-MESSAGES: :[[@LINE-1]]:18: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub_two = s.last(TWO);
+
+ auto sub6 = s.subspan(SIZE_MINUS(s, 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub6 = s.last(2);
+
+ auto sub7 = MAKE_SUBSPAN(s, 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: prefer 'span::first()' over
'subspan()'
+ // CHECK-FIXES: auto sub7 = s.first(3);
+
+ auto sub8 = MAKE_LAST_N(s, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub8 = s.last(2);
+
+}
+
+template
+void testTemplate() {
+ T arr[] = {1, 2, 3, 4, 5};
+ std::span 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(s.size() - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub2 = s.last(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
+ auto sub5 = s.subspan(2); // No warning
+
+ auto complex = s.subspan(0 + (s.size() - 2), 3); // No warning
+
+ auto complex2 = s.subspan(100 + (s.size() - 2)); // No warning
+}
+
+// Test instantiation
+void testInt() {
+ testTemplate();
+}
+
+void test_ranges() {
+ int arr[] = {1, 2, 3, 4, 5};
+ std::span s(arr, 5);
+
+ auto sub1 = s.subspan(std::ranges::size(s) - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub1 = s.last(2);
+
+ __SIZE_TYPE__ n = 2;
+ auto sub2 = s.subspan(std::ranges::size(s) - n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub2 = s.last(n);
+}
+
+void test_different_spans() {
+ int arr1[] = {1, 2, 3, 4, 5};
+ int arr2[] = {6, 7, 8, 9, 10};
+ std::span s1(arr1, 5);
+ std::span s2(arr2, 5);
+
+ // These should NOT trigger warnings as they use size() from a different span
+ auto sub1 = s1.subspan(s2.size() - 2); // No warning
+ auto sub2 = s2.subspan(s1.size() - 3); // No warning
+
+ // Also check with std::ranges::size
+ auto sub3 = s1.subspan(std::ranges::size(s2) - 2); // No warning
+ auto sub4 = s2.subspan(std::ranges::size(s1) - 3); // No warning
+
+ // Mixed usage should also not trigger
+ auto sub5 = s1.subspan(s2.size() - s1.size()); // No warning
+
+ // Verify that correct usage still triggers warnings
+ auto good1 = s1.subspan(s1.size() - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto good1 = s1.last(2
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/hjanuschka updated
https://github.com/llvm/llvm-project/pull/118074
>From 1c37553ff1c184c182aa41031730a4482f596c2c Mon Sep 17 00:00:00 2001
From: Helmut Januschka
Date: Wed, 18 Feb 2026 08:58:31 +0100
Subject: [PATCH] [clang-tidy] Add readability-use-span-first-last check
Add new clang-tidy check that suggests using std::span's more expressive
first() and last() member functions instead of equivalent subspan() calls.
These dedicated methods were added to C++20 to provide clearer alternatives
to common subspan operations. They improve readability by better expressing
intent and are less error-prone by eliminating manual offset calculations.
For example:
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)) are not transformed as they have no clearer equivalent.
---
.../clang-tidy/readability/CMakeLists.txt | 1 +
.../readability/ReadabilityTidyModule.cpp | 3 +
.../readability/UseSpanFirstLastCheck.cpp | 117 +++
.../readability/UseSpanFirstLastCheck.h | 46 +
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../readability/use-span-first-last.rst | 24 +++
.../readability/use-span-first-last.cpp | 194 ++
8 files changed, 392 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index f1f3cde32feff..941416160cb21 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -63,6 +63,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
UppercaseLiteralSuffixCheck.cpp
UseAnyOfAllOfCheck.cpp
UseConcisePreprocessorDirectivesCheck.cpp
+ UseSpanFirstLastCheck.cpp
UseStdMinMaxCheck.cpp
LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index c582dc98eac6b..f213c02dd9fee 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -65,6 +65,7 @@
#include "UppercaseLiteralSuffixCheck.h"
#include "UseAnyOfAllOfCheck.h"
#include "UseConcisePreprocessorDirectivesCheck.h"
+#include "UseSpanFirstLastCheck.h"
#include "UseStdMinMaxCheck.h"
namespace clang::tidy {
@@ -188,6 +189,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-use-anyofallof");
CheckFactories.registerCheck(
"readability-use-concise-preprocessor-directives");
+CheckFactories.registerCheck(
+"readability-use-span-first-last");
CheckFactories.registerCheck(
"readability-use-std-min-max");
}
diff --git a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
new file mode 100644
index 0..aa27e3f4c31fe
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
@@ -0,0 +1,117 @@
+//===--===//
+//
+// 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 "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ // Type matcher for concrete std::span types.
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Type matcher for dependent std::span types (in templates).
+ const auto HasDependentSpanType = hasType(
+ hasCanonicalType(hasDeclaration(namedDecl(hasName("::std::span");
+
+ // --- Non-dependent matchers (concrete types) ---
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ argumentCountIs(2),
+
callee(
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,121 @@
+//===--===//
+//
+// 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 "../utils/Matchers.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;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ // Type matcher for concrete std::span types.
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Type matcher for dependent std::span types (in templates).
+ const auto HasDependentSpanType = hasType(
+ hasCanonicalType(hasDeclaration(namedDecl(hasName("::std::span");
+
+ // --- Non-dependent matchers (concrete types) ---
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ argumentCountIs(2),
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")))
+ .bind("subspan_call"),
+ this);
+
+ // Match span.subspan(span.size() - n) -> last(n)
zeyi2 wrote:
Can we also support this pattern?
```
s.subspan(s.size() - n, n) -> last(n)
```
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,44 @@
+//===--- 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_READABILITY_USESPANFIRSTLASTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESPANFIRSTLASTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Suggests using clearer std::span member functions first()/last() instead of
+/// equivalent subspan() calls where applicable.
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
hjanuschka wrote: Done. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,110 @@
+//===--- 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::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus20)
+return;
+
+ // 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("subspan");
+ if (!Call)
+return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ 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;
+
+ // Check if this is subspan(0, n) -> first(n)
+ bool IsZeroOffset = false;
+ const Expr *OffsetE = Offset->IgnoreImpCasts();
+ if (const auto *IL = dyn_cast(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(OffsetE)) {
+if (BO->getOpcode() == BO_Sub) {
+ if (const auto *SizeCall = dyn_cast(BO->getLHS())) {
+if (SizeCall->getMethodDecl()->getName() == "size") {
+ IsSizeMinusN = true;
+ SizeMinusArg = BO->getRHS();
+}
+ }
+}
+ }
+
+ // 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(Call)->getImplicitObjectArgument();
+auto BaseStr = Lexer::getSourceText(
+CharSourceRange::getTokenRange(Base->getSourceRange()),
+Context.getSourceManager(), Context.getLangOpts());
+Replacement = BaseStr.str() + ".first(" + CountStr.str() + ")";
+ } 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(Call)->getImplicitObjectArgument();
+auto BaseStr = Lexer::getSourceText(
+CharSourceRange::getTokenRange(Base->getSourceRange()),
+Context.getSourceManager(), Context.getLangOpts());
+Replacement = BaseStr.str() + ".last(" + ArgStr.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);
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,110 @@
+//===--- 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::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus20)
+return;
+
+ // 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("subspan");
+ if (!Call)
+return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ 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;
+
+ // Check if this is subspan(0, n) -> first(n)
+ bool IsZeroOffset = false;
+ const Expr *OffsetE = Offset->IgnoreImpCasts();
+ if (const auto *IL = dyn_cast(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(OffsetE)) {
+if (BO->getOpcode() == BO_Sub) {
+ if (const auto *SizeCall = dyn_cast(BO->getLHS())) {
+if (SizeCall->getMethodDecl()->getName() == "size") {
+ IsSizeMinusN = true;
+ SizeMinusArg = BO->getRHS();
+}
+ }
+}
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+// subspan(0, count) -> first(count)
+auto CountStr = Lexer::getSourceText(
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,110 @@
+//===--- 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::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus20)
+return;
+
+ // 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("subspan");
+ if (!Call)
+return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ 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;
+
+ // Check if this is subspan(0, n) -> first(n)
+ bool IsZeroOffset = false;
+ const Expr *OffsetE = Offset->IgnoreImpCasts();
+ if (const auto *IL = dyn_cast(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(OffsetE)) {
+if (BO->getOpcode() == BO_Sub) {
+ if (const auto *SizeCall = dyn_cast(BO->getLHS())) {
+if (SizeCall->getMethodDecl()->getName() == "size") {
+ IsSizeMinusN = true;
+ SizeMinusArg = BO->getRHS();
+}
+ }
+}
+ }
+
+ // 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(Call)->getImplicitObjectArgument();
+auto BaseStr = Lexer::getSourceText(
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,110 @@
+//===--- 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::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus20)
+return;
+
+ // 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("subspan");
+ if (!Call)
+return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ 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;
+
+ // Check if this is subspan(0, n) -> first(n)
+ bool IsZeroOffset = false;
+ const Expr *OffsetE = Offset->IgnoreImpCasts();
+ if (const auto *IL = dyn_cast(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(OffsetE)) {
+if (BO->getOpcode() == BO_Sub) {
+ if (const auto *SizeCall = dyn_cast(BO->getLHS())) {
+if (SizeCall->getMethodDecl()->getName() == "size") {
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -172,6 +173,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-use-anyofallof");
CheckFactories.registerCheck(
"readability-use-std-min-max");
+CheckFactories.registerCheck(
+"readability-use-span-first-last");
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,51 @@
+// RUN: %check_clang_tidy -std=c++20 %s readability-use-span-first-last %t
+
+namespace std {
+template
+class span {
+ T* ptr;
+ __SIZE_TYPE__ len;
+
+public:
+ span(T* p, __SIZE_TYPE__ l) : ptr(p), len(l) {}
+
+ span subspan(__SIZE_TYPE__ offset) const {
+return span(ptr + offset, len - offset);
+ }
+
+ span subspan(__SIZE_TYPE__ offset, __SIZE_TYPE__ count) const {
+return span(ptr + offset, count);
+ }
+
+ span first(__SIZE_TYPE__ count) const {
+return span(ptr, count);
+ }
+
+ span 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 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(s.size() - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer span::last() over
subspan()
+ // CHECK-FIXES: auto sub2 = s.last(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
+ auto sub5 = s.subspan(2); // No warning
+}
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,110 @@
+//===--- 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::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus20)
+return;
+
+ // 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("subspan");
+ if (!Call)
+return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ 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;
+
+ // Check if this is subspan(0, n) -> first(n)
+ bool IsZeroOffset = false;
+ const Expr *OffsetE = Offset->IgnoreImpCasts();
+ if (const auto *IL = dyn_cast(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -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: + + == hjanuschka wrote: Done. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,110 @@
+//===--- 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::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus20)
+return;
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -145,6 +145,10 @@ New checks New check aliases ^ +- New check `readability-use-span-first-last` has been added that suggests using hjanuschka wrote: Done. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,44 @@
+//===--- 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_READABILITY_USESPANFIRSTLASTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESPANFIRSTLASTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Suggests using clearer std::span member functions first()/last() instead of
+/// equivalent subspan() calls where applicable.
+///
+/// For example:
+/// \code
+/// std::span s = ...;
+/// 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)
+ : ClangTidyCheck(Name, Context) {}
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,44 @@
+//===--- 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_READABILITY_USESPANFIRSTLASTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESPANFIRSTLASTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Suggests using clearer std::span member functions first()/last() instead of
+/// equivalent subspan() calls where applicable.
+///
+/// For example:
+/// \code
+/// std::span s = ...;
+/// 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)
+ : 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::readability
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESPANFIRSTLASTCHECK_H
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -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 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)
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -58,6 +58,7 @@ add_clang_library(clangTidyReadabilityModule STATIC UppercaseLiteralSuffixCheck.cpp UseAnyOfAllOfCheck.cpp UseStdMinMaxCheck.cpp + UseSpanFirstLastCheck.cpp hjanuschka wrote: Done. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,110 @@
+//===--- 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::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus20)
+return;
+
+ // 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("subspan");
+ if (!Call)
+return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ 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;
+
+ // Check if this is subspan(0, n) -> first(n)
+ bool IsZeroOffset = false;
+ const Expr *OffsetE = Offset->IgnoreImpCasts();
+ if (const auto *IL = dyn_cast(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(OffsetE)) {
+if (BO->getOpcode() == BO_Sub) {
+ if (const auto *SizeCall = dyn_cast(BO->getLHS())) {
+if (SizeCall->getMethodDecl()->getName() == "size") {
+ IsSizeMinusN = true;
+ SizeMinusArg = BO->getRHS();
+}
+ }
+}
+ }
+
+ // 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(Call)->getImplicitObjectArgument();
+auto BaseStr = Lexer::getSourceText(
+CharSourceRange::getTokenRange(Base->getSourceRange()),
+Context.getSourceManager(), Context.getLangOpts());
+Replacement = BaseStr.str() + ".first(" + CountStr.str() + ")";
+ } else if (IsSizeMinusN && SizeMinusArg) {
+// subspan(size() - n) -> last(n)
+auto ArgStr = Lexer::getSourceText(
hjanuschka wrote:
Done.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,44 @@ +//===--- UseSpanFirstLastCheck.h - clang-tidy---*- C++ -*-===// hjanuschka wrote: Done. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,110 @@ +//===--- UseSpanFirstLastCheck.cpp - clang-tidy-*- C++ -*-===// hjanuschka wrote: Done. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/hjanuschka updated
https://github.com/llvm/llvm-project/pull/118074
>From 4850a5c18d14f1c533041bb69e0e37ae7ca06d00 Mon Sep 17 00:00:00 2001
From: Helmut Januschka
Date: Wed, 18 Feb 2026 08:58:31 +0100
Subject: [PATCH] [clang-tidy] Add readability-use-span-first-last check
Add new clang-tidy check that suggests using std::span's more expressive
first() and last() member functions instead of equivalent subspan() calls.
These dedicated methods were added to C++20 to provide clearer alternatives
to common subspan operations. They improve readability by better expressing
intent and are less error-prone by eliminating manual offset calculations.
For example:
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)) are not transformed as they have no clearer equivalent.
---
.../clang-tidy/readability/CMakeLists.txt | 1 +
.../readability/ReadabilityTidyModule.cpp | 3 +
.../readability/UseSpanFirstLastCheck.cpp | 125
.../readability/UseSpanFirstLastCheck.h | 43
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../readability/use-span-first-last.rst | 24 +++
.../readability/use-span-first-last.cpp | 186 ++
8 files changed, 389 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index f1f3cde32feff..941416160cb21 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -63,6 +63,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
UppercaseLiteralSuffixCheck.cpp
UseAnyOfAllOfCheck.cpp
UseConcisePreprocessorDirectivesCheck.cpp
+ UseSpanFirstLastCheck.cpp
UseStdMinMaxCheck.cpp
LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index c582dc98eac6b..f213c02dd9fee 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -65,6 +65,7 @@
#include "UppercaseLiteralSuffixCheck.h"
#include "UseAnyOfAllOfCheck.h"
#include "UseConcisePreprocessorDirectivesCheck.h"
+#include "UseSpanFirstLastCheck.h"
#include "UseStdMinMaxCheck.h"
namespace clang::tidy {
@@ -188,6 +189,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-use-anyofallof");
CheckFactories.registerCheck(
"readability-use-concise-preprocessor-directives");
+CheckFactories.registerCheck(
+"readability-use-span-first-last");
CheckFactories.registerCheck(
"readability-use-std-min-max");
}
diff --git a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
new file mode 100644
index 0..54f4ee0ef2973
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
@@ -0,0 +1,125 @@
+//===--===//
+//
+// 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 "../utils/Matchers.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;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ // Type matcher for concrete std::span types.
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Type matcher for dependent std::span types (in templates).
+ const auto HasDependentSpanType = hasType(
+ hasCanonicalType(hasDeclaration(namedDecl(hasName("::std::span");
+
+ // --- Non-dependent matchers (concrete types) ---
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ argumentCountIs(2),
+
callee(memb
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,43 @@
+//===--- 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_READABILITY_USESPANFIRSTLASTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_USESPANFIRSTLASTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::readability {
+
+/// Suggests using clearer 'std::span' member functions 'first()'/'last()'
+/// instead of equivalent 'subspan()' calls where applicable.
+///
+/// For example:
+/// \code
+/// std::span s = ...;
+/// 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.
EugeneZelenko wrote:
Missing link to documentation. See other such headers as example.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,43 @@ +//===--- UseSpanFirstLastCheck.h - clang-tidy ---*- C++ -*-===// EugeneZelenko wrote: ```suggestion //===--===// ``` https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,125 @@ +//===--- UseSpanFirstLastCheck.cpp - clang-tidy -*- C++ -*-===// EugeneZelenko wrote: ```suggestion //===--===// ``` https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,24 @@ +.. title:: clang-tidy - readability-use-span-first-last + +readability-use-span-first-last +=== + +Suggests using ``std::span::first()`` and ``std::span::last()`` member +functions instead of equivalent ``subspan()`` calls. These dedicated methods +were added to C++20 to provide more expressive alternatives to common subspan +operations. + +Covered scenarios: + +=== EugeneZelenko wrote: Please reduce columns width to match content. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,24 @@ +.. title:: clang-tidy - readability-use-span-first-last + +readability-use-span-first-last +=== + +Suggests using ``std::span::first()`` and ``std::span::last()`` member +functions instead of equivalent ``subspan()`` calls. 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. EugeneZelenko wrote: ```suggestion This check is enabled for C++20 or later. ``` https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/hjanuschka updated
https://github.com/llvm/llvm-project/pull/118074
>From 35e04fe6516af26106195cc58b4f208ae6f36b74 Mon Sep 17 00:00:00 2001
From: Helmut Januschka
Date: Wed, 18 Feb 2026 08:58:31 +0100
Subject: [PATCH] [clang-tidy] Add readability-use-span-first-last check
Add new clang-tidy check that suggests using std::span's more expressive
first() and last() member functions instead of equivalent subspan() calls.
These dedicated methods were added to C++20 to provide clearer alternatives
to common subspan operations. They improve readability by better expressing
intent and are less error-prone by eliminating manual offset calculations.
For example:
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)) are not transformed as they have no clearer equivalent.
---
.../clang-tidy/readability/CMakeLists.txt | 1 +
.../readability/ReadabilityTidyModule.cpp | 3 +
.../readability/UseSpanFirstLastCheck.cpp | 126
.../readability/UseSpanFirstLastCheck.h | 43
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../readability/use-span-first-last.rst | 24 +++
.../readability/use-span-first-last.cpp | 186 ++
8 files changed, 390 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index f1f3cde32feff..941416160cb21 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -63,6 +63,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
UppercaseLiteralSuffixCheck.cpp
UseAnyOfAllOfCheck.cpp
UseConcisePreprocessorDirectivesCheck.cpp
+ UseSpanFirstLastCheck.cpp
UseStdMinMaxCheck.cpp
LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index c582dc98eac6b..f213c02dd9fee 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -65,6 +65,7 @@
#include "UppercaseLiteralSuffixCheck.h"
#include "UseAnyOfAllOfCheck.h"
#include "UseConcisePreprocessorDirectivesCheck.h"
+#include "UseSpanFirstLastCheck.h"
#include "UseStdMinMaxCheck.h"
namespace clang::tidy {
@@ -188,6 +189,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-use-anyofallof");
CheckFactories.registerCheck(
"readability-use-concise-preprocessor-directives");
+CheckFactories.registerCheck(
+"readability-use-span-first-last");
CheckFactories.registerCheck(
"readability-use-std-min-max");
}
diff --git a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
new file mode 100644
index 0..0e3a35dcf0ab0
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
@@ -0,0 +1,126 @@
+//===--- 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 "../utils/Matchers.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;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ // Type matcher for concrete std::span types.
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Type matcher for dependent std::span types (in templates).
+ const auto HasDependentSpanType =
+
hasType(hasCanonicalType(hasDeclaration(namedDecl(hasName("::std::span");
+
+ // --- Non-dependent matchers (concrete types) ---
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ argumentCountIs(2),
+
callee(mem
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/hjanuschka updated
https://github.com/llvm/llvm-project/pull/118074
>From 39e01dbbf4941f86f7b635a2d26b976e1ee0ed89 Mon Sep 17 00:00:00 2001
From: Helmut Januschka
Date: Wed, 18 Feb 2026 08:58:31 +0100
Subject: [PATCH] [clang-tidy] Add readability-use-span-first-last check
Add new clang-tidy check that suggests using std::span's more expressive
first() and last() member functions instead of equivalent subspan() calls.
These dedicated methods were added to C++20 to provide clearer alternatives
to common subspan operations. They improve readability by better expressing
intent and are less error-prone by eliminating manual offset calculations.
For example:
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)) are not transformed as they have no clearer equivalent.
---
.../clang-tidy/readability/CMakeLists.txt | 1 +
.../readability/ReadabilityTidyModule.cpp | 3 +
.../readability/UseSpanFirstLastCheck.cpp | 93 ++
.../readability/UseSpanFirstLastCheck.h | 43 +
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../readability/use-span-first-last.rst | 24 +++
.../readability/use-span-first-last.cpp | 171 ++
8 files changed, 342 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index f1f3cde32feff..941416160cb21 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -63,6 +63,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
UppercaseLiteralSuffixCheck.cpp
UseAnyOfAllOfCheck.cpp
UseConcisePreprocessorDirectivesCheck.cpp
+ UseSpanFirstLastCheck.cpp
UseStdMinMaxCheck.cpp
LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index c582dc98eac6b..f213c02dd9fee 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -65,6 +65,7 @@
#include "UppercaseLiteralSuffixCheck.h"
#include "UseAnyOfAllOfCheck.h"
#include "UseConcisePreprocessorDirectivesCheck.h"
+#include "UseSpanFirstLastCheck.h"
#include "UseStdMinMaxCheck.h"
namespace clang::tidy {
@@ -188,6 +189,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-use-anyofallof");
CheckFactories.registerCheck(
"readability-use-concise-preprocessor-directives");
+CheckFactories.registerCheck(
+"readability-use-span-first-last");
CheckFactories.registerCheck(
"readability-use-std-min-max");
}
diff --git a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
new file mode 100644
index 0..16e56ec45607c
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
@@ -0,0 +1,93 @@
+//===--- 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 "../utils/Matchers.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;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ argumentCountIs(2),
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(spa
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
hjanuschka wrote: a million of business days later 😄 - ready for re-review! https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/hjanuschka updated
https://github.com/llvm/llvm-project/pull/118074
>From 3cda3103586149ee93ff2702d811c6d945318f0a Mon Sep 17 00:00:00 2001
From: Helmut Januschka
Date: Wed, 18 Feb 2026 08:58:31 +0100
Subject: [PATCH] [clang-tidy] Add readability-use-span-first-last check
Add new clang-tidy check that suggests using std::span's more expressive
first() and last() member functions instead of equivalent subspan() calls.
These dedicated methods were added to C++20 to provide clearer alternatives
to common subspan operations. They improve readability by better expressing
intent and are less error-prone by eliminating manual offset calculations.
For example:
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)) are not transformed as they have no clearer equivalent.
---
.../clang-tidy/readability/CMakeLists.txt | 1 +
.../readability/ReadabilityTidyModule.cpp | 3 +
.../readability/UseSpanFirstLastCheck.cpp | 93 ++
.../readability/UseSpanFirstLastCheck.h | 43 +
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../readability/use-span-first-last.rst | 24 +++
.../readability/use-span-first-last.cpp | 171 ++
8 files changed, 342 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index f1f3cde32feff..941416160cb21 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -63,6 +63,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
UppercaseLiteralSuffixCheck.cpp
UseAnyOfAllOfCheck.cpp
UseConcisePreprocessorDirectivesCheck.cpp
+ UseSpanFirstLastCheck.cpp
UseStdMinMaxCheck.cpp
LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index c582dc98eac6b..f213c02dd9fee 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -65,6 +65,7 @@
#include "UppercaseLiteralSuffixCheck.h"
#include "UseAnyOfAllOfCheck.h"
#include "UseConcisePreprocessorDirectivesCheck.h"
+#include "UseSpanFirstLastCheck.h"
#include "UseStdMinMaxCheck.h"
namespace clang::tidy {
@@ -188,6 +189,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-use-anyofallof");
CheckFactories.registerCheck(
"readability-use-concise-preprocessor-directives");
+CheckFactories.registerCheck(
+"readability-use-span-first-last");
CheckFactories.registerCheck(
"readability-use-std-min-max");
}
diff --git a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
new file mode 100644
index 0..a887f6d6e32ab
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
@@ -0,0 +1,93 @@
+//===--- 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 "../utils/Matchers.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;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ argumentCountIs(2),
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(spa
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
github-actions[bot] wrote: :warning: C/C++ code linter, clang-tidy found issues in your code. :warning: You can test this locally with the following command: ```bash git diff -U0 origin/main...HEAD -- clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp | python3 clang-tools-extra/clang-tidy/tool/clang-tidy-diff.py -path build -p1 -quiet ``` View the output from clang-tidy here. ``` clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp:79:3: warning: variable 'CountText' of type 'StringRef' can be declared 'const' [misc-const-correctness] 79 | StringRef CountText = Lexer::getSourceText( | ^ | const clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp:82:3: warning: variable 'SpanText' of type 'StringRef' can be declared 'const' [misc-const-correctness] 82 | StringRef SpanText = Lexer::getSourceText( | ^ | const clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp:86:3: warning: variable 'Replacement' of type 'std::string' (aka 'basic_string') can be declared 'const' [misc-const-correctness] 86 | std::string Replacement = | ^ | const ``` https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/hjanuschka updated
https://github.com/llvm/llvm-project/pull/118074
>From 85ef34a32da77e145701b6a392b10e532d4ea6e5 Mon Sep 17 00:00:00 2001
From: Helmut Januschka
Date: Wed, 18 Feb 2026 08:58:31 +0100
Subject: [PATCH] [clang-tidy] Add readability-use-span-first-last check
Add new clang-tidy check that suggests using std::span's more expressive
first() and last() member functions instead of equivalent subspan() calls.
These dedicated methods were added to C++20 to provide clearer alternatives
to common subspan operations. They improve readability by better expressing
intent and are less error-prone by eliminating manual offset calculations.
For example:
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)) are not transformed as they have no clearer equivalent.
---
.../clang-tidy/readability/CMakeLists.txt | 1 +
.../readability/ReadabilityTidyModule.cpp | 3 +
.../readability/UseSpanFirstLastCheck.cpp | 93 +++
.../readability/UseSpanFirstLastCheck.h | 43 +
clang-tools-extra/docs/ReleaseNotes.rst | 6 +
.../docs/clang-tidy/checks/list.rst | 1 +
.../readability/use-span-first-last.rst | 24 +++
.../readability/use-span-first-last.cpp | 150 ++
8 files changed, 321 insertions(+)
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
create mode 100644
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
create mode 100644
clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
create mode 100644
clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
diff --git a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
index f1f3cde32feff..941416160cb21 100644
--- a/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/readability/CMakeLists.txt
@@ -63,6 +63,7 @@ add_clang_library(clangTidyReadabilityModule STATIC
UppercaseLiteralSuffixCheck.cpp
UseAnyOfAllOfCheck.cpp
UseConcisePreprocessorDirectivesCheck.cpp
+ UseSpanFirstLastCheck.cpp
UseStdMinMaxCheck.cpp
LINK_LIBS
diff --git a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
index c582dc98eac6b..f213c02dd9fee 100644
--- a/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp
@@ -65,6 +65,7 @@
#include "UppercaseLiteralSuffixCheck.h"
#include "UseAnyOfAllOfCheck.h"
#include "UseConcisePreprocessorDirectivesCheck.h"
+#include "UseSpanFirstLastCheck.h"
#include "UseStdMinMaxCheck.h"
namespace clang::tidy {
@@ -188,6 +189,8 @@ class ReadabilityModule : public ClangTidyModule {
"readability-use-anyofallof");
CheckFactories.registerCheck(
"readability-use-concise-preprocessor-directives");
+CheckFactories.registerCheck(
+"readability-use-span-first-last");
CheckFactories.registerCheck(
"readability-use-std-min-max");
}
diff --git a/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
new file mode 100644
index 0..a887f6d6e32ab
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
@@ -0,0 +1,93 @@
+//===--- 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 "../utils/Matchers.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;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ argumentCountIs(2),
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(sp
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
5chmidti wrote:
> but now templates seem to be skipped completely
Template instantiations are skipped, not their definition. The matchers no
longer work, because the types you are working with are no longer necessarily
concrete types, but instead they can be dependent. And so can be expressions.
In addition to the matchers for non-dependent code, you need to add matchers
for the dependent case. See godbolt link I had linked:
https://godbolt.org/z/GrfMTxeGa
and the included matcher (example, not full matcher):
```
callExpr(// no longer a CXXMemberCallExpr
callee(
cxxDependentScopeMemberExpr( // the callee is now a dependent member
hasMemberName("subspan"),
hasObjectExpression(
hasType( // this part is effectively HasSpanType
but for a dependent type of `std::span`
elaboratedType(
namesType(
qualType(
hasCanonicalType(
qualType(
hasDeclaration(
namedDecl(hasName("::std::span"))
)
)
)
)
)
)
)
)
)
)
)
```
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,149 @@
+// RUN: %check_clang_tidy -std=c++20 %s readability-use-span-first-last %t
+
+namespace std {
+template
+class span {
+ T* ptr;
+ __SIZE_TYPE__ len;
+
+public:
+ span(T* p, __SIZE_TYPE__ l) : ptr(p), len(l) {}
+
+ span subspan(__SIZE_TYPE__ offset) const {
+return span(ptr + offset, len - offset);
+ }
+
+ span subspan(__SIZE_TYPE__ offset, __SIZE_TYPE__ count) const {
+return span(ptr + offset, count);
+ }
+
+ span first(__SIZE_TYPE__ count) const {
+return span(ptr, count);
+ }
+
+ span last(__SIZE_TYPE__ count) const {
+return span(ptr + (len - count), count);
+ }
+
+ __SIZE_TYPE__ size() const { return len; }
+};
+} // namespace std
+
+// Add here, right after the std namespace closes:
+namespace std::ranges {
+ template
+ __SIZE_TYPE__ size(const span& s) { return s.size(); }
+}
+
+void test() {
+ int arr[] = {1, 2, 3, 4, 5};
+ std::span 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(s.size() - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub2 = s.last(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
+ auto sub5 = s.subspan(2); // No warning
+
+
+#define ZERO 0
+#define TWO 2
+#define SIZE_MINUS(s, n) s.size() - n
+#define MAKE_SUBSPAN(obj, n) obj.subspan(0, n)
+#define MAKE_LAST_N(obj, n) obj.subspan(obj.size() - n)
+
+ auto sub6 = s.subspan(SIZE_MINUS(s, 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub6 = s.last(2);
+
+ auto sub7 = MAKE_SUBSPAN(s, 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: prefer 'span::first()' over
'subspan()'
+ // CHECK-FIXES: auto sub7 = s.first(3);
+
+ auto sub8 = MAKE_LAST_N(s, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub8 = s.last(2);
+
+}
+
+template
+void testTemplate() {
+ T arr[] = {1, 2, 3, 4, 5};
+ std::span 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(s.size() - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub2 = s.last(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
+ auto sub5 = s.subspan(2); // No warning
+
+ auto complex = s.subspan(0 + (s.size() - 2), 3); // No warning
+
+ auto complex2 = s.subspan(100 + (s.size() - 2)); // No warning
+}
+
+// Test instantiation
+void testInt() {
+ testTemplate();
+}
+
+void test_ranges() {
+ int arr[] = {1, 2, 3, 4, 5};
+ std::span s(arr, 5);
+
+ auto sub1 = s.subspan(std::ranges::size(s) - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub1 = s.last(2);
+
+ __SIZE_TYPE__ n = 2;
+ auto sub2 = s.subspan(std::ranges::size(s) - n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub2 = s.last(n);
+}
+
+void test_different_spans() {
+ int arr1[] = {1, 2, 3, 4, 5};
+ int arr2[] = {6, 7, 8, 9, 10};
+ std::span s1(arr1, 5);
+ std::span s2(arr2, 5);
+
+ // These should NOT trigger warnings as they use size() from a different span
+ auto sub1 = s1.subspan(s2.size() - 2); // No warning
+ auto sub2 = s2.subspan(s1.size() - 3); // No warning
+
+ // Also check with std::ranges::size
+ auto sub3 = s1.subspan(std::ranges::size(s2) - 2); // No warning
+ auto sub4 = s2.subspan(std::ranges::size(s1) - 3); // No warning
+
+ // Mixed usage should also not trigger
+ auto sub5 = s1.subspan(s2.size() - s1.size()); // No warning
+
+ // Verify that correct usage still triggers warnings
+ auto good1 = s1.subspan(s1.size() - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto good1 = s1.last(2);
+
+ auto good2 = s2.subspan(std::ranges::size(s2) - 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto good2 = s2.last(3);
+}
denzor200 wrote:
please add `test_span_of_bytes`:
```
std::byte arr[] = {0x1, 0x2, 0x3, 0x4, 0x5};
std::span s(arr, 5);
// ...
auto sub2 = s.subspan(s.size_bytes() - 2);
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()'
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
hjanuschka wrote:
feedback addressed, you were right about the template case, it is really
broken, adding:
```
std::optional getCheckTraversalKind() const override {
return TK_IgnoreUnlessSpelledInSource;
}
```
fixes it, but now templates seem to be skipped completely. and existing tests
are not "passing" anymore:
```
template
void testTemplate() {
T arr[] = {1, 2, 3, 4, 5};
std::span 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(s.size() - 2);
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
// CHECK-FIXES: auto sub2 = s.last(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
auto sub5 = s.subspan(2); // No warning
auto complex = s.subspan(0 + (s.size() - 2), 3); // No warning
auto complex2 = s.subspan(100 + (s.size() - 2)); // No warning
}
```
do you have any ideas?
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/5chmidti commented:
You could try to implement the check with `TK_TraverseUnlessSpelledInSource` if
you'd like, which would only add two more matchers via `addMatcher`, and the
traversal kind function (https://godbolt.org/z/GrfMTxeGa). IMO, this can be
done in a follow-up by someone else as well.
`TK_TraversUnlessSpelledInSource` would not only have performance implications,
but also niche correctness improvements:
```diff
struct IntSpan {
IntSpan(int*);
IntSpan subspan(int, int);
};
template
void test_instantiations(T val) {
-val.subspan(0, 5);
+val.first(5); // `IntSpan` does not have `first`
}
void test_intantiations() {
int array[] = {0, 1, 2, 3, 4};
auto sp = std::span{array, 5};
test_instantiations(sp);
IntSpan intsp = array;
test_instantiations(intsp);
}
```
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -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 "../utils/ASTUtils.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ argumentCountIs(2),
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(span.size() - n) or
span.subspan(std::ranges::size(span)
+ // - n)
+ // -> last(n)
+ const auto SizeCall = anyOf(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size"),
+ on(expr(isStatementIdenticalToBoundNode("span_object",
+ callExpr(callee(functionDecl(
+ hasAnyName("::std::size", "::std::ranges::size"))),
+ hasArgument(
+ 0, expr(isStatementIdenticalToBoundNode("span_object");
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+ argumentCountIs(1),
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, binaryOperator(hasOperatorName("-"), hasLHS(SizeCall),
+hasRHS(expr().bind("count")
+ .bind("last_subspan"),
+ this);
+}
+
+void UseSpanFirstLastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *SpanObj = Result.Nodes.getNodeAs("span_object");
+ if (!SpanObj)
+return;
+
+ StringRef SpanText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(SpanObj->getSourceRange()),
+ *Result.SourceManager, Result.Context->getLangOpts());
5chmidti wrote:
Please move this variable down, just before it is used, somewhere around
`CountText`.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/5chmidti edited https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -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 "../utils/ASTUtils.h" +#include "../utils/Matchers.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/RecursiveASTVisitor.h" 5chmidti wrote: Please remove this unused include https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/hjanuschka commented: ready for re-review https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
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
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/31] [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(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck("modernize-use-using");
+
CheckFactories.registerCheck("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 00..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("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(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,104 @@
+//===--- 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 "../utils/ASTUtils.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(span.size() - n) or
span.subspan(std::ranges::size(span)
+ // - n)
+ // -> last(n)
+ const auto SizeCall = anyOf(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size"),
+ on(expr(isStatementIdenticalToBoundNode("span_object",
+ callExpr(callee(functionDecl(
+ hasAnyName("::std::size", "::std::ranges::size"))),
+ hasArgument(
+ 0, expr(isStatementIdenticalToBoundNode("span_object");
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, binaryOperator(hasOperatorName("-"), hasLHS(SizeCall),
+hasRHS(expr().bind("count",
+ argumentCountIs(1))
+ .bind("last_subspan"),
+ this);
+}
+
+void UseSpanFirstLastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *SpanObj = Result.Nodes.getNodeAs("span_object");
+ if (!SpanObj)
+return;
+
+ StringRef SpanText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(SpanObj->getSourceRange()),
+ *Result.SourceManager, Result.Context->getLangOpts());
+
+ if (const auto *FirstCall =
hjanuschka wrote:
thank you - haven't thought of this, great idea and super improvement!
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,104 @@
+//===--- 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 "../utils/ASTUtils.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(span.size() - n) or
span.subspan(std::ranges::size(span)
+ // - n)
+ // -> last(n)
+ const auto SizeCall = anyOf(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size"),
+ on(expr(isStatementIdenticalToBoundNode("span_object",
+ callExpr(callee(functionDecl(
+ hasAnyName("::std::size", "::std::ranges::size"))),
+ hasArgument(
+ 0, expr(isStatementIdenticalToBoundNode("span_object");
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, binaryOperator(hasOperatorName("-"), hasLHS(SizeCall),
+hasRHS(expr().bind("count",
+ argumentCountIs(1))
5chmidti wrote:
The `argumentCountIs` matcher should be the first this that is checked inside
the `cxxMemberCallExpr` matcher, because that is a simple integer comparison
that can exclude a lot of AST nodes from further matching. The same applies to
the other `cxxMemberCallExpr` above.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,104 @@
+//===--- 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 "../utils/ASTUtils.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(span.size() - n) or
span.subspan(std::ranges::size(span)
+ // - n)
+ // -> last(n)
+ const auto SizeCall = anyOf(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size"),
+ on(expr(isStatementIdenticalToBoundNode("span_object",
+ callExpr(callee(functionDecl(
+ hasAnyName("::std::size", "::std::ranges::size"))),
+ hasArgument(
+ 0, expr(isStatementIdenticalToBoundNode("span_object");
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, binaryOperator(hasOperatorName("-"), hasLHS(SizeCall),
+hasRHS(expr().bind("count",
+ argumentCountIs(1))
+ .bind("last_subspan"),
+ this);
+}
+
+void UseSpanFirstLastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *SpanObj = Result.Nodes.getNodeAs("span_object");
+ if (!SpanObj)
+return;
+
+ StringRef SpanText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(SpanObj->getSourceRange()),
+ *Result.SourceManager, Result.Context->getLangOpts());
+
+ if (const auto *FirstCall =
5chmidti wrote:
Except for which of the two functions should replace the matched code, these
two `if` statements are identical. You could bind both cases to the same name,
retrieve the `CXXMemberCallExpr`, and compare the number of arguments to
determine which case it is:
```c++
const auto IsFirst = SubSpan->getNumArgs() == 2;
const StringRef FirstOrLast = IsFirst ? "first" : "last";
std::string Replacement =
(SpanText + "." + FirstOrLast + "(" + CountText + ")").str();
diag(SubSpan->getBeginLoc(), "prefer 'span::%0()' over 'subspan()'")
<< FirstOrLast
<< FixItHint::CreateReplacement(SubSpan->getSourceRange(), Replacement);
```
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,104 @@
+//===--- 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 "../utils/ASTUtils.h"
+#include "../utils/Matchers.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+using namespace clang::tidy::matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(span.size() - n) or
span.subspan(std::ranges::size(span)
+ // - n)
+ // -> last(n)
+ const auto SizeCall = anyOf(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size"),
+ on(expr(isStatementIdenticalToBoundNode("span_object",
+ callExpr(callee(functionDecl(
+ hasAnyName("::std::size", "::std::ranges::size"))),
+ hasArgument(
+ 0, expr(isStatementIdenticalToBoundNode("span_object");
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, binaryOperator(hasOperatorName("-"), hasLHS(SizeCall),
+hasRHS(expr().bind("count",
+ argumentCountIs(1))
+ .bind("last_subspan"),
+ this);
+}
+
+void UseSpanFirstLastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *SpanObj = Result.Nodes.getNodeAs("span_object");
+ if (!SpanObj)
+return;
+
+ StringRef SpanText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(SpanObj->getSourceRange()),
+ *Result.SourceManager, Result.Context->getLangOpts());
+
+ if (const auto *FirstCall =
+ Result.Nodes.getNodeAs("first_subspan")) {
+const auto *Count = Result.Nodes.getNodeAs("count");
+assert(Count && "Count expression must exist due to AST matcher");
+
+StringRef CountText = Lexer::getSourceText(
+CharSourceRange::getTokenRange(Count->getSourceRange()),
+*Result.SourceManager, Result.Context->getLangOpts());
+
+std::string Replacement =
+(Twine(SpanText) + ".first(" + CountText + ")").str();
+
+diag(FirstCall->getBeginLoc(), "prefer 'span::first()' over 'subspan()'")
+<< FixItHint::CreateReplacement(FirstCall->getSourceRange(),
+Replacement);
+return;
+ }
+
+ if (const auto *LastCall =
+ Result.Nodes.getNodeAs("last_subspan")) {
+const auto *Count = Result.Nodes.getNodeAs("count");
+assert(Count && "Count expression must exist due to AST matcher");
+
+StringRef CountText = Lexer::getSourceText(
+CharSourceRange::getTokenRange(Count->getSourceRange()),
+*Result.SourceManager, Result.Context->getLangOpts());
+
+std::string Replacement = SpanText.str() + ".last(" + CountText.str() +
")";
5chmidti wrote:
Please use `llvm::Twine`, as in line 80
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/5chmidti edited https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -120,3 +120,30 @@ void test_ranges() {
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
// CHECK-FIXES: auto sub2 = s.last(n);
}
+
+void test_different_spans() {
+ int arr1[] = {1, 2, 3, 4, 5};
+ int arr2[] = {6, 7, 8, 9, 10};
+ std::span s1(arr1, 5);
+ std::span s2(arr2, 5);
+
+ // These should NOT trigger warnings as they use size() from a different span
+ auto sub1 = s1.subspan(s2.size() - 2); // No warning
+ auto sub2 = s2.subspan(s1.size() - 3); // No warning
+
+ // Also check with std::ranges::size
+ auto sub3 = s1.subspan(std::ranges::size(s2) - 2); // No warning
+ auto sub4 = s2.subspan(std::ranges::size(s1) - 3); // No warning
+
+ // Mixed usage should also not trigger
+ auto sub5 = s1.subspan(s2.size() - s1.size()); // No warning
+
+ // Verify that correct usage still triggers warnings
+ auto good1 = s1.subspan(s1.size() - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto good1 = s1.last(2);
+
+ auto good2 = s2.subspan(std::ranges::size(s2) - 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:16: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto good2 = s2.last(3);
+}
EugeneZelenko wrote:
Please add newline.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
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
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/29] [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(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck("modernize-use-using");
+
CheckFactories.registerCheck("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 00..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("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(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,96 @@
+//===--- 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/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(size() - n) or span.subspan(std::ranges::size(span) -
n)
+ // -> last(n)
+ const auto SizeCall = anyOf(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size")),
+ callExpr(callee(
+ functionDecl(hasAnyName("::std::size", "::std::ranges::size");
hjanuschka wrote:
🙇 - that did the trick, awesome!
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,96 @@
+//===--- 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/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(size() - n) or span.subspan(std::ranges::size(span) -
n)
+ // -> last(n)
+ const auto SizeCall = anyOf(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size")),
+ callExpr(callee(
+ functionDecl(hasAnyName("::std::size", "::std::ranges::size");
5chmidti wrote:
This has generally been done with `utils::areStatementsIdentical` or its
matcher equivalent, which is not
See
https://github.com/llvm/llvm-project/blob/3c464d23682b0f9e6f70965e8f8f3861c9ba5417/clang-tools-extra/clang-tidy/modernize/UseStartsEndsWithCheck.cpp#L73-L75
https://github.com/llvm/llvm-project/blob/3c464d23682b0f9e6f70965e8f8f3861c9ba5417/clang-tools-extra/clang-tidy/modernize/MinMaxUseInitializerListCheck.cpp#L151-L152
https://github.com/llvm/llvm-project/blob/3c464d23682b0f9e6f70965e8f8f3861c9ba5417/clang-tools-extra/clang-tidy/utils/Matchers.h#L164-L168
https://github.com/llvm/llvm-project/blob/3c464d23682b0f9e6f70965e8f8f3861c9ba5417/clang-tools-extra/clang-tidy/utils/ASTUtils.h#L40-L41
You can add `isStatementIdenticalToBoundNode` to your `SizeCall` matcher,
checking the argument or object to be equivalent to `span_object`, e.g.,
`on(expr(isStatementIdenticalToBoundNode("span_object")))`
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,96 @@
+//===--- 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/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(size() - n) or span.subspan(std::ranges::size(span) -
n)
+ // -> last(n)
+ const auto SizeCall = anyOf(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size")),
+ callExpr(callee(
+ functionDecl(hasAnyName("::std::size", "::std::ranges::size");
hjanuschka wrote:
i'v been banging my head against this problem the whole weekend, with no
success 🙈
do you have any existing check in mind that has a similar use case
`span.subspan(other.size() - n)`
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,96 @@
+//===--- 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/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(size() - n) or span.subspan(std::ranges::size(span) -
n)
5chmidti wrote:
nit: `size()` -> `span.size()`
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,96 @@
+//===--- 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/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(size() - n) or span.subspan(std::ranges::size(span) -
n)
+ // -> last(n)
+ const auto SizeCall = anyOf(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size")),
+ callExpr(callee(
+ functionDecl(hasAnyName("::std::size", "::std::ranges::size");
5chmidti wrote:
Both of these are not constrained to be the span object. You'll probably want
to swap the `callee` and `on` matchers, and add an
`equalsBoundNode("span_object")` check for the `on` and `hasArgument(0,...` in
the matchers of `SizeCall`.
Right now, this would match `span.subspan(other.size() - n)` and turn it into
`span.last(n)`.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,96 @@
+//===--- 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/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(size() - n) or span.subspan(std::ranges::size(span) -
n)
+ // -> last(n)
+ const auto SizeCall = anyOf(
+ cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size")),
+ callExpr(callee(
+ functionDecl(hasAnyName("::std::size", "::std::ranges::size");
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, binaryOperator(hasOperatorName("-"), hasLHS(SizeCall),
+hasRHS(expr().bind("count",
+ argumentCountIs(1))
+ .bind("last_subspan"),
+ this);
+}
+
+void UseSpanFirstLastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *SpanObj = Result.Nodes.getNodeAs("span_object");
+ if (!SpanObj)
+return;
+
+ StringRef SpanText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(SpanObj->getSourceRange()),
+ *Result.SourceManager, Result.Context->getLangOpts());
+
+ if (const auto *FirstCall =
+ Result.Nodes.getNodeAs("first_subspan")) {
+const auto *Count = Result.Nodes.getNodeAs("count");
+assert(Count && "Count expression must exist due to AST matcher");
+
+StringRef CountText = Lexer::getSourceText(
+CharSourceRange::getTokenRange(Count->getSourceRange()),
+*Result.SourceManager, Result.Context->getLangOpts());
+
+std::string Replacement =
+(Twine(SpanText) + ".first(" + CountText + ")").str();
+
+diag(FirstCall->getBeginLoc(), "prefer 'span::first()' over 'subspan()'")
+<< FixItHint::CreateReplacement(FirstCall->getSourceRange(),
+Replacement);
+ }
5chmidti wrote:
When you're inside the `FirstCall` case, you can't enter the `LastCall` case,
so just return at the end of this if-branch.
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/5chmidti commented: > wasting effort would not be the problem (it never is; learning is the goal) - > right now i just dont understand the difference, and therefore dont see the > benefit, as far as i see it would introduce more complicated code to still > handle templates. `TK_IgnoreUnlessSpelledInSource` changes how the AST is seen by excluding AST nodes that are implicit, e.g., implicit template instantiations or implicit casts. This can in some cases improve the readability of the check by simplifying the matcher, or improve performance because not every implicit AST node, such as instantiated templates, are traversed and checked. The effect of this varies a lot, depending on what a check is trying to achieve. Checkout the [Traverse Mode](https://clang.llvm.org/docs/LibASTMatchersReference.html#:~:text=Traverse%20Mode) section on top of the AST matcher reference for more details, and importantly, a lot of examples on the differences. For this check, the benefit will be performance, and not readability. It will be possible to skip template instantiations, of which there may be many because `span` is *the* generic view on contiguous memory (joined by `mdspan`). Overall, the checking done by this check is not too costly, so there would not be a massive difference. Godbolt with the AST view and clang-query are great to iteratively build up a matcher, checkout https://godbolt.org/z/GrfMTxeGa for matching uninstantiated templates with `TK_IgnoreUnlessSpelledInSource` https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/5chmidti edited https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,24 @@ +.. title:: clang-tidy - readability-use-span-first-last + +readability-use-span-first-last +=== + +Suggests using ``std::span::first()`` and ``std::span::last()`` member functions +instead of equivalent ``subspan()``. These dedicated methods were added to C++20 +to provide more expressive alternatives to common subspan operations. + +Covered scenarios: + +=== EugeneZelenko wrote: You just need to look on diff :-) https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,24 @@ +.. title:: clang-tidy - readability-use-span-first-last + +readability-use-span-first-last +=== + +Suggests using ``std::span::first()`` and ``std::span::last()`` member functions +instead of equivalent ``subspan()``. These dedicated methods were added to C++20 +to provide more expressive alternatives to common subspan operations. + +Covered scenarios: + +=== hjanuschka wrote: thank you, super sorry that this is such a back n forth, but my brain seems to be wired a way to not being able to figure RST out 🙈 https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
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
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/26] [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(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck("modernize-use-using");
+
CheckFactories.registerCheck("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 00..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("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(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,24 @@ +.. title:: clang-tidy - readability-use-span-first-last + +readability-use-span-first-last +=== + +Suggests using ``std::span::first()`` and ``std::span::last()`` member functions +instead of equivalent ``subspan()``. These dedicated methods were added to C++20 +to provide more expressive alternatives to common subspan operations. + +Covered scenarios: + +=== EugeneZelenko wrote: Second column should be expanded on two characters to fit `s.first(n)` with all back-ticks. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
HerrCai0907 wrote: > tried using `TK_IgnoreUnlessSpelledInSource` but this breaks the template > based cases. Thanks! If it will waste you more effort, it's good to keep it like this. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
hjanuschka wrote: feedback addressed. @5chmidti - added range support @HerrCai0907 tried using `TK_IgnoreUnlessSpelledInSource` but this breaks the template based cases. i am afraid having `TK_IgnoreUnlessSpelledInSource` would require way more complex matchers? how big of a deal is to not use it? https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
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
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/25] [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(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck("modernize-use-using");
+
CheckFactories.registerCheck("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 00..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("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(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -18,108 +18,77 @@ using namespace clang::ast_matchers;
namespace clang::tidy::readability {
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);
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(size() - n) -> last(n)
+ const auto SizeCall = cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size"));
hjanuschka wrote:
thanks, great feedback, added matchers and test cases for ranges
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/HerrCai0907 edited https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/HerrCai0907 commented: In general LGTM after considering more size cases. It may still work on `TraversalKind::TK_IgnoreUnlessSpelledInSource`. Could you try to set traversal mode in `getCheckTraversalKind`. It can avoid noise of something like implicit cast. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,94 @@
+//===--- 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/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"));
+
+ // Match span.subspan(0, n) -> first(n)
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, integerLiteral(equals(0))),
+ hasArgument(1, expr().bind("count")), argumentCountIs(2))
+ .bind("first_subspan"),
+ this);
+
+ // Match span.subspan(size() - n) -> last(n)
+ const auto SizeCall = cxxMemberCallExpr(
+ callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("size"));
+
+ Finder->addMatcher(
+ cxxMemberCallExpr(
+
callee(memberExpr(hasDeclaration(cxxMethodDecl(hasName("subspan"),
+ on(expr(HasSpanType).bind("span_object")),
+ hasArgument(0, binaryOperator(hasOperatorName("-"), hasLHS(SizeCall),
+hasRHS(expr().bind("count",
+ argumentCountIs(1))
+ .bind("last_subspan"),
+ this);
+}
+
+void UseSpanFirstLastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *SpanObj = Result.Nodes.getNodeAs("span_object");
+ if (!SpanObj)
+return;
+
+ StringRef SpanText = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(SpanObj->getSourceRange()),
+ *Result.SourceManager, Result.Context->getLangOpts());
+
+ if (const auto *FirstCall =
+ Result.Nodes.getNodeAs("first_subspan")) {
+const auto *Count = Result.Nodes.getNodeAs("count");
+if (!Count)
+ return;
+
+StringRef CountText = Lexer::getSourceText(
+CharSourceRange::getTokenRange(Count->getSourceRange()),
+*Result.SourceManager, Result.Context->getLangOpts());
+
+std::string Replacement =
+SpanText.str() + ".first(" + CountText.str() + ")";
+
+diag(FirstCall->getBeginLoc(), "prefer 'span::first()' over 'subspan()'")
+<< FixItHint::CreateReplacement(FirstCall->getSourceRange(),
+Replacement);
+ }
+
+ if (const auto *LastCall =
+ Result.Nodes.getNodeAs("last_subspan")) {
+const auto *Count = Result.Nodes.getNodeAs("count");
+if (!Count)
+ return;
HerrCai0907 wrote:
can be assert
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
carlosgalvezp wrote: You have duplicated `readability` when referring to the document: ``` - 'clang-tidy/checks/readability/readability-use-span-first-last' + 'clang-tidy/checks/readability/use-span-first-last' ``` You can build them with: `ninja docs-clang-tools-html`, with following CMake config: ``` cmake -B build -GNinja -DCMAKE_BUILD_TYPE=Release -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra" -DLLVM_ENABLE_SPHINX=ON ./llvm ``` https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
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
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/23] [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(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck("modernize-use-using");
+
CheckFactories.registerCheck("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 00..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("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(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,24 @@ +.. title:: clang-tidy - readability-use-span-first-last + +readability-use-span-first-last +=== + +Suggests using ``std::span::first()`` and ``std::span::last()`` member functions +instead of equivalent ``subspan()``. These dedicated methods were added to C++20 +to provide more expressive alternatives to common subspan operations. + +Covered scenarios: + +=== EugeneZelenko wrote: Please reduce first column on 4 characters and add two characters to second column, so all cell content will be covered. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/hjanuschka commented: @5chmidti removed the recursive visitor and refactored it, hope this is correct now, added the test cases, you mentioned, and you where right in the complex version they failed, work now! https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
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
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/22] [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(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck("modernize-use-using");
+
CheckFactories.registerCheck("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 00..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("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(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,24 @@ +.. 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: + +== hjanuschka wrote: rst formatting is really tough 😬 https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,98 @@
+// RUN: %check_clang_tidy -std=c++20 %s readability-use-span-first-last %t
+
+namespace std {
+template
+class span {
+ T* ptr;
+ __SIZE_TYPE__ len;
+
+public:
+ span(T* p, __SIZE_TYPE__ l) : ptr(p), len(l) {}
+
+ span subspan(__SIZE_TYPE__ offset) const {
+return span(ptr + offset, len - offset);
+ }
+
+ span subspan(__SIZE_TYPE__ offset, __SIZE_TYPE__ count) const {
+return span(ptr + offset, count);
+ }
+
+ span first(__SIZE_TYPE__ count) const {
+return span(ptr, count);
+ }
+
+ span 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 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(s.size() - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub2 = s.last(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
+ auto sub5 = s.subspan(2); // No warning
+
+
+#define ZERO 0
+#define TWO 2
+#define SIZE_MINUS(s, n) s.size() - n
+#define MAKE_SUBSPAN(obj, n) obj.subspan(0, n)
+#define MAKE_LAST_N(obj, n) obj.subspan(obj.size() - n)
+
+ auto sub6 = s.subspan(SIZE_MINUS(s, 2));
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub6 = s.last(2);
+
+ auto sub7 = MAKE_SUBSPAN(s, 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:28: warning: prefer 'span::first()' over
'subspan()'
+ // CHECK-FIXES: auto sub7 = s.first(3);
+
+ auto sub8 = MAKE_LAST_N(s, 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:27: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub8 = s.last(2);
+
+}
+
+template
+void testTemplate() {
+ T arr[] = {1, 2, 3, 4, 5};
+ std::span 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(s.size() - 2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over
'subspan()'
+ // CHECK-FIXES: auto sub2 = s.last(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
+ auto sub5 = s.subspan(2); // No warning
+}
+
+// Test instantiation
+void testInt() {
+ testTemplate();
+}
5chmidti wrote:
Missing newline
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,125 @@
+//===--- 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/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+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("subspan");
+ if (!Call)
+return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ 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;
+
+ class SubspanVisitor : public RecursiveASTVisitor {
+ public:
+SubspanVisitor(const ASTContext &Context) : Context(Context) {}
+
+TraversalKind getTraversalKind() const {
+ return TK_IgnoreUnlessSpelledInSource;
+}
+
+bool VisitIntegerLiteral(IntegerLiteral *IL) {
+ if (IL->getValue() == 0)
+IsZeroOffset = true;
+ return true;
+}
+
+bool VisitBinaryOperator(BinaryOperator *BO) {
+ if (BO->getOpcode() == BO_Sub) {
+if (const auto *SizeCall = dyn_cast(BO->getLHS())) {
+ if (SizeCall->getMethodDecl()->getName() == "size") {
+IsSizeMinusN = true;
+SizeMinusArg = BO->getRHS();
+ }
+}
+ }
+ return true;
+}
+
+bool IsZeroOffset = false;
+bool IsSizeMinusN = false;
+const Expr *SizeMinusArg = nullptr;
+
+ private:
+const ASTContext &Context;
+ };
+
+ SubspanVisitor Visitor(Context);
+ Visitor.TraverseStmt(const_cast(Offset->IgnoreImpCasts()));
+
+ // Build replacement text
+ std::string Replacement;
+ if (Visitor.IsZeroOffset && Count) {
+// subspan(0, count) -> first(count)
+const StringRef CountStr = Lexer::getSourceText(
+CharSourceRange::getTokenRange(Count->getSourceRange()),
+Context.getSourceManager(), Context.getLangOpts());
+const auto *Base =
+cast(Call)->getImplicitObjectArgument();
5chmidti wrote:
`Call` is already a `CXXMemberCallExpr`
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,125 @@
+//===--- 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/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+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("subspan");
+ if (!Call)
+return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ 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;
+
+ class SubspanVisitor : public RecursiveASTVisitor {
+ public:
+SubspanVisitor(const ASTContext &Context) : Context(Context) {}
+
+TraversalKind getTraversalKind() const {
+ return TK_IgnoreUnlessSpelledInSource;
+}
+
+bool VisitIntegerLiteral(IntegerLiteral *IL) {
+ if (IL->getValue() == 0)
+IsZeroOffset = true;
+ return true;
+}
+
+bool VisitBinaryOperator(BinaryOperator *BO) {
+ if (BO->getOpcode() == BO_Sub) {
+if (const auto *SizeCall = dyn_cast(BO->getLHS())) {
+ if (SizeCall->getMethodDecl()->getName() == "size") {
+IsSizeMinusN = true;
+SizeMinusArg = BO->getRHS();
+ }
+}
+ }
+ return true;
+}
+
+bool IsZeroOffset = false;
+bool IsSizeMinusN = false;
+const Expr *SizeMinusArg = nullptr;
+
+ private:
+const ASTContext &Context;
+ };
+
+ SubspanVisitor Visitor(Context);
+ Visitor.TraverseStmt(const_cast(Offset->IgnoreImpCasts()));
+
+ // Build replacement text
+ std::string Replacement;
+ if (Visitor.IsZeroOffset && Count) {
+// subspan(0, count) -> first(count)
+const StringRef CountStr = Lexer::getSourceText(
+CharSourceRange::getTokenRange(Count->getSourceRange()),
+Context.getSourceManager(), Context.getLangOpts());
+const auto *Base =
+cast(Call)->getImplicitObjectArgument();
+const StringRef BaseStr = Lexer::getSourceText(
+CharSourceRange::getTokenRange(Base->getSourceRange()),
+Context.getSourceManager(), Context.getLangOpts());
+Replacement = BaseStr.str() + ".first(" + CountStr.str() + ")";
+ } else if (Visitor.IsSizeMinusN && Visitor.SizeMinusArg) {
+// subspan(size() - n) -> last(n)
+const StringRef ArgStr = Lexer::getSourceText(
+CharSourceRange::getTokenRange(Visitor.SizeMinusArg->getSourceRange()),
+Context.getSourceManager(), Context.getLangOpts());
+const auto *Base =
+cast(Call)->getImplicitObjectArgument();
5chmidti wrote:
Same as above
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,125 @@
+//===--- 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/AST/RecursiveASTVisitor.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::readability {
+
+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("subspan");
+ if (!Call)
+return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ 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;
+
+ class SubspanVisitor : public RecursiveASTVisitor {
5chmidti wrote:
(on mobile, so I can't check)
The visitor is overkill and will, most importantly, produce false positives:
`s.subspan(0, 100 + (s.size() - n))`, because the visitor only searches for the
specific pattern being contained in the arg, and not that it is actually the
arg itself. It would be much cleaner to check this in the matcher:
`anyOf(callExpr(has argument(0, integer literal(equals(0)).bind("zero_offset"),
/*n arg*/), callExpr(/*size-n arg*/)`
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,24 @@ +.. 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. 5chmidti wrote: Missing newline https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,24 @@ +.. 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: + +== EugeneZelenko wrote: Please enlarge first column to `s.subspan(s.size() - n)` (with all back-ticks) width. Second column is too wide. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,24 @@ +.. 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 EugeneZelenko wrote: Please synchronize first statement with Release Notes. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
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
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/20] [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(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck("modernize-use-using");
+
CheckFactories.registerCheck("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 00..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("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(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
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
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/19] [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(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck("modernize-use-using");
+
CheckFactories.registerCheck("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 00..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("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(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -58,6 +58,7 @@ add_clang_library(clangTidyReadabilityModule STATIC UppercaseLiteralSuffixCheck.cpp UseAnyOfAllOfCheck.cpp UseStdMinMaxCheck.cpp + UseSpanFirstLastCheck.cpp EugeneZelenko wrote: I think one-line change is fine in this pull request. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/hjanuschka edited https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/hjanuschka commented: MANY thanks, yet again, feedback addressed. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
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
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/18] [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(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck("modernize-use-using");
+
CheckFactories.registerCheck("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 00..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("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(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
hjanuschka wrote: 🙈 https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
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
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/15] [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(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck("modernize-use-using");
+
CheckFactories.registerCheck("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 00..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("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(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/5chmidti edited https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
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
Date: Fri, 29 Nov 2024 10:17:49 +0100
Subject: [PATCH 01/14] [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(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck("modernize-use-using");
+
CheckFactories.registerCheck("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 00..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("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(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -58,6 +58,7 @@ add_clang_library(clangTidyReadabilityModule STATIC UppercaseLiteralSuffixCheck.cpp UseAnyOfAllOfCheck.cpp UseStdMinMaxCheck.cpp + UseSpanFirstLastCheck.cpp hjanuschka wrote: fixed, with the 2 "Use" (https://github.com/llvm/llvm-project/pull/118074/files/c7fd4d5f508d43cd7fdaf7ed9a0006687a4215ed#diff-4be36b5009d110fd7bf319b8b06cf0e86d91b04f4c30dcd8aaf94825626b2e2aL26) filenames, but there is `RedundantInlineSpecifierCheck` also wrong, should i fix this in the same PR? https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
https://github.com/5chmidti edited https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,110 @@
+//===--- 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::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus20)
+return;
+
+ // 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("subspan");
+ if (!Call)
+return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ 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;
+
+ // Check if this is subspan(0, n) -> first(n)
+ bool IsZeroOffset = false;
+ const Expr *OffsetE = Offset->IgnoreImpCasts();
+ if (const auto *IL = dyn_cast(OffsetE)) {
+IsZeroOffset = IL->getValue() == 0;
+ }
5chmidti wrote:
nit: simple single-expr `if`-bodies don't use braces in LLVM
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
@@ -0,0 +1,110 @@
+//===--- 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::readability {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus20)
+return;
+
+ // 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("subspan");
+ if (!Call)
+return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ 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;
+
+ // Check if this is subspan(0, n) -> first(n)
+ bool IsZeroOffset = false;
+ const Expr *OffsetE = Offset->IgnoreImpCasts();
+ if (const auto *IL = dyn_cast(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(OffsetE)) {
+if (BO->getOpcode() == BO_Sub) {
+ if (const auto *SizeCall = dyn_cast(BO->getLHS())) {
+if (SizeCall->getMethodDecl()->getName() == "size") {
+ IsSizeMinusN = true;
+ SizeMinusArg = BO->getRHS();
+}
+ }
+}
+ }
+
+ // 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(Call)->getImplicitObjectArgument();
+auto BaseStr = Lexer::getSourceText(
5chmidti wrote:
Please add `const`
https://github.com/llvm/llvm-project/pull/118074
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
