From 4432bf007abb40cdbe2679dd94ec5b11c5795355 Mon Sep 17 00:00:00 2001 From: Kaelyn Uhrain Date: Mon, 12 Aug 2013 19:57:06 +0000 Subject: Forgot to add unittests/Sema/ before committing r188196 :( git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@188197 91177308-0d34-0410-b5e6-96231b3b80d8 --- unittests/Sema/CMakeLists.txt | 7 + unittests/Sema/ExternalSemaSourceTest.cpp | 214 ++++++++++++++++++++++++++++++ unittests/Sema/Makefile | 19 +++ 3 files changed, 240 insertions(+) create mode 100644 unittests/Sema/CMakeLists.txt create mode 100644 unittests/Sema/ExternalSemaSourceTest.cpp create mode 100644 unittests/Sema/Makefile diff --git a/unittests/Sema/CMakeLists.txt b/unittests/Sema/CMakeLists.txt new file mode 100644 index 0000000000..d491655d41 --- /dev/null +++ b/unittests/Sema/CMakeLists.txt @@ -0,0 +1,7 @@ +add_clang_unittest(SemaTests + ExternalSemaSourceTest.cpp + ) + +target_link_libraries(SemaTests + clangAST clangASTMatchers clangTooling + ) diff --git a/unittests/Sema/ExternalSemaSourceTest.cpp b/unittests/Sema/ExternalSemaSourceTest.cpp new file mode 100644 index 0000000000..8aa626c2df --- /dev/null +++ b/unittests/Sema/ExternalSemaSourceTest.cpp @@ -0,0 +1,214 @@ +//=== unittests/Sema/ExternalSemaSourceTest.cpp - ExternalSemaSource tests ===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// + +#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 err_using_directive_member_suggest diagnostics +// correcting from one namespace to another while still passing all diagnostics +// along a chain of consumers. +class NamespaceDiagnosticWatcher : public clang::DiagnosticConsumer { + DiagnosticConsumer *Chained; + std::string FromNS; + std::string ToNS; + +public: + NamespaceDiagnosticWatcher(StringRef From, StringRef To) + : Chained(NULL), FromNS(From), ToNS("'"), SeenCount(0) { + ToNS.append(To); + ToNS.append("'"); + } + + virtual void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel, + const Diagnostic &Info) { + 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() == FromNS && CorrectedQuotedStr == ToNS) + ++SeenCount; + } + } + + virtual void clear() { + DiagnosticConsumer::clear(); + if (Chained) + Chained->clear(); + } + + virtual bool IncludeInDiagnosticCounts() const { + if (Chained) + return Chained->IncludeInDiagnosticCounts(); + return false; + } + + NamespaceDiagnosticWatcher *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(NULL), CallCount(0) {} + + virtual void InitializeSema(Sema &S) { CurrentSema = &S; } + + virtual void ForgetSema() { CurrentSema = NULL; } + + virtual TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, + int LookupKind, Scope *S, CXXScopeSpec *SS, + CorrectionCandidateCallback &CCC, + DeclContext *MemberContext, + bool EnteringContext, + const ObjCObjectPointerType *OPT) { + ++CallCount; + if (CurrentSema && Typo.getName().getAsString() == CorrectFrom) { + DeclContext *DestContext = NULL; + ASTContext &Context = CurrentSema->getASTContext(); + if (SS != NULL) + DestContext = CurrentSema->computeDeclContext(*SS, EnteringContext); + if (DestContext == NULL) + DestContext = Context.getTranslationUnitDecl(); + IdentifierInfo *ToIdent = + CurrentSema->getPreprocessor().getIdentifierInfo(CorrectTo); + NamespaceDecl *NewNamespace = + NamespaceDecl::Create(Context, DestContext, false, Typo.getBeginLoc(), + Typo.getLoc(), ToIdent, NULL); + DestContext->addDecl(NewNamespace); + TypoCorrection Correction(ToIdent); + Correction.addCorrectionDecl(NewNamespace); + return Correction; + } + return TypoCorrection(); + } + + int CallCount; +}; + +// \brief Chains together a vector of NamespaceDiagnosticWatchers and +// adds a vector of ExternalSemaSources to the CompilerInstance before +// performing semantic analysis. +class ExternalSemaSourceInstaller : public clang::ASTFrontendAction { + std::vector Watchers; + std::vector Sources; + llvm::OwningPtr OwnedClient; + +protected: + virtual clang::ASTConsumer * + CreateASTConsumer(clang::CompilerInstance &Compiler, + llvm::StringRef /* dummy */) { + return new clang::ASTConsumer(); + } + + virtual void ExecuteAction() { + CompilerInstance &CI = getCompilerInstance(); + ASSERT_FALSE(CI.hasSema()); + CI.createSema(getTranslationUnitKind(), NULL); + ASSERT_TRUE(CI.hasDiagnostics()); + DiagnosticsEngine &Diagnostics = CI.getDiagnostics(); + DiagnosticConsumer *Client = Diagnostics.getClient(); + if (Diagnostics.ownsClient()) + OwnedClient.reset(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(NamespaceDiagnosticWatcher *Watcher) { + Watchers.push_back(Watcher); + } +}; + +// Make sure that the NamespaceDiagnosticWatcher is not miscounting. +TEST(ExternalSemaSource, SanityCheck) { + llvm::OwningPtr Installer( + new ExternalSemaSourceInstaller); + NamespaceDiagnosticWatcher Watcher("AAB", "BBB"); + Installer->PushWatcher(&Watcher); + std::vector Args(1, "-std=c++11"); + ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs( + Installer.take(), "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) { + llvm::OwningPtr Installer( + new ExternalSemaSourceInstaller); + NamespaceTypoProvider Provider("AAB", "BBB"); + NamespaceDiagnosticWatcher Watcher("AAB", "BBB"); + Installer->PushSource(&Provider); + Installer->PushWatcher(&Watcher); + std::vector Args(1, "-std=c++11"); + ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs( + Installer.take(), "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) { + llvm::OwningPtr Installer( + new ExternalSemaSourceInstaller); + NamespaceTypoProvider First("XXX", "BBB"); + NamespaceTypoProvider Second("AAB", "CCC"); + NamespaceTypoProvider Third("AAB", "DDD"); + NamespaceDiagnosticWatcher Watcher("AAB", "CCC"); + Installer->PushSource(&First); + Installer->PushSource(&Second); + Installer->PushSource(&Third); + Installer->PushWatcher(&Watcher); + std::vector Args(1, "-std=c++11"); + ASSERT_TRUE(clang::tooling::runToolOnCodeWithArgs( + Installer.take(), "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); +} + +} // anonymous namespace diff --git a/unittests/Sema/Makefile b/unittests/Sema/Makefile new file mode 100644 index 0000000000..cd1d93df5b --- /dev/null +++ b/unittests/Sema/Makefile @@ -0,0 +1,19 @@ +##===- unittests/Sema/Makefile -----------------------------*- Makefile -*-===## +# +# The LLVM Compiler Infrastructure +# +# This file is distributed under the University of Illinois Open Source +# License. See LICENSE.TXT for details. +# +##===----------------------------------------------------------------------===## + +CLANG_LEVEL = ../.. +TESTNAME = Sema +include $(CLANG_LEVEL)/../../Makefile.config +LINK_COMPONENTS := $(TARGETS_TO_BUILD) asmparser bitreader support mc option +USEDLIBS = clangTooling.a clangFrontend.a clangSerialization.a clangDriver.a \ + clangRewriteCore.a clangRewriteFrontend.a \ + clangParse.a clangSema.a clangAnalysis.a \ + clangEdit.a clangAST.a clangASTMatchers.a clangLex.a clangBasic.a + +include $(CLANG_LEVEL)/unittests/Makefile -- cgit v1.2.3