Skip to content

Commit 3ec09df

Browse files
committed
fixup! [clang-tidy] Added check to detect redundant inline keyword
Added StrictMode option to decide if we want to flag redundant inline specifiers rather than implicit ones
1 parent a72870f commit 3ec09df

File tree

4 files changed

+59
-34
lines changed

4 files changed

+59
-34
lines changed

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

Lines changed: 34 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ using namespace clang::ast_matchers;
2424

2525
namespace clang::tidy::readability {
2626

27+
namespace {
2728
AST_POLYMORPHIC_MATCHER(isInlineSpecified,
2829
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl,
2930
VarDecl)) {
@@ -34,12 +35,26 @@ AST_POLYMORPHIC_MATCHER(isInlineSpecified,
3435
llvm_unreachable("Not a valid polymorphic type");
3536
}
3637

37-
static std::optional<SourceLocation>
38-
getInlineTokenLocation(SourceRange RangeLocation, const SourceManager &Sources,
39-
const LangOptions &LangOpts) {
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) {
4055
SourceLocation Loc = RangeLocation.getBegin();
4156
if (Loc.isMacroID())
42-
return std::nullopt;
57+
return {};
4358

4459
Token FirstToken;
4560
Lexer::getRawToken(Loc, FirstToken, Sources, LangOpts, true);
@@ -53,25 +68,29 @@ getInlineTokenLocation(SourceRange RangeLocation, const SourceManager &Sources,
5368
CurrentToken =
5469
Lexer::findNextToken(CurrentToken->getLocation(), Sources, LangOpts);
5570
}
56-
return std::nullopt;
71+
return {};
5772
}
5873

5974
void RedundantInlineSpecifierCheck::registerMatchers(MatchFinder *Finder) {
6075
Finder->addMatcher(
6176
functionDecl(isInlineSpecified(),
6277
anyOf(isConstexpr(), isDeleted(), isDefaulted(),
63-
isInAnonymousNamespace(),
78+
isInternalLinkage(StrictMode),
6479
allOf(isDefinition(), hasAncestor(recordDecl()))))
6580
.bind("fun_decl"),
6681
this);
67-
Finder->addMatcher(
68-
functionTemplateDecl(has(functionDecl(isInlineSpecified())))
69-
.bind("templ_decl"),
70-
this);
82+
83+
if (StrictMode)
84+
Finder->addMatcher(
85+
functionTemplateDecl(
86+
has(functionDecl(allOf(isInlineSpecified(), isDefinition()))))
87+
.bind("templ_decl"),
88+
this);
89+
7190
if (getLangOpts().CPlusPlus17) {
7291
Finder->addMatcher(
7392
varDecl(isInlineSpecified(),
74-
anyOf(isInAnonymousNamespace(),
93+
anyOf(isInternalLinkage(StrictMode),
7594
allOf(isConstexpr(), hasAncestor(recordDecl()))))
7695
.bind("var_decl"),
7796
this);
@@ -82,10 +101,10 @@ template <typename T>
82101
void RedundantInlineSpecifierCheck::handleMatchedDecl(
83102
const T *MatchedDecl, const SourceManager &Sources,
84103
const MatchFinder::MatchResult &Result, StringRef Message) {
85-
if (std::optional<SourceLocation> Loc =
86-
getInlineTokenLocation(MatchedDecl->getSourceRange(), Sources,
87-
Result.Context->getLangOpts()))
88-
diag(*Loc, Message) << MatchedDecl << FixItHint::CreateRemoval(*Loc);
104+
SourceLocation Loc = getInlineTokenLocation(
105+
MatchedDecl->getSourceRange(), Sources, Result.Context->getLangOpts());
106+
if (Loc.isValid())
107+
diag(Loc, Message) << MatchedDecl << FixItHint::CreateRemoval(Loc);
89108
}
90109

91110
void RedundantInlineSpecifierCheck::check(

clang-tools-extra/clang-tidy/readability/RedundantInlineSpecifierCheck.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ namespace clang::tidy::readability {
2121
class RedundantInlineSpecifierCheck : public ClangTidyCheck {
2222
public:
2323
RedundantInlineSpecifierCheck(StringRef Name, ClangTidyContext *Context)
24-
: ClangTidyCheck(Name, Context) {}
24+
: ClangTidyCheck(Name, Context),
25+
StrictMode(Options.getLocalOrGlobal("StrictMode", false)) {}
2526
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
2627
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
2728
std::optional<TraversalKind> getCheckTraversalKind() const override {
@@ -33,6 +34,7 @@ class RedundantInlineSpecifierCheck : public ClangTidyCheck {
3334
void handleMatchedDecl(const T *MatchedDecl, const SourceManager &Sources,
3435
const ast_matchers::MatchFinder::MatchResult &Result,
3536
StringRef Message);
37+
const bool StrictMode;
3638
};
3739

3840
} // namespace clang::tidy::readability

clang-tools-extra/docs/clang-tidy/checks/readability/redundant-inline-specifier.rst

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,3 +22,11 @@ functions are implicitly inlined
2222

2323
In the example above the keyword ``inline`` is redundant since member functions
2424
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.

clang-tools-extra/test/clang-tidy/checkers/readability/redundant-inline-specifier.cpp

Lines changed: 14 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
1-
// RUN: %check_clang_tidy %s readability-redundant-inline-specifier -std=c++17 %t
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'}}"
23

34
template <typename T> inline T f()
4-
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: function 'f' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
5-
// CHECK-FIXES: template <typename T> 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()
67
{
78
return T{};
89
}
@@ -12,7 +13,6 @@ template <> inline double f<double>() = delete;
1213
// CHECK-FIXES: template <> double f<double>() = delete;
1314

1415
inline int g(float a)
15-
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:1: warning: function 'g' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
1616
{
1717
return static_cast<int>(a - 5.F);
1818
}
@@ -30,6 +30,7 @@ class C
3030

3131
inline C(const C&) = default;
3232
// 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;
3334

3435
constexpr inline C& operator=(int a);
3536
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: function 'operator=' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
@@ -52,7 +53,6 @@ class C
5253
// CHECK-FIXES: static constexpr int C_STATIC = 42;
5354

5455
static constexpr int C_STATIC_2 = 42;
55-
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:5: warning: variable 'C_STATIC_2' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
5656
};
5757

5858
constexpr inline int Get42() { return 42; }
@@ -61,10 +61,10 @@ constexpr inline int Get42() { return 42; }
6161

6262

6363
static constexpr inline int NAMESPACE_STATIC = 42;
64-
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:18: warning: variable 'NAMESPACE_STATIC' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
6564

6665
inline static int fn0(int i)
67-
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:1: warning: function 'fn0' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
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)
6868
{
6969
return i - 1;
7070
}
@@ -79,7 +79,8 @@ static constexpr inline int fn1(int i)
7979
namespace
8080
{
8181
inline int fn2(int i)
82-
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'fn2' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
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)
8384
{
8485
return i - 1;
8586
}
@@ -92,13 +93,13 @@ namespace
9293
}
9394

9495
inline constexpr int MY_CONSTEXPR_VAR = 42;
95-
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: variable 'MY_CONSTEXPR_VAR' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
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;
9698
}
9799

98100
namespace ns
99101
{
100102
inline int fn4(int i)
101-
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:5: warning: function 'fn4' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
102103
{
103104
return i - 1;
104105
}
@@ -112,24 +113,19 @@ namespace ns
112113
}
113114

114115
auto fn6 = [](){};
115-
//CHECK-MESSAGES-NOT: :[[@LINE-1]]:1: warning: function 'operator()' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
116116

117117
template <typename T> inline T fn7();
118-
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: function 'fn7' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
119-
// CHECK-FIXES: template <typename T> T fn7();
120118

121-
template <typename T> T fn7()
122-
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:1: warning: function 'fn7' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
119+
template <typename T> T fn7()
123120
{
124121
return T{};
125122
}
126123

127124
template <typename T> T fn8();
128-
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:1: warning: function 'fn8' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
129125

130126
template <typename T> inline T fn8()
131-
// CHECK-MESSAGES: :[[@LINE-1]]:23: warning: function 'fn8' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
132-
// CHECK-FIXES: template <typename T> 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()
133129
{
134130
return T{};
135131
}

0 commit comments

Comments
 (0)