https://github.com/rockwotj updated https://github.com/llvm/llvm-project/pull/76101
>From 5afeaab9f148b10d951e37fd27cb32687f310a9c Mon Sep 17 00:00:00 2001 From: Tyler Rockwood <rockw...@redpanda.com> Date: Thu, 21 Dec 2023 16:31:12 -0600 Subject: [PATCH] clang-tidy/bugprone: introduce unused-local-non-trivial-variable check Signed-off-by: Tyler Rockwood <rockw...@redpanda.com> --- .../bugprone/BugproneTidyModule.cpp | 3 + .../clang-tidy/bugprone/CMakeLists.txt | 1 + .../UnusedLocalNonTrivialVariableCheck.cpp | 89 +++++++++++++++++++ .../UnusedLocalNonTrivialVariableCheck.h | 44 +++++++++ clang-tools-extra/docs/ReleaseNotes.rst | 13 +++ .../unused-local-non-trivial-variable.rst | 29 ++++++ .../docs/clang-tidy/checks/list.rst | 1 + .../unused-local-non-trivial-variable.cpp | 79 ++++++++++++++++ 8 files changed, 259 insertions(+) create mode 100644 clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.cpp create mode 100644 clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.h create mode 100644 clang-tools-extra/docs/clang-tidy/checks/bugprone/unused-local-non-trivial-variable.rst create mode 100644 clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-local-non-trivial-variable.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp index 7a910037368c83..435cb1e3fbcff3 100644 --- a/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp +++ b/clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp @@ -83,6 +83,7 @@ #include "UnhandledSelfAssignmentCheck.h" #include "UniquePtrArrayMismatchCheck.h" #include "UnsafeFunctionsCheck.h" +#include "UnusedLocalNonTrivialVariableCheck.h" #include "UnusedRaiiCheck.h" #include "UnusedReturnValueCheck.h" #include "UseAfterMoveCheck.h" @@ -235,6 +236,8 @@ class BugproneModule : public ClangTidyModule { "bugprone-unique-ptr-array-mismatch"); CheckFactories.registerCheck<UnsafeFunctionsCheck>( "bugprone-unsafe-functions"); + CheckFactories.registerCheck<UnusedLocalNonTrivialVariableCheck>( + "bugprone-unused-local-non-trivial-variable"); CheckFactories.registerCheck<UnusedRaiiCheck>("bugprone-unused-raii"); CheckFactories.registerCheck<UnusedReturnValueCheck>( "bugprone-unused-return-value"); diff --git a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt index d443fd8d1452f1..70e7fbc7ec0c14 100644 --- a/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt +++ b/clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt @@ -79,6 +79,7 @@ add_clang_library(clangTidyBugproneModule UnhandledSelfAssignmentCheck.cpp UniquePtrArrayMismatchCheck.cpp UnsafeFunctionsCheck.cpp + UnusedLocalNonTrivialVariableCheck.cpp UnusedRaiiCheck.cpp UnusedReturnValueCheck.cpp UseAfterMoveCheck.cpp diff --git a/clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.cpp new file mode 100644 index 00000000000000..2c61078e1d1cc1 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.cpp @@ -0,0 +1,89 @@ +//===--- UnusedLocalNonTrivialVariableCheck.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 "UnusedLocalNonTrivialVariableCheck.h" +#include "../utils/Matchers.h" +#include "../utils/OptionsUtils.h" +#include "clang/AST/ASTContext.h" +#include "clang/AST/ASTTypeTraits.h" +#include "clang/AST/Type.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchers.h" +#include "clang/ASTMatchers/ASTMatchersMacros.h" + +using namespace clang::ast_matchers; +using namespace clang::tidy::matchers; + +namespace clang::tidy::bugprone { + +namespace { +static constexpr StringRef DefaultIncludeTypeRegex = "std::.*mutex;std::future"; + +AST_MATCHER(VarDecl, isLocalVarDecl) { return Node.isLocalVarDecl(); } +AST_MATCHER(VarDecl, isReferenced) { return Node.isReferenced(); } +AST_MATCHER(Type, isReferenceType) { return Node.isReferenceType(); } +} // namespace + +UnusedLocalNonTrivialVariableCheck::UnusedLocalNonTrivialVariableCheck( + StringRef Name, ClangTidyContext *Context) + : ClangTidyCheck(Name, Context), + IncludeTypeRegex(utils::options::parseStringList( + Options.get("IncludeTypeRegex", DefaultIncludeTypeRegex))), + ExcludeTypeRegex(utils::options::parseStringList( + Options.get("ExcludeTypeRegex", ""))) {} + +void UnusedLocalNonTrivialVariableCheck::storeOptions( + ClangTidyOptions::OptionMap &Opts) { + Options.store(Opts, "IncludeTypeRegex", + utils::options::serializeStringList(IncludeTypeRegex)); + Options.store(Opts, "ExcludeTypeRegex", + utils::options::serializeStringList(ExcludeTypeRegex)); +} + +void UnusedLocalNonTrivialVariableCheck::registerMatchers(MatchFinder *Finder) { + if (IncludeTypeRegex.empty()) + return; + + Finder->addMatcher( + varDecl( + isLocalVarDecl(), unless(isReferenced()), + unless(isExpansionInSystemHeader()), unless(isImplicit()), + unless(isExceptionVariable()), hasLocalStorage(), isDefinition(), + unless(hasType(isReferenceType())), + hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration( + recordDecl(matchesAnyListedName(IncludeTypeRegex)))))), + unless(hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration( + recordDecl(matchesAnyListedName(ExcludeTypeRegex)))))))) + .bind("var"), + this); +} + +void UnusedLocalNonTrivialVariableCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *MatchedDecl = Result.Nodes.getNodeAs<VarDecl>("var"); + const auto Type = MatchedDecl->getType(); + + if (Type.isTrivialType(*Result.Context) || + Type.isTriviallyCopyableType(*Result.Context)) + return; + + diag(MatchedDecl->getLocation(), "unused local variable %0 of type %1") + << MatchedDecl << Type; +} + +bool UnusedLocalNonTrivialVariableCheck::isLanguageVersionSupported( + const LangOptions &LangOpts) const { + return LangOpts.CPlusPlus; +} + +std::optional<TraversalKind> +UnusedLocalNonTrivialVariableCheck::getCheckTraversalKind() const { + return TK_IgnoreUnlessSpelledInSource; +} + +} // namespace clang::tidy::bugprone diff --git a/clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.h b/clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.h new file mode 100644 index 00000000000000..5e7778d229aa82 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/UnusedLocalNonTrivialVariableCheck.h @@ -0,0 +1,44 @@ +//===--- UnusedLocalNonTrivialVariableCheck.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_BUGPRONE_UNUSEDLOCALNONTRIVIALVARIABLECHECK_H +#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDLOCALNONTRIVIALVARIABLECHECK_H + +#include "../ClangTidyCheck.h" + +namespace clang::tidy::bugprone { + +/// Warns when a local non trivial variable is unused within a function. By +/// default std::.*mutex and std::future are included. +/// +/// The check supports these options: +/// - 'IncludeTypeRegex': a semicolon-separated list of regular expressions +/// matching types to ensure must be used. +/// - 'ExcludeTypeRegex': a semicolon-separated list of regular expressions +/// matching types that are excluded from the +/// 'IncludeTypeRegex' matches. +/// +/// For the user-facing documentation see: +/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/unused-local-non-trivial-variable.html +class UnusedLocalNonTrivialVariableCheck : public ClangTidyCheck { +public: + UnusedLocalNonTrivialVariableCheck(StringRef Name, ClangTidyContext *Context); + void registerMatchers(ast_matchers::MatchFinder *Finder) override; + void check(const ast_matchers::MatchFinder::MatchResult &Result) override; + void storeOptions(ClangTidyOptions::OptionMap &Opts) override; + bool isLanguageVersionSupported(const LangOptions &LangOpts) const override; + std::optional<TraversalKind> getCheckTraversalKind() const override; + +private: + const std::vector<StringRef> IncludeTypeRegex; + const std::vector<StringRef> ExcludeTypeRegex; +}; + +} // namespace clang::tidy::bugprone + +#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDLOCALNONTRIVIALVARIABLECHECK_H diff --git a/clang-tools-extra/docs/ReleaseNotes.rst b/clang-tools-extra/docs/ReleaseNotes.rst index 6d91748e4cef18..2e0a9f9aeae75e 100644 --- a/clang-tools-extra/docs/ReleaseNotes.rst +++ b/clang-tools-extra/docs/ReleaseNotes.rst @@ -168,6 +168,19 @@ New checks extracted from an optional-like type and then used to create a new instance of the same optional-like type. +- New :doc:`bugprone-unused-local-non-trivial-variable + <clang-tidy/checks/bugprone/unused-local-non-trivial-variable>` check. + + Warns when a local non trivial variable is unused within a function. By + default std::.*mutex and std::future are included. + + The check supports these options: + - 'IncludeTypeRegex': a semicolon-separated list of regular expressions + matching types to ensure must be used. + - 'ExcludeTypeRegex': a semicolon-separated list of regular expressions + matching types that are excluded from the + 'IncludeTypeRegex' matches. + - New :doc:`cppcoreguidelines-no-suspend-with-lock <clang-tidy/checks/cppcoreguidelines/no-suspend-with-lock>` check. diff --git a/clang-tools-extra/docs/clang-tidy/checks/bugprone/unused-local-non-trivial-variable.rst b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unused-local-non-trivial-variable.rst new file mode 100644 index 00000000000000..59c50419657ec6 --- /dev/null +++ b/clang-tools-extra/docs/clang-tidy/checks/bugprone/unused-local-non-trivial-variable.rst @@ -0,0 +1,29 @@ +.. title:: clang-tidy - bugprone-unused-local-non-trivial-variable + +bugprone-unused-local-non-trivial-variable +========================================== + +Warns when a local non trivial variable is unused within a function. + +In the following example, `future2` would generate a warning that it is unused. + +.. code-block:: c++ + + std::future<MyObject> future1; + std::future<MyObject> future2; + // ... + MyObject foo = future1.get(); + // future2 is not used. + +Options +------- + +.. option:: IncludeTypeRegex + + Semicolon-separated list of regular expressions matching types of variables to check. + By default it 'std::.*mutex;std::future'. + +.. option:: ExcludeTypeRegex + + A semicolon-separated list of regular expressions matching types that are excluded from the + 'IncludeTypeRegex' matches. By default it is an empty list. diff --git a/clang-tools-extra/docs/clang-tidy/checks/list.rst b/clang-tools-extra/docs/clang-tidy/checks/list.rst index 31f0e090db1d7d..65b23defd73fa0 100644 --- a/clang-tools-extra/docs/clang-tidy/checks/list.rst +++ b/clang-tools-extra/docs/clang-tidy/checks/list.rst @@ -149,6 +149,7 @@ Clang-Tidy Checks :doc:`bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment>`, :doc:`bugprone-unique-ptr-array-mismatch <bugprone/unique-ptr-array-mismatch>`, "Yes" :doc:`bugprone-unsafe-functions <bugprone/unsafe-functions>`, + :doc:`bugprone-unused-local-non-trivial-variable <bugprone/unused-local-non-trivial-variable>`, "Yes" :doc:`bugprone-unused-raii <bugprone/unused-raii>`, "Yes" :doc:`bugprone-unused-return-value <bugprone/unused-return-value>`, :doc:`bugprone-use-after-move <bugprone/use-after-move>`, diff --git a/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-local-non-trivial-variable.cpp b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-local-non-trivial-variable.cpp new file mode 100644 index 00000000000000..c6e45378b9c3e2 --- /dev/null +++ b/clang-tools-extra/test/clang-tidy/checkers/bugprone/unused-local-non-trivial-variable.cpp @@ -0,0 +1,79 @@ +// RUN: %check_clang_tidy %s bugprone-unused-local-non-trivial-variable %t -- \ +// RUN: -config="{CheckOptions: {bugprone-unused-local-non-trivial-variable.IncludeTypeRegex: '::async::Future'}}" + + +namespace async { +template <typename T> +class Ptr { + public: + explicit Ptr(T Arg) : Underlying(new T(Arg)) {} + T& operator->() { + return Underlying; + } + ~Ptr() { + delete Underlying; + } + private: + T* Underlying; +}; + +template<typename T> +class Future { +public: + T get() { + return Pending; + } + ~Future(); +private: + T Pending; +}; + + +} // namespace async + +// Warning is still emitted if there are type aliases. +namespace a { +template<typename T> +using Future = async::Future<T>; +} // namespace a + +void releaseUnits(); +struct Units { + ~Units() { + releaseUnits(); + } +}; +a::Future<Units> acquireUnits(); + +template<typename T> +T qux(T Generic) { + async::Future<Units> PendingA = acquireUnits(); + auto PendingB = acquireUnits(); + // CHECK-MESSAGES: :[[@LINE-1]]:10: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'Future<Units>') [bugprone-unused-local-non-trivial-variable] + async::Future<Units> MustBeUsed; + // CHECK-MESSAGES: :[[@LINE-1]]:26: warning: unused local variable 'MustBeUsed' of type 'async::Future<Units>' [bugprone-unused-local-non-trivial-variable] + PendingA.get(); + return Generic; +} + +async::Future<int> Global; + +int bar(int Num) { + a::Future<Units> PendingA = acquireUnits(); + a::Future<Units> PendingB = acquireUnits(); // not used at all, unused variable not fired because of destructor side effect + // CHECK-MESSAGES: :[[@LINE-1]]:22: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'Future<Units>') [bugprone-unused-local-non-trivial-variable] + auto Num2 = PendingA.get(); + auto Num3 = qux(Num); + async::Ptr<a::Future<Units>> Shared = async::Ptr<a::Future<Units>>(acquireUnits()); + static auto UnusedStatic = async::Future<Units>(); + thread_local async::Future<Units> UnusedThreadLocal; + auto Captured = acquireUnits(); + Num3 += [Captured]() { + return 1; + }(); + a::Future<Units> Referenced = acquireUnits(); + a::Future<Units>* Pointer = &Referenced; + a::Future<Units>& Reference = Referenced; + const a::Future<Units>& ConstReference = Referenced; + return Num * Num3; +} _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits