Skip to content

Commit 89281cc

Browse files
committed
[clang-tidy] Added check to detect redundant inline keyword
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 3311112 commit 89281cc

File tree

9 files changed

+290
-1
lines changed

9 files changed

+290
-1
lines changed

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ add_clang_library(clangTidyReadabilityModule
2020
IdentifierLengthCheck.cpp
2121
IdentifierNamingCheck.cpp
2222
ImplicitBoolConversionCheck.cpp
23+
RedundantInlineSpecifierCheck.cpp
2324
InconsistentDeclarationParameterNameCheck.cpp
2425
IsolateDeclarationCheck.cpp
2526
MagicNumbersCheck.cpp

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

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
#include "RedundantControlFlowCheck.h"
4040
#include "RedundantDeclarationCheck.h"
4141
#include "RedundantFunctionPtrDereferenceCheck.h"
42+
#include "RedundantInlineSpecifierCheck.h"
4243
#include "RedundantMemberInitCheck.h"
4344
#include "RedundantPreprocessorCheck.h"
4445
#include "RedundantSmartptrGetCheck.h"
@@ -93,6 +94,8 @@ class ReadabilityModule : public ClangTidyModule {
9394
"readability-identifier-naming");
9495
CheckFactories.registerCheck<ImplicitBoolConversionCheck>(
9596
"readability-implicit-bool-conversion");
97+
CheckFactories.registerCheck<RedundantInlineSpecifierCheck>(
98+
"readability-redundant-inline-specifier");
9699
CheckFactories.registerCheck<InconsistentDeclarationParameterNameCheck>(
97100
"readability-inconsistent-declaration-parameter-name");
98101
CheckFactories.registerCheck<IsolateDeclarationCheck>(
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
//===--- RedundantInlineSpecifierCheck.cpp -
2+
// clang-tidy----------------------===//
3+
//
4+
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
5+
// See https://llvm.org/LICENSE.txt for license information.
6+
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
7+
//
8+
//===----------------------------------------------------------------------===//
9+
10+
#include "RedundantInlineSpecifierCheck.h"
11+
#include "clang/AST/ASTContext.h"
12+
#include "clang/AST/Decl.h"
13+
#include "clang/AST/DeclCXX.h"
14+
#include "clang/AST/DeclTemplate.h"
15+
#include "clang/AST/ExprCXX.h"
16+
#include "clang/ASTMatchers/ASTMatchers.h"
17+
#include "clang/Basic/Diagnostic.h"
18+
#include "clang/Basic/SourceLocation.h"
19+
#include "clang/Basic/SourceManager.h"
20+
21+
#include "../utils/LexerUtils.h"
22+
23+
using namespace clang::ast_matchers;
24+
25+
namespace clang::tidy::readability {
26+
27+
static std::optional<SourceLocation>
28+
getInlineTokenLocation(SourceRange RangeLocation, const SourceManager &Sources,
29+
const LangOptions &LangOpts) {
30+
SourceLocation CurrentLocation = RangeLocation.getBegin();
31+
Token CurrentToken;
32+
while (!Lexer::getRawToken(CurrentLocation, CurrentToken, Sources, LangOpts,
33+
true) &&
34+
CurrentLocation < RangeLocation.getEnd() &&
35+
CurrentToken.isNot(tok::eof)) {
36+
if (CurrentToken.is(tok::raw_identifier)) {
37+
if (CurrentToken.getRawIdentifier() == "inline") {
38+
return CurrentToken.getLocation();
39+
}
40+
}
41+
CurrentLocation = CurrentToken.getEndLoc();
42+
}
43+
return std::nullopt;
44+
}
45+
46+
void RedundantInlineSpecifierCheck::registerMatchers(MatchFinder *Finder) {
47+
Finder->addMatcher(
48+
functionDecl(unless(isExpansionInSystemHeader()), unless(isImplicit()),
49+
unless(hasAncestor(lambdaExpr())), isInline(),
50+
anyOf(isConstexpr(), isDeleted(),
51+
allOf(isDefinition(), hasAncestor(recordDecl()),
52+
unless(hasAncestor(cxxConstructorDecl())))))
53+
.bind("fun_decl"),
54+
this);
55+
56+
Finder->addMatcher(
57+
varDecl(isInline(), unless(isImplicit()),
58+
anyOf(allOf(isConstexpr(), unless(isStaticStorageClass())),
59+
hasAncestor(recordDecl())))
60+
.bind("var_decl"),
61+
this);
62+
63+
Finder->addMatcher(
64+
functionTemplateDecl(has(functionDecl(isInline()))).bind("templ_decl"),
65+
this);
66+
}
67+
68+
template <typename T>
69+
void RedundantInlineSpecifierCheck::handleMatchedDecl(
70+
const T *MatchedDecl, const SourceManager &Sources,
71+
const MatchFinder::MatchResult &Result, const char *Message) {
72+
if (auto Loc = getInlineTokenLocation(MatchedDecl->getSourceRange(), Sources,
73+
Result.Context->getLangOpts()))
74+
diag(*Loc, Message) << MatchedDecl << FixItHint::CreateRemoval(*Loc);
75+
}
76+
77+
void RedundantInlineSpecifierCheck::check(
78+
const MatchFinder::MatchResult &Result) {
79+
const SourceManager &Sources = *Result.SourceManager;
80+
81+
if (const auto *MatchedDecl =
82+
Result.Nodes.getNodeAs<FunctionDecl>("fun_decl")) {
83+
handleMatchedDecl(
84+
MatchedDecl, Sources, Result,
85+
"Function %0 has inline specifier but is implicitly inlined");
86+
} else if (const auto *MatchedDecl =
87+
Result.Nodes.getNodeAs<VarDecl>("var_decl")) {
88+
handleMatchedDecl(
89+
MatchedDecl, Sources, Result,
90+
"Variable %0 has inline specifier but is implicitly inlined");
91+
} else if (const auto *MatchedDecl =
92+
Result.Nodes.getNodeAs<FunctionTemplateDecl>("templ_decl")) {
93+
handleMatchedDecl(
94+
MatchedDecl, Sources, Result,
95+
"Function %0 has inline specifier but is implicitly inlined");
96+
}
97+
}
98+
99+
} // namespace clang::tidy::readability
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
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+
/// Finds redundant `inline` specifiers in function and variable declarations.
17+
///
18+
/// For the user-facing documentation see:
19+
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/readability-redundant-inline-specifier.html
20+
class RedundantInlineSpecifierCheck : public ClangTidyCheck {
21+
public:
22+
RedundantInlineSpecifierCheck(StringRef Name, ClangTidyContext *Context)
23+
: ClangTidyCheck(Name, Context) {}
24+
void registerMatchers(ast_matchers::MatchFinder *Finder) override;
25+
void check(const ast_matchers::MatchFinder::MatchResult &Result) override;
26+
27+
private:
28+
template <typename T>
29+
void handleMatchedDecl(const T *MatchedDecl, const SourceManager &Sources,
30+
const ast_matchers::MatchFinder::MatchResult &Result,
31+
const char *Message);
32+
};
33+
34+
} // namespace clang::tidy::readability
35+
36+
#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
@@ -192,6 +192,11 @@ New checks
192192
Recommends the smallest possible underlying type for an ``enum`` or ``enum``
193193
class based on the range of its enumerators.
194194

195+
- New :doc:`readability-redundant-inline-specifier
196+
<clang-tidy/checks/readability/readability-redundant-inline-specifier>` check.
197+
198+
Detects redundant ``inline`` specifiers on function and variable declarations.
199+
195200
- New :doc:`readability-reference-to-constructed-temporary
196201
<clang-tidy/checks/readability/reference-to-constructed-temporary>` check.
197202

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

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,7 @@ Clang-Tidy Checks
348348
:doc:`readability-identifier-length <readability/identifier-length>`,
349349
:doc:`readability-identifier-naming <readability/identifier-naming>`, "Yes"
350350
:doc:`readability-implicit-bool-conversion <readability/implicit-bool-conversion>`, "Yes"
351+
:doc:`readability-redundant-inline-specifier <readability/readability-redundant-inline-specifier>`, "Yes"
351352
:doc:`readability-inconsistent-declaration-parameter-name <readability/inconsistent-declaration-parameter-name>`, "Yes"
352353
:doc:`readability-isolate-declaration <readability/isolate-declaration>`, "Yes"
353354
:doc:`readability-magic-numbers <readability/magic-numbers>`,
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
.. title:: clang-tidy - readability-redundant-inline-specifier
2+
3+
readability-redundant-inline-specifier
4+
=================
5+
6+
Checks for instances of the `inline` keyword in code where it is redundant
7+
and recommends its removal.
8+
9+
Examples:
10+
11+
.. code-block:: c++
12+
13+
constexpr inline void f() {}
14+
15+
In the example abvove the keyword `inline` is redundant since constexpr
16+
functions are implicitly inlined
17+
18+
.. code-block:: c++
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+
The token `inline` is considered redundant in the following cases:
27+
28+
- When it is used in a function definition that is constexpr.
29+
- When it is used in a member function definition that is defined entirely
30+
inside a class/struct/union definition.
31+
- When it is used on a deleted function.
32+
- When it is used on a template declaration.
33+
- When it is used on a member variable that is constexpr and static.
34+
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
// RUN: %check_clang_tidy %s readability-redundant-inline-specifier %t
2+
3+
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()
6+
{
7+
return T{};
8+
}
9+
10+
template <> inline double f<double>() = delete;
11+
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: Function 'f<double>' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
12+
// CHECK-FIXES: template <> double f<double>() = delete;
13+
14+
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]
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+
constexpr inline C& operator=(int a);
32+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: Function 'operator=' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
33+
// CHECK-FIXES: constexpr C& operator=(int a);
34+
35+
inline C() {}
36+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Function 'C' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
37+
// CHECK-FIXES: C() {}
38+
39+
constexpr inline C(int);
40+
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: Function 'C' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
41+
// CHECK-FIXES: constexpr C(int);
42+
43+
inline int Get42() const { return 42; }
44+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Function 'Get42' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
45+
// CHECK-FIXES: int Get42() const { return 42; }
46+
47+
static inline constexpr int C_STATIC = 42;
48+
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: Variable 'C_STATIC' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
49+
// CHECK-FIXES: static constexpr int C_STATIC = 42;
50+
51+
static constexpr int C_STATIC_2 = 42;
52+
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:5: warning: Variable 'C_STATIC_2' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
53+
};
54+
55+
constexpr inline int Get42() { return 42; }
56+
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: Function 'Get42' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
57+
// CHECK-FIXES: constexpr int Get42() { return 42; }
58+
59+
60+
static constexpr inline int NAMESPACE_STATIC = 42;
61+
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:18: warning: Variable 'NAMESPACE_STATIC' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
62+
63+
inline static int fn0(int i)
64+
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:1: warning: Function 'fn0' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
65+
{
66+
return i - 1;
67+
}
68+
69+
static constexpr inline int fn1(int i)
70+
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: Function 'fn1' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
71+
// CHECK-FIXES: static constexpr int fn1(int i)
72+
{
73+
return i - 1;
74+
}
75+
76+
namespace
77+
{
78+
inline int fn2(int i)
79+
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:5: warning: Function 'fn2' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
80+
{
81+
return i - 1;
82+
}
83+
84+
inline constexpr int fn3(int i)
85+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Function 'fn3' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
86+
// CHECK-FIXES: constexpr int fn3(int i)
87+
{
88+
return i - 1;
89+
}
90+
}
91+
92+
namespace ns
93+
{
94+
inline int fn4(int i)
95+
// CHECK-MESSAGES-NOT: :[[@LINE-1]]:5: warning: Function 'fn4' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
96+
{
97+
return i - 1;
98+
}
99+
100+
inline constexpr int fn5(int i)
101+
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: Function 'fn5' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
102+
// CHECK-FIXES: constexpr int fn5(int i)
103+
{
104+
return i - 1;
105+
}
106+
}
107+
108+
auto fn6 = [](){};
109+
//CHECK-MESSAGES-NOT: :[[@LINE-1]]:1: warning: Function 'operator()' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier]
110+

clang/include/clang/ASTMatchers/ASTMatchers.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7889,7 +7889,7 @@ AST_POLYMORPHIC_MATCHER(isInline, AST_POLYMORPHIC_SUPPORTED_TYPES(NamespaceDecl,
78897889
if (const auto *NSD = dyn_cast<NamespaceDecl>(&Node))
78907890
return NSD->isInline();
78917891
if (const auto *VD = dyn_cast<VarDecl>(&Node))
7892-
return VD->isInline();
7892+
return VD->isInlineSpecified();
78937893
llvm_unreachable("Not a valid polymorphic type");
78947894
}
78957895

0 commit comments

Comments
 (0)