brintos

brintos / llvm-project-archived public Read only

0
0
Text · 2.6 KiB · dd1ef07 Raw
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