Skip to content

Commit 6f0b7e5

Browse files
committed
clang-tidy/bugprone: introduce unused-local-non-trivial-variable check
Signed-off-by: Tyler Rockwood <[email protected]>
1 parent 8bbeed0 commit 6f0b7e5

File tree

8 files changed

+311
-0
lines changed

8 files changed

+311
-0
lines changed

clang-tools-extra/clang-tidy/bugprone/BugproneTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,7 @@
8383
#include "UnhandledSelfAssignmentCheck.h"
8484
#include "UniquePtrArrayMismatchCheck.h"
8585
#include "UnsafeFunctionsCheck.h"
86+
#include "UnusedLocalNonTrivialVariableCheck.h"
8687
#include "UnusedRaiiCheck.h"
8788
#include "UnusedReturnValueCheck.h"
8889
#include "UseAfterMoveCheck.h"
@@ -235,6 +236,8 @@ class BugproneModule : public ClangTidyModule {
235236
"bugprone-unique-ptr-array-mismatch");
236237
CheckFactories.registerCheck<UnsafeFunctionsCheck>(
237238
"bugprone-unsafe-functions");
239+
CheckFactories.registerCheck<UnusedLocalNonTrivialVariableCheck>(
240+
"bugprone-unused-local-non-trivial-variable");
238241
CheckFactories.registerCheck<UnusedRaiiCheck>("bugprone-unused-raii");
239242
CheckFactories.registerCheck<UnusedReturnValueCheck>(
240243
"bugprone-unused-return-value");

clang-tools-extra/clang-tidy/bugprone/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,7 @@ add_clang_library(clangTidyBugproneModule
7979
UnhandledSelfAssignmentCheck.cpp
8080
UniquePtrArrayMismatchCheck.cpp
8181
UnsafeFunctionsCheck.cpp
82+
UnusedLocalNonTrivialVariableCheck.cpp
8283
UnusedRaiiCheck.cpp
8384
UnusedReturnValueCheck.cpp
8485
UseAfterMoveCheck.cpp
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
//===--- UnusedLocalNonTrivialVariableCheck.cpp - clang-tidy --------------===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#include "UnusedLocalNonTrivialVariableCheck.h"
10+
#include "../utils/Matchers.h"
11+
#include "../utils/OptionsUtils.h"
12+
#include "clang/AST/ASTContext.h"
13+
#include "clang/AST/ASTTypeTraits.h"
14+
#include "clang/AST/Type.h"
15+
#include "clang/ASTMatchers/ASTMatchFinder.h"
16+
#include "clang/ASTMatchers/ASTMatchers.h"
17+
#include "clang/ASTMatchers/ASTMatchersMacros.h"
18+
19+
using namespace clang::ast_matchers;
20+
using namespace clang::tidy::matchers;
21+
22+
namespace clang::tidy::bugprone {
23+
24+
namespace {
25+
static constexpr StringRef DefaultIncludeTypeRegex =
26+
"::std::.*mutex;::std::future;::std::basic_string;::std::basic_regex;"
27+
"::std::base_istringstream;::std::base_stringstream;::std::bitset;"
28+
"::std::filesystem::path";
29+
30+
AST_MATCHER(VarDecl, isLocalVarDecl) { return Node.isLocalVarDecl(); }
31+
AST_MATCHER(VarDecl, isReferenced) { return Node.isReferenced(); }
32+
AST_MATCHER(Type, isReferenceType) { return Node.isReferenceType(); }
33+
AST_MATCHER(QualType, isTrivial) {
34+
return Node.isTrivialType(Finder->getASTContext()) ||
35+
Node.isTriviallyCopyableType(Finder->getASTContext());
36+
}
37+
} // namespace
38+
39+
UnusedLocalNonTrivialVariableCheck::UnusedLocalNonTrivialVariableCheck(
40+
StringRef Name, ClangTidyContext *Context)
41+
: ClangTidyCheck(Name, Context),
42+
IncludeTypes(utils::options::parseStringList(
43+
Options.get("IncludeTypes", DefaultIncludeTypeRegex))),
44+
ExcludeTypes(
45+
utils::options::parseStringList(Options.get("ExcludeTypes", ""))) {}
46+
47+
void UnusedLocalNonTrivialVariableCheck::storeOptions(
48+
ClangTidyOptions::OptionMap &Opts) {
49+
Options.store(Opts, "IncludeTypes",
50+
utils::options::serializeStringList(IncludeTypes));
51+
Options.store(Opts, "ExcludeTypes",
52+
utils::options::serializeStringList(ExcludeTypes));
53+
}
54+
55+
void UnusedLocalNonTrivialVariableCheck::registerMatchers(MatchFinder *Finder) {
56+
if (IncludeTypes.empty())
57+
return;
58+
59+
Finder->addMatcher(
60+
varDecl(isLocalVarDecl(), unless(isReferenced()),
61+
unless(isExceptionVariable()), hasLocalStorage(), isDefinition(),
62+
unless(hasType(isReferenceType())), unless(hasType(isTrivial())),
63+
hasType(hasUnqualifiedDesugaredType(recordType(hasDeclaration(
64+
recordDecl(matchesAnyListedName(IncludeTypes),
65+
unless(matchesAnyListedName(ExcludeTypes))))))))
66+
.bind("var"),
67+
this);
68+
}
69+
70+
void UnusedLocalNonTrivialVariableCheck::check(
71+
const MatchFinder::MatchResult &Result) {
72+
const auto *MatchedDecl = Result.Nodes.getNodeAs<VarDecl>("var");
73+
diag(MatchedDecl->getLocation(), "unused local variable %0 of type %1")
74+
<< MatchedDecl << MatchedDecl->getType();
75+
}
76+
77+
bool UnusedLocalNonTrivialVariableCheck::isLanguageVersionSupported(
78+
const LangOptions &LangOpts) const {
79+
return LangOpts.CPlusPlus;
80+
}
81+
82+
std::optional<TraversalKind>
83+
UnusedLocalNonTrivialVariableCheck::getCheckTraversalKind() const {
84+
return TK_IgnoreUnlessSpelledInSource;
85+
}
86+
87+
} // namespace clang::tidy::bugprone
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
//===--- UnusedLocalNonTrivialVariableCheck.h - clang-tidy ------*- C++ -*-===//
2+
//
3+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4+
// See https://llvm.org/LICENSE.txt for license information.
5+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6+
//
7+
//===----------------------------------------------------------------------===//
8+
9+
#ifndef LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDLOCALNONTRIVIALVARIABLECHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDLOCALNONTRIVIALVARIABLECHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::bugprone {
15+
16+
/// Warns when a local non trivial variable is unused within a function. By
17+
/// default std::.*mutex and std::future are included.
18+
///
19+
/// The check supports these options:
20+
/// - 'IncludeTypes': a semicolon-separated list of regular expressions
21+
/// matching types to ensure must be used.
22+
/// - 'ExcludeTypes': a semicolon-separated list of regular expressions
23+
/// matching types that are excluded from the
24+
/// 'IncludeTypes' matches.
25+
///
26+
/// For the user-facing documentation see:
27+
/// http://clang.llvm.org/extra/clang-tidy/checks/bugprone/unused-local-non-trivial-variable.html
28+
class UnusedLocalNonTrivialVariableCheck : public ClangTidyCheck {
29+
public:
30+
UnusedLocalNonTrivialVariableCheck(StringRef Name, ClangTidyContext *Context);
31+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
32+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
33+
void storeOptions(ClangTidyOptions::OptionMap &Opts) override;
34+
bool isLanguageVersionSupported(const LangOptions &LangOpts) const override;
35+
std::optional<TraversalKind> getCheckTraversalKind() const override;
36+
37+
private:
38+
const std::vector<StringRef> IncludeTypes;
39+
const std::vector<StringRef> ExcludeTypes;
40+
};
41+
42+
} // namespace clang::tidy::bugprone
43+
44+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_BUGPRONE_UNUSEDLOCALNONTRIVIALVARIABLECHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,11 @@ New checks
168168
extracted from an optional-like type and then used to create a new instance
169169
of the same optional-like type.
170170

171+
- New :doc:`bugprone-unused-local-non-trivial-variable
172+
<clang-tidy/checks/bugprone/unused-local-non-trivial-variable>` check.
173+
174+
Warns when a local non trivial variable is unused within a function.
175+
171176
- New :doc:`cppcoreguidelines-no-suspend-with-lock
172177
<clang-tidy/checks/cppcoreguidelines/no-suspend-with-lock>` check.
173178

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
.. title:: clang-tidy - bugprone-unused-local-non-trivial-variable
2+
3+
bugprone-unused-local-non-trivial-variable
4+
==========================================
5+
6+
Warns when a local non trivial variable is unused within a function.
7+
The following types of variables are excluded from this check:
8+
9+
* trivial and trivially copyable
10+
* references and pointers
11+
* exception variables in catch clauses
12+
* static or thread local
13+
* structured bindings
14+
15+
This check can be configured to warn on all non-trivial variables by setting
16+
`IncludeTypes` to `.*`, and excluding specific types using `ExcludeTypes`.
17+
18+
In the this example, `my_lock` would generate a warning that it is unused.
19+
20+
.. code-block:: c++
21+
22+
std::mutex my_lock;
23+
// my_lock local variable is never used
24+
25+
In the next example, `future2` would generate a warning that it is unused.
26+
27+
.. code-block:: c++
28+
29+
std::future<MyObject> future1;
30+
std::future<MyObject> future2;
31+
// ...
32+
MyObject foo = future1.get();
33+
// future2 is not used.
34+
35+
Options
36+
-------
37+
38+
.. option:: IncludeTypes
39+
40+
Semicolon-separated list of regular expressions matching types of variables
41+
to check.
42+
By default the following types are checked:
43+
44+
* `::std::.*mutex`
45+
* `::std::future`
46+
* `::std::string`
47+
* `::std::basic_regex`
48+
* `::std::basic_istringstream`
49+
* `::std::basic_stringstream`
50+
* `::std::bitset`
51+
* `::std::path`
52+
53+
.. option:: ExcludeTypes
54+
55+
A semicolon-separated list of regular expressions matching types that are
56+
excluded from the `IncludeTypes` matches. By default it is an empty list.

clang-tools-extra/docs/clang-tidy/checks/list.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ Clang-Tidy Checks
149149
:doc:`bugprone-unhandled-self-assignment <bugprone/unhandled-self-assignment>`,
150150
:doc:`bugprone-unique-ptr-array-mismatch <bugprone/unique-ptr-array-mismatch>`, "Yes"
151151
:doc:`bugprone-unsafe-functions <bugprone/unsafe-functions>`,
152+
:doc:`bugprone-unused-local-non-trivial-variable <bugprone/unused-local-non-trivial-variable>`,
152153
:doc:`bugprone-unused-raii <bugprone/unused-raii>`, "Yes"
153154
:doc:`bugprone-unused-return-value <bugprone/unused-return-value>`,
154155
:doc:`bugprone-use-after-move <bugprone/use-after-move>`,
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
// RUN: %check_clang_tidy -std=c++17-or-later %s bugprone-unused-local-non-trivial-variable %t -- \
2+
// RUN: -config="{CheckOptions: {bugprone-unused-local-non-trivial-variable.IncludeTypes: '::async::Future;::async::Foo.*', bugprone-unused-local-non-trivial-variable.ExcludeTypes: '::async::FooBar'}}"
3+
4+
5+
namespace async {
6+
template <typename T>
7+
class Ptr {
8+
public:
9+
explicit Ptr(T Arg) : Underlying(new T(Arg)) {}
10+
T& operator->() {
11+
return Underlying;
12+
}
13+
~Ptr() {
14+
delete Underlying;
15+
}
16+
private:
17+
T* Underlying;
18+
};
19+
20+
template<typename T>
21+
class Future {
22+
public:
23+
T get() {
24+
return Pending;
25+
}
26+
~Future();
27+
private:
28+
T Pending;
29+
};
30+
31+
class FooBar {
32+
public:
33+
~FooBar();
34+
private:
35+
Future<int> Fut;
36+
};
37+
38+
class FooQux {
39+
public:
40+
~FooQux();
41+
private:
42+
Future<int> Fut;
43+
};
44+
45+
class FizzFoo {
46+
public:
47+
~FizzFoo();
48+
private:
49+
Future<int> Fut;
50+
};
51+
52+
} // namespace async
53+
54+
// Warning is still emitted if there are type aliases.
55+
namespace a {
56+
template<typename T>
57+
using Future = async::Future<T>;
58+
} // namespace a
59+
60+
void releaseUnits();
61+
struct Units {
62+
~Units() {
63+
releaseUnits();
64+
}
65+
};
66+
a::Future<Units> acquireUnits();
67+
68+
template<typename T>
69+
T qux(T Generic) {
70+
async::Future<Units> PendingA = acquireUnits();
71+
auto PendingB = acquireUnits();
72+
// CHECK-MESSAGES: :[[@LINE-1]]:10: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'Future<Units>') [bugprone-unused-local-non-trivial-variable]
73+
async::Future<Units> MustBeUsed;
74+
// CHECK-MESSAGES: :[[@LINE-1]]:26: warning: unused local variable 'MustBeUsed' of type 'async::Future<Units>' [bugprone-unused-local-non-trivial-variable]
75+
PendingA.get();
76+
return Generic;
77+
}
78+
79+
async::Future<int> Global;
80+
81+
int bar(int Num) {
82+
a::Future<Units> PendingA = acquireUnits();
83+
a::Future<Units> PendingB = acquireUnits(); // not used at all, unused variable not fired because of destructor side effect
84+
// CHECK-MESSAGES: :[[@LINE-1]]:22: warning: unused local variable 'PendingB' of type 'a::Future<Units>' (aka 'Future<Units>') [bugprone-unused-local-non-trivial-variable]
85+
auto Num2 = PendingA.get();
86+
auto Num3 = qux(Num);
87+
async::Ptr<a::Future<Units>> Shared = async::Ptr<a::Future<Units>>(acquireUnits());
88+
static auto UnusedStatic = async::Future<Units>();
89+
thread_local async::Future<Units> UnusedThreadLocal;
90+
auto Captured = acquireUnits();
91+
Num3 += [Captured]() {
92+
return 1;
93+
}();
94+
a::Future<Units> Referenced = acquireUnits();
95+
a::Future<Units>* Pointer = &Referenced;
96+
a::Future<Units>& Reference = Referenced;
97+
const a::Future<Units>& ConstReference = Referenced;
98+
try {
99+
} catch (a::Future<Units> Fut) {
100+
}
101+
struct Holder {
102+
a::Future<Units> Fut;
103+
};
104+
Holder H;
105+
auto [fut] = H;
106+
return Num * Num3;
107+
}
108+
109+
void exclusion() {
110+
async::FizzFoo A;
111+
async::FooBar B;
112+
async::FooQux C;
113+
// CHECK-MESSAGES: :[[@LINE-1]]:17: warning: unused local variable 'C' of type 'async::FooQux' [bugprone-unused-local-non-trivial-variable]
114+
}

0 commit comments

Comments
 (0)