[clang-tools-extra] [clang-tidy] Add readability-use-span-first-last check (PR #118074)

2024-12-23 Thread Julian Schmidt via cfe-commits

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)

2024-12-21 Thread Denis Mikhailov via cfe-commits


@@ -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)

2024-12-20 Thread Helmut Januschka via cfe-commits

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)

2024-12-19 Thread Julian Schmidt via cfe-commits

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)

2024-12-19 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-19 Thread Julian Schmidt via cfe-commits

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)

2024-12-19 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-14 Thread Helmut Januschka via cfe-commits

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)

2024-12-14 Thread Helmut Januschka via cfe-commits

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)

2024-12-14 Thread Helmut Januschka via cfe-commits


@@ -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)

2024-12-11 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-11 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-11 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-11 Thread Julian Schmidt via cfe-commits

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)

2024-12-11 Thread via cfe-commits


@@ -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)

2024-12-11 Thread Helmut Januschka via cfe-commits

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)

2024-12-11 Thread Helmut Januschka via cfe-commits


@@ -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)

2024-12-11 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-09 Thread Helmut Januschka via cfe-commits


@@ -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)

2024-12-06 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-06 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-06 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-06 Thread Julian Schmidt via cfe-commits

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)

2024-12-06 Thread Julian Schmidt via cfe-commits

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)

2024-12-05 Thread via cfe-commits


@@ -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)

2024-12-05 Thread Helmut Januschka via cfe-commits


@@ -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)

2024-12-05 Thread Helmut Januschka via cfe-commits

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)

2024-12-05 Thread via cfe-commits


@@ -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)

2024-12-05 Thread Congcong Cai via cfe-commits

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)

2024-12-05 Thread Helmut Januschka via cfe-commits

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)

2024-12-05 Thread Helmut Januschka via cfe-commits

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)

2024-12-05 Thread Helmut Januschka via cfe-commits


@@ -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)

2024-12-04 Thread Congcong Cai via cfe-commits

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)

2024-12-04 Thread Congcong Cai via cfe-commits

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)

2024-12-04 Thread Congcong Cai via cfe-commits


@@ -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)

2024-12-04 Thread Carlos Galvez via cfe-commits

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)

2024-12-04 Thread Helmut Januschka via cfe-commits

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)

2024-12-03 Thread via cfe-commits


@@ -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)

2024-12-03 Thread Helmut Januschka via cfe-commits

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)

2024-12-03 Thread Helmut Januschka via cfe-commits

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)

2024-12-03 Thread Helmut Januschka via cfe-commits


@@ -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)

2024-12-03 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-03 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-03 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-03 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-03 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-12-02 Thread via cfe-commits


@@ -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)

2024-12-02 Thread via cfe-commits


@@ -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)

2024-12-02 Thread Helmut Januschka via cfe-commits

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)

2024-12-02 Thread Helmut Januschka via cfe-commits

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)

2024-12-02 Thread via cfe-commits


@@ -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)

2024-12-02 Thread Helmut Januschka via cfe-commits

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)

2024-12-02 Thread Helmut Januschka via cfe-commits

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)

2024-12-02 Thread Helmut Januschka via cfe-commits

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)

2024-12-02 Thread Helmut Januschka via cfe-commits




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)

2024-12-02 Thread Helmut Januschka via cfe-commits

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)

2024-12-02 Thread Julian Schmidt via cfe-commits

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)

2024-12-02 Thread Helmut Januschka via cfe-commits

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)

2024-12-02 Thread Helmut Januschka via cfe-commits


@@ -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)

2024-11-30 Thread Julian Schmidt via cfe-commits

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)

2024-11-30 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-11-30 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-11-30 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-11-30 Thread Julian Schmidt via cfe-commits




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)

2024-11-30 Thread Julian Schmidt via cfe-commits

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)

2024-11-30 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-11-30 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-11-30 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-11-30 Thread Julian Schmidt via cfe-commits

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)

2024-11-30 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-11-30 Thread Julian Schmidt via cfe-commits

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)

2024-11-30 Thread Julian Schmidt via cfe-commits




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)

2024-11-30 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-11-30 Thread Julian Schmidt via cfe-commits


@@ -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)

2024-11-29 Thread via cfe-commits

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)

2024-11-29 Thread via cfe-commits


@@ -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)

2024-11-29 Thread via cfe-commits


@@ -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)

2024-11-29 Thread via cfe-commits


@@ -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)

2024-11-29 Thread via cfe-commits


@@ -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)

2024-11-29 Thread via cfe-commits


@@ -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)

2024-11-29 Thread via cfe-commits


@@ -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)

2024-11-29 Thread via cfe-commits


@@ -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)

2024-11-29 Thread via cfe-commits


@@ -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)

2024-11-29 Thread via cfe-commits


@@ -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)

2024-11-29 Thread Helmut Januschka via cfe-commits

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)

2024-11-29 Thread Helmut Januschka via cfe-commits

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)

2024-11-29 Thread Helmut Januschka via cfe-commits

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)

2024-11-29 Thread Helmut Januschka via cfe-commits

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)

2024-11-29 Thread Helmut Januschka via cfe-commits

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)

2024-11-29 Thread Helmut Januschka via cfe-commits

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)

2024-11-29 Thread Helmut Januschka via cfe-commits

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)

2024-11-29 Thread Helmut Januschka via cfe-commits

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