yvvan updated this revision to Diff 145664.
yvvan added a comment.

Address comments and provide diff with full context


https://reviews.llvm.org/D41537

Files:
  include/clang-c/Index.h
  include/clang/Basic/DiagnosticSemaKinds.td
  include/clang/Sema/CodeCompleteConsumer.h
  include/clang/Sema/CodeCompleteOptions.h
  include/clang/Sema/Sema.h
  lib/Frontend/ASTUnit.cpp
  lib/Parse/ParseExpr.cpp
  lib/Sema/CodeCompleteConsumer.cpp
  lib/Sema/SemaCodeComplete.cpp
  test/FixIt/fixit.cpp
  test/Index/complete-arrow-dot.cpp
  test/SemaCXX/member-expr.cpp
  tools/c-index-test/c-index-test.c
  tools/libclang/CIndexCodeCompletion.cpp
  tools/libclang/libclang.exports

Index: tools/libclang/libclang.exports
===================================================================
--- tools/libclang/libclang.exports
+++ tools/libclang/libclang.exports
@@ -171,6 +171,8 @@
 clang_getCompletionChunkCompletionString
 clang_getCompletionChunkKind
 clang_getCompletionChunkText
+clang_getCompletionNumFixIts
+clang_getCompletionFixIt
 clang_getCompletionNumAnnotations
 clang_getCompletionParent
 clang_getCompletionPriority
Index: tools/libclang/CIndexCodeCompletion.cpp
===================================================================
--- tools/libclang/CIndexCodeCompletion.cpp
+++ tools/libclang/CIndexCodeCompletion.cpp
@@ -16,6 +16,7 @@
 #include "CIndexDiagnostic.h"
 #include "CLog.h"
 #include "CXCursor.h"
+#include "CXSourceLocation.h"
 #include "CXString.h"
 #include "CXTranslationUnit.h"
 #include "clang/AST/Decl.h"
@@ -306,6 +307,48 @@
 
 } // end anonymous namespace
 
+unsigned clang_getCompletionNumFixIts(CXCompletionString completion_string) {
+  CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
+
+  if (!CCStr)
+    return 0;
+  return static_cast<unsigned>(CCStr->getFixIts().size());
+}
+
+CXString clang_getCompletionFixIt(CXCompletionString completion_string,
+                                  unsigned correction_index,
+                                  CXSourceRange *replacement_range,
+                                  CXCodeCompleteResults *results) {
+  CodeCompletionString *CCStr = (CodeCompletionString *)completion_string;
+
+  if (!CCStr) {
+    if (replacement_range)
+      *replacement_range = clang_getNullRange();
+    return cxstring::createNull();
+  }
+
+  llvm::ArrayRef<FixItHint> FixIts = CCStr->getFixIts();
+  if (FixIts.size() <= correction_index) {
+    if (replacement_range)
+      *replacement_range = clang_getNullRange();
+    return cxstring::createNull();
+  }
+
+  const FixItHint &FixIt = FixIts[correction_index];
+  if (replacement_range) {
+    AllocatedCXCodeCompleteResults *allocated_results = (AllocatedCXCodeCompleteResults *)results;
+    if (!allocated_results) {
+      *replacement_range = clang_getNullRange();
+    } else {
+      *replacement_range = cxloc::translateSourceRange(*allocated_results->SourceMgr,
+                                                       allocated_results->LangOpts,
+                                                       FixIt.RemoveRange);
+    }
+  }
+
+  return cxstring::createRef(FixIt.CodeToInsert.c_str());
+}
+
 /// \brief Tracks the number of code-completion result objects that are 
 /// currently active.
 ///
@@ -532,7 +575,7 @@
                                     unsigned NumResults) override {
       StoredResults.reserve(StoredResults.size() + NumResults);
       for (unsigned I = 0; I != NumResults; ++I) {
-        CodeCompletionString *StoredCompletion        
+        CodeCompletionString *StoredCompletion
           = Results[I].CreateCodeCompletionString(S, Context, getAllocator(),
                                                   getCodeCompletionTUInfo(),
                                                   includeBriefComments());
@@ -644,13 +687,13 @@
                           unsigned options) {
   bool IncludeBriefComments = options & CXCodeComplete_IncludeBriefComments;
   bool SkipPreamble = options & CXCodeComplete_SkipPreamble;
+  bool IncludeFixIts = options & CXCodeComplete_IncludeFixIts;
 
 #ifdef UDP_CODE_COMPLETION_LOGGER
 #ifdef UDP_CODE_COMPLETION_LOGGER_PORT
   const llvm::TimeRecord &StartTime =  llvm::TimeRecord::getCurrentTime();
 #endif
 #endif
-
   bool EnableLogging = getenv("LIBCLANG_CODE_COMPLETION_LOGGING") != nullptr;
 
   if (cxtu::isNotUsableTU(TU)) {
@@ -691,6 +734,7 @@
   CodeCompleteOptions Opts;
   Opts.IncludeBriefComments = IncludeBriefComments;
   Opts.LoadExternal = !SkipPreamble;
+  Opts.IncludeFixIts = IncludeFixIts;
   CaptureCompletionResults Capture(Opts, *Results, &TU);
 
   // Perform completion.
@@ -964,7 +1008,7 @@
         = (CodeCompletionString *)XR.CompletionString;
       CodeCompletionString *Y
         = (CodeCompletionString *)YR.CompletionString;
-      
+
       SmallString<256> XBuffer;
       StringRef XText = GetTypedName(X, XBuffer);
       SmallString<256> YBuffer;
Index: tools/c-index-test/c-index-test.c
===================================================================
--- tools/c-index-test/c-index-test.c
+++ tools/c-index-test/c-index-test.c
@@ -2268,14 +2268,32 @@
 
 }
 
-static void print_completion_result(CXCompletionResult *completion_result,
+static void tokens_spelling_at_range(char *result_buffer,
+                                     CXTranslationUnit translation_unit,
+                                     CXSourceRange range) {
+  CXToken *tokens;
+  unsigned tokens_number;
+  clang_tokenize(translation_unit, range, &tokens, &tokens_number);
+  CXString replaced_string;
+  for (unsigned j = 0; j < tokens_number; ++j) {
+    strcat(result_buffer, clang_getCString(clang_getTokenSpelling(
+                              translation_unit, tokens[j])));
+  }
+  clang_disposeTokens(translation_unit, tokens, tokens_number);
+}
+
+static void print_completion_result(CXTranslationUnit translation_unit,
+                                    CXCodeCompleteResults *completion_results,
+                                    unsigned index,
                                     FILE *file) {
+  CXCompletionResult *completion_result = completion_results->Results + index;
   CXString ks = clang_getCursorKindSpelling(completion_result->CursorKind);
   unsigned annotationCount;
   enum CXCursorKind ParentKind;
   CXString ParentName;
   CXString BriefComment;
   CXString Annotation;
+  CXString FixIt;
   const char *BriefCommentCString;
   
   fprintf(file, "%s:", clang_getCString(ks));
@@ -2337,7 +2355,20 @@
     fprintf(file, "(brief comment: %s)", BriefCommentCString);
   }
   clang_disposeString(BriefComment);
-  
+
+  for (unsigned i = 0;
+       i < clang_getCompletionNumFixIts(completion_result->CompletionString);
+       ++i) {
+    CXSourceRange correction_range;
+    FixIt = clang_getCompletionFixIt(completion_result->CompletionString, i,
+                                     &correction_range, completion_results);
+    char replaced_string[8];
+    tokens_spelling_at_range(replaced_string, translation_unit,
+                             correction_range);
+    fprintf(file, " (requires fix: \"%s\" to \"%s\")", replaced_string,
+            clang_getCString(FixIt));
+  }
+
   fprintf(file, "\n");
 }
 
@@ -2436,6 +2467,8 @@
     completionOptions |= CXCodeComplete_IncludeBriefComments;
   if (getenv("CINDEXTEST_COMPLETION_SKIP_PREAMBLE"))
     completionOptions |= CXCodeComplete_SkipPreamble;
+  if (getenv("CINDEXTEST_COMPLETION_INCLUDE_CORRECTIONS"))
+    completionOptions |= CXCodeComplete_IncludeFixIts;
   
   if (timing_only)
     input += strlen("-code-completion-timing=");
@@ -2500,7 +2533,7 @@
       clang_sortCodeCompletionResults(results->Results, results->NumResults);
 
       for (i = 0; i != n; ++i)
-        print_completion_result(results->Results + i, stdout);
+        print_completion_result(TU, results, i, stdout);
     }
     n = clang_codeCompleteGetNumDiagnostics(results);
     for (i = 0; i != n; ++i) {
Index: test/SemaCXX/member-expr.cpp
===================================================================
--- test/SemaCXX/member-expr.cpp
+++ test/SemaCXX/member-expr.cpp
@@ -188,6 +188,11 @@
     return c->a;  // expected-error {{member reference type 'PR15045::Cl0' is not a pointer; did you mean to use '.'?}}
   }
 
+  int g() {
+    Cl0* c;
+    return c.a;  // expected-error {{member reference type 'PR15045::Cl0 *' is a pointer; did you mean to use '->'?}}
+  }
+
   struct bar {
     void func();  // expected-note {{'func' declared here}}
   };
Index: test/Index/complete-arrow-dot.cpp
===================================================================
--- test/Index/complete-arrow-dot.cpp
+++ test/Index/complete-arrow-dot.cpp
@@ -0,0 +1,54 @@
+struct X {
+  void doSomething();
+
+  int field;
+};
+
+void X::doSomething() {
+  // RUN: c-index-test -code-completion-at=%s:10:8 %s | FileCheck %s
+  // RUN: env CINDEXTEST_COMPLETION_INCLUDE_CORRECTIONS=1 c-index-test -code-completion-at=%s:10:8 %s | FileCheck -check-prefix=CHECK-WITH-CORRECTION %s
+  this.;
+}
+
+void doSomething() {
+  // RUN: c-index-test -code-completion-at=%s:17:6 %s | FileCheck -check-prefix=CHECK-ARROW-TO-DOT %s
+  // RUN: env CINDEXTEST_COMPLETION_INCLUDE_CORRECTIONS=1 c-index-test -code-completion-at=%s:17:6 %s | FileCheck -check-prefix=CHECK-ARROW-TO-DOT-WITH-CORRECTION %s
+  X x;
+  x->
+}
+
+// CHECK-NOT: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34) (requires fix: "." to "->")
+// CHECK-NOT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix: "." to "->")
+// CHECK-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK-NOT: StructDecl:{TypedText X}{Text ::} (75) (requires fix: "." to "->")
+// CHECK-NOT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK: Completion contexts:
+// CHECK-NEXT: Dot member access
+
+// CHECK-WITH-CORRECTION: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: StructDecl:{TypedText X}{Text ::} (75) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix: "." to "->")
+// CHECK-WITH-CORRECTION-NEXT: Completion contexts:
+// CHECK-WITH-CORRECTION-NEXT: Dot member access
+
+// CHECK-ARROW-TO-DOT-NOT: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-NOT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-NOT: StructDecl:{TypedText X}{Text ::} (75) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-NOT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT: Completion contexts:
+// CHECK-ARROW-TO-DOT-NEXT: Unknown
+
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: FieldDecl:{ResultType int}{TypedText field} (35) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: StructDecl:{TypedText X}{Text ::} (75) (requires fix: "->" to ".")
+// CHECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires fix: "->" to ".")
+// HECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: Completion contexts:
+// HECK-ARROW-TO-DOT-WITH-CORRECTION-NEXT: Arrow member access
Index: test/FixIt/fixit.cpp
===================================================================
--- test/FixIt/fixit.cpp
+++ test/FixIt/fixit.cpp
@@ -359,7 +359,12 @@
   int f() {
     Cl0 c;
     return c->a;  // expected-error {{member reference type 'PR15045::Cl0' is not a pointer; did you mean to use '.'?}}
-  }
+  }  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:15}:"."
+
+  int g() {
+    Cl0* c = NULL;
+    return c.a;  // expected-error {{member reference type 'PR15045::Cl0 *' is a pointer; did you mean to use '->'?}}
+  }  // CHECK: fix-it:"{{.*}}":{[[@LINE-1]]:13-[[@LINE-1]]:14}:"->"
 }
 
 namespace curly_after_base_clause {
Index: lib/Sema/SemaCodeComplete.cpp
===================================================================
--- lib/Sema/SemaCodeComplete.cpp
+++ lib/Sema/SemaCodeComplete.cpp
@@ -2755,8 +2755,9 @@
                                            CodeCompletionAllocator &Allocator,
                                            CodeCompletionTUInfo &CCTUInfo,
                                            bool IncludeBriefComments) {
-  CodeCompletionBuilder Result(Allocator, CCTUInfo, Priority, Availability);
-  
+  CodeCompletionBuilder Result(Allocator, CCTUInfo, Priority, Availability,
+                               FixIts);
+
   PrintingPolicy Policy = getCompletionPrintingPolicy(Ctx, PP);
   if (Kind == RK_Pattern) {
     Pattern->Priority = Priority;
@@ -3120,7 +3121,7 @@
   PrintingPolicy Policy = getCompletionPrintingPolicy(S);
 
   // FIXME: Set priority, availability appropriately.
-  CodeCompletionBuilder Result(Allocator,CCTUInfo, 1, CXAvailability_Available);
+  CodeCompletionBuilder Result(Allocator, CCTUInfo, 1, CXAvailability_Available, {});
   FunctionDecl *FDecl = getFunction();
   const FunctionProtoType *Proto
     = dyn_cast<FunctionProtoType>(getFunctionType());
@@ -3981,107 +3982,153 @@
 }
 
 void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
+                                           Expr *OtherOpBase,
                                            SourceLocation OpLoc, bool IsArrow,
                                            bool IsBaseExprStatement) {
   if (!Base || !CodeCompleter)
     return;
-  
+
   ExprResult ConvertedBase = PerformMemberExprBaseConversion(Base, IsArrow);
   if (ConvertedBase.isInvalid())
     return;
-  Base = ConvertedBase.get();
-  
-  QualType BaseType = Base->getType();
+  QualType ConvertedBaseType = ConvertedBase.get()->getType();
+
+  enum CodeCompletionContext::Kind contextKind;
 
   if (IsArrow) {
-    if (const PointerType *Ptr = BaseType->getAs<PointerType>())
-      BaseType = Ptr->getPointeeType();
-    else if (BaseType->isObjCObjectPointerType())
-      /*Do nothing*/ ;
-    else
-      return;
+    if (const PointerType *Ptr = ConvertedBaseType->getAs<PointerType>())
+      ConvertedBaseType = Ptr->getPointeeType();
   }
-  
-  enum CodeCompletionContext::Kind contextKind;
-  
+
   if (IsArrow) {
     contextKind = CodeCompletionContext::CCC_ArrowMemberAccess;
-  }
-  else {
-    if (BaseType->isObjCObjectPointerType() ||
-        BaseType->isObjCObjectOrInterfaceType()) {
+  } else {
+    if (ConvertedBaseType->isObjCObjectPointerType() ||
+        ConvertedBaseType->isObjCObjectOrInterfaceType()) {
       contextKind = CodeCompletionContext::CCC_ObjCPropertyAccess;
-    }
-    else {
+    } else {
       contextKind = CodeCompletionContext::CCC_DotMemberAccess;
     }
   }
 
-  CodeCompletionContext CCContext(contextKind, BaseType);
+  CodeCompletionContext CCContext(contextKind, ConvertedBaseType);
   ResultBuilder Results(*this, CodeCompleter->getAllocator(),
-                        CodeCompleter->getCodeCompletionTUInfo(),
-                        CCContext,
+                        CodeCompleter->getCodeCompletionTUInfo(), CCContext,
                         &ResultBuilder::IsMember);
+
+  auto DoCompletion = [&](Expr *Base, bool IsArrow) -> bool {
+    if (!Base)
+      return false;
+
+    ExprResult ConvertedBase = PerformMemberExprBaseConversion(Base, IsArrow);
+    if (ConvertedBase.isInvalid())
+      return false;
+    Base = ConvertedBase.get();
+
+    QualType BaseType = Base->getType();
+
+    if (IsArrow) {
+      if (const PointerType *Ptr = BaseType->getAs<PointerType>())
+        BaseType = Ptr->getPointeeType();
+      else if (BaseType->isObjCObjectPointerType())
+        /*Do nothing*/;
+      else
+        return false;
+    }
+
+    if (const RecordType *Record = BaseType->getAs<RecordType>()) {
+      AddRecordMembersCompletionResults(*this, Results, S, BaseType,
+                                        Record->getDecl());
+    } else if (const auto *TST =
+                   BaseType->getAs<TemplateSpecializationType>()) {
+      TemplateName TN = TST->getTemplateName();
+      if (const auto *TD =
+              dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl())) {
+        CXXRecordDecl *RD = TD->getTemplatedDecl();
+        AddRecordMembersCompletionResults(*this, Results, S, BaseType, RD);
+      }
+    } else if (const auto *ICNT = BaseType->getAs<InjectedClassNameType>()) {
+      if (auto *RD = ICNT->getDecl())
+        AddRecordMembersCompletionResults(*this, Results, S, BaseType, RD);
+    } else if (!IsArrow && BaseType->isObjCObjectPointerType()) {
+      // Objective-C property reference.
+      AddedPropertiesSet AddedProperties;
+
+      if (const ObjCObjectPointerType *ObjCPtr =
+              BaseType->getAsObjCInterfacePointerType()) {
+        // Add property results based on our interface.
+        assert(ObjCPtr && "Non-NULL pointer guaranteed above!");
+        AddObjCProperties(CCContext, ObjCPtr->getInterfaceDecl(), true,
+                          /*AllowNullaryMethods=*/true, CurContext,
+                          AddedProperties, Results, IsBaseExprStatement);
+      }
+
+      // Add properties from the protocols in a qualified interface.
+      for (auto *I : BaseType->getAs<ObjCObjectPointerType>()->quals())
+        AddObjCProperties(CCContext, I, true, /*AllowNullaryMethods=*/true,
+                          CurContext, AddedProperties, Results,
+                          IsBaseExprStatement);
+    } else if ((IsArrow && BaseType->isObjCObjectPointerType()) ||
+               (!IsArrow && BaseType->isObjCObjectType())) {
+      // Objective-C instance variable access.
+      ObjCInterfaceDecl *Class = nullptr;
+      if (const ObjCObjectPointerType *ObjCPtr =
+              BaseType->getAs<ObjCObjectPointerType>())
+        Class = ObjCPtr->getInterfaceDecl();
+      else
+        Class = BaseType->getAs<ObjCObjectType>()->getInterface();
+
+      // Add all ivars from this class and its superclasses.
+      if (Class) {
+        CodeCompletionDeclConsumer Consumer(Results, CurContext);
+        Results.setFilter(&ResultBuilder::IsObjCIvar);
+        LookupVisibleDecls(
+            Class, LookupMemberName, Consumer, CodeCompleter->includeGlobals(),
+            /*IncludeDependentBases=*/false, CodeCompleter->loadExternal());
+      }
+    }
+
+    // FIXME: How do we cope with isa?
+    return true;
+  };
+
   Results.EnterNewScope();
-  if (const RecordType *Record = BaseType->getAs<RecordType>()) {
-    AddRecordMembersCompletionResults(*this, Results, S, BaseType,
-                                      Record->getDecl());
-  } else if (const auto *TST = BaseType->getAs<TemplateSpecializationType>()) {
-    TemplateName TN = TST->getTemplateName();
-    if (const auto *TD =
-            dyn_cast_or_null<ClassTemplateDecl>(TN.getAsTemplateDecl())) {
-      CXXRecordDecl *RD = TD->getTemplatedDecl();
-      AddRecordMembersCompletionResults(*this, Results, S, BaseType, RD);
-    }
-  } else if (const auto *ICNT = BaseType->getAs<InjectedClassNameType>()) {
-    if (auto *RD = ICNT->getDecl())
-      AddRecordMembersCompletionResults(*this, Results, S, BaseType, RD);
-  } else if (!IsArrow && BaseType->isObjCObjectPointerType()) {
-    // Objective-C property reference.
-    AddedPropertiesSet AddedProperties;
-
-    if (const ObjCObjectPointerType *ObjCPtr =
-            BaseType->getAsObjCInterfacePointerType()) {
-      // Add property results based on our interface.
-      assert(ObjCPtr && "Non-NULL pointer guaranteed above!");
-      AddObjCProperties(CCContext, ObjCPtr->getInterfaceDecl(), true,
-                        /*AllowNullaryMethods=*/true, CurContext,
-                        AddedProperties, Results, IsBaseExprStatement);
-    }
-
-    // Add properties from the protocols in a qualified interface.
-    for (auto *I : BaseType->getAs<ObjCObjectPointerType>()->quals())
-      AddObjCProperties(CCContext, I, true, /*AllowNullaryMethods=*/true,
-                        CurContext, AddedProperties, Results,
-                        IsBaseExprStatement);
-  } else if ((IsArrow && BaseType->isObjCObjectPointerType()) ||
-             (!IsArrow && BaseType->isObjCObjectType())) {
-    // Objective-C instance variable access.
-    ObjCInterfaceDecl *Class = nullptr;
-    if (const ObjCObjectPointerType *ObjCPtr
-                                    = BaseType->getAs<ObjCObjectPointerType>())
-      Class = ObjCPtr->getInterfaceDecl();
-    else
-      Class = BaseType->getAs<ObjCObjectType>()->getInterface();
-    
-    // Add all ivars from this class and its superclasses.
-    if (Class) {
-      CodeCompletionDeclConsumer Consumer(Results, CurContext);
-      Results.setFilter(&ResultBuilder::IsObjCIvar);
-      LookupVisibleDecls(
-          Class, LookupMemberName, Consumer, CodeCompleter->includeGlobals(),
-          /*IncludeDependentBases=*/false, CodeCompleter->loadExternal());
+
+  bool CompletionSucceded = false;
+
+  if (CodeCompleter->includeFixIts())
+    CompletionSucceded = DoCompletion(OtherOpBase, !IsArrow);
+  unsigned OtherResultsSize = Results.size();
+
+  CompletionSucceded |= DoCompletion(Base, IsArrow);
+
+  if (OtherResultsSize > 0) {
+    const char *Corr = IsArrow ? "." : "->";
+    FixItHint FixIt = FixItHint::CreateReplacement(OpLoc, Corr);
+
+
+    for (size_t i = 0; i < Results.size(); ++i)
+      Results.data()[i].FixIts.push_back(FixIt);
+
+    if (!getDiagnostics().hasErrorOccurred()) {
+      // We don't have an error diagnostic for this case yet.
+      const unsigned DiagID =
+          Results.size() > OtherResultsSize
+              ? diag::note_typecheck_member_reference_full_suggestion
+              : diag::err_typecheck_member_reference_suggestion;
+      Diag(OpLoc, DiagID) << ConvertedBaseType << IsArrow
+                          << Base->getSourceRange() << FixIt;
     }
   }
-  
-  // FIXME: How do we cope with isa?
-  
+
   Results.ExitScope();
 
+  if (!CompletionSucceded)
+    return;
+
   // Hand off the results found for code completion.
-  HandleCodeCompleteResults(this, CodeCompleter, 
-                            Results.getCompletionContext(),
-                            Results.data(),Results.size());
+  HandleCodeCompleteResults(this, CodeCompleter, Results.getCompletionContext(),
+                            Results.data(), Results.size());
 }
 
 void Sema::CodeCompleteObjCClassPropertyRefExpr(Scope *S,
@@ -5624,7 +5671,7 @@
       PP.isMacroDefined("IBAction")) {
     CodeCompletionBuilder Builder(Results.getAllocator(),
                                   Results.getCodeCompletionTUInfo(),
-                                  CCP_CodePattern, CXAvailability_Available);
+                                  CCP_CodePattern, CXAvailability_Available, {});
     Builder.AddTypedTextChunk("IBAction");
     Builder.AddChunk(CodeCompletionString::CK_RightParen);
     Builder.AddPlaceholderChunk("selector");
@@ -6674,7 +6721,7 @@
     typedef CodeCompletionResult Result;
     CodeCompletionAllocator &Allocator = Results.getAllocator();
     CodeCompletionBuilder Builder(Allocator, Results.getCodeCompletionTUInfo(),
-                                  Priority,CXAvailability_Available);
+                                  Priority, CXAvailability_Available, {});
 
     PrintingPolicy Policy = getCompletionPrintingPolicy(*this);
     Builder.AddResultTypeChunk(GetCompletionTypeString(PropertyType, Context,
Index: lib/Sema/CodeCompleteConsumer.cpp
===================================================================
--- lib/Sema/CodeCompleteConsumer.cpp
+++ lib/Sema/CodeCompleteConsumer.cpp
@@ -271,17 +271,20 @@
   return Chunk(CK_CurrentParameter, CurrentParameter);
 }
 
-CodeCompletionString::CodeCompletionString(const Chunk *Chunks, 
+CodeCompletionString::CodeCompletionString(const Chunk *Chunks,
                                            unsigned NumChunks,
                                            unsigned Priority, 
                                            CXAvailabilityKind Availability,
                                            const char **Annotations,
                                            unsigned NumAnnotations,
                                            StringRef ParentName,
-                                           const char *BriefComment)
+                                           const char *BriefComment,
+                                           std::vector<FixItHint> FixIts)
     : NumChunks(NumChunks), NumAnnotations(NumAnnotations),
       Priority(Priority), Availability(Availability),
-      ParentName(ParentName), BriefComment(BriefComment) { 
+      ParentName(ParentName), BriefComment(BriefComment),
+      FixIts(std::move(FixIts))
+{
   assert(NumChunks <= 0xffff);
   assert(NumAnnotations <= 0xffff);
 
@@ -417,7 +420,7 @@
     = new (Mem) CodeCompletionString(Chunks.data(), Chunks.size(),
                                      Priority, Availability,
                                      Annotations.data(), Annotations.size(),
-                                     ParentName, BriefComment);
+                                     ParentName, BriefComment, std::move(FixIts));
   Chunks.clear();
   return Result;
 }
Index: lib/Parse/ParseExpr.cpp
===================================================================
--- lib/Parse/ParseExpr.cpp
+++ lib/Parse/ParseExpr.cpp
@@ -1687,8 +1687,10 @@
       CXXScopeSpec SS;
       ParsedType ObjectType;
       bool MayBePseudoDestructor = false;
+      Expr* OrigLHS = !LHS.isInvalid() ? LHS.get() : nullptr;
+
       if (getLangOpts().CPlusPlus && !LHS.isInvalid()) {
-        Expr *Base = LHS.get();
+        Expr *Base = OrigLHS;
         const Type* BaseType = Base->getType().getTypePtrOrNull();
         if (BaseType && Tok.is(tok::l_paren) &&
             (BaseType->isFunctionType() ||
@@ -1713,11 +1715,25 @@
       }
 
       if (Tok.is(tok::code_completion)) {
+        tok::TokenKind CorrectedOpKind =
+            OpKind == tok::arrow ? tok::period : tok::arrow;
+        ExprResult CorrectedLHS(/*IsInvalid=*/true);
+        if (getLangOpts().CPlusPlus && OrigLHS) {
+          const bool DiagsAreSuppressed = Diags.getSuppressAllDiagnostics();
+          Diags.setSuppressAllDiagnostics(true);
+          CorrectedLHS = Actions.ActOnStartCXXMemberReference(
+              getCurScope(), OrigLHS, OpLoc, CorrectedOpKind, ObjectType,
+              MayBePseudoDestructor);
+          Diags.setSuppressAllDiagnostics(DiagsAreSuppressed);
+        }
+
+        Expr *Base = LHS.get();
+        Expr *CorrectedBase = CorrectedLHS.get();
+
         // Code completion for a member access expression.
-        if (Expr *Base = LHS.get())
-          Actions.CodeCompleteMemberReferenceExpr(
-              getCurScope(), Base, OpLoc, OpKind == tok::arrow,
-              ExprStatementTokLoc == Base->getLocStart());
+        Actions.CodeCompleteMemberReferenceExpr(
+            getCurScope(), Base, CorrectedBase, OpLoc, OpKind == tok::arrow,
+            Base && ExprStatementTokLoc == Base->getLocStart());
 
         cutOffParsing();
         return ExprError();
Index: lib/Frontend/ASTUnit.cpp
===================================================================
--- lib/Frontend/ASTUnit.cpp
+++ lib/Frontend/ASTUnit.cpp
@@ -2062,7 +2062,7 @@
       // Create a new code-completion string that just contains the
       // macro name, without its arguments.
       CodeCompletionBuilder Builder(getAllocator(), getCodeCompletionTUInfo(),
-                                    CCP_CodePattern, C->Availability);
+                                    CCP_CodePattern, C->Availability, {});
       Builder.AddTypedTextChunk(C->Completion->getTypedText());
       Priority = CCP_CodePattern;
       Completion = Builder.TakeString();
@@ -2111,6 +2111,7 @@
   CodeCompleteOpts.IncludeGlobals = CachedCompletionResults.empty();
   CodeCompleteOpts.IncludeBriefComments = IncludeBriefComments;
   CodeCompleteOpts.LoadExternal = Consumer.loadExternal();
+  CodeCompleteOpts.IncludeFixIts = Consumer.includeFixIts();
 
   assert(IncludeBriefComments == this->IncludeBriefCommentsInCodeCompletion);
 
Index: include/clang-c/Index.h
===================================================================
--- include/clang-c/Index.h
+++ include/clang-c/Index.h
@@ -32,7 +32,7 @@
  * compatible, thus CINDEX_VERSION_MAJOR is expected to remain stable.
  */
 #define CINDEX_VERSION_MAJOR 0
-#define CINDEX_VERSION_MINOR 48
+#define CINDEX_VERSION_MINOR 49
 
 #define CINDEX_VERSION_ENCODE(major, minor) ( \
       ((major) * 10000)                       \
@@ -5228,6 +5228,40 @@
 } CXCodeCompleteResults;
 
 /**
+ * \brief Retrieve the number of fix-its for the given completion string.
+ */
+CINDEX_LINKAGE unsigned
+clang_getCompletionNumFixIts(CXCompletionString completion_string);
+
+/**
+ * \brief FixIts that *must* be applied before inserting the text for the
+ * corresponding completion item. Completion items with non-empty fixits will
+ * not be returned by default, they should be explicitly requested by setting
+ * CXCodeComplete_IncludeFixIts. For the editors to be able to
+ * compute position of the cursor for the completion item itself, the
+ * following conditions are guaranteed to hold for RemoveRange of the stored
+ * fixits:
+ *  - Ranges in the fixits are guaranteed to never contain the completion
+ *  point (or identifier under completion point, if any) inside them, except
+ *  at the start or at the end of the range.
+ *  - If a fixit range starts or ends with completion point (or starts or
+ *  ends after the identifier under completion point), it will contain at
+ *  least one character. It allows to unambiguously recompute completion
+ *  point after applying the fixit.
+ * The intuition is that provided fixits change code around the identifier we
+ * complete, but are not allowed to touch the identifier itself or the
+ * completion point. One example of completion items with corrections are the
+ * ones replacing '.' with '->' and vice versa:
+ *      std::unique_ptr<std::vector<int>> vec_ptr;
+ *      vec_ptr.^  // completion returns an item 'push_back', replacing '.'
+ *      with '->' vec_ptr->^ // completion returns an item 'release',
+ *      replacing '->' with '.'
+ */
+CINDEX_LINKAGE CXString clang_getCompletionFixIt(
+    CXCompletionString completion_string, unsigned correction_index,
+    CXSourceRange *replacement_range, CXCodeCompleteResults *results);
+
+/**
  * \brief Flags that can be passed to \c clang_codeCompleteAt() to
  * modify its behavior.
  *
@@ -5258,7 +5292,13 @@
    * defined in the preamble. There's no guarantee any particular entity is
    * omitted. This may be useful if the headers are indexed externally.
    */
-  CXCodeComplete_SkipPreamble = 0x08
+  CXCodeComplete_SkipPreamble = 0x08,
+
+  /**
+   * \brief Whether to include completion items with small
+   * fix-its, e.g. change '.' to '->' on member access, etc.
+   */
+  CXCodeComplete_IncludeFixIts = 0x10
 };
 
 /**
Index: include/clang/Sema/Sema.h
===================================================================
--- include/clang/Sema/Sema.h
+++ include/clang/Sema/Sema.h
@@ -10229,7 +10229,7 @@
   struct CodeCompleteExpressionData;
   void CodeCompleteExpression(Scope *S,
                               const CodeCompleteExpressionData &Data);
-  void CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
+  void CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base, Expr *OtherOpBase,
                                        SourceLocation OpLoc, bool IsArrow,
                                        bool IsBaseExprStatement);
   void CodeCompletePostfixExpression(Scope *S, ExprResult LHS);
Index: include/clang/Sema/CodeCompleteOptions.h
===================================================================
--- include/clang/Sema/CodeCompleteOptions.h
+++ include/clang/Sema/CodeCompleteOptions.h
@@ -39,10 +39,14 @@
   /// If false, namespace-level declarations from the preamble may be omitted.
   unsigned LoadExternal : 1;
 
+  /// Show also results after corrections (small fix-its), e.g. change '.' to
+  /// '->' on member access, etc.
+  unsigned IncludeFixIts : 1;
+
   CodeCompleteOptions()
       : IncludeMacros(0), IncludeCodePatterns(0), IncludeGlobals(1),
         IncludeNamespaceLevelDecls(1), IncludeBriefComments(0),
-        LoadExternal(1) {}
+        LoadExternal(1), IncludeFixIts(0) {}
 };
 
 } // namespace clang
Index: include/clang/Sema/CodeCompleteConsumer.h
===================================================================
--- include/clang/Sema/CodeCompleteConsumer.h
+++ include/clang/Sema/CodeCompleteConsumer.h
@@ -553,19 +553,43 @@
 
   /// \brief The availability of this code-completion result.
   unsigned Availability : 2;
-  
+
   /// \brief The name of the parent context.
   StringRef ParentName;
 
   /// \brief A brief documentation comment attached to the declaration of
   /// entity being completed by this result.
   const char *BriefComment;
-  
+
+  /// \brief FixIts that *must* be applied before inserting the text for the
+  /// corresponding completion item. Completion items with non-empty fixits will
+  /// not be returned by default, they should be explicitly requested by setting
+  /// CompletionOptions::IncludeFixIts. For the editors to be able to
+  /// compute position of the cursor for the completion item itself, the
+  /// following conditions are guaranteed to hold for RemoveRange of the stored
+  /// fixits:
+  ///  - Ranges in the fixits are guaranteed to never contain the completion
+  ///  point (or identifier under completion point, if any) inside them, except
+  ///  at the start or at the end of the range.
+  ///  - If a fixit range starts or ends with completion point (or starts or
+  ///  ends after the identifier under completion point), it will contain at
+  ///  least one character. It allows to unambiguously recompute completion
+  ///  point after applying the fixit.
+  /// The intuition is that provided fixits change code around the identifier we
+  /// complete, but are not allowed to touch the identifier itself or the
+  /// completion point. One example of completion items with corrections are the
+  /// ones replacing '.' with '->' and vice versa:
+  ///      std::unique_ptr<std::vector<int>> vec_ptr;
+  ///      vec_ptr.^  // completion returns an item 'push_back', replacing '.'
+  ///      with '->' vec_ptr->^ // completion returns an item 'release',
+  ///      replacing '->' with '.'
+  std::vector<FixItHint> FixIts;
+
   CodeCompletionString(const Chunk *Chunks, unsigned NumChunks,
                        unsigned Priority, CXAvailabilityKind Availability,
                        const char **Annotations, unsigned NumAnnotations,
-                       StringRef ParentName,
-                       const char *BriefComment);
+                       StringRef ParentName, const char *BriefComment,
+                       std::vector<FixItHint> FixIts);
   ~CodeCompletionString() = default;
 
 public:
@@ -593,6 +617,9 @@
   /// \brief Retrieve the availability of this code completion result.
   unsigned getAvailability() const { return Availability; }
 
+  /// \brief Get all required FixIts for this completion.
+  llvm::ArrayRef<FixItHint> getFixIts() const { return FixIts; }
+
   /// \brief Retrieve the number of annotations for this code completion result.
   unsigned getAnnotationCount() const;
 
@@ -668,7 +695,8 @@
   CXAvailabilityKind Availability = CXAvailability_Available;
   StringRef ParentName;
   const char *BriefComment = nullptr;
-  
+  std::vector<FixItHint> FixIts;
+
   /// \brief The chunks stored in this string.
   SmallVector<Chunk, 4> Chunks;
 
@@ -680,10 +708,11 @@
       : Allocator(Allocator), CCTUInfo(CCTUInfo) {}
 
   CodeCompletionBuilder(CodeCompletionAllocator &Allocator,
-                        CodeCompletionTUInfo &CCTUInfo,
-                        unsigned Priority, CXAvailabilityKind Availability)
+                        CodeCompletionTUInfo &CCTUInfo, unsigned Priority,
+                        CXAvailabilityKind Availability,
+                        const std::vector<FixItHint> &FixIts)
       : Allocator(Allocator), CCTUInfo(CCTUInfo), Priority(Priority),
-        Availability(Availability) {}
+        Availability(Availability), FixIts(FixIts) {}
 
   /// \brief Retrieve the allocator into which the code completion
   /// strings should be allocated.
@@ -782,6 +811,30 @@
   /// \brief The availability of this result.
   CXAvailabilityKind Availability = CXAvailability_Available;
 
+  /// \brief FixIts that *must* be applied before inserting the text for the
+  /// corresponding completion item. Completion items with non-empty fixits will
+  /// not be returned by default, they should be explicitly requested by setting
+  /// CompletionOptions::IncludeFixIts. For the editors to be able to
+  /// compute position of the cursor for the completion item itself, the
+  /// following conditions are guaranteed to hold for RemoveRange of the stored
+  /// fixits:
+  ///  - Ranges in the fixits are guaranteed to never contain the completion
+  ///  point (or identifier under completion point, if any) inside them, except
+  ///  at the start or at the end of the range.
+  ///  - If a fixit range starts or ends with completion point (or starts or
+  ///  ends after the identifier under completion point), it will contain at
+  ///  least one character. It allows to unambiguously recompute completion
+  ///  point after applying the fixit.
+  /// The intuition is that provided fixits change code around the identifier we
+  /// complete, but are not allowed to touch the identifier itself or the
+  /// completion point. One example of completion items with corrections are the
+  /// ones replacing '.' with '->' and vice versa:
+  ///      std::unique_ptr<std::vector<int>> vec_ptr;
+  ///      vec_ptr.^  // completion returns an item 'push_back', replacing '.'
+  ///      with '->' vec_ptr->^ // completion returns an item 'release',
+  ///      replacing '->' with '.'
+  std::vector<FixItHint> FixIts;
+
   /// \brief Whether this result is hidden by another name.
   bool Hidden : 1;
 
@@ -1026,6 +1079,10 @@
     return CodeCompleteOpts.IncludeBriefComments;
   }
 
+  /// \brief Whether to include completion items with small fix-its, e.g. change
+  /// '.' to '->' on member access, etc.
+  bool includeFixIts() const { return CodeCompleteOpts.IncludeFixIts; }
+
   /// \brief Hint whether to load data from the external AST in order to provide
   /// full results. If false, declarations from the preamble may be omitted.
   bool loadExternal() const {
Index: include/clang/Basic/DiagnosticSemaKinds.td
===================================================================
--- include/clang/Basic/DiagnosticSemaKinds.td
+++ include/clang/Basic/DiagnosticSemaKinds.td
@@ -5712,6 +5712,8 @@
   "member reference type %0 is not a pointer">;
 def err_typecheck_member_reference_suggestion : Error<
   "member reference type %0 is %select{a|not a}1 pointer; did you mean to use '%select{->|.}1'?">;
+def note_typecheck_member_reference_full_suggestion : Note<
+  "member reference type %0 may %select{|not}1 be pointer; did you mean to use '%select{->|.}1'?">;
 def note_typecheck_member_reference_suggestion : Note<
   "did you mean to use '.' instead?">;
 def note_member_reference_arrow_from_operator_arrow : Note<
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to