xref: /llvm-project/clang-tools-extra/clang-tidy/llvmlibc/InlineFunctionDeclCheck.cpp (revision c92cf315c67edaffea1c4f3a914197dce2a194f3)
1 //===-- InlineFunctionDeclCheck.cpp ---------------------------------------===//
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 #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"
14 
15 #include "llvm/ADT/StringSet.h"
16 
17 using namespace clang::ast_matchers;
18 
19 namespace clang::tidy::llvm_libc {
20 
21 namespace {
22 
23 const TemplateParameterList *
getLastTemplateParameterList(const FunctionDecl * FuncDecl)24 getLastTemplateParameterList(const FunctionDecl *FuncDecl) {
25   const TemplateParameterList *ReturnList =
26       FuncDecl->getDescribedTemplateParams();
27 
28   if (!ReturnList) {
29     const unsigned NumberOfTemplateParameterLists =
30         FuncDecl->getNumTemplateParameterLists();
31 
32     if (NumberOfTemplateParameterLists > 0)
33       ReturnList = FuncDecl->getTemplateParameterList(
34           NumberOfTemplateParameterLists - 1);
35   }
36 
37   return ReturnList;
38 }
39 
40 } // namespace
41 
InlineFunctionDeclCheck(StringRef Name,ClangTidyContext * Context)42 InlineFunctionDeclCheck::InlineFunctionDeclCheck(StringRef Name,
43                                                  ClangTidyContext *Context)
44     : ClangTidyCheck(Name, Context),
45       HeaderFileExtensions(Context->getHeaderFileExtensions()) {}
46 
registerMatchers(MatchFinder * Finder)47 void InlineFunctionDeclCheck::registerMatchers(MatchFinder *Finder) {
48   // Ignore functions that have been deleted.
49   Finder->addMatcher(decl(functionDecl(unless(isDeleted()))).bind("func_decl"),
50                      this);
51 }
52 
check(const MatchFinder::MatchResult & Result)53 void InlineFunctionDeclCheck::check(const MatchFinder::MatchResult &Result) {
54   const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
55 
56   // Consider only explicitly or implicitly inline functions.
57   if (FuncDecl == nullptr || !FuncDecl->isInlined())
58     return;
59 
60   SourceLocation SrcBegin = FuncDecl->getBeginLoc();
61 
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());
70     if (NextToken)
71       SrcBegin = NextToken->getLocation();
72   }
73 
74   // Consider functions only in header files.
75   if (!utils::isSpellingLocInHeaderFile(SrcBegin, *Result.SourceManager,
76                                         HeaderFileExtensions))
77     return;
78 
79   // Ignore lambda functions as they are internal and implicit.
80   if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl))
81     if (MethodDecl->getParent()->isLambda())
82       return;
83 
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"))
89     return;
90 
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 ");
94 }
95 
96 } // namespace clang::tidy::llvm_libc
97