1 //===-- CalleeNamespaceCheck.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 "CalleeNamespaceCheck.h" 10 #include "NamespaceConstants.h" 11 #include "clang/AST/ASTContext.h" 12 #include "clang/ASTMatchers/ASTMatchFinder.h" 13 14 #include "clang/ASTMatchers/ASTMatchers.h" 15 #include "llvm/ADT/StringSet.h" 16 17 using namespace clang::ast_matchers; 18 19 namespace clang::tidy::llvm_libc { 20 21 // Gets the outermost namespace of a DeclContext, right under the Translation 22 // Unit. 23 const DeclContext *getOutermostNamespace(const DeclContext *Decl) { 24 const DeclContext *Parent = Decl->getParent(); 25 if (Parent->isTranslationUnit()) 26 return Decl; 27 return getOutermostNamespace(Parent); 28 } 29 30 void CalleeNamespaceCheck::registerMatchers(MatchFinder *Finder) { 31 Finder->addMatcher( 32 declRefExpr(to(functionDecl().bind("func"))).bind("use-site"), this); 33 } 34 35 // A list of functions that are exempted from this check. The __errno_location 36 // function is for setting errno, which is allowed in libc, and the other 37 // functions are specifically allowed to be external so that they can be 38 // intercepted. 39 static const llvm::StringSet<> IgnoredFunctions = { 40 "__errno_location", "malloc", "calloc", "realloc", "free", "aligned_alloc"}; 41 42 void CalleeNamespaceCheck::check(const MatchFinder::MatchResult &Result) { 43 const auto *UsageSiteExpr = Result.Nodes.getNodeAs<DeclRefExpr>("use-site"); 44 const auto *FuncDecl = Result.Nodes.getNodeAs<FunctionDecl>("func"); 45 46 // Ignore compiler builtin functions. 47 if (FuncDecl->getBuiltinID() != 0) 48 return; 49 50 // If the outermost namespace of the function is a macro that starts with 51 // __llvm_libc, we're good. 52 const auto *NS = dyn_cast<NamespaceDecl>(getOutermostNamespace(FuncDecl)); 53 if (NS && Result.SourceManager->isMacroBodyExpansion(NS->getLocation()) && 54 NS->getName().starts_with(RequiredNamespaceRefStart)) 55 return; 56 57 const DeclarationName &Name = FuncDecl->getDeclName(); 58 if (Name.isIdentifier() && 59 IgnoredFunctions.contains(Name.getAsIdentifierInfo()->getName())) 60 return; 61 62 diag(UsageSiteExpr->getBeginLoc(), 63 "%0 must resolve to a function declared " 64 "within the namespace defined by the '%1' macro") 65 << FuncDecl << RequiredNamespaceRefMacroName; 66 67 diag(FuncDecl->getLocation(), "resolves to this declaration", 68 clang::DiagnosticIDs::Note); 69 } 70 71 } // namespace clang::tidy::llvm_libc 72