zarko added you to the CC list for the revision "Allow ExternalSemaSource to
participate in typo correction".
Hi jdennett,
This patch allows ExternalSemaSource to participate in typo correction. This is
useful for providing functionality that performs open-ended searches for
missing identifiers. For example, an ExternalSemaSource can be defined that
scans a ctags files; if it encounters an identifier in a header file (of the
correct LookupKind), it can produce a diagnostic that suggests the inclusion of
that header.
http://llvm-reviews.chandlerc.com/D1211
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
@@ -3787,6 +3787,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