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