clang-tools  16.0.0git
DontModifyStdNamespaceCheck.cpp
Go to the documentation of this file.
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 
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()),
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 
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 }
Pack
ArrayRef< const ParmVarDecl * > Pack
Definition: AST.cpp:794
clang::ast_matchers
Definition: AbseilMatcher.h:14
clang::tidy::cert::DontModifyStdNamespaceCheck::check
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
Definition: DontModifyStdNamespaceCheck.cpp:116
ns1::ns2::D
@ D
Definition: CategoricalFeature.h:3
Decl
const FunctionDecl * Decl
Definition: AvoidBindCheck.cpp:100
Builder
CodeCompletionBuilder Builder
Definition: CodeCompletionStringsTests.cpp:36
Args
llvm::json::Object Args
Definition: Trace.cpp:138
clang
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//
Definition: ApplyReplacements.h:27
getTopLevelLexicalNamespaceDecl
static const NamespaceDecl * getTopLevelLexicalNamespaceDecl(const Decl *D)
Definition: DontModifyStdNamespaceCheck.cpp:106
clang::clangd::isExplicitTemplateSpecialization
bool isExplicitTemplateSpecialization(const NamedDecl *D)
Indicates if D is an explicit template specialization, e.g.
Definition: AST.cpp:164
DontModifyStdNamespaceCheck.h