brintos

brintos / llvm-project-archived public Read only

0
0
Text · 5.7 KiB · 1dff741 Raw
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