66 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 "ImplementationInNamespaceCheck.h"10#include "NamespaceConstants.h"11#include "clang/ASTMatchers/ASTMatchFinder.h"12 13using namespace clang::ast_matchers;14 15namespace clang::tidy::llvm_libc {16 17void ImplementationInNamespaceCheck::registerMatchers(MatchFinder *Finder) {18 Finder->addMatcher(19 translationUnitDecl(20 forEach(decl(isExpansionInMainFile(), unless(linkageSpecDecl()),21 // anonymous namespaces generate usingDirective22 unless(usingDirectiveDecl(isImplicit())))23 .bind("child_of_translation_unit"))),24 this);25}26 27void ImplementationInNamespaceCheck::check(28 const MatchFinder::MatchResult &Result) {29 const auto *MatchedDecl =30 Result.Nodes.getNodeAs<Decl>("child_of_translation_unit");31 const auto *NS = dyn_cast<NamespaceDecl>(MatchedDecl);32 33 // LLVM libc declarations should be inside of a non-anonymous namespace.34 if (NS == nullptr || NS->isAnonymousNamespace()) {35 diag(MatchedDecl->getLocation(),36 "declaration must be enclosed within the '%0' namespace")37 << RequiredNamespaceDeclMacroName;38 return;39 }40 41 // Enforce that the namespace is the result of macro expansion42 if (Result.SourceManager->isMacroBodyExpansion(NS->getLocation()) == false) {43 diag(NS->getLocation(), "the outermost namespace should be the '%0' macro")44 << RequiredNamespaceDeclMacroName;45 return;46 }47 48 // We want the macro to have [[gnu::visibility("hidden")]] as a prefix, but49 // visibility is just an attribute in the AST construct, so we check that50 // instead.51 if (NS->getVisibility() != Visibility::HiddenVisibility) {52 diag(NS->getLocation(), "the '%0' macro should start with '%1'")53 << RequiredNamespaceDeclMacroName << RequiredNamespaceDeclStart;54 return;55 }56 57 // Lastly, make sure the namespace name actually has the __llvm_libc prefix58 if (NS->getName().starts_with(RequiredNamespaceRefStart) == false) {59 diag(NS->getLocation(), "the '%0' macro expansion should start with '%1'")60 << RequiredNamespaceDeclMacroName << RequiredNamespaceRefStart;61 return;62 }63}64 65} // namespace clang::tidy::llvm_libc66