123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130 |
- //===--- DontModifyStdNamespaceCheck.cpp - clang-tidy----------------------===//
- //
- // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
- // See https://llvm.org/LICENSE.txt for license information.
- // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
- //
- //===----------------------------------------------------------------------===//
- #include "DontModifyStdNamespaceCheck.h"
- #include "clang/AST/ASTContext.h"
- #include "clang/ASTMatchers/ASTMatchFinder.h"
- #include "clang/ASTMatchers/ASTMatchersInternal.h"
- using namespace clang;
- using namespace clang::ast_matchers;
- namespace {
- AST_POLYMORPHIC_MATCHER_P(
- hasAnyTemplateArgumentIncludingPack,
- AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl,
- TemplateSpecializationType, FunctionDecl),
- clang::ast_matchers::internal::Matcher<TemplateArgument>, InnerMatcher) {
- ArrayRef<TemplateArgument> Args =
- clang::ast_matchers::internal::getTemplateSpecializationArgs(Node);
- for (const auto &Arg : Args) {
- if (Arg.getKind() != TemplateArgument::Pack)
- continue;
- ArrayRef<TemplateArgument> PackArgs = Arg.getPackAsArray();
- if (matchesFirstInRange(InnerMatcher, PackArgs.begin(), PackArgs.end(),
- Finder, Builder) != PackArgs.end())
- return true;
- }
- return matchesFirstInRange(InnerMatcher, Args.begin(), Args.end(), Finder,
- Builder) != Args.end();
- }
- } // namespace
- namespace clang::tidy::cert {
- void DontModifyStdNamespaceCheck::registerMatchers(MatchFinder *Finder) {
- auto HasStdParent =
- hasDeclContext(namespaceDecl(hasAnyName("std", "posix"),
- unless(hasParent(namespaceDecl())))
- .bind("nmspc"));
- auto UserDefinedType = qualType(
- hasUnqualifiedDesugaredType(tagType(unless(hasDeclaration(tagDecl(
- hasAncestor(namespaceDecl(hasAnyName("std", "posix"),
- unless(hasParent(namespaceDecl()))))))))));
- auto HasNoProgramDefinedTemplateArgument = unless(
- hasAnyTemplateArgumentIncludingPack(refersToType(UserDefinedType)));
- auto InsideStdClassOrClassTemplateSpecialization = hasDeclContext(
- anyOf(cxxRecordDecl(HasStdParent),
- classTemplateSpecializationDecl(
- HasStdParent, HasNoProgramDefinedTemplateArgument)));
- // Try to follow exactly CERT rule DCL58-CPP (this text is taken from C++
- // standard into the CERT rule):
- // "
- // 1 The behavior of a C++ program is undefined if it adds declarations or
- // definitions to namespace std or to a namespace within namespace std unless
- // otherwise specified. A program may add a template specialization for any
- // standard library template to namespace std only if the declaration depends
- // on a user-defined type and the specialization meets the standard library
- // requirements for the original template and is not explicitly prohibited. 2
- // The behavior of a C++ program is undefined if it declares — an explicit
- // specialization of any member function of a standard library class template,
- // or — an explicit specialization of any member function template of a
- // standard library class or class template, or — an explicit or partial
- // specialization of any member class template of a standard library class or
- // class template.
- // "
- // The "standard library requirements" and explicit prohibition are not
- // checked.
- auto BadNonTemplateSpecializationDecl =
- decl(unless(anyOf(functionDecl(isExplicitTemplateSpecialization()),
- varDecl(isExplicitTemplateSpecialization()),
- cxxRecordDecl(isExplicitTemplateSpecialization()))),
- HasStdParent);
- auto BadClassTemplateSpec = classTemplateSpecializationDecl(
- HasNoProgramDefinedTemplateArgument, HasStdParent);
- auto BadInnerClassTemplateSpec = classTemplateSpecializationDecl(
- InsideStdClassOrClassTemplateSpecialization);
- auto BadFunctionTemplateSpec =
- functionDecl(unless(cxxMethodDecl()), isExplicitTemplateSpecialization(),
- HasNoProgramDefinedTemplateArgument, HasStdParent);
- auto BadMemberFunctionSpec =
- cxxMethodDecl(isExplicitTemplateSpecialization(),
- InsideStdClassOrClassTemplateSpecialization);
- Finder->addMatcher(decl(anyOf(BadNonTemplateSpecializationDecl,
- BadClassTemplateSpec, BadInnerClassTemplateSpec,
- BadFunctionTemplateSpec, BadMemberFunctionSpec),
- unless(isExpansionInSystemHeader()))
- .bind("decl"),
- this);
- }
- } // namespace clang::tidy::cert
- static const NamespaceDecl *getTopLevelLexicalNamespaceDecl(const Decl *D) {
- const NamespaceDecl *LastNS = nullptr;
- while (D) {
- if (const auto *NS = dyn_cast<NamespaceDecl>(D))
- LastNS = NS;
- D = dyn_cast_or_null<Decl>(D->getLexicalDeclContext());
- }
- return LastNS;
- }
- void clang::tidy::cert::DontModifyStdNamespaceCheck::check(
- const MatchFinder::MatchResult &Result) {
- const auto *D = Result.Nodes.getNodeAs<Decl>("decl");
- const auto *NS = Result.Nodes.getNodeAs<NamespaceDecl>("nmspc");
- if (!D || !NS)
- return;
- diag(D->getLocation(),
- "modification of %0 namespace can result in undefined behavior")
- << NS;
- // 'NS' is not always the namespace declaration that lexically contains 'D',
- // try to find such a namespace.
- if (const NamespaceDecl *LexNS = getTopLevelLexicalNamespaceDecl(D)) {
- assert(NS->getCanonicalDecl() == LexNS->getCanonicalDecl() &&
- "Mismatch in found namespace");
- diag(LexNS->getLocation(), "%0 namespace opened here", DiagnosticIDs::Note)
- << LexNS;
- }
- }
|