summaryrefslogtreecommitdiffstats
path: root/gnu/llvm/clang/unittests/Sema/ExternalSemaSourceTest.cpp
diff options
context:
space:
mode:
authorpatrick <patrick@openbsd.org>2020-08-03 14:31:31 +0000
committerpatrick <patrick@openbsd.org>2020-08-03 14:31:31 +0000
commite5dd70708596ae51455a0ffa086a00c5b29f8583 (patch)
tree5d676f27b570bacf71e786c3b5cff3e6f6679b59 /gnu/llvm/clang/unittests/Sema/ExternalSemaSourceTest.cpp
parentImport LLVM 10.0.0 release including clang, lld and lldb. (diff)
downloadwireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.tar.xz
wireguard-openbsd-e5dd70708596ae51455a0ffa086a00c5b29f8583.zip
Import LLVM 10.0.0 release including clang, lld and lldb.
ok hackroom tested by plenty
Diffstat (limited to 'gnu/llvm/clang/unittests/Sema/ExternalSemaSourceTest.cpp')
-rw-r--r--gnu/llvm/clang/unittests/Sema/ExternalSemaSourceTest.cpp317
1 files changed, 317 insertions, 0 deletions
diff --git a/gnu/llvm/clang/unittests/Sema/ExternalSemaSourceTest.cpp b/gnu/llvm/clang/unittests/Sema/ExternalSemaSourceTest.cpp
new file mode 100644
index 00000000000..44006e349cb
--- /dev/null
+++ b/gnu/llvm/clang/unittests/Sema/ExternalSemaSourceTest.cpp
@@ -0,0 +1,317 @@
+//=== unittests/Sema/ExternalSemaSourceTest.cpp - ExternalSemaSource tests ===//
+//
+// 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 "clang/AST/ASTConsumer.h"
+#include "clang/AST/ASTContext.h"
+#include "clang/Frontend/CompilerInstance.h"
+#include "clang/Lex/Preprocessor.h"
+#include "clang/Parse/ParseAST.h"
+#include "clang/Sema/ExternalSemaSource.h"
+#include "clang/Sema/Sema.h"
+#include "clang/Sema/SemaDiagnostic.h"
+#include "clang/Sema/TypoCorrection.h"
+#include "clang/Tooling/Tooling.h"
+#include "gtest/gtest.h"
+
+using namespace clang;
+using namespace clang::tooling;
+
+namespace {
+
+// \brief Counts the number of times MaybeDiagnoseMissingCompleteType
+// is called. Returns the result it was provided on creation.
+class CompleteTypeDiagnoser : public clang::ExternalSemaSource {
+public:
+ CompleteTypeDiagnoser(bool MockResult) : CallCount(0), Result(MockResult) {}
+
+ bool MaybeDiagnoseMissingCompleteType(SourceLocation L, QualType T) override {
+ ++CallCount;
+ return Result;
+ }
+
+ int CallCount;
+ bool Result;
+};
+
+/// Counts the number of typo-correcting diagnostics correcting from one name to
+/// another while still passing all diagnostics along a chain of consumers.
+class DiagnosticWatcher : public clang::DiagnosticConsumer {
+ DiagnosticConsumer *Chained;
+ std::string FromName;
+ std::string ToName;
+
+public:
+ DiagnosticWatcher(StringRef From, StringRef To)
+ : Chained(nullptr), FromName(From), ToName("'"), SeenCount(0) {
+ ToName.append(To);
+ ToName.append("'");
+ }
+
+ void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+ const Diagnostic &Info) override {
+ if (Chained)
+ Chained->HandleDiagnostic(DiagLevel, Info);
+ if (Info.getID() - 1 == diag::err_using_directive_member_suggest) {
+ const IdentifierInfo *Ident = Info.getArgIdentifier(0);
+ const std::string &CorrectedQuotedStr = Info.getArgStdStr(1);
+ if (Ident->getName() == FromName && CorrectedQuotedStr == ToName)
+ ++SeenCount;
+ } else if (Info.getID() == diag::err_no_member_suggest) {
+ auto Ident = DeclarationName::getFromOpaqueInteger(Info.getRawArg(0));
+ const std::string &CorrectedQuotedStr = Info.getArgStdStr(3);
+ if (Ident.getAsString() == FromName && CorrectedQuotedStr == ToName)
+ ++SeenCount;
+ }
+ }
+
+ void clear() override {
+ DiagnosticConsumer::clear();
+ if (Chained)
+ Chained->clear();
+ }
+
+ bool IncludeInDiagnosticCounts() const override {
+ if (Chained)
+ return Chained->IncludeInDiagnosticCounts();
+ return false;
+ }
+
+ DiagnosticWatcher *Chain(DiagnosticConsumer *ToChain) {
+ Chained = ToChain;
+ return this;
+ }
+
+ int SeenCount;
+};
+
+// \brief Always corrects a typo matching CorrectFrom with a new namespace
+// with the name CorrectTo.
+class NamespaceTypoProvider : public clang::ExternalSemaSource {
+ std::string CorrectFrom;
+ std::string CorrectTo;
+ Sema *CurrentSema;
+
+public:
+ NamespaceTypoProvider(StringRef From, StringRef To)
+ : CorrectFrom(From), CorrectTo(To), CurrentSema(nullptr), CallCount(0) {}
+
+ void InitializeSema(Sema &S) override { CurrentSema = &S; }
+
+ void ForgetSema() override { CurrentSema = nullptr; }
+
+ TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
+ Scope *S, CXXScopeSpec *SS,
+ CorrectionCandidateCallback &CCC,
+ DeclContext *MemberContext, bool EnteringContext,
+ const ObjCObjectPointerType *OPT) override {
+ ++CallCount;
+ if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {
+ DeclContext *DestContext = nullptr;
+ ASTContext &Context = CurrentSema->getASTContext();
+ if (SS)
+ DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);
+ if (!DestContext)
+ DestContext = Context.getTranslationUnitDecl();
+ IdentifierInfo *ToIdent =
+ CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);
+ NamespaceDecl *NewNamespace =
+ NamespaceDecl::Create(Context, DestContext, false, Typo.getBeginLoc(),
+ Typo.getLoc(), ToIdent, nullptr);
+ DestContext->addDecl(NewNamespace);
+ TypoCorrection Correction(ToIdent);
+ Correction.addCorrectionDecl(NewNamespace);
+ return Correction;
+ }
+ return TypoCorrection();
+ }
+
+ int CallCount;
+};
+
+class FunctionTypoProvider : public clang::ExternalSemaSource {
+ std::string CorrectFrom;
+ std::string CorrectTo;
+ Sema *CurrentSema;
+
+public:
+ FunctionTypoProvider(StringRef From, StringRef To)
+ : CorrectFrom(From), CorrectTo(To), CurrentSema(nullptr), CallCount(0) {}
+
+ void InitializeSema(Sema &S) override { CurrentSema = &S; }
+
+ void ForgetSema() override { CurrentSema = nullptr; }
+
+ TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
+ Scope *S, CXXScopeSpec *SS,
+ CorrectionCandidateCallback &CCC,
+ DeclContext *MemberContext, bool EnteringContext,
+ const ObjCObjectPointerType *OPT) override {
+ ++CallCount;
+ if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) {
+ DeclContext *DestContext = nullptr;
+ ASTContext &Context = CurrentSema->getASTContext();
+ if (SS)
+ DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext);
+ if (!DestContext)
+ DestContext = Context.getTranslationUnitDecl();
+ IdentifierInfo *ToIdent =
+ CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo);
+ auto *NewFunction = FunctionDecl::Create(
+ Context, DestContext, SourceLocation(), SourceLocation(), ToIdent,
+ Context.getFunctionType(Context.VoidTy, {}, {}), nullptr, SC_Static);
+ DestContext->addDecl(NewFunction);
+ TypoCorrection Correction(ToIdent);
+ Correction.addCorrectionDecl(NewFunction);
+ return Correction;
+ }
+ return TypoCorrection();
+ }
+
+ int CallCount;
+};
+
+// \brief Chains together a vector of DiagnosticWatchers and
+// adds a vector of ExternalSemaSources to the CompilerInstance before
+// performing semantic analysis.
+class ExternalSemaSourceInstaller : public clang::ASTFrontendAction {
+ std::vector<DiagnosticWatcher *> Watchers;
+ std::vector<clang::ExternalSemaSource *> Sources;
+ std::unique_ptr<DiagnosticConsumer> OwnedClient;
+
+protected:
+ std::unique_ptr<clang::ASTConsumer>
+ CreateASTConsumer(clang::CompilerInstance &Compiler,
+ llvm::StringRef /* dummy */) override {
+ return std::make_unique<clang::ASTConsumer>();
+ }
+
+ void ExecuteAction() override {
+ CompilerInstance &CI = getCompilerInstance();
+ ASSERT_FALSE(CI.hasSema());
+ CI.createSema(getTranslationUnitKind(), nullptr);
+ ASSERT_TRUE(CI.hasDiagnostics());
+ DiagnosticsEngine &Diagnostics = CI.getDiagnostics();
+ DiagnosticConsumer *Client = Diagnostics.getClient();
+ if (Diagnostics.ownsClient())
+ OwnedClient = Diagnostics.takeClient();
+ for (size_t I = 0, E = Watchers.size(); I < E; ++I)
+ Client = Watchers[I]->Chain(Client);
+ Diagnostics.setClient(Client, false);
+ for (size_t I = 0, E = Sources.size(); I < E; ++I) {
+ Sources[I]->InitializeSema(CI.getSema());
+ CI.getSema().addExternalSource(Sources[I]);
+ }
+ ParseAST(CI.getSema(), CI.getFrontendOpts().ShowStats,
+ CI.getFrontendOpts().SkipFunctionBodies);
+ }
+
+public:
+ void PushSource(clang::ExternalSemaSource *Source) {
+ Sources.push_back(Source);
+ }
+
+ void PushWatcher(DiagnosticWatcher *Watcher) { Watchers.push_back(Watcher); }
+};
+
+// Make sure that the DiagnosticWatcher is not miscounting.
+TEST(ExternalSemaSource, SanityCheck) {
+ auto Installer = std::make_unique<ExternalSemaSourceInstaller>();
+ DiagnosticWatcher Watcher("AAB", "BBB");
+ Installer->PushWatcher(&Watcher);
+ std::vector<std::string> Args(1, "-std=c++11");
+ ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
+ std::move(Installer), "namespace AAA { } using namespace AAB;", Args));
+ ASSERT_EQ(0, Watcher.SeenCount);
+}
+
+// Check that when we add a NamespaceTypeProvider, we use that suggestion
+// instead of the usual suggestion we would use above.
+TEST(ExternalSemaSource, ExternalTypoCorrectionPrioritized) {
+ auto Installer = std::make_unique<ExternalSemaSourceInstaller>();
+ NamespaceTypoProvider Provider("AAB", "BBB");
+ DiagnosticWatcher Watcher("AAB", "BBB");
+ Installer->PushSource(&Provider);
+ Installer->PushWatcher(&Watcher);
+ std::vector<std::string> Args(1, "-std=c++11");
+ ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
+ std::move(Installer), "namespace AAA { } using namespace AAB;", Args));
+ ASSERT_LE(0, Provider.CallCount);
+ ASSERT_EQ(1, Watcher.SeenCount);
+}
+
+// Check that we use the first successful TypoCorrection returned from an
+// ExternalSemaSource.
+TEST(ExternalSemaSource, ExternalTypoCorrectionOrdering) {
+ auto Installer = std::make_unique<ExternalSemaSourceInstaller>();
+ NamespaceTypoProvider First("XXX", "BBB");
+ NamespaceTypoProvider Second("AAB", "CCC");
+ NamespaceTypoProvider Third("AAB", "DDD");
+ DiagnosticWatcher Watcher("AAB", "CCC");
+ Installer->PushSource(&First);
+ Installer->PushSource(&Second);
+ Installer->PushSource(&Third);
+ Installer->PushWatcher(&Watcher);
+ std::vector<std::string> Args(1, "-std=c++11");
+ ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
+ std::move(Installer), "namespace AAA { } using namespace AAB;", Args));
+ ASSERT_LE(1, First.CallCount);
+ ASSERT_LE(1, Second.CallCount);
+ ASSERT_EQ(0, Third.CallCount);
+ ASSERT_EQ(1, Watcher.SeenCount);
+}
+
+TEST(ExternalSemaSource, ExternalDelayedTypoCorrection) {
+ auto Installer = std::make_unique<ExternalSemaSourceInstaller>();
+ FunctionTypoProvider Provider("aaa", "bbb");
+ DiagnosticWatcher Watcher("aaa", "bbb");
+ Installer->PushSource(&Provider);
+ Installer->PushWatcher(&Watcher);
+ std::vector<std::string> Args(1, "-std=c++11");
+ ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
+ std::move(Installer), "namespace AAA { } void foo() { AAA::aaa(); }",
+ Args));
+ ASSERT_LE(0, Provider.CallCount);
+ ASSERT_EQ(1, Watcher.SeenCount);
+}
+
+// We should only try MaybeDiagnoseMissingCompleteType if we can't otherwise
+// solve the problem.
+TEST(ExternalSemaSource, TryOtherTacticsBeforeDiagnosing) {
+ auto Installer = std::make_unique<ExternalSemaSourceInstaller>();
+ CompleteTypeDiagnoser Diagnoser(false);
+ Installer->PushSource(&Diagnoser);
+ std::vector<std::string> Args(1, "-std=c++11");
+ // This code hits the class template specialization/class member of a class
+ // template specialization checks in Sema::RequireCompleteTypeImpl.
+ ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs(
+ std::move(Installer),
+ "template <typename T> struct S { class C { }; }; S<char>::C SCInst;",
+ Args));
+ ASSERT_EQ(0, Diagnoser.CallCount);
+}
+
+// The first ExternalSemaSource where MaybeDiagnoseMissingCompleteType returns
+// true should be the last one called.
+TEST(ExternalSemaSource, FirstDiagnoserTaken) {
+ auto Installer = std::make_unique<ExternalSemaSourceInstaller>();
+ CompleteTypeDiagnoser First(false);
+ CompleteTypeDiagnoser Second(true);
+ CompleteTypeDiagnoser Third(true);
+ Installer->PushSource(&First);
+ Installer->PushSource(&Second);
+ Installer->PushSource(&Third);
+ std::vector<std::string> Args(1, "-std=c++11");
+ ASSERT_FALSE(clang::tooling::runToolOnCodeWithArgs(
+ std::move(Installer), "class Incomplete; Incomplete IncompleteInstance;",
+ Args));
+ ASSERT_EQ(1, First.CallCount);
+ ASSERT_EQ(1, Second.CallCount);
+ ASSERT_EQ(0, Third.CallCount);
+}
+
+} // anonymous namespace