Ping/making sure that this still applies cleanly.

  To clarify, the difference between this and typo correction into PCH is that 
the calls for the latter are designed for a closed context ('fill a vector with 
all the namespaces you know about and return it to me'), whereas the former is 
designed for an open context ('I'll give you a qualified name and you tell me 
if it's something that exists').

Hi jdennett,

http://llvm-reviews.chandlerc.com/D1211

CHANGE SINCE LAST DIFF
  http://llvm-reviews.chandlerc.com/D1211?vs=3003&id=3083#toc

Files:
  include/clang/Sema/ExternalSemaSource.h
  include/clang/Sema/MultiplexExternalSemaSource.h
  lib/Sema/MultiplexExternalSemaSource.cpp
  lib/Sema/SemaLookup.cpp
  unittests/CMakeLists.txt
  unittests/Makefile
  unittests/Sema/CMakeLists.txt
  unittests/Sema/ExternalSemaSourceTest.cpp
  unittests/Sema/Makefile
Index: include/clang/Sema/ExternalSemaSource.h
===================================================================
--- include/clang/Sema/ExternalSemaSource.h
+++ include/clang/Sema/ExternalSemaSource.h
@@ -14,6 +14,7 @@
 #define LLVM_CLANG_SEMA_EXTERNAL_SEMA_SOURCE_H
 
 #include "clang/AST/ExternalASTSource.h"
+#include "clang/Sema/TypoCorrection.h"
 #include "clang/Sema/Weak.h"
 #include "llvm/ADT/MapVector.h"
 #include <utility>
@@ -177,6 +178,22 @@
                  SmallVectorImpl<std::pair<ValueDecl *, 
                                            SourceLocation> > &Pending) {}
 
+  /// \copydoc Sema::CorrectTypo
+  /// \note LookupKind must correspond to a valid Sema::LookupNameKind
+  ///
+  /// ExternalSemaSource::CorrectTypo is always given the first chance to
+  /// correct a typo (really, to offer suggestions to repair a failed lookup).
+  /// It will even be called when SpellChecking is turned off or after a
+  /// fatal error has already been detected.
+  virtual TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,
+                                     int LookupKind, Scope *S, CXXScopeSpec *SS,
+                                     CorrectionCandidateCallback &CCC,
+                                     DeclContext *MemberContext,
+                                     bool EnteringContext,
+                                     const ObjCObjectPointerType *OPT) {
+    return TypoCorrection();
+  }
+
   // isa/cast/dyn_cast support
   static bool classof(const ExternalASTSource *Source) {
     return Source->SemaSource;
Index: include/clang/Sema/MultiplexExternalSemaSource.h
===================================================================
--- include/clang/Sema/MultiplexExternalSemaSource.h
+++ include/clang/Sema/MultiplexExternalSemaSource.h
@@ -322,6 +322,15 @@
   virtual void ReadPendingInstantiations(
               SmallVectorImpl<std::pair<ValueDecl*, SourceLocation> >& Pending);
 
+  /// \copydoc ExternalSemaSource::CorrectTypo
+  /// \note Returns the first nonempty correction.
+  virtual TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo,
+                                     int LookupKind, Scope *S, CXXScopeSpec *SS,
+                                     CorrectionCandidateCallback &CCC,
+                                     DeclContext *MemberContext,
+                                     bool EnteringContext,
+                                     const ObjCObjectPointerType *OPT);
+
   // isa/cast/dyn_cast support
   static bool classof(const MultiplexExternalSemaSource*) { return true; }
   //static bool classof(const ExternalSemaSource*) { return true; }
Index: lib/Sema/MultiplexExternalSemaSource.cpp
===================================================================
--- lib/Sema/MultiplexExternalSemaSource.cpp
+++ lib/Sema/MultiplexExternalSemaSource.cpp
@@ -267,3 +267,19 @@
   for(size_t i = 0; i < Sources.size(); ++i)
     Sources[i]->ReadPendingInstantiations(Pending);
 }
+
+TypoCorrection MultiplexExternalSemaSource::CorrectTypo(
+                                     const DeclarationNameInfo &Typo,
+                                     int LookupKind, Scope *S, CXXScopeSpec *SS,
+                                     CorrectionCandidateCallback &CCC,
+                                     DeclContext *MemberContext,
+                                     bool EnteringContext,
+                                     const ObjCObjectPointerType *OPT) {
+  for (size_t I = 0, E = Sources.size(); I < E; ++I) {
+    if (TypoCorrection C = Sources[I]->CorrectTypo(Typo, LookupKind, S, SS, CCC,
+                                                   MemberContext,
+                                                   EnteringContext, OPT))
+      return C;
+  }
+  return TypoCorrection();
+}
Index: lib/Sema/SemaLookup.cpp
===================================================================
--- lib/Sema/SemaLookup.cpp
+++ lib/Sema/SemaLookup.cpp
@@ -3863,6 +3863,14 @@
                                  DeclContext *MemberContext,
                                  bool EnteringContext,
                                  const ObjCObjectPointerType *OPT) {
+  // Always let the ExternalSource have the first chance at correction, even
+  // if we would otherwise have given up.
+  if (ExternalSource) {
+    if (TypoCorrection Correction = ExternalSource->CorrectTypo(
+        TypoName, LookupKind, S, SS, CCC, MemberContext, EnteringContext, OPT))
+      return Correction;
+  }
+
   if (Diags.hasFatalErrorOccurred() || !getLangOpts().SpellChecking)
     return TypoCorrection();
 
Index: unittests/CMakeLists.txt
===================================================================
--- unittests/CMakeLists.txt
+++ unittests/CMakeLists.txt
@@ -19,4 +19,5 @@
   add_subdirectory(AST)
   add_subdirectory(Tooling)
   add_subdirectory(Format)
+  add_subdirectory(Sema)
 endif()
Index: unittests/Makefile
===================================================================
--- unittests/Makefile
+++ unittests/Makefile
@@ -23,7 +23,7 @@
 endif
 
 ifeq ($(ENABLE_CLANG_REWRITER),1)
-PARALLEL_DIRS += ASTMatchers AST Tooling
+PARALLEL_DIRS += ASTMatchers AST Tooling Sema
 endif
 
 ifeq ($(ENABLE_CLANG_STATIC_ANALYZER),1)
Index: unittests/Sema/CMakeLists.txt
===================================================================
--- /dev/null
+++ unittests/Sema/CMakeLists.txt
@@ -0,0 +1,7 @@
+add_clang_unittest(SemaTests
+  ExternalSemaSourceTest.cpp
+  )
+
+target_link_libraries(SemaTests
+  clangAST clangASTMatchers clangTooling
+  )
Index: unittests/Sema/ExternalSemaSourceTest.cpp
===================================================================
--- /dev/null
+++ 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<NamespaceDiagnosticWatcher *> Watchers;
+  std::vector<clang::ExternalSemaSource *> Sources;
+  llvm::OwningPtr<DiagnosticConsumer> 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<ExternalSemaSourceInstaller> Installer(
+      new ExternalSemaSourceInstaller);
+  NamespaceDiagnosticWatcher Watcher("AAB", "BBB");
+  Installer->PushWatcher(&Watcher);
+  std::vector<std::string> 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<ExternalSemaSourceInstaller> Installer(
+      new ExternalSemaSourceInstaller);
+  NamespaceTypoProvider Provider("AAB", "BBB");
+  NamespaceDiagnosticWatcher Watcher("AAB", "BBB");
+  Installer->PushSource(&Provider);
+  Installer->PushWatcher(&Watcher);
+  std::vector<std::string> 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<ExternalSemaSourceInstaller> 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<std::string> 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
Index: unittests/Sema/Makefile
===================================================================
--- /dev/null
+++ 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
_______________________________________________
cfe-commits mailing list
[email protected]
http://lists.cs.uiuc.edu/mailman/listinfo/cfe-commits

Reply via email to