1 //===--- DontModifyStdNamespaceCheck.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 "DontModifyStdNamespaceCheck.h" 10 #include "clang/AST/ASTContext.h" 11 #include "clang/ASTMatchers/ASTMatchFinder.h" 12 #include "clang/ASTMatchers/ASTMatchersInternal.h" 13 14 using namespace clang; 15 using namespace clang::ast_matchers; 16 17 namespace { 18 19 AST_POLYMORPHIC_MATCHER_P( 20 hasAnyTemplateArgumentIncludingPack, 21 AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl, 22 TemplateSpecializationType, FunctionDecl), 23 clang::ast_matchers::internal::Matcher<TemplateArgument>, InnerMatcher) { 24 ArrayRef<TemplateArgument> Args = 25 clang::ast_matchers::internal::getTemplateSpecializationArgs(Node); 26 for (const auto &Arg : Args) { 27 if (Arg.getKind() != TemplateArgument::Pack) 28 continue; 29 ArrayRef<TemplateArgument> PackArgs = Arg.getPackAsArray(); 30 if (matchesFirstInRange(InnerMatcher, PackArgs.begin(), PackArgs.end(), 31 Finder, Builder) != PackArgs.end()) 32 return true; 33 } 34 return matchesFirstInRange(InnerMatcher, Args.begin(), Args.end(), Finder, 35 Builder) != Args.end(); 36 } 37 38 } // namespace 39 40 namespace clang { 41 namespace tidy { 42 namespace cert { 43 44 void DontModifyStdNamespaceCheck::registerMatchers(MatchFinder *Finder) { 45 auto HasStdParent = 46 hasDeclContext(namespaceDecl(hasAnyName("std", "posix"), 47 unless(hasParent(namespaceDecl()))) 48 .bind("nmspc")); 49 auto UserDefinedType = qualType( 50 hasUnqualifiedDesugaredType(tagType(unless(hasDeclaration(tagDecl( 51 hasAncestor(namespaceDecl(hasAnyName("std", "posix"), 52 unless(hasParent(namespaceDecl())))))))))); 53 auto HasNoProgramDefinedTemplateArgument = unless( 54 hasAnyTemplateArgumentIncludingPack(refersToType(UserDefinedType))); 55 auto InsideStdClassOrClassTemplateSpecialization = hasDeclContext( 56 anyOf(cxxRecordDecl(HasStdParent), 57 classTemplateSpecializationDecl( 58 HasStdParent, HasNoProgramDefinedTemplateArgument))); 59 60 // Try to follow exactly CERT rule DCL58-CPP (this text is taken from C++ 61 // standard into the CERT rule): 62 // " 63 // 1 The behavior of a C++ program is undefined if it adds declarations or 64 // definitions to namespace std or to a namespace within namespace std unless 65 // otherwise specified. A program may add a template specialization for any 66 // standard library template to namespace std only if the declaration depends 67 // on a user-defined type and the specialization meets the standard library 68 // requirements for the original template and is not explicitly prohibited. 2 69 // The behavior of a C++ program is undefined if it declares — an explicit 70 // specialization of any member function of a standard library class template, 71 // or — an explicit specialization of any member function template of a 72 // standard library class or class template, or — an explicit or partial 73 // specialization of any member class template of a standard library class or 74 // class template. 75 // " 76 // The "standard library requirements" and explicit prohibition are not 77 // checked. 78 79 auto BadNonTemplateSpecializationDecl = 80 decl(unless(anyOf(functionDecl(isExplicitTemplateSpecialization()), 81 varDecl(isExplicitTemplateSpecialization()), 82 cxxRecordDecl(isExplicitTemplateSpecialization()))), 83 HasStdParent); 84 auto BadClassTemplateSpec = classTemplateSpecializationDecl( 85 HasNoProgramDefinedTemplateArgument, HasStdParent); 86 auto BadInnerClassTemplateSpec = classTemplateSpecializationDecl( 87 InsideStdClassOrClassTemplateSpecialization); 88 auto BadFunctionTemplateSpec = 89 functionDecl(unless(cxxMethodDecl()), isExplicitTemplateSpecialization(), 90 HasNoProgramDefinedTemplateArgument, HasStdParent); 91 auto BadMemberFunctionSpec = 92 cxxMethodDecl(isExplicitTemplateSpecialization(), 93 InsideStdClassOrClassTemplateSpecialization); 94 95 Finder->addMatcher(decl(anyOf(BadNonTemplateSpecializationDecl, 96 BadClassTemplateSpec, BadInnerClassTemplateSpec, 97 BadFunctionTemplateSpec, BadMemberFunctionSpec), 98 unless(isExpansionInSystemHeader())) 99 .bind("decl"), 100 this); 101 } 102 } // namespace cert 103 } // namespace tidy 104 } // namespace clang 105 106 static const NamespaceDecl *getTopLevelLexicalNamespaceDecl(const Decl *D) { 107 const NamespaceDecl *LastNS = nullptr; 108 while (D) { 109 if (const auto *NS = dyn_cast<NamespaceDecl>(D)) 110 LastNS = NS; 111 D = dyn_cast_or_null<Decl>(D->getLexicalDeclContext()); 112 } 113 return LastNS; 114 } 115 116 void clang::tidy::cert::DontModifyStdNamespaceCheck::check( 117 const MatchFinder::MatchResult &Result) { 118 const auto *D = Result.Nodes.getNodeAs<Decl>("decl"); 119 const auto *NS = Result.Nodes.getNodeAs<NamespaceDecl>("nmspc"); 120 if (!D || !NS) 121 return; 122 123 diag(D->getLocation(), 124 "modification of %0 namespace can result in undefined behavior") 125 << NS; 126 // 'NS' is not always the namespace declaration that lexically contains 'D', 127 // try to find such a namespace. 128 if (const NamespaceDecl *LexNS = getTopLevelLexicalNamespaceDecl(D)) { 129 assert(NS->getCanonicalDecl() == LexNS->getCanonicalDecl() && 130 "Mismatch in found namespace"); 131 diag(LexNS->getLocation(), "%0 namespace opened here", DiagnosticIDs::Note) 132 << LexNS; 133 } 134 } 135