1b7b28c6cSSiva Chandra Reddy //===-- InlineFunctionDeclCheck.cpp ---------------------------------------===//
2b7b28c6cSSiva Chandra Reddy //
3b7b28c6cSSiva Chandra Reddy // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4b7b28c6cSSiva Chandra Reddy // See https://llvm.org/LICENSE.txt for license information.
5b7b28c6cSSiva Chandra Reddy // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6b7b28c6cSSiva Chandra Reddy //
7b7b28c6cSSiva Chandra Reddy //===----------------------------------------------------------------------===//
8b7b28c6cSSiva Chandra Reddy
9b7b28c6cSSiva Chandra Reddy #include "InlineFunctionDeclCheck.h"
105b37cddfSCarlos Galvez #include "../utils/FileExtensionsUtils.h"
11c96306dbSAMS21 #include "../utils/LexerUtils.h"
12b7b28c6cSSiva Chandra Reddy #include "clang/AST/ASTContext.h"
13b7b28c6cSSiva Chandra Reddy #include "clang/ASTMatchers/ASTMatchFinder.h"
14b7b28c6cSSiva Chandra Reddy
15b7b28c6cSSiva Chandra Reddy #include "llvm/ADT/StringSet.h"
16b7b28c6cSSiva Chandra Reddy
17b7b28c6cSSiva Chandra Reddy using namespace clang::ast_matchers;
18b7b28c6cSSiva Chandra Reddy
19b7b28c6cSSiva Chandra Reddy namespace clang::tidy::llvm_libc {
20b7b28c6cSSiva Chandra Reddy
21c96306dbSAMS21 namespace {
22c96306dbSAMS21
23c96306dbSAMS21 const TemplateParameterList *
getLastTemplateParameterList(const FunctionDecl * FuncDecl)24c96306dbSAMS21 getLastTemplateParameterList(const FunctionDecl *FuncDecl) {
25c96306dbSAMS21 const TemplateParameterList *ReturnList =
26c96306dbSAMS21 FuncDecl->getDescribedTemplateParams();
27c96306dbSAMS21
28c96306dbSAMS21 if (!ReturnList) {
29c96306dbSAMS21 const unsigned NumberOfTemplateParameterLists =
30c96306dbSAMS21 FuncDecl->getNumTemplateParameterLists();
31c96306dbSAMS21
32c96306dbSAMS21 if (NumberOfTemplateParameterLists > 0)
33c96306dbSAMS21 ReturnList = FuncDecl->getTemplateParameterList(
34c96306dbSAMS21 NumberOfTemplateParameterLists - 1);
35c96306dbSAMS21 }
36c96306dbSAMS21
37c96306dbSAMS21 return ReturnList;
38c96306dbSAMS21 }
39c96306dbSAMS21
40c96306dbSAMS21 } // namespace
41c96306dbSAMS21
InlineFunctionDeclCheck(StringRef Name,ClangTidyContext * Context)42b7b28c6cSSiva Chandra Reddy InlineFunctionDeclCheck::InlineFunctionDeclCheck(StringRef Name,
43b7b28c6cSSiva Chandra Reddy ClangTidyContext *Context)
44b7b28c6cSSiva Chandra Reddy : ClangTidyCheck(Name, Context),
455b37cddfSCarlos Galvez HeaderFileExtensions(Context->getHeaderFileExtensions()) {}
46b7b28c6cSSiva Chandra Reddy
registerMatchers(MatchFinder * Finder)47b7b28c6cSSiva Chandra Reddy void InlineFunctionDeclCheck::registerMatchers(MatchFinder *Finder) {
48*c92cf315Smichaelrj-google // Ignore functions that have been deleted.
49*c92cf315Smichaelrj-google Finder->addMatcher(decl(functionDecl(unless(isDeleted()))).bind("func_decl"),
50*c92cf315Smichaelrj-google this);
51b7b28c6cSSiva Chandra Reddy }
52b7b28c6cSSiva Chandra Reddy
check(const MatchFinder::MatchResult & Result)53b7b28c6cSSiva Chandra Reddy void InlineFunctionDeclCheck::check(const MatchFinder::MatchResult &Result) {
54b7b28c6cSSiva Chandra Reddy const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("func_decl");
55b7b28c6cSSiva Chandra Reddy
56b7b28c6cSSiva Chandra Reddy // Consider only explicitly or implicitly inline functions.
57b7b28c6cSSiva Chandra Reddy if (FuncDecl == nullptr || !FuncDecl->isInlined())
58b7b28c6cSSiva Chandra Reddy return;
59b7b28c6cSSiva Chandra Reddy
60b7b28c6cSSiva Chandra Reddy SourceLocation SrcBegin = FuncDecl->getBeginLoc();
61c96306dbSAMS21
62c96306dbSAMS21 // If we have a template parameter list, we need to skip that because the
63c96306dbSAMS21 // LIBC_INLINE macro must be placed after that.
64c96306dbSAMS21 if (const TemplateParameterList *TemplateParams =
65c96306dbSAMS21 getLastTemplateParameterList(FuncDecl)) {
66c96306dbSAMS21 SrcBegin = TemplateParams->getRAngleLoc();
67c96306dbSAMS21 std::optional<Token> NextToken =
68c96306dbSAMS21 utils::lexer::findNextTokenSkippingComments(
69c96306dbSAMS21 SrcBegin, *Result.SourceManager, Result.Context->getLangOpts());
70c96306dbSAMS21 if (NextToken)
71c96306dbSAMS21 SrcBegin = NextToken->getLocation();
72c96306dbSAMS21 }
73c96306dbSAMS21
74b7b28c6cSSiva Chandra Reddy // Consider functions only in header files.
75b7b28c6cSSiva Chandra Reddy if (!utils::isSpellingLocInHeaderFile(SrcBegin, *Result.SourceManager,
76b7b28c6cSSiva Chandra Reddy HeaderFileExtensions))
77b7b28c6cSSiva Chandra Reddy return;
78b7b28c6cSSiva Chandra Reddy
791663016bSJoseph Huber // Ignore lambda functions as they are internal and implicit.
80c96306dbSAMS21 if (const auto *MethodDecl = dyn_cast<CXXMethodDecl>(FuncDecl))
811663016bSJoseph Huber if (MethodDecl->getParent()->isLambda())
821663016bSJoseph Huber return;
831663016bSJoseph Huber
84b7b28c6cSSiva Chandra Reddy // Check if decl starts with LIBC_INLINE
85b7b28c6cSSiva Chandra Reddy auto Loc = FullSourceLoc(Result.SourceManager->getFileLoc(SrcBegin),
86b7b28c6cSSiva Chandra Reddy *Result.SourceManager);
87b7b28c6cSSiva Chandra Reddy llvm::StringRef SrcText = Loc.getBufferData().drop_front(Loc.getFileOffset());
88b7b28c6cSSiva Chandra Reddy if (SrcText.starts_with("LIBC_INLINE"))
89b7b28c6cSSiva Chandra Reddy return;
90b7b28c6cSSiva Chandra Reddy
91b7b28c6cSSiva Chandra Reddy diag(SrcBegin, "%0 must be tagged with the LIBC_INLINE macro; the macro "
92b7b28c6cSSiva Chandra Reddy "should be placed at the beginning of the declaration")
939d4162ffSRoland McGrath << FuncDecl << FixItHint::CreateInsertion(Loc, "LIBC_INLINE ");
94b7b28c6cSSiva Chandra Reddy }
95b7b28c6cSSiva Chandra Reddy
96b7b28c6cSSiva Chandra Reddy } // namespace clang::tidy::llvm_libc
97