hintonda updated this revision to Diff 192854.
hintonda added a comment.
- Improve warning message.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D59802/new/
https://reviews.llvm.org/D59802
Files:
clang-tools-extra/clang-tidy/llvm/AvoidCastInConditionalCheck.cpp
clang-tools-extra/clang-tidy/llvm/AvoidCastInConditionalCheck.h
clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
clang-tools-extra/clang-tidy/utils/HeaderFileExtensionsUtils.h
clang-tools-extra/docs/ReleaseNotes.rst
clang-tools-extra/docs/clang-tidy/checks/list.rst
clang-tools-extra/docs/clang-tidy/checks/llvm-avoid-cast-in-conditional.rst
clang-tools-extra/test/clang-tidy/llvm-avoid-cast-in-conditional.cpp
Index: clang-tools-extra/test/clang-tidy/llvm-avoid-cast-in-conditional.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/test/clang-tidy/llvm-avoid-cast-in-conditional.cpp
@@ -0,0 +1,118 @@
+// RUN: %check_clang_tidy %s llvm-avoid-cast-in-conditional %t
+
+#define CAST(T, Obj) cast<T>(Obj)
+#define ISA(T, Obj) isa<T>(Obj)
+#define ISA_OR_NULL(T, Obj) Obj &&isa<T>(Obj)
+
+struct X;
+struct Y;
+struct Z {
+ int foo();
+ X *bar();
+};
+
+template <class X, class Y>
+bool isa(Y *);
+template <class X, class Y>
+X *cast(Y *);
+template <class X, class Y>
+X *dyn_cast(Y *);
+template <class X, class Y>
+X *dyn_cast_or_null(Y *);
+
+bool foo(Y *y, Z *z) {
+ if (auto x = cast<X>(y))
+ return true;
+ // CHECK-MESSAGES: :[[@LINE-2]]:16: warning: {{cast<> in conditional .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: if (auto x = dyn_cast<X>(y))
+
+ if (cast<X>(y))
+ return true;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: {{cast<> in conditional .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: if (isa<X>(y))
+
+ if (dyn_cast<X>(y))
+ return true;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: {{.*dyn_cast<> not used .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: if (isa<X>(y))
+
+ // These don't trigger a warning.
+ if (auto x = cast<Z>(y)->foo())
+ return true;
+ if (cast<Z>(y)->foo())
+ return true;
+
+ while (auto x = cast<X>(y))
+ break;
+ // CHECK-MESSAGES: :[[@LINE-2]]:19: warning: {{cast<> in conditional .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: while (auto x = dyn_cast<X>(y))
+
+ while (cast<X>(y))
+ break;
+ // CHECK-MESSAGES: :[[@LINE-2]]:10: warning: {{cast<> in conditional .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: while (isa<X>(y))
+
+ while (dyn_cast<X>(y))
+ break;
+ // CHECK-MESSAGES: :[[@LINE-2]]:10: warning: {{.*dyn_cast<> not used .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: while (isa<X>(y))
+
+ // These don't trigger a warning.
+ while (auto x = cast<Z>(y)->foo())
+ break;
+ while (cast<Z>(y)->foo())
+ break;
+
+ do {
+ break;
+ } while (cast<X>(y));
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{cast<> in conditional .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: while (isa<X>(y));
+
+ do {
+ break;
+ } while (dyn_cast<X>(y));
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{.*dyn_cast<> not used .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: while (isa<X>(y));
+
+ if (z->bar() && isa<Y>(z->bar()))
+ return true;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: {{method .* is called twice .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: if (dyn_cast_or_null<Y>(z->bar()))
+
+ if (z->bar() && cast<Y>(z->bar()))
+ return true;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: {{method .* is called twice .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: if (dyn_cast_or_null<Y>(z->bar()))
+
+ if (z->bar() && dyn_cast<Y>(z->bar()))
+ return true;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: {{method .* is called twice .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: if (dyn_cast_or_null<Y>(z->bar()))
+
+ if (z->bar() && dyn_cast_or_null<Y>(z->bar()))
+ return true;
+ // CHECK-MESSAGES: :[[@LINE-2]]:7: warning: {{method .* is called twice .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: if (dyn_cast_or_null<Y>(z->bar()))
+
+ bool b = z->bar() && cast<Y>(z->bar());
+ // CHECK-MESSAGES: :[[@LINE-1]]:12: warning: {{method .* is called twice .*llvm-avoid-cast-in-conditional}}
+ // CHECK-FIXES: bool b = dyn_cast_or_null<Y>(z->bar());
+
+ // These don't trigger a warning.
+ if (auto x = CAST(X, y))
+ return true;
+ if (z->bar() && ISA(Y, z->bar()))
+ return true;
+ if (ISA_OR_NULL(Y, z->bar()))
+ return true;
+ if (y && isa<X>(y))
+ return true;
+ if (y && cast<X>(z->bar()))
+ return true;
+ if (z && cast<Z>(y)->foo())
+ return true;
+ bool b2 = y && cast<X>(z);
+
+ return false;
+}
Index: clang-tools-extra/docs/clang-tidy/checks/llvm-avoid-cast-in-conditional.rst
===================================================================
--- /dev/null
+++ clang-tools-extra/docs/clang-tidy/checks/llvm-avoid-cast-in-conditional.rst
@@ -0,0 +1,28 @@
+.. title:: clang-tidy - llvm-avoid-cast-in-conditional
+
+llvm-avoid-cast-in-conditional
+==============================
+
+ Finds uses of ``cast<>`` in conditionals of ``if``, ``while`` or
+ ``do`` statements, which will assert rather than return a null
+ pointer. It also finds uses of ``dyn_cast<>`` in conditionals where
+ the return value is not captured.
+
+.. code-block:: c++
+
+ // Finds these:
+ if (auto x = cast<X>(y)) {}
+ // is replaced by:
+ if (auto x = dyn_cast<X>(y)) {}
+
+ if (cast<X>(y)) {}
+ // is replaced by:
+ if (isa<X>(y)) {}
+
+ if (dyn_cast<X>(y)) {}
+ // is replaced by:
+ if (isa<X>(y)) {}
+
+ // These are ignored.
+ if (auto f = cast<Z>(y)->foo()) {}
+ if (cast<Z>(y)->foo()) {}
Index: clang-tools-extra/docs/clang-tidy/checks/list.rst
===================================================================
--- clang-tools-extra/docs/clang-tidy/checks/list.rst
+++ clang-tools-extra/docs/clang-tidy/checks/list.rst
@@ -175,6 +175,7 @@
hicpp-use-nullptr (redirects to modernize-use-nullptr) <hicpp-use-nullptr>
hicpp-use-override (redirects to modernize-use-override) <hicpp-use-override>
hicpp-vararg (redirects to cppcoreguidelines-pro-type-vararg) <hicpp-vararg>
+ llvm-avoid-cast-in-conditional
llvm-header-guard
llvm-include-order
llvm-namespace-comment
Index: clang-tools-extra/docs/ReleaseNotes.rst
===================================================================
--- clang-tools-extra/docs/ReleaseNotes.rst
+++ clang-tools-extra/docs/ReleaseNotes.rst
@@ -130,6 +130,14 @@
<clang-tidy/checks/modernize-use-override>` now supports `OverrideSpelling`
and `FinalSpelling` options.
+- New :doc:`llvm-avoid-cast-in-conditional
+ <clang-tidy/checks/llvm-avoid-cast-in-conditional>` check.
+
+ Finds uses of ``cast<>`` in conditionals of ``if``, ``while`` or
+ ``do`` statements, which will assert rather than return a null
+ pointer. It also finds uses of ``dyn_cast<>`` in conditionals where
+ the return value is not captured.
+
- New :doc:`openmp-exception-escape
<clang-tidy/checks/openmp-exception-escape>` check.
Index: clang-tools-extra/clang-tidy/utils/HeaderFileExtensionsUtils.h
===================================================================
--- clang-tools-extra/clang-tidy/utils/HeaderFileExtensionsUtils.h
+++ clang-tools-extra/clang-tidy/utils/HeaderFileExtensionsUtils.h
@@ -18,7 +18,7 @@
namespace tidy {
namespace utils {
-typedef llvm::SmallSet<llvm::StringRef, 5> HeaderFileExtensionsSet;
+using HeaderFileExtensionsSet = ::llvm::SmallSet<StringRef, 5>;
/// \brief Checks whether expansion location of \p Loc is in header file.
bool isExpansionLocInHeaderFile(
Index: clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
===================================================================
--- clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
+++ clang-tools-extra/clang-tidy/llvm/LLVMTidyModule.cpp
@@ -10,6 +10,7 @@
#include "../ClangTidyModule.h"
#include "../ClangTidyModuleRegistry.h"
#include "../readability/NamespaceCommentCheck.h"
+#include "AvoidCastInConditionalCheck.h"
#include "HeaderGuardCheck.h"
#include "IncludeOrderCheck.h"
#include "TwineLocalCheck.h"
@@ -21,6 +22,8 @@
class LLVMModule : public ClangTidyModule {
public:
void addCheckFactories(ClangTidyCheckFactories &CheckFactories) override {
+ CheckFactories.registerCheck<AvoidCastInConditionalCheck>(
+ "llvm-avoid-cast-in-conditional");
CheckFactories.registerCheck<LLVMHeaderGuardCheck>("llvm-header-guard");
CheckFactories.registerCheck<IncludeOrderCheck>("llvm-include-order");
CheckFactories.registerCheck<readability::NamespaceCommentCheck>(
Index: clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
===================================================================
--- clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
+++ clang-tools-extra/clang-tidy/llvm/CMakeLists.txt
@@ -1,6 +1,7 @@
set(LLVM_LINK_COMPONENTS support)
add_clang_library(clangTidyLLVMModule
+ AvoidCastInConditionalCheck.cpp
HeaderGuardCheck.cpp
IncludeOrderCheck.cpp
LLVMTidyModule.cpp
Index: clang-tools-extra/clang-tidy/llvm/AvoidCastInConditionalCheck.h
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/llvm/AvoidCastInConditionalCheck.h
@@ -0,0 +1,58 @@
+//===--- AvoidCastInConditionalCheck.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_LLVM_AVOIDCASTINCONDITIONALCHECK_H
+#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_AVOIDCASTINCONDITIONALCHECK_H
+
+#include "../ClangTidy.h"
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+/// \brief Finds uses of ``cast<>`` in conditionals of ``if``, ``while`` or
+/// ``do`` statements, which will assert rather than return a null pointer. It
+/// also finds uses of ``dyn_cast<>`` in conditionals where the return value is
+/// not captured.
+///
+/// Finds cases like these:
+/// \code
+/// if (auto x = cast<X>(y)) {}
+/// // is replaced by:
+/// if (auto x = dyn_cast<X>(y)) {}
+///
+/// if (cast<X>(y)) {}
+/// // is replaced by:
+/// if (isa<X>(y)) {}
+///
+/// if (dyn_cast<X>(y)) {}
+/// // is replaced by:
+/// if (isa<X>(y)) {}
+/// \endcode
+///
+/// // These are ignored.
+/// \code
+/// if (auto f = cast<Z>(y)->foo()) {}
+/// if (cast<Z>(y)->foo()) {}
+/// \endcode
+///
+/// For the user-facing documentation see:
+/// http://clang.llvm.org/extra/clang-tidy/checks/llvm-avoid-cast-in-conditional.html
+class AvoidCastInConditionalCheck : public ClangTidyCheck {
+public:
+ AvoidCastInConditionalCheck(StringRef Name, ClangTidyContext *Context)
+ : ClangTidyCheck(Name, Context) {}
+ void registerMatchers(ast_matchers::MatchFinder *Finder) override;
+ void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
+};
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
+
+#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_LLVM_AVOIDCASTINCONDITIONALCHECK_H
Index: clang-tools-extra/clang-tidy/llvm/AvoidCastInConditionalCheck.cpp
===================================================================
--- /dev/null
+++ clang-tools-extra/clang-tidy/llvm/AvoidCastInConditionalCheck.cpp
@@ -0,0 +1,156 @@
+//===--- AvoidCastInConditionalCheck.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 "AvoidCastInConditionalCheck.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/ASTMatchers/ASTMatchFinder.h"
+
+using namespace clang::ast_matchers;
+
+namespace clang {
+namespace tidy {
+namespace llvm {
+
+void AvoidCastInConditionalCheck::registerMatchers(MatchFinder *Finder) {
+ if (!getLangOpts().CPlusPlus)
+ return;
+
+ Finder->addMatcher(
+ stmt(anyOf(
+ ifStmt(anyOf(
+ hasConditionVariableStatement(declStmt(containsDeclaration(
+ 0, varDecl(hasInitializer(
+ callExpr(callee(namedDecl(hasName("cast"))))
+ .bind("cast-assign")))))),
+ hasCondition(implicitCastExpr(anyOf(
+ has(callExpr(callee(namedDecl(hasName("cast"))))
+ .bind("cast")),
+ has(callExpr(callee(namedDecl(hasName("dyn_cast"))))
+ .bind("dyn_cast"))))))),
+ whileStmt(anyOf(
+ has(declStmt(containsDeclaration(
+ 0, varDecl(hasInitializer(
+ callExpr(callee(namedDecl(hasName("cast"))))
+ .bind("cast-assign")))))),
+ hasCondition(implicitCastExpr(anyOf(
+ has(callExpr(callee(namedDecl(hasName("cast"))))
+ .bind("cast")),
+ has(callExpr(callee(namedDecl(hasName("dyn_cast"))))
+ .bind("dyn_cast"))))))),
+ doStmt(hasCondition(implicitCastExpr(anyOf(
+ has(callExpr(callee(namedDecl(hasName("cast")))).bind("cast")),
+ has(callExpr(callee(namedDecl(hasName("dyn_cast"))))
+ .bind("dyn_cast")))))),
+ binaryOperator(
+ allOf(
+ unless(isExpansionInFileMatching(
+ "llvm/include/llvm/Support/Casting.h")),
+ hasOperatorName("&&"),
+ hasLHS(
+ implicitCastExpr(has(cxxMemberCallExpr().bind("lhs")))),
+ hasRHS(anyOf(
+ callExpr(
+ allOf(hasArgument(0, cxxMemberCallExpr().bind("arg")),
+ callee(namedDecl(hasName("isa")).bind("func"))))
+ .bind("rhs"),
+ implicitCastExpr(has(
+ callExpr(
+ allOf(hasArgument(
+ 0, cxxMemberCallExpr().bind("arg")),
+ callee(
+ namedDecl(
+ anyOf(hasName("cast"),
+ hasName("dyn_cast"),
+ hasName("dyn_cast_or_null")))
+ .bind("func"))))
+ .bind("rhs")))))))
+ .bind("and"))),
+ this);
+}
+
+void AvoidCastInConditionalCheck::check(
+ const MatchFinder::MatchResult &Result) {
+ if (const auto *MatchedDecl =
+ Result.Nodes.getNodeAs<CallExpr>("cast-assign")) {
+ SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
+ SourceLocation EndLoc =
+ StartLoc.getLocWithOffset(StringRef("cast").size() - 1);
+
+ if (StartLoc.isMacroID())
+ return;
+
+ diag(MatchedDecl->getBeginLoc(),
+ "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>("cast")) {
+ SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
+ SourceLocation EndLoc =
+ StartLoc.getLocWithOffset(StringRef("cast").size() - 1);
+
+ if (StartLoc.isMacroID())
+ return;
+
+ diag(MatchedDecl->getBeginLoc(),
+ "cast<> in conditional will assert rather than return a null pointer")
+ << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc), "isa");
+ } else if (const auto *MatchedDecl =
+ Result.Nodes.getNodeAs<CallExpr>("dyn_cast")) {
+ SourceLocation StartLoc = MatchedDecl->getCallee()->getExprLoc();
+ SourceLocation EndLoc =
+ StartLoc.getLocWithOffset(StringRef("dyn_cast").size() - 1);
+
+ if (StartLoc.isMacroID())
+ return;
+
+ diag(MatchedDecl->getBeginLoc(), "return value from dyn_cast<> not used")
+ << FixItHint::CreateReplacement(SourceRange(StartLoc, EndLoc), "isa");
+ } else if (const auto *MatchedDecl =
+ Result.Nodes.getNodeAs<BinaryOperator>("and")) {
+ const auto *Arg = Result.Nodes.getNodeAs<CXXMemberCallExpr>("arg");
+ const auto *LHS = Result.Nodes.getNodeAs<CXXMemberCallExpr>("lhs");
+ const auto *RHS = Result.Nodes.getNodeAs<Expr>("rhs");
+ const auto *Func = Result.Nodes.getNodeAs<NamedDecl>("func");
+
+ if (RHS->getBeginLoc().isMacroID())
+ return;
+
+ CharSourceRange ArgRange =
+ CharSourceRange::getTokenRange(Arg->getSourceRange());
+ std::string ArgString(
+ Lexer::getSourceText(ArgRange, *Result.SourceManager, getLangOpts()));
+
+ CharSourceRange LHSRange =
+ CharSourceRange::getTokenRange(LHS->getSourceRange());
+ std::string LHSString(
+ Lexer::getSourceText(LHSRange, *Result.SourceManager, getLangOpts()));
+
+ if (ArgString != LHSString)
+ return;
+
+ CharSourceRange RHSRange =
+ CharSourceRange::getTokenRange(RHS->getSourceRange());
+ std::string RHSString(
+ Lexer::getSourceText(RHSRange, *Result.SourceManager, getLangOpts()));
+
+ std::string Replacement("dyn_cast_or_null");
+ Replacement += RHSString.substr(Func->getName().size());
+
+ diag(MatchedDecl->getBeginLoc(),
+ "method %0 is called twice and could be expensive")
+ << LHS->getMethodDecl()
+ << FixItHint::CreateReplacement(SourceRange(MatchedDecl->getBeginLoc(),
+ MatchedDecl->getEndLoc()),
+ Replacement);
+ }
+}
+
+} // namespace llvm
+} // namespace tidy
+} // namespace clang
_______________________________________________
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits