Skip to content

Commit 7a8f5d9

Browse files
authored
[clang-tidy] Added new check to detect redundant inline keyword (#73069)
This checks find usages of the inline keywork where it is already implicitly defined by the compiler and suggests it's removal. Fixes #72397
1 parent 14d5952 commit 7a8f5d9

File tree

8 files changed

+353
-0
lines changed

8 files changed

+353
-0
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ add_clang_library(clangTidyReadabilityModule
2222
IdentifierLengthCheck.cpp
2323
IdentifierNamingCheck.cpp
2424
ImplicitBoolConversionCheck.cpp
25+
RedundantInlineSpecifierCheck.cpp
2526
InconsistentDeclarationParameterNameCheck.cpp
2627
IsolateDeclarationCheck.cpp
2728
MagicNumbersCheck.cpp

clang-tools-extra/clang-tidy/readability/ReadabilityTidyModule.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@
4242
#include "RedundantControlFlowCheck.h"
4343
#include "RedundantDeclarationCheck.h"
4444
#include "RedundantFunctionPtrDereferenceCheck.h"
45+
#include "RedundantInlineSpecifierCheck.h"
4546
#include "RedundantMemberInitCheck.h"
4647
#include "RedundantPreprocessorCheck.h"
4748
#include "RedundantSmartptrGetCheck.h"
@@ -100,6 +101,8 @@ class ReadabilityModule : public ClangTidyModule {
100101
"readability-identifier-naming");
101102
CheckFactories.registerCheck<ImplicitBoolConversionCheck>(
102103
"readability-implicit-bool-conversion");
104+
CheckFactories.registerCheck<RedundantInlineSpecifierCheck>(
105+
"readability-redundant-inline-specifier");
103106
CheckFactories.registerCheck<InconsistentDeclarationParameterNameCheck>(
104107
"readability-inconsistent-declaration-parameter-name");
105108
CheckFactories.registerCheck<IsolateDeclarationCheck>(
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
//===--- RedundantInlineSpecifierCheck.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 "RedundantInlineSpecifierCheck.h"
10+
#include "clang/AST/ASTContext.h"
11+
#include "clang/AST/Decl.h"
12+
#include "clang/AST/DeclCXX.h"
13+
#include "clang/AST/DeclTemplate.h"
14+
#include "clang/AST/ExprCXX.h"
15+
#include "clang/ASTMatchers/ASTMatchers.h"
16+
#include "clang/Basic/Diagnostic.h"
17+
#include "clang/Basic/SourceLocation.h"
18+
#include "clang/Basic/SourceManager.h"
19+
#include "clang/Lex/Token.h"
20+
21+
#include "../utils/LexerUtils.h"
22+
23+
using namespace clang::ast_matchers;
24+
25+
namespace clang::tidy::readability {
26+
27+
namespace {
28+
AST_POLYMORPHIC_MATCHER(isInlineSpecified,
29+
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
30+
VarDecl)) {
31+
if (const auto *FD = dyn_cast<FunctionDecl>(&Node))
32+
return FD->isInlineSpecified();
33+
if (const auto *VD = dyn_cast<VarDecl>(&Node))
34+
return VD->isInlineSpecified();
35+
llvm_unreachable("Not a valid polymorphic type");
36+
}
37+
38+
AST_POLYMORPHIC_MATCHER_P(isInternalLinkage,
39+
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
40+
VarDecl),
41+
bool, strictMode) {
42+
if (!strictMode)
43+
return false;
44+
if (const auto *FD = dyn_cast<FunctionDecl>(&Node))
45+
return FD->getStorageClass() == SC_Static || FD->isInAnonymousNamespace();
46+
if (const auto *VD = dyn_cast<VarDecl>(&Node))
47+
return VD->isInAnonymousNamespace();
48+
llvm_unreachable("Not a valid polymorphic type");
49+
}
50+
} // namespace
51+
52+
static SourceLocation getInlineTokenLocation(SourceRange RangeLocation,
53+
const SourceManager &Sources,
54+
const LangOptions &LangOpts) {
55+
SourceLocation Loc = RangeLocation.getBegin();
56+
if (Loc.isMacroID())
57+
return {};
58+
59+
Token FirstToken;
60+
Lexer::getRawToken(Loc, FirstToken, Sources, LangOpts, true);
61+
std::optional<Token> CurrentToken = FirstToken;
62+
while (CurrentToken && CurrentToken->getLocation() < RangeLocation.getEnd() &&
63+
CurrentToken->isNot(tok::eof)) {
64+
if (CurrentToken->is(tok::raw_identifier) &&
65+
CurrentToken->getRawIdentifier() == "inline")
66+
return CurrentToken->getLocation();
67+
68+
CurrentToken =
69+
Lexer::findNextToken(CurrentToken->getLocation(), Sources, LangOpts);
70+
}
71+
return {};
72+
}
73+
74+
void RedundantInlineSpecifierCheck::registerMatchers(MatchFinder *Finder) {
75+
Finder->addMatcher(
76+
functionDecl(isInlineSpecified(),
77+
anyOf(isConstexpr(), isDeleted(), isDefaulted(),
78+
isInternalLinkage(StrictMode),
79+
allOf(isDefinition(), hasAncestor(recordDecl()))))
80+
.bind("fun_decl"),
81+
this);
82+
83+
if (StrictMode)
84+
Finder->addMatcher(
85+
functionTemplateDecl(
86+
has(functionDecl(allOf(isInlineSpecified(), isDefinition()))))
87+
.bind("templ_decl"),
88+
this);
89+
90+
if (getLangOpts().CPlusPlus17) {
91+
Finder->addMatcher(
92+
varDecl(isInlineSpecified(),
93+
anyOf(isInternalLinkage(StrictMode),
94+
allOf(isConstexpr(), hasAncestor(recordDecl()))))
95+
.bind("var_decl"),
96+
this);
97+
}
98+
}
99+
100+
template <typename T>
101+
void RedundantInlineSpecifierCheck::handleMatchedDecl(
102+
const T *MatchedDecl, const SourceManager &Sources,
103+
const MatchFinder::MatchResult &Result, StringRef Message) {
104+
SourceLocation Loc = getInlineTokenLocation(
105+
MatchedDecl->getSourceRange(), Sources, Result.Context->getLangOpts());
106+
if (Loc.isValid())
107+
diag(Loc, Message) << MatchedDecl << FixItHint::CreateRemoval(Loc);
108+
}
109+
110+
void RedundantInlineSpecifierCheck::check(
111+
const MatchFinder::MatchResult &Result) {
112+
const SourceManager &Sources = *Result.SourceManager;
113+
114+
if (const auto *MatchedDecl =
115+
Result.Nodes.getNodeAs<FunctionDecl>("fun_decl")) {
116+
handleMatchedDecl(
117+
MatchedDecl, Sources, Result,
118+
"function %0 has inline specifier but is implicitly inlined");
119+
} else if (const auto *MatchedDecl =
120+
Result.Nodes.getNodeAs<VarDecl>("var_decl")) {
121+
handleMatchedDecl(
122+
MatchedDecl, Sources, Result,
123+
"variable %0 has inline specifier but is implicitly inlined");
124+
} else if (const auto *MatchedDecl =
125+
Result.Nodes.getNodeAs<FunctionTemplateDecl>("templ_decl")) {
126+
handleMatchedDecl(
127+
MatchedDecl, Sources, Result,
128+
"function %0 has inline specifier but is implicitly inlined");
129+
}
130+
}
131+
132+
} // namespace clang::tidy::readability
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
//===--- RedundantInlineSpecifierCheck.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_READABILITY_REDUNDANTINLINESPECIFIERCHECK_H
10+
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTINLINESPECIFIERCHECK_H
11+
12+
#include "../ClangTidyCheck.h"
13+
14+
namespace clang::tidy::readability {
15+
16+
/// Detects redundant ``inline`` specifiers on function and variable
17+
/// declarations.
18+
///
19+
/// For the user-facing documentation see:
20+
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-inline-specifier.html
21+
class RedundantInlineSpecifierCheck : public ClangTidyCheck {
22+
public:
23+
RedundantInlineSpecifierCheck(StringRef Name, ClangTidyContext *Context)
24+
: ClangTidyCheck(Name, Context),
25+
StrictMode(Options.getLocalOrGlobal("StrictMode", false)) {}
26+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
27+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
28+
std::optional<TraversalKind> getCheckTraversalKind() const override {
29+
return TK_IgnoreUnlessSpelledInSource;
30+
}
31+
32+
private:
33+
template <typename T>
34+
void handleMatchedDecl(const T *MatchedDecl, const SourceManager &Sources,
35+
const ast_matchers::MatchFinder::MatchResult &Result,
36+
StringRef Message);
37+
const bool StrictMode;
38+
};
39+
40+
} // namespace clang::tidy::readability
41+
42+
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTINLINESPECIFIERCHECK_H

clang-tools-extra/docs/ReleaseNotes.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -240,6 +240,11 @@ New checks
240240

241241
Detects explicit type casting operations that involve the same source and
242242
destination types, and subsequently recommend their removal.
243+
244+
- New :doc:`readability-redundant-inline-specifier
245+
<clang-tidy/checks/readability/redundant-inline-specifier>` check.
246+
247+
Detects redundant ``inline`` specifiers on function and variable declarations.
243248

244249
- New :doc:`readability-reference-to-constructed-temporary
245250
<clang-tidy/checks/readability/reference-to-constructed-temporary>` check.

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,7 @@ Clang-Tidy Checks
354354
:doc:`readability-identifier-length <readability/identifier-length>`,
355355
:doc:`readability-identifier-naming <readability/identifier-naming>`, "Yes"
356356
:doc:`readability-implicit-bool-conversion <readability/implicit-bool-conversion>`, "Yes"
357+
:doc:`readability-redundant-inline-specifier <readability/redundant-inline-specifier>`, "Yes"
357358
:doc:`readability-inconsistent-declaration-parameter-name <readability/inconsistent-declaration-parameter-name>`, "Yes"
358359
:doc:`readability-isolate-declaration <readability/isolate-declaration>`, "Yes"
359360
:doc:`readability-magic-numbers <readability/magic-numbers>`,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
.. title:: clang-tidy - readability-redundant-inline-specifier
2+
3+
readability-redundant-inline-specifier
4+
======================================
5+
6+
Detects redundant ``inline`` specifiers on function and variable declarations.
7+
8+
Examples:
9+
10+
.. code-block:: c++
11+
12+
constexpr inline void f() {}
13+
14+
In the example above the keyword ``inline`` is redundant since constexpr
15+
functions are implicitly inlined
16+
17+
.. code-block:: c++
18+
19+
class MyClass {
20+
inline void myMethod() {}
21+
};
22+
23+
In the example above the keyword ``inline`` is redundant since member functions
24+
defined entirely inside a class/struct/union definition are implicitly inlined.
25+
26+
Options
27+
-------
28+
29+
.. option:: StrictMode
30+
31+
If set to `true`, the check will also flag functions and variables that
32+
already have internal linkage as redundant.
Lines changed: 137 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,137 @@
1+
// RUN: %check_clang_tidy -std=c++17 %s readability-redundant-inline-specifier %t
2+
// RUN: %check_clang_tidy -std=c++17 -check-suffixes=,STRICT %s readability-redundant-inline-specifier %t -- -config="{CheckOptions: {readability-redundant-inline-specifier.StrictMode: 'true'}}"
3+
4+
template <typename T> inline T f()
5+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:23: warning: function 'f' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
6+
// CHECK-FIXES-STRICT: template <typename T> T f()
7+
{
8+
return T{};
9+
}
10+
11+
template <> inline double f<double>() = delete;
12+
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: function 'f<double>' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
13+
// CHECK-FIXES: template <> double f<double>() = delete;
14+
15+
inline int g(float a)
16+
{
17+
return static_cast<int>(a - 5.F);
18+
}
19+
20+
inline int g(double) = delete;
21+
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: function 'g' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
22+
// CHECK-FIXES: int g(double) = delete;
23+
24+
class C
25+
{
26+
public:
27+
inline C& operator=(const C&) = delete;
28+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'operator=' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
29+
// CHECK-FIXES: C& operator=(const C&) = delete;
30+
31+
inline C(const C&) = default;
32+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'C' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
33+
// CHECK-FIXES: C(const C&) = default;
34+
35+
constexpr inline C& operator=(int a);
36+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: function 'operator=' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
37+
// CHECK-FIXES: constexpr C& operator=(int a);
38+
39+
inline C() {}
40+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'C' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
41+
// CHECK-FIXES: C() {}
42+
43+
constexpr inline C(int);
44+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: function 'C' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
45+
// CHECK-FIXES: constexpr C(int);
46+
47+
inline int Get42() const { return 42; }
48+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'Get42' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
49+
// CHECK-FIXES: int Get42() const { return 42; }
50+
51+
static inline constexpr int C_STATIC = 42;
52+
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: variable 'C_STATIC' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
53+
// CHECK-FIXES: static constexpr int C_STATIC = 42;
54+
55+
static constexpr int C_STATIC_2 = 42;
56+
};
57+
58+
constexpr inline int Get42() { return 42; }
59+
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: function 'Get42' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
60+
// CHECK-FIXES: constexpr int Get42() { return 42; }
61+
62+
63+
static constexpr inline int NAMESPACE_STATIC = 42;
64+
65+
inline static int fn0(int i)
66+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:1: warning: function 'fn0' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
67+
// CHECK-FIXES-STRICT: static int fn0(int i)
68+
{
69+
return i - 1;
70+
}
71+
72+
static constexpr inline int fn1(int i)
73+
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: function 'fn1' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
74+
// CHECK-FIXES: static constexpr int fn1(int i)
75+
{
76+
return i - 1;
77+
}
78+
79+
namespace
80+
{
81+
inline int fn2(int i)
82+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:5: warning: function 'fn2' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
83+
// CHECK-FIXES-STRICT: int fn2(int i)
84+
{
85+
return i - 1;
86+
}
87+
88+
inline constexpr int fn3(int i)
89+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'fn3' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
90+
// CHECK-FIXES: constexpr int fn3(int i)
91+
{
92+
return i - 1;
93+
}
94+
95+
inline constexpr int MY_CONSTEXPR_VAR = 42;
96+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:5: warning: variable 'MY_CONSTEXPR_VAR' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
97+
// CHECK-FIXES-STRICT: constexpr int MY_CONSTEXPR_VAR = 42;
98+
}
99+
100+
namespace ns
101+
{
102+
inline int fn4(int i)
103+
{
104+
return i - 1;
105+
}
106+
107+
inline constexpr int fn5(int i)
108+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'fn5' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
109+
// CHECK-FIXES: constexpr int fn5(int i)
110+
{
111+
return i - 1;
112+
}
113+
}
114+
115+
auto fn6 = [](){};
116+
117+
template <typename T> inline T fn7();
118+
119+
template <typename T> T fn7()
120+
{
121+
return T{};
122+
}
123+
124+
template <typename T> T fn8();
125+
126+
template <typename T> inline T fn8()
127+
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:23: warning: function 'fn8' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
128+
// CHECK-FIXES-STRICT: template <typename T> T fn8()
129+
{
130+
return T{};
131+
}
132+
133+
#define INLINE_MACRO() inline void fn9() { }
134+
INLINE_MACRO()
135+
136+
#define INLINE_KW inline
137+
INLINE_KW void fn10() { }

0 commit comments

Comments
 (0)