-
Notifications
You must be signed in to change notification settings - Fork 15.3k
[clang-tidy] Add readability-use-span-first-last check #118074
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
Add new check that modernizes std::span::subspan() calls to use the more expressive first() and last() member functions where applicable.
|
@llvm/pr-subscribers-clang-tools-extra @llvm/pr-subscribers-clang-tidy Author: Helmut Januschka (hjanuschka) ChangesAdd new clang-tidy check that suggests using std::span's more expressive first() and last() member functions instead of equivalent subspan() calls. The check modernizes code by replacing:
These dedicated methods were added to the standard 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: std::span<int> s = ...;
auto sub1 = s.subspan(0, n); // transforms to: auto sub1 = s.first(n);
auto sub2 = s.subspan(n); // transforms to: auto sub2 = s.last(s.size() - n);
auto sub3 = s.subspan(1, n); // not transformed, no direct equivalentnot sure if this is a readability or a modernize check ❓ Full diff: https://github.com/llvm/llvm-project/pull/118074.diff 7 Files Affected:
diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
index c919d49b42873a..47dd12a2640b6c 100644
--- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
+++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt
@@ -49,6 +49,7 @@ add_clang_library(clangTidyModernizeModule STATIC
UseTransparentFunctorsCheck.cpp
UseUncaughtExceptionsCheck.cpp
UseUsingCheck.cpp
+ UseSpanFirstLastCheck.cpp
LINK_LIBS
clangTidy
diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
index 18607593320635..6fc5de5aad20b7 100644
--- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
+++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp
@@ -42,6 +42,7 @@
#include "UseNullptrCheck.h"
#include "UseOverrideCheck.h"
#include "UseRangesCheck.h"
+#include "UseSpanFirstLastCheck.h"
#include "UseStartsEndsWithCheck.h"
#include "UseStdFormatCheck.h"
#include "UseStdNumbersCheck.h"
@@ -122,6 +123,8 @@ class ModernizeModule : public ClangTidyModule {
CheckFactories.registerCheck<UseUncaughtExceptionsCheck>(
"modernize-use-uncaught-exceptions");
CheckFactories.registerCheck<UseUsingCheck>("modernize-use-using");
+ CheckFactories.registerCheck<UseSpanFirstLastCheck>("modernize-use-span-first-last");
+
}
};
diff --git a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp
new file mode 100644
index 00000000000000..6cf01386b0c3fb
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp
@@ -0,0 +1,98 @@
+//===--- UseSpanFirstLastCheck.cpp - clang-tidy-----------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#include "UseSpanFirstLastCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/Lex/Lexer.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::modernize {
+
+void UseSpanFirstLastCheck::registerMatchers(MatchFinder *Finder) {
+ // Match span::subspan calls
+ const auto HasSpanType =
+ hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
+ classTemplateSpecializationDecl(hasName("::std::span"))))));
+
+ Finder->addMatcher(cxxMemberCallExpr(callee(memberExpr(hasDeclaration(
+ cxxMethodDecl(hasName("subspan"))))),
+ on(expr(HasSpanType)))
+ .bind("subspan"),
+ this);
+}
+
+void UseSpanFirstLastCheck::check(const MatchFinder::MatchResult &Result) {
+ const auto *Call = Result.Nodes.getNodeAs<CXXMemberCallExpr>("subspan");
+ if (!Call)
+ return;
+
+ handleSubspanCall(Result, Call);
+}
+
+void UseSpanFirstLastCheck::handleSubspanCall(
+ const MatchFinder::MatchResult &Result, const CXXMemberCallExpr *Call) {
+ // Get arguments
+ unsigned NumArgs = Call->getNumArgs();
+ if (NumArgs == 0 || NumArgs > 2)
+ return;
+
+ const Expr *Offset = Call->getArg(0);
+ const Expr *Count = NumArgs > 1 ? Call->getArg(1) : nullptr;
+ auto &Context = *Result.Context;
+ bool IsZeroOffset = false;
+
+ // Check if offset is zero through any implicit casts
+ const Expr *OffsetE = Offset->IgnoreImpCasts();
+ if (const auto *IL = dyn_cast<IntegerLiteral>(OffsetE)) {
+ IsZeroOffset = IL->getValue() == 0;
+ }
+
+ // Build replacement text
+ std::string Replacement;
+ if (IsZeroOffset && Count) {
+ // subspan(0, count) -> first(count)
+ auto CountStr = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Count->getSourceRange()),
+ Context.getSourceManager(), Context.getLangOpts());
+ const auto *Base =
+ cast<CXXMemberCallExpr>(Call)->getImplicitObjectArgument();
+ auto BaseStr = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Base->getSourceRange()),
+ Context.getSourceManager(), Context.getLangOpts());
+ Replacement = BaseStr.str() + ".first(" + CountStr.str() + ")";
+ } else if (NumArgs == 1) {
+ // subspan(n) -> last(size() - n)
+ auto OffsetStr = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Offset->getSourceRange()),
+ Context.getSourceManager(), Context.getLangOpts());
+
+ const auto *Base =
+ cast<CXXMemberCallExpr>(Call)->getImplicitObjectArgument();
+ auto BaseStr = Lexer::getSourceText(
+ CharSourceRange::getTokenRange(Base->getSourceRange()),
+ Context.getSourceManager(), Context.getLangOpts());
+
+ Replacement = BaseStr.str() + ".last(" + BaseStr.str() + ".size() - " +
+ OffsetStr.str() + ")";
+ }
+
+ if (!Replacement.empty()) {
+ if (IsZeroOffset && Count) {
+ diag(Call->getBeginLoc(), "prefer span::first() over subspan()")
+ << FixItHint::CreateReplacement(Call->getSourceRange(), Replacement);
+ } else {
+ diag(Call->getBeginLoc(), "prefer span::last() over subspan()")
+ << FixItHint::CreateReplacement(Call->getSourceRange(), Replacement);
+ }
+ }
+}
+
+} // namespace clang::tidy::modernize
\ No newline at end of file
diff --git a/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h
new file mode 100644
index 00000000000000..8d4c6035f7ec76
--- /dev/null
+++ b/clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h
@@ -0,0 +1,40 @@
+//===--- UseSpanFirstLastCheck.h - clang-tidy-------------------*- C++ -*-===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANFIRSTLASTCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANFIRSTLASTCHECK_H
+
+#include "../ClangTidyCheck.h"
+
+namespace clang::tidy::modernize {
+
+/// Converts std::span::subspan() calls to the more modern first()/last()
+/// methods where applicable.
+///
+/// For example:
+/// \code
+/// std::span<int> s = ...;
+/// auto sub = s.subspan(0, n); // -> auto sub = s.first(n);
+/// auto sub2 = s.subspan(n); // -> auto sub2 = s.last(s.size() - n);
+/// \endcode
+class UseSpanFirstLastCheck : public ClangTidyCheck {
+public:
+ UseSpanFirstLastCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+
+private:
+ void handleSubspanCall(const ast_matchers::MatchFinder::MatchResult &Result,
+ const CXXMemberCallExpr *Call);
+};
+
+} // namespace clang::tidy::modernize
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MODERNIZE_USESPANFIRSTLASTCHECK_H
\ No newline at end of file
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst
index f050391110385e..04a45d002c0d1d 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -145,6 +145,10 @@ New checks
New check aliases
^^^^^^^^^^^^^^^^^
+- New check `modernize-use-span-first-last` has been added that suggests using
+ ``std::span::first()`` and ``std::span::last()`` member functions instead of
+ equivalent ``subspan()``.
+
- New alias :doc:`cert-arr39-c <clang-tidy/checks/cert/arr39-c>` to
:doc:`bugprone-sizeof-expression
<clang-tidy/checks/bugprone/sizeof-expression>` was added.
diff --git a/clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst
new file mode 100644
index 00000000000000..e8aad59bb2264f
--- /dev/null
+++ b/clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst
@@ -0,0 +1,19 @@
+.. title:: clang-tidy - modernize-use-span-first-last
+
+modernize-use-span-first-last
+============================
+
+Checks for uses of ``std::span::subspan()`` that can be replaced with clearer
+``first()`` or ``last()`` member functions.
+
+Covered scenarios:
+
+==================================== ==================================
+Expression Replacement
+------------------------------------ ----------------------------------
+``s.subspan(0, n)`` ``s.first(n)``
+``s.subspan(n)`` ``s.last(s.size() - n)``
+==================================== ==================================
+
+Non-zero offset with count (like ``subspan(1, n)``) has no direct equivalent
+using ``first()`` or ``last()``, so these cases are not transformed.
\ No newline at end of file
diff --git a/clang-tools-extra/test/clang-tidy/checkers/modernize/modernize-subspan-conversion.cpp b/clang-tools-extra/test/clang-tidy/checkers/modernize/modernize-subspan-conversion.cpp
new file mode 100644
index 00000000000000..cb78bc02f22d4f
--- /dev/null
+++ b/clang-tools-extra/test/clang-tidy/checkers/modernize/modernize-subspan-conversion.cpp
@@ -0,0 +1,50 @@
+// RUN: %check_clang_tidy %s modernize-use-span-first-last %t
+
+namespace std {
+template <typename T>
+class span {
+ T* ptr;
+ __SIZE_TYPE__ len;
+
+public:
+ span(T* p, __SIZE_TYPE__ l) : ptr(p), len(l) {}
+
+ span<T> subspan(__SIZE_TYPE__ offset) const {
+ return span(ptr + offset, len - offset);
+ }
+
+ span<T> subspan(__SIZE_TYPE__ offset, __SIZE_TYPE__ count) const {
+ return span(ptr + offset, count);
+ }
+
+ span<T> first(__SIZE_TYPE__ count) const {
+ return span(ptr, count);
+ }
+
+ span<T> last(__SIZE_TYPE__ count) const {
+ return span(ptr + (len - count), count);
+ }
+
+ __SIZE_TYPE__ size() const { return len; }
+};
+} // namespace std
+
+void test() {
+ int arr[] = {1, 2, 3, 4, 5};
+ std::span<int> s(arr, 5);
+
+ auto sub1 = s.subspan(0, 3);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer span::first() over subspan()
+ // CHECK-FIXES: auto sub1 = s.first(3);
+
+ auto sub2 = s.subspan(2);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer span::last() over subspan()
+ // CHECK-FIXES: auto sub2 = s.last(s.size() - 2);
+
+ __SIZE_TYPE__ n = 2;
+ auto sub3 = s.subspan(0, n);
+ // CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer span::first() over subspan()
+ // CHECK-FIXES: auto sub3 = s.first(n);
+
+ auto sub4 = s.subspan(1, 2); // No warning
+}
\ No newline at end of file
|
|
✅ With the latest revision this PR passed the C/C++ code formatter. |
|
auto sub2 = s.subspan(n); // transforms to: auto sub2 = s.last(s.size() - n);IMHO the transformed code is less readable. It would be more useful to do the opposite transformation, maybe that's what you intended? auto sub2 = s.subspan(s.size() - n); // transforms to: auto sub2 = s.last(n); |
|
@carlosgalvezp thanks for the feedback. I've removed the transformation: auto sub2 = s.subspan(n); // transforms to: auto sub2 = s.last(s.size() - n);While I agree that the transformed expression is more complex (due to size() - x), I initially included it for two reasons: Consistency: Using Let me know if you think we should revisit this decision. |
| UppercaseLiteralSuffixCheck.cpp | ||
| UseAnyOfAllOfCheck.cpp | ||
| UseStdMinMaxCheck.cpp | ||
| UseSpanFirstLastCheck.cpp |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please keep alphabetical order.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think one-line change is fine in this pull request.
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
Outdated
Show resolved
Hide resolved
clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
Outdated
Show resolved
Hide resolved
clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
Outdated
Show resolved
Hide resolved
|
FWIW I agree that |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It would be great to have this check work with uninstantiated templates and use TK_TraversUnlessSpelledInSource as well.
Here is an example of a matcher for the uninstantiated case:
https://godbolt.org/z/1xnGGj7z9
I couldn't fully write it, because there is no matcher to get the member information and constrain it to subspan.
Please also adjust your PR comment to reflect the change for what pattern is matched in the last case.
Overall a good readability improvement check, nice
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/docs/clang-tidy/checks/modernize/use-starts-ends-with.rst
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.h
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
Show resolved
Hide resolved
….cpp Co-authored-by: EugeneZelenko <[email protected]>
Co-authored-by: EugeneZelenko <[email protected]>
clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
Outdated
Show resolved
Hide resolved
|
@EugeneZelenko CI fails because of
could you help me, i don't get it why this fails, bet its a small thing but i am too blind for this. also, since i have troubles doing the RST's is there any documentation on how to run the "docs-CI" locally (to prevent me from pushing wrong commits back and forth 🙈 in the hope to get a green light) |
|
You have duplicated You can build them with: You can see that's the actual command the job is running: |
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
|
feedback addressed. @5chmidti - added range support |
Thanks! If it will waste you more effort, it's good to keep it like this. |
|
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. |
clang-tools-extra/docs/clang-tidy/checks/readability/use-span-first-last.rst
Outdated
Show resolved
Hide resolved
5chmidti
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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 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
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/test/clang-tidy/checkers/readability/use-span-first-last.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
hjanuschka
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ready for re-review
5chmidti
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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:
struct IntSpan {
IntSpan(int*);
IntSpan subspan(int, int);
};
template <typename T>
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<int>{array, 5};
test_instantiations(sp);
IntSpan intsp = array;
test_instantiations(intsp);
}
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
clang-tools-extra/clang-tidy/readability/UseSpanFirstLastCheck.cpp
Outdated
Show resolved
Hide resolved
|
feedback addressed, you were right about the template case, it is really broken, adding: fixes it, but now templates seem to be skipped completely. and existing tests are not "passing" anymore: do you have any ideas? |
| 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); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
please add test_span_of_bytes:
std::byte arr[] = {0x1, 0x2, 0x3, 0x4, 0x5};
std::span<std::byte> s(arr, 5);
// ...
auto sub2 = s.subspan(s.size_bytes() - 2);
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: prefer 'span::last()' over 'subspan()'
// CHECK-FIXES: auto sub2 = s.last(2);
// ...
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 |
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 the standard 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:
not sure if this is a readability or a modernize check ❓