1 //===--- DontModifyStdNamespaceCheck.cpp - clang-tidy----------------------===//
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
7 //===----------------------------------------------------------------------===//
9 #include "DontModifyStdNamespaceCheck.h"
10 #include "clang/AST/ASTContext.h"
11 #include "clang/ASTMatchers/ASTMatchFinder.h"
12 #include "clang/ASTMatchers/ASTMatchersInternal.h"
14 using namespace clang
;
15 using namespace clang::ast_matchers
;
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
)
29 ArrayRef
<TemplateArgument
> PackArgs
= Arg
.getPackAsArray();
30 if (matchesFirstInRange(InnerMatcher
, PackArgs
.begin(), PackArgs
.end(),
31 Finder
, Builder
) != PackArgs
.end())
34 return matchesFirstInRange(InnerMatcher
, Args
.begin(), Args
.end(), Finder
,
35 Builder
) != Args
.end();
40 namespace clang::tidy::cert
{
42 void DontModifyStdNamespaceCheck::registerMatchers(MatchFinder
*Finder
) {
44 hasDeclContext(namespaceDecl(hasAnyName("std", "posix"),
45 unless(hasParent(namespaceDecl())))
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
)));
58 // Try to follow exactly CERT rule DCL58-CPP (this text is taken from C++
59 // standard into the CERT rule):
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
74 // The "standard library requirements" and explicit prohibition are not
77 auto BadNonTemplateSpecializationDecl
=
78 decl(unless(anyOf(functionDecl(isExplicitTemplateSpecialization()),
79 varDecl(isExplicitTemplateSpecialization()),
80 cxxRecordDecl(isExplicitTemplateSpecialization()))),
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
);
93 Finder
->addMatcher(decl(anyOf(BadNonTemplateSpecializationDecl
,
94 BadClassTemplateSpec
, BadInnerClassTemplateSpec
,
95 BadFunctionTemplateSpec
, BadMemberFunctionSpec
),
96 unless(isExpansionInSystemHeader()))
100 } // namespace clang::tidy::cert
102 static const NamespaceDecl
*getTopLevelLexicalNamespaceDecl(const Decl
*D
) {
103 const NamespaceDecl
*LastNS
= nullptr;
105 if (const auto *NS
= dyn_cast
<NamespaceDecl
>(D
))
107 D
= dyn_cast_or_null
<Decl
>(D
->getLexicalDeclContext());
112 void clang::tidy::cert::DontModifyStdNamespaceCheck::check(
113 const MatchFinder::MatchResult
&Result
) {
114 const auto *D
= Result
.Nodes
.getNodeAs
<Decl
>("decl");
115 const auto *NS
= Result
.Nodes
.getNodeAs
<NamespaceDecl
>("nmspc");
119 diag(D
->getLocation(),
120 "modification of %0 namespace can result in undefined behavior")
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
)