1 //===-- InlineFunctionDeclCheck.cpp ---------------------------------------===//
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 "InlineFunctionDeclCheck.h"
10 #include "../utils/FileExtensionsUtils.h"
11 #include "../utils/LexerUtils.h"
12 #include "clang/AST/ASTContext.h"
13 #include "clang/ASTMatchers/ASTMatchFinder.h"
15 #include "llvm/ADT/StringSet.h"
17 using namespace clang::ast_matchers
;
19 namespace clang::tidy::llvm_libc
{
23 const TemplateParameterList
*
24 getLastTemplateParameterList(const FunctionDecl
*FuncDecl
) {
25 const TemplateParameterList
*ReturnList
=
26 FuncDecl
->getDescribedTemplateParams();
29 const unsigned NumberOfTemplateParameterLists
=
30 FuncDecl
->getNumTemplateParameterLists();
32 if (NumberOfTemplateParameterLists
> 0)
33 ReturnList
= FuncDecl
->getTemplateParameterList(
34 NumberOfTemplateParameterLists
- 1);
42 InlineFunctionDeclCheck::InlineFunctionDeclCheck(StringRef Name
,
43 ClangTidyContext
*Context
)
44 : ClangTidyCheck(Name
, Context
),
45 HeaderFileExtensions(Context
->getHeaderFileExtensions()) {}
47 void InlineFunctionDeclCheck::registerMatchers(MatchFinder
*Finder
) {
48 // Ignore functions that have been deleted.
49 Finder
->addMatcher(decl(functionDecl(unless(isDeleted()))).bind("func_decl"),
53 void InlineFunctionDeclCheck::check(const MatchFinder::MatchResult
&Result
) {
54 const auto *FuncDecl
= Result
.Nodes
.getNodeAs
<FunctionDecl
>("func_decl");
56 // Consider only explicitly or implicitly inline functions.
57 if (FuncDecl
== nullptr || !FuncDecl
->isInlined())
60 SourceLocation SrcBegin
= FuncDecl
->getBeginLoc();
62 // If we have a template parameter list, we need to skip that because the
63 // LIBC_INLINE macro must be placed after that.
64 if (const TemplateParameterList
*TemplateParams
=
65 getLastTemplateParameterList(FuncDecl
)) {
66 SrcBegin
= TemplateParams
->getRAngleLoc();
67 std::optional
<Token
> NextToken
=
68 utils::lexer::findNextTokenSkippingComments(
69 SrcBegin
, *Result
.SourceManager
, Result
.Context
->getLangOpts());
71 SrcBegin
= NextToken
->getLocation();
74 // Consider functions only in header files.
75 if (!utils::isSpellingLocInHeaderFile(SrcBegin
, *Result
.SourceManager
,
76 HeaderFileExtensions
))
79 // Ignore lambda functions as they are internal and implicit.
80 if (const auto *MethodDecl
= dyn_cast
<CXXMethodDecl
>(FuncDecl
))
81 if (MethodDecl
->getParent()->isLambda())
84 // Check if decl starts with LIBC_INLINE
85 auto Loc
= FullSourceLoc(Result
.SourceManager
->getFileLoc(SrcBegin
),
86 *Result
.SourceManager
);
87 llvm::StringRef SrcText
= Loc
.getBufferData().drop_front(Loc
.getFileOffset());
88 if (SrcText
.starts_with("LIBC_INLINE"))
91 diag(SrcBegin
, "%0 must be tagged with the LIBC_INLINE macro; the macro "
92 "should be placed at the beginning of the declaration")
93 << FuncDecl
<< FixItHint::CreateInsertion(Loc
, "LIBC_INLINE ");
96 } // namespace clang::tidy::llvm_libc