clang-tools 24.0.0git
StdNamespaceModificationCheck.cpp
Go to the documentation of this file.
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-exception
6//
7//===----------------------------------------------------------------------===//
8
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 ast_matchers::internal::Matcher<TemplateArgument>, InnerMatcher) {
23 const ArrayRef<TemplateArgument> Args =
24 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
37AST_MATCHER(Decl, isInStdOrPosixNamespace) {
38 for (const auto *DC = dyn_cast<DeclContext>(&Node); DC;
39 DC = DC->getParent()) {
40 if (DC->isStdNamespace())
41 return true;
42
43 if (const auto *NS = dyn_cast<NamespaceDecl>(DC);
44 NS && NS->getName() == "posix" && NS->getParent()->isTranslationUnit())
45 return true;
46 }
47 return false;
48}
49
50} // namespace
51
52namespace clang::tidy::bugprone {
53
55 const auto HasStdParent =
56 hasDeclContext(namespaceDecl(hasAnyName("std", "posix"),
57 unless(hasParent(namespaceDecl())))
58 .bind("nmspc"));
59 // FIXME: Investigate why lambda closure declarations can be absent from the
60 // AST parent map.
61 const auto UserDefinedDecl =
62 namedDecl(anyOf(classTemplateDecl(), tagDecl()),
63 hasDeclContext(isInStdOrPosixNamespace()));
64 const auto UserDefinedType = qualType(hasUnqualifiedDesugaredType(anyOf(
65 tagType(unless(hasDeclaration(UserDefinedDecl))),
66 templateSpecializationType(unless(hasDeclaration(UserDefinedDecl))))));
67 const auto HasNoProgramDefinedTemplateArgument = unless(
68 hasAnyTemplateArgumentIncludingPack(refersToType(UserDefinedType)));
69 const auto InsideStdClassOrClassTemplateSpecialization = hasDeclContext(
70 anyOf(cxxRecordDecl(HasStdParent),
71 classTemplateSpecializationDecl(
72 HasStdParent, HasNoProgramDefinedTemplateArgument)));
73
74 // Try to follow exactly CERT rule DCL58-CPP (this text is taken from C++
75 // standard into the CERT rule):
76 // "
77 // 1 The behavior of a C++ program is undefined if it adds declarations or
78 // definitions to namespace std or to a namespace within namespace std unless
79 // otherwise specified. A program may add a template specialization for any
80 // standard library template to namespace std only if the declaration depends
81 // on a user-defined type and the specialization meets the standard library
82 // requirements for the original template and is not explicitly prohibited. 2
83 // The behavior of a C++ program is undefined if it declares — an explicit
84 // specialization of any member function of a standard library class template,
85 // or — an explicit specialization of any member function template of a
86 // standard library class or class template, or — an explicit or partial
87 // specialization of any member class template of a standard library class or
88 // class template.
89 // "
90 // The "standard library requirements" and explicit prohibition are not
91 // checked.
92
93 auto BadNonTemplateSpecializationDecl =
94 decl(unless(anyOf(functionDecl(isExplicitTemplateSpecialization()),
95 varDecl(isExplicitTemplateSpecialization()),
96 cxxRecordDecl(isExplicitTemplateSpecialization()))),
97 HasStdParent);
98 auto BadClassTemplateSpec = classTemplateSpecializationDecl(
99 HasNoProgramDefinedTemplateArgument, HasStdParent);
100 auto BadInnerClassTemplateSpec = classTemplateSpecializationDecl(
101 InsideStdClassOrClassTemplateSpecialization);
102 auto BadFunctionTemplateSpec =
103 functionDecl(unless(cxxMethodDecl()), isExplicitTemplateSpecialization(),
104 HasNoProgramDefinedTemplateArgument, HasStdParent);
105 auto BadMemberFunctionSpec =
106 cxxMethodDecl(isExplicitTemplateSpecialization(),
107 InsideStdClassOrClassTemplateSpecialization);
108
109 Finder->addMatcher(decl(anyOf(BadNonTemplateSpecializationDecl,
110 BadClassTemplateSpec, BadInnerClassTemplateSpec,
111 BadFunctionTemplateSpec, BadMemberFunctionSpec))
112 .bind("decl"),
113 this);
114}
115} // namespace clang::tidy::bugprone
116
117static const NamespaceDecl *getTopLevelLexicalNamespaceDecl(const Decl *D) {
118 const NamespaceDecl *LastNS = nullptr;
119 while (D) {
120 if (const auto *NS = dyn_cast<NamespaceDecl>(D))
121 LastNS = NS;
122 D = dyn_cast_or_null<Decl>(D->getLexicalDeclContext());
123 }
124 return LastNS;
125}
126
128 const MatchFinder::MatchResult &Result) {
129 const auto *D = Result.Nodes.getNodeAs<Decl>("decl");
130 const auto *NS = Result.Nodes.getNodeAs<NamespaceDecl>("nmspc");
131 if (!D || !NS)
132 return;
133
134 // Skip compiler-generated implicit declarations (e.g. std::align_val_t).
135 if (D->isImplicit())
136 return;
137
138 diag(D->getLocation(),
139 "modification of %0 namespace can result in undefined behavior")
140 << NS;
141 // 'NS' is not always the namespace declaration that lexically contains 'D',
142 // try to find such a namespace.
143 if (const NamespaceDecl *LexNS = getTopLevelLexicalNamespaceDecl(D)) {
144 assert(NS->getCanonicalDecl() == LexNS->getCanonicalDecl() &&
145 "Mismatch in found namespace");
146 diag(LexNS->getLocation(), "%0 namespace opened here", DiagnosticIDs::Note)
147 << LexNS;
148 }
149}
static const NamespaceDecl * getTopLevelLexicalNamespaceDecl(const Decl *D)
void check(const ast_matchers::MatchFinder::MatchResult &Result) override
void registerMatchers(ast_matchers::MatchFinder *Finder) override
AST_MATCHER(BinaryOperator, isRelationalOperator)
===– Representation.cpp - ClangDoc Representation --------—*- C++ -*-===//