[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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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; 5chmidti wrote: This should be done in `isLanguageVersionSupported` instead: https://github.com/llvm/llvm-project/blob/a8a494faab8af60754c4647dbb7b24bc86a80aab/clang-tools-extra/clang-tidy/modernize/TypeTraitsCheck.h#L26-L28 https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
5chmidti wrote: Please add tests with macros involved (e.g., fully wrapped `#define F(obj, n)...` or `#define N 2`, etc), and with templates https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org 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); 5chmidti wrote: Names from code should be put into `'` inside of diagnostics. (Because it's the default behavior when streaming named AST nodes into diagnostics) https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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( 5chmidti wrote: Please add `const` https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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; 5chmidti wrote: This replacement should be possible to do without the need to go through the lexer, and just replace the needed parts instead. As in, replace from `substr` until before the `n` with `first(/last(` (though that would be problematic with a macro that does `#define K s.size() - 3`, because the `3` would be inside the macro (location wise))... https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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 requested changes to this pull request. 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 https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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. 5chmidti wrote: Please escape the function/class names with `'` https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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 cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
5chmidti wrote: You removed this by mistake, it's from another check https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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") { 5chmidti wrote: Please merge each pair of matching `if`s to reduce nesting (e.g. `if (const auto*p...; p && ...)`) https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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"); 5chmidti wrote: Please move this up one entry, so that the list is alphabetically sorted https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)
pkasting wrote: FWIW I agree that `last(size() - n)` is worse than `subspan(n)`, and that the reverse transform (`subspan(size() - n)` -> `last(n)`) is better. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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 EugeneZelenko wrote: Please keep alphabetical order. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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++ -*-===// EugeneZelenko wrote: ```suggestion //===--- UseSpanFirstLastCheck.h - clang-tidy ---*- C++ -*-===// ``` https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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 EugeneZelenko wrote: Wrong section and format. See `New checks` section for examples. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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; + EugeneZelenko wrote: Please add `isLanguageVersionSupported`. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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( EugeneZelenko wrote: Please use type. Same for `BaseStr`. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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 EugeneZelenko wrote: Please add newline. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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 +} EugeneZelenko wrote: Missing newline. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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++ -*-===// EugeneZelenko wrote: ```suggestion //===--- UseSpanFirstLastCheck.cpp - clang-tidy -*- C++ -*-===// ``` https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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: + + == EugeneZelenko wrote: Columns width. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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/13] [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/12] [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/11] [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: @carlosgalvezp thanks for the feedback. I've removed the transformation: ```c++ 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 `first()/last()` everywhere possible makes the codebase more uniform The method name `last()` better expresses the intent than `subspan()`, even if the calculation is more verbose Let me know if you think we should revisit this decision. https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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/10] [clang-tidy] Add modernize-use-span-first-last check Add new check that modernizes std::span::subspan() calls to use the more expressive first() and last() member functions where applicable. --- .../clang-tidy/modernize/CMakeLists.txt | 1 + .../modernize/ModernizeTidyModule.cpp | 3 + .../modernize/UseSpanFirstLastCheck.cpp | 97 +++ .../modernize/UseSpanFirstLastCheck.h | 40 clang-tools-extra/docs/ReleaseNotes.rst | 4 + .../checks/modernize/use-span-first-last.rst | 19 .../modernize-subspan-conversion.cpp | 50 ++ 7 files changed, 214 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/modernize/UseSpanFirstLastCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/modernize/use-span-first-last.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/modernize/modernize-subspan-conversion.cpp diff --git a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt index c919d49b42873a..47dd12a2640b6c 100644 --- a/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/modernize/CMakeLists.txt @@ -49,6 +49,7 @@ add_clang_library(clangTidyModernizeModule STATIC UseTransparentFunctorsCheck.cpp UseUncaughtExceptionsCheck.cpp UseUsingCheck.cpp + UseSpanFirstLastCheck.cpp LINK_LIBS clangTidy diff --git a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp index 18607593320635..6fc5de5aad20b7 100644 --- a/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/modernize/ModernizeTidyModule.cpp @@ -42,6 +42,7 @@ #include "UseNullptrCheck.h" #include "UseOverrideCheck.h" #include "UseRangesCheck.h" +#include "UseSpanFirstLastCheck.h" #include "UseStartsEndsWithCheck.h" #include "UseStdFormatCheck.h" #include "UseStdNumbersCheck.h" @@ -122,6 +123,8 @@ class ModernizeModule : public ClangTidyModule { CheckFactories.registerCheck( "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 edited https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org 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 1/8] [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 edited https://github.com/llvm/llvm-project/pull/118074 ___ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits