1 //===--- RedundantInlineSpecifierCheck.cpp - clang-tidy--------------------===//
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
7 //===----------------------------------------------------------------------===//
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"
21 #include "../utils/LexerUtils.h"
23 using namespace clang::ast_matchers
;
25 namespace clang::tidy::readability
{
28 AST_POLYMORPHIC_MATCHER(isInlineSpecified
,
29 AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl
,
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");
38 AST_POLYMORPHIC_MATCHER_P(isInternalLinkage
,
39 AST_POLYMORPHIC_SUPPORTED_TYPES(FunctionDecl
,
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");
52 static SourceLocation
getInlineTokenLocation(SourceRange RangeLocation
,
53 const SourceManager
&Sources
,
54 const LangOptions
&LangOpts
) {
55 SourceLocation Loc
= RangeLocation
.getBegin();
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();
69 Lexer::findNextToken(CurrentToken
->getLocation(), Sources
, LangOpts
);
74 void RedundantInlineSpecifierCheck::registerMatchers(MatchFinder
*Finder
) {
76 functionDecl(isInlineSpecified(),
77 anyOf(isConstexpr(), isDeleted(), isDefaulted(),
78 isInternalLinkage(StrictMode
),
79 allOf(isDefinition(), hasAncestor(recordDecl()))))
86 has(functionDecl(allOf(isInlineSpecified(), isDefinition()))))
90 if (getLangOpts().CPlusPlus17
) {
91 const auto IsPartOfRecordDecl
= hasAncestor(recordDecl());
95 anyOf(allOf(isInternalLinkage(StrictMode
),
96 unless(allOf(hasInitializer(expr()), IsPartOfRecordDecl
,
97 isStaticStorageClass()))),
98 allOf(isConstexpr(), IsPartOfRecordDecl
)))
104 template <typename T
>
105 void RedundantInlineSpecifierCheck::handleMatchedDecl(
106 const T
*MatchedDecl
, const SourceManager
&Sources
,
107 const MatchFinder::MatchResult
&Result
, StringRef Message
) {
108 SourceLocation Loc
= getInlineTokenLocation(
109 MatchedDecl
->getSourceRange(), Sources
, Result
.Context
->getLangOpts());
111 diag(Loc
, Message
) << MatchedDecl
<< FixItHint::CreateRemoval(Loc
);
114 void RedundantInlineSpecifierCheck::check(
115 const MatchFinder::MatchResult
&Result
) {
116 const SourceManager
&Sources
= *Result
.SourceManager
;
118 if (const auto *MatchedDecl
=
119 Result
.Nodes
.getNodeAs
<FunctionDecl
>("fun_decl")) {
121 MatchedDecl
, Sources
, Result
,
122 "function %0 has inline specifier but is implicitly inlined");
123 } else if (const auto *MatchedDecl
=
124 Result
.Nodes
.getNodeAs
<VarDecl
>("var_decl")) {
126 MatchedDecl
, Sources
, Result
,
127 "variable %0 has inline specifier but is implicitly inlined");
128 } else if (const auto *MatchedDecl
=
129 Result
.Nodes
.getNodeAs
<FunctionTemplateDecl
>("templ_decl")) {
131 MatchedDecl
, Sources
, Result
,
132 "function %0 has inline specifier but is implicitly inlined");
136 } // namespace clang::tidy::readability