100a57558SPaula Toth //===--- ImplementationInNamespaceCheck.cpp - clang-tidy ------------------===// 200a57558SPaula Toth // 300a57558SPaula Toth // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 400a57558SPaula Toth // See https://llvm.org/LICENSE.txt for license information. 500a57558SPaula Toth // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 600a57558SPaula Toth // 700a57558SPaula Toth //===----------------------------------------------------------------------===// 800a57558SPaula Toth 900a57558SPaula Toth #include "ImplementationInNamespaceCheck.h" 10daca9721Smichaelrj-google #include "NamespaceConstants.h" 1100a57558SPaula Toth #include "clang/AST/ASTContext.h" 1200a57558SPaula Toth #include "clang/ASTMatchers/ASTMatchFinder.h" 1300a57558SPaula Toth 1400a57558SPaula Toth using namespace clang::ast_matchers; 1500a57558SPaula Toth 167d2ea6c4SCarlos Galvez namespace clang::tidy::llvm_libc { 1700a57558SPaula Toth 1800a57558SPaula Toth void ImplementationInNamespaceCheck::registerMatchers(MatchFinder *Finder) { 1900a57558SPaula Toth Finder->addMatcher( 20774116b8SGuillaume Chatelet translationUnitDecl( 21774116b8SGuillaume Chatelet forEach(decl(isExpansionInMainFile(), unless(linkageSpecDecl()), 22774116b8SGuillaume Chatelet // anonymous namespaces generate usingDirective 23774116b8SGuillaume Chatelet unless(usingDirectiveDecl(isImplicit()))) 24774116b8SGuillaume Chatelet .bind("child_of_translation_unit"))), 2500a57558SPaula Toth this); 2600a57558SPaula Toth } 2700a57558SPaula Toth 2800a57558SPaula Toth void ImplementationInNamespaceCheck::check( 2900a57558SPaula Toth const MatchFinder::MatchResult &Result) { 3000a57558SPaula Toth const auto *MatchedDecl = 3100a57558SPaula Toth Result.Nodes.getNodeAs<Decl>("child_of_translation_unit"); 32774116b8SGuillaume Chatelet const auto *NS = dyn_cast<NamespaceDecl>(MatchedDecl); 33*a074f886SPaul Kirth 34*a074f886SPaul Kirth // LLVM libc declarations should be inside of a non-anonymous namespace. 35774116b8SGuillaume Chatelet if (NS == nullptr || NS->isAnonymousNamespace()) { 3600a57558SPaula Toth diag(MatchedDecl->getLocation(), 37774116b8SGuillaume Chatelet "declaration must be enclosed within the '%0' namespace") 38*a074f886SPaul Kirth << RequiredNamespaceDeclMacroName; 39774116b8SGuillaume Chatelet return; 40774116b8SGuillaume Chatelet } 41*a074f886SPaul Kirth 42*a074f886SPaul Kirth // Enforce that the namespace is the result of macro expansion 43774116b8SGuillaume Chatelet if (Result.SourceManager->isMacroBodyExpansion(NS->getLocation()) == false) { 44774116b8SGuillaume Chatelet diag(NS->getLocation(), "the outermost namespace should be the '%0' macro") 45*a074f886SPaul Kirth << RequiredNamespaceDeclMacroName; 46774116b8SGuillaume Chatelet return; 47774116b8SGuillaume Chatelet } 48*a074f886SPaul Kirth 49*a074f886SPaul Kirth // We want the macro to have [[gnu::visibility("hidden")]] as a prefix, but 50*a074f886SPaul Kirth // visibility is just an attribute in the AST construct, so we check that 51*a074f886SPaul Kirth // instead. 52*a074f886SPaul Kirth if (NS->getVisibility() != Visibility::HiddenVisibility) { 53774116b8SGuillaume Chatelet diag(NS->getLocation(), "the '%0' macro should start with '%1'") 54*a074f886SPaul Kirth << RequiredNamespaceDeclMacroName << RequiredNamespaceDeclStart; 55*a074f886SPaul Kirth return; 56*a074f886SPaul Kirth } 57*a074f886SPaul Kirth 58*a074f886SPaul Kirth // Lastly, make sure the namespace name actually has the __llvm_libc prefix 59*a074f886SPaul Kirth if (NS->getName().starts_with(RequiredNamespaceRefStart) == false) { 60*a074f886SPaul Kirth diag(NS->getLocation(), "the '%0' macro expansion should start with '%1'") 61*a074f886SPaul Kirth << RequiredNamespaceDeclMacroName << RequiredNamespaceRefStart; 62774116b8SGuillaume Chatelet return; 63774116b8SGuillaume Chatelet } 6400a57558SPaula Toth } 6500a57558SPaula Toth 667d2ea6c4SCarlos Galvez } // namespace clang::tidy::llvm_libc 67