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

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of clang-tools-extra/clang-tidy/cert/DontModifyStdNamespaceCheck.cpp