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