diff options
Diffstat (limited to 'clang-tools-extra/clang-tidy/bugprone/StdNamespaceModificationCheck.cpp')
| -rw-r--r-- | clang-tools-extra/clang-tidy/bugprone/StdNamespaceModificationCheck.cpp | 129 |
1 files changed, 129 insertions, 0 deletions
diff --git a/clang-tools-extra/clang-tidy/bugprone/StdNamespaceModificationCheck.cpp b/clang-tools-extra/clang-tidy/bugprone/StdNamespaceModificationCheck.cpp new file mode 100644 index 0000000..13e5c03 --- /dev/null +++ b/clang-tools-extra/clang-tidy/bugprone/StdNamespaceModificationCheck.cpp @@ -0,0 +1,129 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#include "StdNamespaceModificationCheck.h" +#include "clang/ASTMatchers/ASTMatchFinder.h" +#include "clang/ASTMatchers/ASTMatchersInternal.h" + +using namespace clang; +using namespace clang::ast_matchers; + +namespace { + +AST_POLYMORPHIC_MATCHER_P( + hasAnyTemplateArgumentIncludingPack, + AST_POLYMORPHIC_SUPPORTED_TYPES(ClassTemplateSpecializationDecl, + TemplateSpecializationType, FunctionDecl), + clang::ast_matchers::internal::Matcher<TemplateArgument>, InnerMatcher) { + ArrayRef<TemplateArgument> Args = + clang::ast_matchers::internal::getTemplateSpecializationArgs(Node); + for (const auto &Arg : Args) { + if (Arg.getKind() != TemplateArgument::Pack) + continue; + ArrayRef<TemplateArgument> PackArgs = Arg.getPackAsArray(); + if (matchesFirstInRange(InnerMatcher, PackArgs.begin(), PackArgs.end(), + Finder, Builder) != PackArgs.end()) + return true; + } + return matchesFirstInRange(InnerMatcher, Args.begin(), Args.end(), Finder, + Builder) != Args.end(); +} + +} // namespace + +namespace clang::tidy::bugprone { + +void StdNamespaceModificationCheck::registerMatchers(MatchFinder *Finder) { + auto HasStdParent = + hasDeclContext(namespaceDecl(hasAnyName("std", "posix"), + unless(hasParent(namespaceDecl()))) + .bind("nmspc")); + auto UserDefinedType = qualType( + hasUnqualifiedDesugaredType(tagType(unless(hasDeclaration(tagDecl( + hasAncestor(namespaceDecl(hasAnyName("std", "posix"), + unless(hasParent(namespaceDecl())))))))))); + auto HasNoProgramDefinedTemplateArgument = unless( + hasAnyTemplateArgumentIncludingPack(refersToType(UserDefinedType))); + auto InsideStdClassOrClassTemplateSpecialization = hasDeclContext( + anyOf(cxxRecordDecl(HasStdParent), + classTemplateSpecializationDecl( + HasStdParent, HasNoProgramDefinedTemplateArgument))); + + // Try to follow exactly CERT rule DCL58-CPP (this text is taken from C++ + // standard into the CERT rule): + // " + // 1 The behavior of a C++ program is undefined if it adds declarations or + // definitions to namespace std or to a namespace within namespace std unless + // otherwise specified. A program may add a template specialization for any + // standard library template to namespace std only if the declaration depends + // on a user-defined type and the specialization meets the standard library + // requirements for the original template and is not explicitly prohibited. 2 + // The behavior of a C++ program is undefined if it declares — an explicit + // specialization of any member function of a standard library class template, + // or — an explicit specialization of any member function template of a + // standard library class or class template, or — an explicit or partial + // specialization of any member class template of a standard library class or + // class template. + // " + // The "standard library requirements" and explicit prohibition are not + // checked. + + auto BadNonTemplateSpecializationDecl = + decl(unless(anyOf(functionDecl(isExplicitTemplateSpecialization()), + varDecl(isExplicitTemplateSpecialization()), + cxxRecordDecl(isExplicitTemplateSpecialization()))), + HasStdParent); + auto BadClassTemplateSpec = classTemplateSpecializationDecl( + HasNoProgramDefinedTemplateArgument, HasStdParent); + auto BadInnerClassTemplateSpec = classTemplateSpecializationDecl( + InsideStdClassOrClassTemplateSpecialization); + auto BadFunctionTemplateSpec = + functionDecl(unless(cxxMethodDecl()), isExplicitTemplateSpecialization(), + HasNoProgramDefinedTemplateArgument, HasStdParent); + auto BadMemberFunctionSpec = + cxxMethodDecl(isExplicitTemplateSpecialization(), + InsideStdClassOrClassTemplateSpecialization); + + Finder->addMatcher(decl(anyOf(BadNonTemplateSpecializationDecl, + BadClassTemplateSpec, BadInnerClassTemplateSpec, + BadFunctionTemplateSpec, BadMemberFunctionSpec), + unless(isExpansionInSystemHeader())) + .bind("decl"), + this); +} +} // namespace clang::tidy::bugprone + +static const NamespaceDecl *getTopLevelLexicalNamespaceDecl(const Decl *D) { + const NamespaceDecl *LastNS = nullptr; + while (D) { + if (const auto *NS = dyn_cast<NamespaceDecl>(D)) + LastNS = NS; + D = dyn_cast_or_null<Decl>(D->getLexicalDeclContext()); + } + return LastNS; +} + +void clang::tidy::bugprone::StdNamespaceModificationCheck::check( + const MatchFinder::MatchResult &Result) { + const auto *D = Result.Nodes.getNodeAs<Decl>("decl"); + const auto *NS = Result.Nodes.getNodeAs<NamespaceDecl>("nmspc"); + if (!D || !NS) + return; + + diag(D->getLocation(), + "modification of %0 namespace can result in undefined behavior") + << NS; + // 'NS' is not always the namespace declaration that lexically contains 'D', + // try to find such a namespace. + if (const NamespaceDecl *LexNS = getTopLevelLexicalNamespaceDecl(D)) { + assert(NS->getCanonicalDecl() == LexNS->getCanonicalDecl() && + "Mismatch in found namespace"); + diag(LexNS->getLocation(), "%0 namespace opened here", DiagnosticIDs::Note) + << LexNS; + } +} |
