================
@@ -0,0 +1,253 @@
+//===--- UseRangesCheck.cpp - clang-tidy 
----------------------------------===//
+//
+// 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 "UseRangesCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/AST/Decl.h"
+#include "clang/AST/Expr.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/ASTMatchers/ASTMatchers.h"
+#include "clang/ASTMatchers/ASTMatchersInternal.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/SourceLocation.h"
+#include "clang/Basic/SourceManager.h"
+#include "clang/Lex/Lexer.h"
+#include "llvm/ADT/ArrayRef.h"
+#include "llvm/ADT/FoldingSet.h"
+#include "llvm/ADT/STLExtras.h"
+#include "llvm/ADT/SmallBitVector.h"
+#include "llvm/ADT/SmallString.h"
+#include "llvm/ADT/SmallVector.h"
+#include "llvm/Support/raw_ostream.h"
+#include <cassert>
+#include <limits>
+#include <optional>
+#include <string>
+
+using namespace clang::ast_matchers;
+
+static constexpr const char BoundCall[] = "CallExpr";
+static constexpr const char FuncDecl[] = "FuncDecl";
+static constexpr const char ArgName[] = "ArgName";
+
+namespace clang::tidy::utils {
+
+static bool operator==(const UseRangesCheck::Replacer::Indexes &L,
+                       const UseRangesCheck::Replacer::Indexes &R) {
+  return std::tie(L.BeginArg, L.EndArg, L.ReplaceArg) ==
+         std::tie(R.BeginArg, R.EndArg, R.ReplaceArg);
+}
+
+std::string
+getFullPrefix(ArrayRef<UseRangesCheck::Replacer::Indexes> Signature) {
+  std::string Output;
+  llvm::raw_string_ostream OS(Output);
+  for (auto Item : Signature) {
+    OS << Item.BeginArg << ":" << Item.EndArg << ":"
+       << (Item.ReplaceArg == Item.First ? '0' : '1');
+  }
+  return Output;
+}
+
+static llvm::hash_code hash_value(const UseRangesCheck::Replacer::Indexes &L) {
+  return llvm::hash_combine(L.BeginArg, L.EndArg, L.ReplaceArg);
+}
+
+namespace {
+
+AST_MATCHER(Expr, hasSideEffects) {
+  return Node.HasSideEffects(Finder->getASTContext());
+}
+
+AST_MATCHER_P(Expr, isEquivalentToBound, std::string, Other) {
+  llvm::FoldingSetNodeID NodeRepr;
+  Node.Profile(NodeRepr, Finder->getASTContext(), true);
+  return Builder->removeBindings(
+      [this, &Finder,
+       &NodeRepr](const ast_matchers::internal::BoundNodesMap &Nodes) {
+        auto BoundNode = Nodes.getNodeAs<Expr>(Other);
+        if (!BoundNode)
+          return true;
+        llvm::FoldingSetNodeID BoundRepr;
+        BoundNode->Profile(BoundRepr, Finder->getASTContext(), true);
+        return BoundRepr != NodeRepr;
+      });
+}
+} // namespace
+
+static auto makeMatcher(bool IsBegin, StringRef Prefix) {
+  auto Member =
+      IsBegin ? expr(unless(hasSideEffects())).bind((ArgName + Prefix).str())
+              : expr(isEquivalentToBound((ArgName + Prefix).str()));
+  return expr(
+      anyOf(cxxMemberCallExpr(
+                callee(cxxMethodDecl(IsBegin ? hasAnyName("begin", "cbegin")
+                                             : hasAnyName("end", "cend"))),
+                on(Member)),
+            callExpr(argumentCountIs(1), hasArgument(0, Member),
+                     hasDeclaration(functionDecl(
+                         IsBegin ? hasAnyName("::std::begin", "::std::cbegin")
+                                 : hasAnyName("::std::end", 
"::std::cend"))))));
+}
+static ast_matchers::internal::Matcher<CallExpr>
+makeMatcherPair(StringRef State,
+                const UseRangesCheck::Replacer::Indexes &Indexes) {
+  auto ArgPostfix = std::to_string(Indexes.BeginArg);
+  SmallString<64> ID = {BoundCall, State};
+  return callExpr(argumentCountAtLeast(
+                      std::max(Indexes.BeginArg, Indexes.EndArg) + 1),
+                  hasArgument(Indexes.BeginArg, makeMatcher(true, ArgPostfix)),
+                  hasArgument(Indexes.EndArg, makeMatcher(false, ArgPostfix)))
+      .bind(ID);
+}
+
+void UseRangesCheck::registerMatchers(MatchFinder *Finder) {
+  Replaces = GetReplacerMap();
+  llvm::DenseSet<ArrayRef<ArrayRef<Replacer::Indexes>>> Seen;
+  for (auto I = Replaces.begin(), E = Replaces.end(); I != E; ++I) {
+    const auto &Replacer = I->getValue();
+    const auto &Signatures = Replacer->getReplacementSignatures();
+    if (Seen.contains(Signatures))
+      continue;
+    assert(!Signatures.empty() &&
+           llvm::all_of(Signatures, [](auto index) { return !index.empty(); 
}));
+    std::vector<StringRef> Names(1, I->getKey());
+    for (auto J = std::next(I); J != E; ++J) {
+      if (J->getValue()->getReplacementSignatures() == Signatures) {
+        Names.push_back(J->getKey());
+      }
+    }
+    std::vector<ast_matchers::internal::DynTypedMatcher> TotalMatchers;
+    // As we match on the first matched signature, we need to ensure that any
+    SmallVector<ArrayRef<Replacer::Indexes>> SigVec(Signatures);
+    llvm::sort(SigVec, [](auto &L, auto &R) { return R.size() < L.size(); });
+    for (const auto &Signature : SigVec) {
+      std::vector<ast_matchers::internal::DynTypedMatcher> Matchers;
+      for (const auto &ArgPair : Signature) {
+        Matchers.push_back(makeMatcherPair(getFullPrefix(Signature), ArgPair));
+      }
+      TotalMatchers.push_back(
+          ast_matchers::internal::DynTypedMatcher::constructVariadic(
+              ast_matchers::internal::DynTypedMatcher::VO_AllOf,
+              ASTNodeKind::getFromNodeKind<CallExpr>(), std::move(Matchers)));
+    }
+    Finder->addMatcher(
+        callExpr(
----------------
PiotrZSL wrote:

consider using TK_IgnoreUnlessSpelledInSource, to exclude some implicit code, 
like template instances.
Just to reduce risk of fix-conflicts and improve check performance.

https://github.com/llvm/llvm-project/pull/97764
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to