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
[email protected]
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits