bkramer created this revision.
bkramer added a reviewer: klimek.
bkramer added a subscriber: cfe-commits.

This can be used to append alternative typo corrections to an existing diag.
include-fixer can use it to suggest includes to be added.


https://reviews.llvm.org/D26745

Files:
  include/clang/Frontend/CompilerInstance.h
  include/clang/Sema/TypoCorrection.h
  lib/Sema/SemaLookup.cpp
  unittests/Frontend/FrontendActionTest.cpp

Index: unittests/Frontend/FrontendActionTest.cpp
===================================================================
--- unittests/Frontend/FrontendActionTest.cpp
+++ unittests/Frontend/FrontendActionTest.cpp
@@ -13,6 +13,7 @@
 #include "clang/Frontend/CompilerInstance.h"
 #include "clang/Frontend/CompilerInvocation.h"
 #include "clang/Frontend/FrontendAction.h"
+#include "clang/Frontend/FrontendActions.h"
 #include "clang/Lex/Preprocessor.h"
 #include "clang/Lex/PreprocessorOptions.h"
 #include "clang/Sema/Sema.h"
@@ -192,4 +193,66 @@
   ASSERT_TRUE(TestAction.SeenEnd);
 }
 
+class TypoExternalSemaSource : public ExternalSemaSource {
+  CompilerInstance &CI;
+
+public:
+  TypoExternalSemaSource(CompilerInstance &CI) : CI(CI) {}
+
+  TypoCorrection CorrectTypo(const DeclarationNameInfo &Typo, int LookupKind,
+                             Scope *S, CXXScopeSpec *SS,
+                             CorrectionCandidateCallback &CCC,
+                             DeclContext *MemberContext, bool EnteringContext,
+                             const ObjCObjectPointerType *OPT) override {
+    // Generate a fake typo correction with one attached note.
+    ASTContext &Ctx = CI.getASTContext();
+    TypoCorrection TC(DeclarationName(&Ctx.Idents.get("moo")));
+    unsigned DiagID = Ctx.getDiagnostics().getCustomDiagID(
+        DiagnosticsEngine::Note, "This is a note");
+    TC.addExtraDiagnostic(PartialDiagnostic(DiagID, Ctx.getDiagAllocator()));
+    return TC;
+  }
+};
+
+struct TypoDiagnosticConsumer : public DiagnosticConsumer {
+  void HandleDiagnostic(DiagnosticsEngine::Level DiagLevel,
+                        const Diagnostic &Info) override {
+    // Capture errors and notes. There should be one of each.
+    if (DiagLevel == DiagnosticsEngine::Error) {
+      assert(Error.empty());
+      Info.FormatDiagnostic(Error);
+    } else {
+      assert(Note.empty());
+      Info.FormatDiagnostic(Note);
+    }
+  }
+  SmallString<32> Error;
+  SmallString<32> Note;
+};
+
+TEST(ASTFrontendAction, ExternalSemaSource) {
+  auto *Invocation = new CompilerInvocation;
+  Invocation->getLangOpts()->CPlusPlus = true;
+  Invocation->getPreprocessorOpts().addRemappedFile(
+      "test.cc", MemoryBuffer::getMemBuffer("void fooo();\n"
+                                            "int main() { foo(); }")
+                     .release());
+  Invocation->getFrontendOpts().Inputs.push_back(
+      FrontendInputFile("test.cc", IK_CXX));
+  Invocation->getFrontendOpts().ProgramAction = frontend::ParseSyntaxOnly;
+  Invocation->getTargetOpts().Triple = "i386-unknown-linux-gnu";
+  CompilerInstance Compiler;
+  Compiler.setInvocation(Invocation);
+  auto *TDC = new TypoDiagnosticConsumer;
+  Compiler.createDiagnostics(TDC, /*ShouldOwnClient=*/true);
+  Compiler.setExternalSemaSource(new TypoExternalSemaSource(Compiler));
+
+  SyntaxOnlyAction TestAction;
+  ASSERT_TRUE(Compiler.ExecuteAction(TestAction));
+  // There should be one error correcting to 'moo' and a note attached to it.
+  EXPECT_EQ("use of undeclared identifier 'foo'; did you mean 'moo'?",
+            TDC->Error.str().str());
+  EXPECT_EQ("This is a note", TDC->Note.str().str());
+}
+
 } // anonymous namespace
Index: lib/Sema/SemaLookup.cpp
===================================================================
--- lib/Sema/SemaLookup.cpp
+++ lib/Sema/SemaLookup.cpp
@@ -5082,6 +5082,10 @@
   if (PrevNote.getDiagID() && ChosenDecl)
     Diag(ChosenDecl->getLocation(), PrevNote)
       << CorrectedQuotedStr << (ErrorRecovery ? FixItHint() : FixTypo);
+
+  // Add any extra diagnostics.
+  for (const PartialDiagnostic &PD : Correction.getExtraDiagnostics())
+    Diag(Correction.getCorrectionRange().getBegin(), PD);
 }
 
 TypoExpr *Sema::createDelayedTypo(std::unique_ptr<TypoCorrectionConsumer> TCC,
Index: include/clang/Sema/TypoCorrection.h
===================================================================
--- include/clang/Sema/TypoCorrection.h
+++ include/clang/Sema/TypoCorrection.h
@@ -230,6 +230,15 @@
   bool requiresImport() const { return RequiresImport; }
   void setRequiresImport(bool Req) { RequiresImport = Req; }
 
+  /// Extra diagnostics are printed after the first diagnostic for the typo.
+  /// This can be used to attach external notes to the diag.
+  void addExtraDiagnostic(PartialDiagnostic PD) {
+    ExtraDiagnostics.push_back(std::move(PD));
+  }
+  ArrayRef<PartialDiagnostic> getExtraDiagnostics() const {
+    return ExtraDiagnostics;
+  }
+
 private:
   bool hasCorrectionDecl() const {
     return (!isKeyword() && !CorrectionDecls.empty());
@@ -245,6 +254,8 @@
   SourceRange CorrectionRange;
   bool ForceSpecifierReplacement;
   bool RequiresImport;
+
+  std::vector<PartialDiagnostic> ExtraDiagnostics;
 };
 
 /// @brief Base class for callback objects used by Sema::CorrectTypo to check
Index: include/clang/Frontend/CompilerInstance.h
===================================================================
--- include/clang/Frontend/CompilerInstance.h
+++ include/clang/Frontend/CompilerInstance.h
@@ -96,6 +96,9 @@
   /// The AST context.
   IntrusiveRefCntPtr<ASTContext> Context;
 
+  /// An optional sema source that will be attached to sema.
+  IntrusiveRefCntPtr<ExternalSemaSource> ExternalSemaSrc;
+
   /// The AST consumer.
   std::unique_ptr<ASTConsumer> Consumer;
 
@@ -774,6 +777,8 @@
   void addDependencyCollector(std::shared_ptr<DependencyCollector> Listener) {
     DependencyCollectors.push_back(std::move(Listener));
   }
+
+  void setExternalSemaSource(IntrusiveRefCntPtr<ExternalSemaSource> ESS);
 };
 
 } // end namespace clang
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to