clang-tools 19.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
14using namespace clang;
15using namespace clang::ast_matchers;
16
17namespace {
18
19AST_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
40namespace clang::tidy::cert {
41
43 auto HasStdParent =
44 hasDeclContext(namespaceDecl(hasAnyName("std", "posix"),
45 unless(hasParent(namespaceDecl())))
46 .bind("nmspc"));
47 auto UserDefinedType = qualType(
48 hasUnqualifiedDesugaredType(tagType(unless(hasDeclaration(tagDecl(
49 hasAncestor(namespaceDecl(hasAnyName("std", "posix"),
50 unless(hasParent(namespaceDecl()))))))))));
51 auto HasNoProgramDefinedTemplateArgument = unless(
52 hasAnyTemplateArgumentIncludingPack(refersToType(UserDefinedType)));
53 auto InsideStdClassOrClassTemplateSpecialization = hasDeclContext(
54 anyOf(cxxRecordDecl(HasStdParent),
55 classTemplateSpecializationDecl(
56 HasStdParent, HasNoProgramDefinedTemplateArgument)));
57
58 // Try to follow exactly CERT rule DCL58-CPP (this text is taken from C++
59 // standard into the CERT rule):
60 // "
61 // 1 The behavior of a C++ program is undefined if it adds declarations or
62 // definitions to namespace std or to a namespace within namespace std unless
63 // otherwise specified. A program may add a template specialization for any
64 // standard library template to namespace std only if the declaration depends
65 // on a user-defined type and the specialization meets the standard library
66 // requirements for the original template and is not explicitly prohibited. 2
67 // The behavior of a C++ program is undefined if it declares — an explicit
68 // specialization of any member function of a standard library class template,
69 // or — an explicit specialization of any member function template of a
70 // standard library class or class template, or — an explicit or partial
71 // specialization of any member class template of a standard library class or
72 // class template.
73 // "
74 // The "standard library requirements" and explicit prohibition are not
75 // checked.
76
77 auto BadNonTemplateSpecializationDecl =
78 decl(unless(anyOf(functionDecl(isExplicitTemplateSpecialization()),
79 varDecl(isExplicitTemplateSpecialization()),
80 cxxRecordDecl(isExplicitTemplateSpecialization()))),
81 HasStdParent);
82 auto BadClassTemplateSpec = classTemplateSpecializationDecl(
83 HasNoProgramDefinedTemplateArgument, HasStdParent);
84 auto BadInnerClassTemplateSpec = classTemplateSpecializationDecl(
85 InsideStdClassOrClassTemplateSpecialization);
86 auto BadFunctionTemplateSpec =
87 functionDecl(unless(cxxMethodDecl()), isExplicitTemplateSpecialization(),
88 HasNoProgramDefinedTemplateArgument, HasStdParent);
89 auto BadMemberFunctionSpec =
90 cxxMethodDecl(isExplicitTemplateSpecialization(),
91 InsideStdClassOrClassTemplateSpecialization);
92
93 Finder->addMatcher(decl(anyOf(BadNonTemplateSpecializationDecl,
94 BadClassTemplateSpec, BadInnerClassTemplateSpec,
95 BadFunctionTemplateSpec, BadMemberFunctionSpec),
96 unless(isExpansionInSystemHeader()))
97 .bind("decl"),
98 this);
99}
100} // namespace clang::tidy::cert
101
102static const NamespaceDecl *getTopLevelLexicalNamespaceDecl(const Decl *D) {
103 const NamespaceDecl *LastNS = nullptr;
104 while (D) {
105 if (const auto *NS = dyn_cast<NamespaceDecl>(D))
106 LastNS = NS;
107 D = dyn_cast_or_null<Decl>(D->getLexicalDeclContext());
108 }
109 return LastNS;
110}
111
113 const MatchFinder::MatchResult &Result) {
114 const auto *D = Result.Nodes.getNodeAs<Decl>("decl");
115 const auto *NS = Result.Nodes.getNodeAs<NamespaceDecl>("nmspc");
116 if (!D || !NS)
117 return;
118
119 diag(D->getLocation(),
120 "modification of %0 namespace can result in undefined behavior")
121 << NS;
122 // 'NS' is not always the namespace declaration that lexically contains 'D',
123 // try to find such a namespace.
124 if (const NamespaceDecl *LexNS = getTopLevelLexicalNamespaceDecl(D)) {
125 assert(NS->getCanonicalDecl() == LexNS->getCanonicalDecl() &&
126 "Mismatch in found namespace");
127 diag(LexNS->getLocation(), "%0 namespace opened here", DiagnosticIDs::Note)
128 << LexNS;
129 }
130}
const FunctionDecl * Decl
CodeCompletionBuilder Builder
static const NamespaceDecl * getTopLevelLexicalNamespaceDecl(const Decl *D)
llvm::json::Object Args
Definition: Trace.cpp:138
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
ClangTidyChecks that register ASTMatchers should do the actual work in here.
void registerMatchers(ast_matchers::MatchFinder *Finder) override
Override this to register AST matchers with Finder.
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//