diff --git a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt index d9ec268650c0..996c6d2c875e 100644 --- a/clang-tools-extra/clang-tidy/misc/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/misc/CMakeLists.txt @@ -40,6 +40,7 @@ add_clang_library(clangTidyMiscModule UnusedParametersCheck.cpp UnusedUsingDeclsCheck.cpp UseAnonymousNamespaceCheck.cpp + UseFreeFunctionVariantsCheck.cpp LINK_LIBS clangAnalysis diff --git a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp index d8a88324ee63..7887cde136e3 100644 --- a/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/misc/MiscTidyModule.cpp @@ -31,6 +31,7 @@ #include "UnusedParametersCheck.h" #include "UnusedUsingDeclsCheck.h" #include "UseAnonymousNamespaceCheck.h" +#include "UseFreeFunctionVariantsCheck.h" namespace clang::tidy { namespace misc { @@ -78,6 +79,8 @@ class MiscModule : public ClangTidyModule { "misc-unused-using-decls"); CheckFactories.registerCheck( "misc-use-anonymous-namespace"); + CheckFactories.registerCheck( + "misc-use-free-function-variants"); } }; diff --git a/clang-tools-extra/clang-tidy/misc/UseFreeFunctionVariantsCheck.cpp b/clang-tools-extra/clang-tidy/misc/UseFreeFunctionVariantsCheck.cpp new file mode 100644 index 000000000000..f33589eb4b85 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/UseFreeFunctionVariantsCheck.cpp @@ -0,0 +1,79 @@ +//===--- UseFreeFunctionVariantsCheck.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 "UseFreeFunctionVariantsCheck.h" +#include "clang/AST/ASTContext.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/Basic/SourceManager.h" +#include "clang/Lex/Lexer.h" +#include + +using namespace clang::ast_matchers; + +namespace clang::tidy::misc { + +void UseFreeFunctionVariantsCheck::registerMatchers(MatchFinder *Finder) { + Finder->addMatcher(cxxMemberCallExpr(on(expr().bind("base")), + callee(cxxMethodDecl(hasAnyName("isa", "dyn_cast", "cast", "dyn_cast_or_null", "isa_and_nonnull"))) + ).bind("castCall"), + this); +} + +// TODO(askrebko): Add checker for types wrapped in pointers and optional +void UseFreeFunctionVariantsCheck::check( + const MatchFinder::MatchResult &Result) { + const auto* CallExpr = Result.Nodes.getNodeAs("castCall"); + const auto* BaseExpr = Result.Nodes.getNodeAs("base"); + + if (!CallExpr || !BaseExpr) { + return; + } + SourceManager &SM = *Result.SourceManager; + LangOptions LangOpts = getLangOpts(); + + std::string BaseStr = Lexer::getSourceText( + CharSourceRange::getTokenRange(BaseExpr->getSourceRange()), SM, LangOpts).str(); + + const auto* MethodDecl = CallExpr->getMethodDecl(); + if (!MethodDecl->getTemplateSpecializationArgs()) { + return; + } + + std::string TemplateArgStr; + for (const auto& Arg : MethodDecl->getTemplateSpecializationArgs()->asArray()) { + if (Arg.getKind() == TemplateArgument::ArgKind::Type) { + PrintingPolicy Policy(LangOpts); + Policy.adjustForCPlusPlus(); + if (!TemplateArgStr.empty()) { + TemplateArgStr += ", "; + } + TemplateArgStr += Arg.getAsType().getAsString(Policy); + } else if (Arg.getKind() == TemplateArgument::ArgKind::Pack) { + for (const auto& PackArg : Arg.pack_elements()) { + PrintingPolicy Policy(LangOpts); + Policy.adjustForCPlusPlus(); + if (!TemplateArgStr.empty()) { + TemplateArgStr += ", "; + } + TemplateArgStr += PackArg.getAsType().getAsString(Policy); + } + } else { + std::cout << "Unsupported template argument kind: " << Arg.getKind() << std::endl; + return; + } + } + + std::string FuncName = MethodDecl->getNameAsString(); + + std::string Replacement = "mlir::" + FuncName + "<" + TemplateArgStr + ">(" + BaseStr + ")"; + diag(CallExpr->getBeginLoc(), "Replace 'type.isa()' with 'mlir::isa(type)'") + << FixItHint::CreateReplacement(CallExpr->getSourceRange(), Replacement); +} + +} // namespace clang::tidy::misc diff --git a/clang-tools-extra/clang-tidy/misc/UseFreeFunctionVariantsCheck.h b/clang-tools-extra/clang-tidy/misc/UseFreeFunctionVariantsCheck.h new file mode 100644 index 000000000000..f95941a0e330 --- /dev/null +++ b/clang-tools-extra/clang-tidy/misc/UseFreeFunctionVariantsCheck.h @@ -0,0 +1,30 @@ +//===--- UseFreeFunctionVariantsCheck.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_MISC_USEFREEFUNCTIONVARIANTSCHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEFREEFUNCTIONVARIANTSCHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::misc { + +/// FIXME: Write a short description. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/misc/use-free-function-variants.html +class UseFreeFunctionVariantsCheck : public ClangTidyCheck { +public: + UseFreeFunctionVariantsCheck(StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context) {} + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; +}; + +} // namespace clang::tidy::misc + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_MISC_USEFREEFUNCTIONVARIANTSCHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 8621444364fb..2b872138bcb7 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -229,6 +229,11 @@ New checks points in a coroutine. Such hostile types include scoped-lockable types and types belonging to a configurable denylist. +- New :doc:`misc-use-free-function-variants + ` check. + + FIXME: add release notes. + - New :doc:`modernize-use-constraints ` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index f773e80b562e..d9d2d7cd4fd1 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -265,6 +265,7 @@ Clang-Tidy Checks :doc:`misc-unused-parameters `, "Yes" :doc:`misc-unused-using-decls `, "Yes" :doc:`misc-use-anonymous-namespace `, + :doc:`misc-use-free-function-variants `, "Yes" :doc:`modernize-avoid-bind `, "Yes" :doc:`modernize-avoid-c-arrays `, :doc:`modernize-concat-nested-namespaces `, "Yes" diff --git a/clang-tools-extra/docs/clang-tidy/checks/misc/use-free-function-variants.rst b/clang-tools-extra/docs/clang-tidy/checks/misc/use-free-function-variants.rst new file mode 100644 index 000000000000..330ba69d4b19 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/misc/use-free-function-variants.rst @@ -0,0 +1,6 @@ +.. title:: clang-tidy - misc-use-free-function-variants + +misc-use-free-function-variants +=============================== + +FIXME: Describe what patterns does the check detect and why. Give examples. diff --git a/clang-tools-extra/test/clang-tidy/checkers/misc/use-free-function-variants.cpp b/clang-tools-extra/test/clang-tidy/checkers/misc/use-free-function-variants.cpp new file mode 100644 index 000000000000..4a1dcdc853f0 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/misc/use-free-function-variants.cpp @@ -0,0 +1,14 @@ +// RUN: %check_clang_tidy %s misc-use-free-function-variants %t + +// FIXME: Add something that triggers the check here. +void f(); +// CHECK-MESSAGES: :[[@LINE-1]]:6: warning: function 'f' is insufficiently awesome [misc-use-free-function-variants] + +// FIXME: Verify the applied fix. +// * Make the CHECK patterns specific enough and try to make verified lines +// unique to avoid incorrect matches. +// * Use {{}} for regular expressions. +// CHECK-FIXES: {{^}}void awesome_f();{{$}} + +// FIXME: Add something that doesn't trigger the check here. +void awesome_f2();