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