//===-- InlineFunctionDeclCheck.cpp ---------------------------------------===// // // 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 "InlineFunctionDeclCheck.h" #include "../utils/FileExtensionsUtils.h" #include "../utils/LexerUtils.h" #include "clang/AST/ASTContext.h" #include "clang/ASTMatchers/ASTMatchFinder.h" #include "llvm/ADT/StringSet.h" using namespace clang::ast_matchers; namespace clang::tidy::llvm_libc { namespace { const TemplateParameterList * getLastTemplateParameterList(const FunctionDecl *FuncDecl) { const TemplateParameterList *ReturnList = FuncDecl->getDescribedTemplateParams(); if (!ReturnList) { const unsigned NumberOfTemplateParameterLists = FuncDecl->getNumTemplateParameterLists(); if (NumberOfTemplateParameterLists > 0) ReturnList = FuncDecl->getTemplateParameterList( NumberOfTemplateParameterLists - 1); } return ReturnList; } } // namespace InlineFunctionDeclCheck::InlineFunctionDeclCheck(StringRef Name, ClangTidyContext *Context) : ClangTidyCheck(Name, Context), HeaderFileExtensions(Context->getHeaderFileExtensions()) {} void InlineFunctionDeclCheck::registerMatchers(MatchFinder *Finder) { // Ignore functions that have been deleted. Finder->addMatcher(decl(functionDecl(unless(isDeleted()))).bind("func_decl"), this); } void InlineFunctionDeclCheck::check(const MatchFinder::MatchResult &Result) { const auto *FuncDecl = Result.Nodes.getNodeAs("func_decl"); // Consider only explicitly or implicitly inline functions. if (FuncDecl == nullptr || !FuncDecl->isInlined()) return; SourceLocation SrcBegin = FuncDecl->getBeginLoc(); // If we have a template parameter list, we need to skip that because the // LIBC_INLINE macro must be placed after that. if (const TemplateParameterList *TemplateParams = getLastTemplateParameterList(FuncDecl)) { SrcBegin = TemplateParams->getRAngleLoc(); std::optional NextToken = utils::lexer::findNextTokenSkippingComments( SrcBegin, *Result.SourceManager, Result.Context->getLangOpts()); if (NextToken) SrcBegin = NextToken->getLocation(); } // Consider functions only in header files. if (!utils::isSpellingLocInHeaderFile(SrcBegin, *Result.SourceManager, HeaderFileExtensions)) return; // Ignore lambda functions as they are internal and implicit. if (const auto *MethodDecl = dyn_cast(FuncDecl)) if (MethodDecl->getParent()->isLambda()) return; // Check if decl starts with LIBC_INLINE auto Loc = FullSourceLoc(Result.SourceManager->getFileLoc(SrcBegin), *Result.SourceManager); llvm::StringRef SrcText = Loc.getBufferData().drop_front(Loc.getFileOffset()); if (SrcText.starts_with("LIBC_INLINE")) return; diag(SrcBegin, "%0 must be tagged with the LIBC_INLINE macro; the macro " "should be placed at the beginning of the declaration") << FuncDecl << FixItHint::CreateInsertion(Loc, "LIBC_INLINE "); } } // namespace clang::tidy::llvm_libc