-
Notifications
You must be signed in to change notification settings - Fork 14.3k
[clang-tidy] Added new check to detect redundant inline keyword #73069
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
2 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
132 changes: 132 additions & 0 deletions
132
clang-tools-extra/clang-tidy/readability/RedundantInlineSpecifierCheck.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
//===--- RedundantInlineSpecifierCheck.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 "RedundantInlineSpecifierCheck.h" | ||
#include "clang/AST/ASTContext.h" | ||
#include "clang/AST/Decl.h" | ||
#include "clang/AST/DeclCXX.h" | ||
#include "clang/AST/DeclTemplate.h" | ||
#include "clang/AST/ExprCXX.h" | ||
#include "clang/ASTMatchers/ASTMatchers.h" | ||
#include "clang/Basic/Diagnostic.h" | ||
#include "clang/Basic/SourceLocation.h" | ||
#include "clang/Basic/SourceManager.h" | ||
#include "clang/Lex/Token.h" | ||
|
||
#include "../utils/LexerUtils.h" | ||
|
||
using namespace clang::ast_matchers; | ||
|
||
namespace clang::tidy::readability { | ||
|
||
namespace { | ||
AST_POLYMORPHIC_MATCHER(isInlineSpecified, | ||
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, | ||
VarDecl)) { | ||
if (const auto *FD = dyn_cast<FunctionDecl>(&Node)) | ||
return FD->isInlineSpecified(); | ||
if (const auto *VD = dyn_cast<VarDecl>(&Node)) | ||
return VD->isInlineSpecified(); | ||
llvm_unreachable("Not a valid polymorphic type"); | ||
} | ||
|
||
AST_POLYMORPHIC_MATCHER_P(isInternalLinkage, | ||
AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl, | ||
VarDecl), | ||
bool, strictMode) { | ||
if (!strictMode) | ||
return false; | ||
if (const auto *FD = dyn_cast<FunctionDecl>(&Node)) | ||
return FD->getStorageClass() == SC_Static || FD->isInAnonymousNamespace(); | ||
if (const auto *VD = dyn_cast<VarDecl>(&Node)) | ||
return VD->isInAnonymousNamespace(); | ||
llvm_unreachable("Not a valid polymorphic type"); | ||
} | ||
} // namespace | ||
|
||
static SourceLocation getInlineTokenLocation(SourceRange RangeLocation, | ||
const SourceManager &Sources, | ||
const LangOptions &LangOpts) { | ||
SourceLocation Loc = RangeLocation.getBegin(); | ||
if (Loc.isMacroID()) | ||
return {}; | ||
|
||
Token FirstToken; | ||
Lexer::getRawToken(Loc, FirstToken, Sources, LangOpts, true); | ||
std::optional<Token> CurrentToken = FirstToken; | ||
while (CurrentToken && CurrentToken->getLocation() < RangeLocation.getEnd() && | ||
CurrentToken->isNot(tok::eof)) { | ||
if (CurrentToken->is(tok::raw_identifier) && | ||
CurrentToken->getRawIdentifier() == "inline") | ||
return CurrentToken->getLocation(); | ||
|
||
CurrentToken = | ||
Lexer::findNextToken(CurrentToken->getLocation(), Sources, LangOpts); | ||
} | ||
return {}; | ||
} | ||
|
||
void RedundantInlineSpecifierCheck::registerMatchers(MatchFinder *Finder) { | ||
Finder->addMatcher( | ||
functionDecl(isInlineSpecified(), | ||
anyOf(isConstexpr(), isDeleted(), isDefaulted(), | ||
isInternalLinkage(StrictMode), | ||
allOf(isDefinition(), hasAncestor(recordDecl())))) | ||
.bind("fun_decl"), | ||
this); | ||
|
||
if (StrictMode) | ||
Finder->addMatcher( | ||
functionTemplateDecl( | ||
has(functionDecl(allOf(isInlineSpecified(), isDefinition())))) | ||
.bind("templ_decl"), | ||
this); | ||
|
||
if (getLangOpts().CPlusPlus17) { | ||
Finder->addMatcher( | ||
varDecl(isInlineSpecified(), | ||
anyOf(isInternalLinkage(StrictMode), | ||
allOf(isConstexpr(), hasAncestor(recordDecl())))) | ||
.bind("var_decl"), | ||
this); | ||
} | ||
} | ||
|
||
template <typename T> | ||
void RedundantInlineSpecifierCheck::handleMatchedDecl( | ||
const T *MatchedDecl, const SourceManager &Sources, | ||
const MatchFinder::MatchResult &Result, StringRef Message) { | ||
SourceLocation Loc = getInlineTokenLocation( | ||
MatchedDecl->getSourceRange(), Sources, Result.Context->getLangOpts()); | ||
if (Loc.isValid()) | ||
diag(Loc, Message) << MatchedDecl << FixItHint::CreateRemoval(Loc); | ||
} | ||
|
||
void RedundantInlineSpecifierCheck::check( | ||
const MatchFinder::MatchResult &Result) { | ||
const SourceManager &Sources = *Result.SourceManager; | ||
|
||
if (const auto *MatchedDecl = | ||
Result.Nodes.getNodeAs<FunctionDecl>("fun_decl")) { | ||
handleMatchedDecl( | ||
MatchedDecl, Sources, Result, | ||
"function %0 has inline specifier but is implicitly inlined"); | ||
} else if (const auto *MatchedDecl = | ||
Result.Nodes.getNodeAs<VarDecl>("var_decl")) { | ||
handleMatchedDecl( | ||
MatchedDecl, Sources, Result, | ||
"variable %0 has inline specifier but is implicitly inlined"); | ||
} else if (const auto *MatchedDecl = | ||
Result.Nodes.getNodeAs<FunctionTemplateDecl>("templ_decl")) { | ||
handleMatchedDecl( | ||
MatchedDecl, Sources, Result, | ||
"function %0 has inline specifier but is implicitly inlined"); | ||
} | ||
} | ||
|
||
} // namespace clang::tidy::readability |
42 changes: 42 additions & 0 deletions
42
clang-tools-extra/clang-tidy/readability/RedundantInlineSpecifierCheck.h
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
//===--- RedundantInlineSpecifierCheck.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_READABILITY_REDUNDANTINLINESPECIFIERCHECK_H | ||
#define LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTINLINESPECIFIERCHECK_H | ||
|
||
#include "../ClangTidyCheck.h" | ||
|
||
namespace clang::tidy::readability { | ||
|
||
/// Detects redundant ``inline`` specifiers on function and variable | ||
/// declarations. | ||
/// | ||
/// For the user-facing documentation see: | ||
/// http://clang.llvm.org/extra/clang-tidy/checks/readability/redundant-inline-specifier.html | ||
class RedundantInlineSpecifierCheck : public ClangTidyCheck { | ||
public: | ||
RedundantInlineSpecifierCheck(StringRef Name, ClangTidyContext *Context) | ||
: ClangTidyCheck(Name, Context), | ||
StrictMode(Options.getLocalOrGlobal("StrictMode", false)) {} | ||
void registerMatchers(ast_matchers::MatchFinder *Finder) override; | ||
void check(const ast_matchers::MatchFinder::MatchResult &Result) override; | ||
std::optional<TraversalKind> getCheckTraversalKind() const override { | ||
return TK_IgnoreUnlessSpelledInSource; | ||
} | ||
|
||
private: | ||
template <typename T> | ||
void handleMatchedDecl(const T *MatchedDecl, const SourceManager &Sources, | ||
const ast_matchers::MatchFinder::MatchResult &Result, | ||
StringRef Message); | ||
const bool StrictMode; | ||
}; | ||
|
||
} // namespace clang::tidy::readability | ||
|
||
#endif // LLVM_CLANG_TOOLS_EXTRA_CLANG_TIDY_READABILITY_REDUNDANTINLINESPECIFIERCHECK_H |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
32 changes: 32 additions & 0 deletions
32
...g-tools-extra/docs/clang-tidy/checks/readability/redundant-inline-specifier.rst
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
.. title:: clang-tidy - readability-redundant-inline-specifier | ||
|
||
readability-redundant-inline-specifier | ||
====================================== | ||
|
||
Detects redundant ``inline`` specifiers on function and variable declarations. | ||
|
||
Examples: | ||
|
||
.. code-block:: c++ | ||
|
||
constexpr inline void f() {} | ||
|
||
In the example above the keyword ``inline`` is redundant since constexpr | ||
functions are implicitly inlined | ||
|
||
.. code-block:: c++ | ||
|
||
class MyClass { | ||
inline void myMethod() {} | ||
}; | ||
|
||
In the example above the keyword ``inline`` is redundant since member functions | ||
defined entirely inside a class/struct/union definition are implicitly inlined. | ||
|
||
Options | ||
------- | ||
|
||
.. option:: StrictMode | ||
|
||
felix642 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
If set to `true`, the check will also flag functions and variables that | ||
already have internal linkage as redundant. |
137 changes: 137 additions & 0 deletions
137
clang-tools-extra/test/clang-tidy/checkers/readability/redundant-inline-specifier.cpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
// RUN: %check_clang_tidy -std=c++17 %s readability-redundant-inline-specifier %t | ||
// RUN: %check_clang_tidy -std=c++17 -check-suffixes=,STRICT %s readability-redundant-inline-specifier %t -- -config="{CheckOptions: {readability-redundant-inline-specifier.StrictMode: 'true'}}" | ||
|
||
template <typename T> inline T f() | ||
felix642 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:23: warning: function 'f' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES-STRICT: template <typename T> T f() | ||
{ | ||
return T{}; | ||
} | ||
|
||
template <> inline double f<double>() = delete; | ||
// CHECK-MESSAGES: :[[@LINE-1]]:13: warning: function 'f<double>' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: template <> double f<double>() = delete; | ||
|
||
inline int g(float a) | ||
{ | ||
return static_cast<int>(a - 5.F); | ||
} | ||
|
||
inline int g(double) = delete; | ||
// CHECK-MESSAGES: :[[@LINE-1]]:1: warning: function 'g' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: int g(double) = delete; | ||
|
||
class C | ||
{ | ||
public: | ||
inline C& operator=(const C&) = delete; | ||
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'operator=' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: C& operator=(const C&) = delete; | ||
|
||
inline C(const C&) = default; | ||
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'C' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: C(const C&) = default; | ||
|
||
constexpr inline C& operator=(int a); | ||
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: function 'operator=' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: constexpr C& operator=(int a); | ||
|
||
inline C() {} | ||
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'C' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: C() {} | ||
|
||
constexpr inline C(int); | ||
// CHECK-MESSAGES: :[[@LINE-1]]:15: warning: function 'C' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: constexpr C(int); | ||
|
||
inline int Get42() const { return 42; } | ||
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'Get42' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: int Get42() const { return 42; } | ||
|
||
static inline constexpr int C_STATIC = 42; | ||
// CHECK-MESSAGES: :[[@LINE-1]]:12: warning: variable 'C_STATIC' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: static constexpr int C_STATIC = 42; | ||
|
||
static constexpr int C_STATIC_2 = 42; | ||
}; | ||
|
||
constexpr inline int Get42() { return 42; } | ||
// CHECK-MESSAGES: :[[@LINE-1]]:11: warning: function 'Get42' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: constexpr int Get42() { return 42; } | ||
|
||
|
||
static constexpr inline int NAMESPACE_STATIC = 42; | ||
|
||
inline static int fn0(int i) | ||
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:1: warning: function 'fn0' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES-STRICT: static int fn0(int i) | ||
{ | ||
return i - 1; | ||
} | ||
|
||
static constexpr inline int fn1(int i) | ||
// CHECK-MESSAGES: :[[@LINE-1]]:18: warning: function 'fn1' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: static constexpr int fn1(int i) | ||
{ | ||
return i - 1; | ||
} | ||
|
||
namespace | ||
{ | ||
inline int fn2(int i) | ||
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:5: warning: function 'fn2' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES-STRICT: int fn2(int i) | ||
{ | ||
return i - 1; | ||
} | ||
|
||
inline constexpr int fn3(int i) | ||
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'fn3' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: constexpr int fn3(int i) | ||
{ | ||
return i - 1; | ||
} | ||
|
||
inline constexpr int MY_CONSTEXPR_VAR = 42; | ||
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:5: warning: variable 'MY_CONSTEXPR_VAR' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES-STRICT: constexpr int MY_CONSTEXPR_VAR = 42; | ||
} | ||
|
||
namespace ns | ||
{ | ||
inline int fn4(int i) | ||
{ | ||
return i - 1; | ||
} | ||
|
||
inline constexpr int fn5(int i) | ||
// CHECK-MESSAGES: :[[@LINE-1]]:5: warning: function 'fn5' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES: constexpr int fn5(int i) | ||
{ | ||
return i - 1; | ||
} | ||
} | ||
|
||
auto fn6 = [](){}; | ||
|
||
PiotrZSL marked this conversation as resolved.
Show resolved
Hide resolved
|
||
template <typename T> inline T fn7(); | ||
|
||
template <typename T> T fn7() | ||
{ | ||
return T{}; | ||
} | ||
|
||
template <typename T> T fn8(); | ||
|
||
template <typename T> inline T fn8() | ||
// CHECK-MESSAGES-STRICT: :[[@LINE-1]]:23: warning: function 'fn8' has inline specifier but is implicitly inlined [readability-redundant-inline-specifier] | ||
// CHECK-FIXES-STRICT: template <typename T> T fn8() | ||
{ | ||
return T{}; | ||
} | ||
|
||
#define INLINE_MACRO() inline void fn9() { } | ||
INLINE_MACRO() | ||
|
||
#define INLINE_KW inline | ||
INLINE_KW void fn10() { } |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.