https://github.com/zwuis updated 
https://github.com/llvm/llvm-project/pull/155982

>From 426caa9f66cddd1deac23b397baf75f6809f6f52 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zw...@outlook.com>
Date: Fri, 29 Aug 2025 15:15:24 +0800
Subject: [PATCH 1/3] Handle nested-name-specifier in
 "llvm-prefer-isa-or-dyn-cast-in-conditionals"

---
 .../PreferIsaOrDynCastInConditionalsCheck.cpp | 119 ++++++++----------
 clang-tools-extra/docs/ReleaseNotes.rst       |  13 ++
 ...prefer-isa-or-dyn-cast-in-conditionals.cpp |  75 +++++++++++
 3 files changed, 143 insertions(+), 64 deletions(-)

diff --git 
a/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp 
b/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp
index 4c138bcc564d8..5e9777248896a 100644
--- 
a/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp
+++ 
b/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp
@@ -11,6 +11,7 @@
 #include "clang/AST/ASTContext.h"
 #include "clang/ASTMatchers/ASTMatchFinder.h"
 #include "clang/Lex/Lexer.h"
+#include "llvm/Support/FormatVariadic.h"
 
 using namespace clang::ast_matchers;
 
@@ -22,105 +23,95 @@ AST_MATCHER(Expr, isMacroID) { return 
Node.getExprLoc().isMacroID(); }
 
 void PreferIsaOrDynCastInConditionalsCheck::registerMatchers(
     MatchFinder *Finder) {
-  auto Condition = hasCondition(implicitCastExpr(has(
-      callExpr(unless(isMacroID()), unless(cxxMemberCallExpr()),
-               anyOf(callee(namedDecl(hasName("cast"))),
-                     callee(namedDecl(hasName("dyn_cast")).bind("dyn_cast"))))
-          .bind("call"))));
-
-  auto Any = anyOf(
-      has(declStmt(containsDeclaration(
-          0, varDecl(hasInitializer(callExpr(unless(isMacroID()),
-                                             unless(cxxMemberCallExpr()),
-                                             
callee(namedDecl(hasName("cast"))))
-                                        .bind("assign")))))),
-      Condition);
-
-  auto CallExpression =
+  auto AnyCalleeName = [](ArrayRef<StringRef> CalleeName) {
+    return allOf(unless(isMacroID()), unless(cxxMemberCallExpr()),
+                 callee(expr(ignoringImpCasts(
+                     declRefExpr(to(namedDecl(hasAnyName(CalleeName))),
+                                 hasAnyTemplateArgumentLoc(anything()))
+                         .bind("callee")))));
+  };
+
+  auto Condition = hasCondition(implicitCastExpr(
+      has(callExpr(AnyCalleeName({"cast", "dyn_cast"})).bind("cond"))));
+
+  auto Any =
+      anyOf(hasConditionVariableStatement(containsDeclaration(
+                0, varDecl(hasInitializer(callExpr(AnyCalleeName({"cast"}))))
+                       .bind("var"))),
+            Condition);
+
+  auto CallWithBindedArg =
       callExpr(
-
-          unless(isMacroID()), unless(cxxMemberCallExpr()),
-          callee(namedDecl(hasAnyName("isa", "cast", "cast_or_null", 
"dyn_cast",
-                                      "dyn_cast_or_null"))
-                     .bind("func")),
+          AnyCalleeName({"isa", "cast", "cast_or_null", "cast_if_present",
+                         "dyn_cast", "dyn_cast_or_null",
+                         "dyn_cast_if_present"}),
           hasArgument(0, mapAnyOf(declRefExpr, cxxMemberCallExpr).bind("arg")))
           .bind("rhs");
 
   Finder->addMatcher(
-      traverse(
-          TK_AsIs,
-          stmt(anyOf(
-              ifStmt(Any), whileStmt(Any), doStmt(Condition),
-              binaryOperator(unless(isExpansionInFileMatching(
-                                 "llvm/include/llvm/Support/Casting.h")),
-                             hasOperatorName("&&"),
-                             hasLHS(implicitCastExpr().bind("lhs")),
-                             
hasRHS(anyOf(implicitCastExpr(has(CallExpression)),
-                                          CallExpression)))
-                  .bind("and")))),
+      stmt(anyOf(ifStmt(Any), forStmt(Any), whileStmt(Any), doStmt(Condition),
+                 binaryOperator(unless(isExpansionInFileMatching(
+                                    "llvm/include/llvm/Support/Casting.h")),
+                                hasOperatorName("&&"),
+                                hasLHS(implicitCastExpr().bind("lhs")),
+                                hasRHS(ignoringImpCasts(CallWithBindedArg)))
+                     .bind("and"))),
       this);
 }
 
 void PreferIsaOrDynCastInConditionalsCheck::check(
     const MatchFinder::MatchResult &Result) {
-  if (const auto *MatchedDecl = Result.Nodes.getNodeAs<CallExpr>("assign")) {
-    SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
-    SourceLocation EndLoc =
-        StartLoc.getLocWithOffset(StringRef("cast").size() - 1);
+  const auto *Callee = Result.Nodes.getNodeAs<DeclRefExpr>("callee");
+  if (!Callee)
+    return;
+
+  SourceLocation StartLoc = Callee->getLocation();
+  SourceLocation EndLoc = Callee->getNameInfo().getEndLoc();
 
-    diag(MatchedDecl->getBeginLoc(),
+  if (Result.Nodes.getNodeAs<VarDecl>("var")) {
+    diag(StartLoc,
          "cast<> in conditional will assert rather than return a null pointer")
         << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc),
                                         "dyn_cast");
-  } else if (const auto *MatchedDecl =
-                 Result.Nodes.getNodeAs<CallExpr>("call")) {
-    SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
-    SourceLocation EndLoc =
-        StartLoc.getLocWithOffset(StringRef("cast").size() - 1);
-
+  } else if (Result.Nodes.getNodeAs<CallExpr>("cond")) {
     StringRef Message =
         "cast<> in conditional will assert rather than return a null pointer";
-    if (Result.Nodes.getNodeAs<NamedDecl>("dyn_cast"))
+    if (Callee->getDecl()->getName() == "dyn_cast")
       Message = "return value from dyn_cast<> not used";
 
-    diag(MatchedDecl->getBeginLoc(), Message)
+    diag(StartLoc, Message)
         << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc), "isa");
-  } else if (const auto *MatchedDecl =
-                 Result.Nodes.getNodeAs<BinaryOperator>("and")) {
+  } else if (Result.Nodes.getNodeAs<BinaryOperator>("and")) {
     const auto *LHS = Result.Nodes.getNodeAs<ImplicitCastExpr>("lhs");
     const auto *RHS = Result.Nodes.getNodeAs<CallExpr>("rhs");
     const auto *Arg = Result.Nodes.getNodeAs<Expr>("arg");
-    const auto *Func = Result.Nodes.getNodeAs<NamedDecl>("func");
 
     assert(LHS && "LHS is null");
     assert(RHS && "RHS is null");
     assert(Arg && "Arg is null");
-    assert(Func && "Func is null");
 
-    StringRef LHSString(Lexer::getSourceText(
-        CharSourceRange::getTokenRange(LHS->getSourceRange()),
-        *Result.SourceManager, getLangOpts()));
+    auto GetText = [&](SourceRange R) {
+      return Lexer::getSourceText(CharSourceRange::getTokenRange(R),
+                                  *Result.SourceManager, getLangOpts());
+    };
 
-    StringRef ArgString(Lexer::getSourceText(
-        CharSourceRange::getTokenRange(Arg->getSourceRange()),
-        *Result.SourceManager, getLangOpts()));
+    StringRef LHSString = GetText(LHS->getSourceRange());
+
+    StringRef ArgString = GetText(Arg->getSourceRange());
 
     if (ArgString != LHSString)
       return;
 
-    StringRef RHSString(Lexer::getSourceText(
-        CharSourceRange::getTokenRange(RHS->getSourceRange()),
-        *Result.SourceManager, getLangOpts()));
-
-    std::string Replacement("isa_and_nonnull");
-    Replacement += RHSString.substr(Func->getName().size());
+    std::string Replacement = llvm::formatv(
+        "{}{}{}", GetText(Callee->getQualifierLoc().getSourceRange()),
+        "isa_and_nonnull",
+        GetText(SourceRange(Callee->getLAngleLoc(), RHS->getEndLoc())));
 
-    diag(MatchedDecl->getBeginLoc(),
+    diag(LHS->getBeginLoc(),
          "isa_and_nonnull<> is preferred over an explicit test for null "
          "followed by calling isa<>")
-        << FixItHint::CreateReplacement(SourceRange(MatchedDecl->getBeginLoc(),
-                                                    MatchedDecl->getEndLoc()),
-                                        Replacement);
+        << FixItHint::CreateReplacement(
+               SourceRange(LHS->getBeginLoc(), RHS->getEndLoc()), Replacement);
   }
 }
 
diff --git a/clang-tools-extra/docs/ReleaseNotes.rst 
b/clang-tools-extra/docs/ReleaseNotes.rst
index 06641a602e28f..0829d336932c6 100644
--- a/clang-tools-extra/docs/ReleaseNotes.rst
+++ b/clang-tools-extra/docs/ReleaseNotes.rst
@@ -244,6 +244,19 @@ Changes in existing checks
   <clang-tidy/checks/readability/qualified-auto>` check by adding the option
   `IgnoreAliasing`, that allows not looking at underlying types of type 
aliases.
 
+- Improved :doc:`llvm-prefer-isa-or-dyn-cast-in-conditionals
+  <clang-tidy/checks/llvm/prefer-isa-or-dyn-cast-in-conditionals>` check:
+
+  - Fix-it handles Callees with nested-name-specifier correctly.
+
+  - ``if`` statements with init-statement (``if (auto X = ...; ...)``) are
+    handled correctly.
+
+  - ``for`` loops are supported.
+
+  - ``cast_if_present`` and ``dyn_cast_if_present`` are supported in the case
+    that ``isa_and_nonnull`` is preferred.
+
 Removed checks
 ^^^^^^^^^^^^^^
 
diff --git 
a/clang-tools-extra/test/clang-tidy/checkers/llvm/prefer-isa-or-dyn-cast-in-conditionals.cpp
 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/prefer-isa-or-dyn-cast-in-conditionals.cpp
index 88e4b643004fc..8683926bace11 100644
--- 
a/clang-tools-extra/test/clang-tidy/checkers/llvm/prefer-isa-or-dyn-cast-in-conditionals.cpp
+++ 
b/clang-tools-extra/test/clang-tidy/checkers/llvm/prefer-isa-or-dyn-cast-in-conditionals.cpp
@@ -9,14 +9,24 @@ struct Z {
   bool baz(Y*);
 };
 
+namespace llvm {
 template <class X, class Y>
 bool isa(Y *);
 template <class X, class Y>
 X *cast(Y *);
 template <class X, class Y>
+X *cast_or_null(Y *);
+template <class X, class Y>
+X *cast_if_present(Y *);
+template <class X, class Y>
 X *dyn_cast(Y *);
 template <class X, class Y>
 X *dyn_cast_or_null(Y *);
+template <class X, class Y>
+X *dyn_cast_if_present(Y *);
+} // namespace llvm
+
+using namespace llvm;
 
 bool foo(Y *y, Z *z) {
   if (auto x = cast<X>(y))
@@ -24,6 +34,26 @@ bool foo(Y *y, Z *z) {
   // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: cast<> in conditional will 
assert rather than return a null pointer 
[llvm-prefer-isa-or-dyn-cast-in-conditionals]
   // CHECK-FIXES: if (auto x = dyn_cast<X>(y))
 
+  if (auto x = ::cast<X>(y))
+    return true;
+  // CHECK-MESSAGES: :[[@LINE-2]]:18: warning: cast<> in conditional will 
assert rather than return a null pointer 
[llvm-prefer-isa-or-dyn-cast-in-conditionals]
+  // CHECK-FIXES: if (auto x = ::dyn_cast<X>(y))
+
+  if (auto x = llvm::cast<X>(y))
+    return true;
+  // CHECK-MESSAGES: :[[@LINE-2]]:22: warning: cast<> in conditional will 
assert rather than return a null pointer 
[llvm-prefer-isa-or-dyn-cast-in-conditionals]
+  // CHECK-FIXES: if (auto x = llvm::dyn_cast<X>(y))
+
+  if (auto x = ::llvm::cast<X>(y))
+    return true;
+  // CHECK-MESSAGES: :[[@LINE-2]]:24: warning: cast<> in conditional will 
assert rather than return a null pointer 
[llvm-prefer-isa-or-dyn-cast-in-conditionals]
+  // CHECK-FIXES: if (auto x = ::llvm::dyn_cast<X>(y))
+
+  for (; auto x = cast<X>(y); )
+    break;
+  // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: cast<> in conditional
+  // CHECK-FIXES: for (; auto x = dyn_cast<X>(y); )
+
   while (auto x = cast<X>(y))
     break;
   // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: cast<> in conditional
@@ -34,6 +64,16 @@ bool foo(Y *y, Z *z) {
   // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: cast<> in conditional
   // CHECK-FIXES: if (isa<X>(y))
 
+  if (auto x = cast<X>(y); cast<X>(y))
+    return true;
+  // CHECK-MESSAGES: :[[@LINE-2]]:28: warning: cast<> in conditional will 
assert rather than return a null pointer 
[llvm-prefer-isa-or-dyn-cast-in-conditionals]
+  // CHECK-FIXES: if (auto x = cast<X>(y); isa<X>(y))
+
+  for (; cast<X>(y); )
+    break;
+  // CHECK-MESSAGES: :[[@LINE-2]]:10: warning: cast<> in conditional
+  // CHECK-FIXES: for (; isa<X>(y); )
+
   while (cast<X>(y))
     break;
   // CHECK-MESSAGES: :[[@LINE-2]]:10: warning: cast<> in conditional
@@ -50,6 +90,11 @@ bool foo(Y *y, Z *z) {
   // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: return value from dyn_cast<> not 
used [llvm-prefer-isa-or-dyn-cast-in-conditionals]
   // CHECK-FIXES: if (isa<X>(y))
 
+  for (; dyn_cast<X>(y); )
+    break;
+  // CHECK-MESSAGES: :[[@LINE-2]]:10: warning: return value from dyn_cast<> 
not used
+  // CHECK-FIXES: for (; isa<X>(y); )
+
   while (dyn_cast<X>(y))
     break;
   // CHECK-MESSAGES: :[[@LINE-2]]:10: warning: return value from dyn_cast<> 
not used
@@ -66,6 +111,21 @@ bool foo(Y *y, Z *z) {
   // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: isa_and_nonnull<> is preferred 
over an explicit test for null followed by calling isa<> 
[llvm-prefer-isa-or-dyn-cast-in-conditionals]
   // CHECK-FIXES: if (isa_and_nonnull<X>(y))
 
+  if (y && ::isa<X>(y))
+    return true;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: isa_and_nonnull<> is preferred 
over an explicit test for null followed by calling isa<> 
[llvm-prefer-isa-or-dyn-cast-in-conditionals]
+  // CHECK-FIXES: if (::isa_and_nonnull<X>(y))
+
+  if (y && llvm::isa<X>(y))
+    return true;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: isa_and_nonnull<> is preferred 
over an explicit test for null followed by calling isa<> 
[llvm-prefer-isa-or-dyn-cast-in-conditionals]
+  // CHECK-FIXES: if (llvm::isa_and_nonnull<X>(y))
+
+  if (y && ::llvm::isa<X>(y))
+    return true;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: isa_and_nonnull<> is preferred 
over an explicit test for null followed by calling isa<> 
[llvm-prefer-isa-or-dyn-cast-in-conditionals]
+  // CHECK-FIXES: if (::llvm::isa_and_nonnull<X>(y))
+
   if (z->bar() && isa<Y>(z->bar()))
     return true;
   // CHECK-MESSAGES: :[[@LINE-2]]:7: warning:  isa_and_nonnull<> is preferred
@@ -76,6 +136,16 @@ bool foo(Y *y, Z *z) {
   // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: isa_and_nonnull<> is preferred
   // CHECK-FIXES: if (isa_and_nonnull<Y>(z->bar()))
 
+  if (z->bar() && cast_or_null<Y>(z->bar()))
+    return true;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: isa_and_nonnull<> is preferred
+  // CHECK-FIXES: if (isa_and_nonnull<Y>(z->bar()))
+
+  if (z->bar() && cast_if_present<Y>(z->bar()))
+    return true;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: isa_and_nonnull<> is preferred
+  // CHECK-FIXES: if (isa_and_nonnull<Y>(z->bar()))
+
   if (z->bar() && dyn_cast<Y>(z->bar()))
     return true;
   // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: isa_and_nonnull<> is preferred
@@ -86,6 +156,11 @@ bool foo(Y *y, Z *z) {
   // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: isa_and_nonnull<> is preferred
   // CHECK-FIXES: if (isa_and_nonnull<Y>(z->bar()))
 
+  if (z->bar() && dyn_cast_if_present<Y>(z->bar()))
+    return true;
+  // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: isa_and_nonnull<> is preferred
+  // CHECK-FIXES: if (isa_and_nonnull<Y>(z->bar()))
+
   bool b = z->bar() && cast<Y>(z->bar());
   // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: isa_and_nonnull<> is preferred
   // CHECK-FIXES: bool b = isa_and_nonnull<Y>(z->bar());

>From fe32d8a0ad42b7bc6ce4a0dfaee53bde950237d8 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zw...@outlook.com>
Date: Sat, 30 Aug 2025 22:11:25 +0800
Subject: [PATCH 2/3] Remove empty line, add `const`, and make known string
 inline

---
 .../llvm/PreferIsaOrDynCastInConditionalsCheck.cpp         | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git 
a/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp 
b/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp
index 5e9777248896a..45a808cafa941 100644
--- 
a/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp
+++ 
b/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp
@@ -96,15 +96,14 @@ void PreferIsaOrDynCastInConditionalsCheck::check(
     };
 
     StringRef LHSString = GetText(LHS->getSourceRange());
-
     StringRef ArgString = GetText(Arg->getSourceRange());
 
     if (ArgString != LHSString)
       return;
 
-    std::string Replacement = llvm::formatv(
-        "{}{}{}", GetText(Callee->getQualifierLoc().getSourceRange()),
-        "isa_and_nonnull",
+    const std::string Replacement = llvm::formatv(
+        "{}isa_and_nonnull{}",
+        GetText(Callee->getQualifierLoc().getSourceRange()),
         GetText(SourceRange(Callee->getLAngleLoc(), RHS->getEndLoc())));
 
     diag(LHS->getBeginLoc(),

>From 52265dbc2641938ca955f279d351a9c9f1d42114 Mon Sep 17 00:00:00 2001
From: Yanzuo Liu <zw...@outlook.com>
Date: Sat, 30 Aug 2025 23:55:07 +0800
Subject: [PATCH 3/3] Use `assert` instead of branch

---
 .../llvm/PreferIsaOrDynCastInConditionalsCheck.cpp           | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git 
a/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp 
b/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp
index 45a808cafa941..dae5f2bb4a379 100644
--- 
a/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp
+++ 
b/clang-tools-extra/clang-tidy/llvm/PreferIsaOrDynCastInConditionalsCheck.cpp
@@ -62,8 +62,9 @@ void PreferIsaOrDynCastInConditionalsCheck::registerMatchers(
 void PreferIsaOrDynCastInConditionalsCheck::check(
     const MatchFinder::MatchResult &Result) {
   const auto *Callee = Result.Nodes.getNodeAs<DeclRefExpr>("callee");
-  if (!Callee)
-    return;
+
+  // Callee should be matched if anything is matched.
+  assert(Callee && "Callee is null");
 
   SourceLocation StartLoc = Callee->getLocation();
   SourceLocation EndLoc = Callee->getNameInfo().getEndLoc();

_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to