DontModifyStdNamespaceCheck.cpp 5.7 KB

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