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