================
@@ -0,0 +1,221 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 "MissingEndComparisonCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+#include "clang/Lex/Lexer.h"
+#include "clang/Tooling/FixIt.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang::tidy::bugprone {
+
+namespace {
+
+constexpr llvm::StringRef IteratorAlgorithms[] = {
+    "::std::find",          "::std::find_if",
+    "::std::find_if_not",   "::std::search",
+    "::std::search_n",      "::std::find_end",
+    "::std::find_first_of", "::std::lower_bound",
+    "::std::upper_bound",   "::std::partition_point",
+    "::std::min_element",   "::std::max_element",
+    "::std::adjacent_find", "::std::is_sorted_until"};
+
+constexpr llvm::StringRef RangeAlgorithms[] = {
+    "::std::ranges::find",          "::std::ranges::find_if",
+    "::std::ranges::find_if_not",   "::std::ranges::lower_bound",
+    "::std::ranges::upper_bound",   "::std::ranges::min_element",
+    "::std::ranges::max_element",   "::std::ranges::find_first_of",
+    "::std::ranges::adjacent_find", "::std::ranges::is_sorted_until"};
+
+AST_MATCHER(DeclStmt, isConditionVariableStatement) {
+  return !match(
+              declStmt(hasAncestor(stmt(anyOf(
+                  ifStmt(hasConditionVariableStatement(equalsNode(&Node))),
+                  whileStmt(hasConditionVariableStatement(equalsNode(&Node))),
+                  
forStmt(hasConditionVariableStatement(equalsNode(&Node))))))),
+              Node, Finder->getASTContext())
+              .empty();
+}
+
+} // namespace
+
+void MissingEndComparisonCheck::registerMatchers(MatchFinder *Finder) {
+  const auto StdAlgoCall = callExpr(
+      callee(functionDecl(hasAnyName(IteratorAlgorithms), 
isInStdNamespace())));
+
+  const auto RangesCall = cxxOperatorCallExpr(
+      hasOverloadedOperatorName("()"),
+      hasArgument(0, declRefExpr(to(
+                         varDecl(hasAnyName(RangeAlgorithms)).bind("cpo")))));
+
+  const auto AnyAlgoCall =
+      getLangOpts().CPlusPlus20
+          ? expr(anyOf(StdAlgoCall, RangesCall)).bind("algoCall")
+          : expr(StdAlgoCall).bind("algoCall");
+
+  // Captures implicit pointer-to-bool casts and operator bool() calls.
+  const auto IsBoolUsage = anyOf(
+      implicitCastExpr(hasCastKind(CK_PointerToBoolean),
+                       
hasSourceExpression(ignoringParenImpCasts(AnyAlgoCall))),
+      cxxMemberCallExpr(callee(cxxConversionDecl(returns(booleanType()))),
+                        on(ignoringParenImpCasts(AnyAlgoCall))));
+
+  // Captures variable usage: `auto it = std::find(...); if (it)`
+  // FIXME: This only handles variables initialized directly by the algorithm.
+  // We may need to introduce more accurate dataflow analysis in the future.
+  const auto VarWithAlgoInit =
+      varDecl(hasInitializer(ignoringParenImpCasts(AnyAlgoCall)))
+          .bind("initVar");
+
+  const auto IsVariableBoolUsage =
+      anyOf(implicitCastExpr(hasCastKind(CK_PointerToBoolean),
+                             hasSourceExpression(ignoringParenImpCasts(
+                                 declRefExpr(to(VarWithAlgoInit))))),
+            cxxMemberCallExpr(
+                callee(cxxConversionDecl(returns(booleanType()))),
+                on(ignoringParenImpCasts(declRefExpr(to(VarWithAlgoInit))))));
+
+  Finder->addMatcher(
+      expr(anyOf(IsBoolUsage, IsVariableBoolUsage)).bind("boolOp"), this);
+}
+
+void MissingEndComparisonCheck::check(const MatchFinder::MatchResult &Result) {
+  const auto *Call = Result.Nodes.getNodeAs<CallExpr>("algoCall");
+  const auto *BoolOp = Result.Nodes.getNodeAs<Expr>("boolOp");
+  const auto *CPO = Result.Nodes.getNodeAs<VarDecl>("cpo");
+
+  if (!Call || !BoolOp)
+    return;
+
+  std::string EndExprText;
+
+  if (!CPO) {
+    if (Call->getNumArgs() < 2)
+      return;
+
+    unsigned EndIdx = 1;
+    const Expr *FirstArg = Call->getArg(0);
+    if (const auto *Record =
+            FirstArg->getType().getNonReferenceType()->getAsCXXRecordDecl()) {
+      if (Record->getName().ends_with("_policy"))
+        EndIdx = 2;
+    }
+
+    if (Call->getNumArgs() <= EndIdx)
+      return;
+
+    const Expr *EndArg = Call->getArg(EndIdx);
+    // Filters nullptr, we assume the intent might be a valid check against 
null
+    if (EndArg->IgnoreParenCasts()->isNullPointerConstant(
+            *Result.Context, Expr::NPC_ValueDependentIsNull))
+      return;
+
+    EndExprText = tooling::fixit::getText(*EndArg, *Result.Context).str();
+  } else {
+    const FunctionDecl *Callee = Call->getDirectCallee();
+    if (!Callee || Callee->getNumParams() == 0)
+      return;
----------------
vbvictor wrote:

Check in matchers for `Callee->getNumParams() == 0`?

https://github.com/llvm/llvm-project/pull/182543
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to