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 45 +#define CINDEX_VERSION_MINOR 46 #define CINDEX_VERSION_ENCODE(major, minor) ( \ ((major) * 10000) \ @@ -5096,6 +5096,12 @@ clang_getCompletionBriefComment(CXCompletionString completion_string); /** + * \brief Retrieve the necessity of dot to arrow correction for the given completion string. + */ +CINDEX_LINKAGE unsigned +clang_getCompletionRequiresDotToArrowCorrection(CXCompletionString completion_string); + +/** * \brief Retrieve a completion string for an arbitrary declaration or macro * definition cursor. * @@ -5151,7 +5157,12 @@ * \brief Whether to include brief documentation within the set of code * completions returned. */ - CXCodeComplete_IncludeBriefComments = 0x04 + CXCodeComplete_IncludeBriefComments = 0x04, + + /** + * \brief Whether to try dot to arrow correction if arrow operator can be applied. + */ + CXCodeComplete_TryArrowInsteadOfDot = 0x08 }; /** Index: include/clang/Sema/CodeCompleteConsumer.h =================================================================== --- include/clang/Sema/CodeCompleteConsumer.h +++ include/clang/Sema/CodeCompleteConsumer.h @@ -456,22 +456,25 @@ /// \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 If this is the result of arrow completion when dot is present. + bool RequiresDotToArrowCorrection : 1; + CodeCompletionString(const CodeCompletionString &) = delete; void operator=(const CodeCompletionString &) = delete; 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, + bool RequiresDotToArrowCorrection = false); ~CodeCompletionString() = default; friend class CodeCompletionBuilder; @@ -498,6 +501,9 @@ /// \brief Retrieve the availability of this code completion result. unsigned getAvailability() const { return Availability; } + /// \brief Check if this completion requires dot to arrow correction. + bool requiresDotToArrowCorrection() const { return RequiresDotToArrowCorrection; } + /// \brief Retrieve the number of annotations for this code completion result. unsigned getAnnotationCount() const; @@ -570,6 +576,7 @@ CXAvailabilityKind Availability; StringRef ParentName; const char *BriefComment; + bool RequiresDotToArrowCorrection = false; /// \brief The chunks stored in this string. SmallVector Chunks; @@ -585,10 +592,12 @@ CodeCompletionBuilder(CodeCompletionAllocator &Allocator, CodeCompletionTUInfo &CCTUInfo, - unsigned Priority, CXAvailabilityKind Availability) + unsigned Priority, CXAvailabilityKind Availability, + bool RequiresDotToArrowCorrection = false) : Allocator(Allocator), CCTUInfo(CCTUInfo), Priority(Priority), Availability(Availability), - BriefComment(nullptr) { } + BriefComment(nullptr), + RequiresDotToArrowCorrection(RequiresDotToArrowCorrection) { } /// \brief Retrieve the allocator into which the code completion /// strings should be allocated. @@ -698,6 +707,9 @@ /// rather than a use of that entity. bool DeclaringEntity : 1; + /// \brief If this is the result of arrow completion when dot is present. + bool RequiresDotToArrowCorrection : 1; + /// \brief If the result should have a nested-name-specifier, this is it. /// When \c QualifierIsInformative, the nested-name-specifier is /// informative rather than required. @@ -714,7 +726,8 @@ Availability(CXAvailability_Available), Hidden(false), QualifierIsInformative(QualifierIsInformative), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), - DeclaringEntity(false), Qualifier(Qualifier) { + DeclaringEntity(false), RequiresDotToArrowCorrection(false), + Qualifier(Qualifier) { computeCursorKindAndAvailability(Accessible); } @@ -725,7 +738,7 @@ Availability(CXAvailability_Available), Hidden(false), QualifierIsInformative(0), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), DeclaringEntity(false), - Qualifier(nullptr) {} + RequiresDotToArrowCorrection(false), Qualifier(nullptr) {} /// \brief Build a result that refers to a macro. CodeCompletionResult(const IdentifierInfo *Macro, @@ -735,7 +748,7 @@ Availability(CXAvailability_Available), Hidden(false), QualifierIsInformative(0), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), DeclaringEntity(false), - Qualifier(nullptr) {} + RequiresDotToArrowCorrection(false), Qualifier(nullptr) {} /// \brief Build a result that refers to a pattern. CodeCompletionResult(CodeCompletionString *Pattern, @@ -747,7 +760,8 @@ Kind(RK_Pattern), CursorKind(CursorKind), Availability(Availability), Hidden(false), QualifierIsInformative(0), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), - DeclaringEntity(false), Qualifier(nullptr) + DeclaringEntity(false), RequiresDotToArrowCorrection(false), + Qualifier(nullptr) { } @@ -759,7 +773,7 @@ Kind(RK_Pattern), Availability(CXAvailability_Available), Hidden(false), QualifierIsInformative(false), StartsNestedNameSpecifier(false), AllParametersAreInformative(false), DeclaringEntity(false), - Qualifier(nullptr) { + RequiresDotToArrowCorrection(false), Qualifier(nullptr) { computeCursorKindAndAvailability(); } @@ -934,6 +948,11 @@ return CodeCompleteOpts.IncludeBriefComments; } + /// \brief Whether to try dot to arrow correction if arrow operator can be applied. + bool tryArrowInsteadOfDot() const { + return CodeCompleteOpts.TryArrowInsteadOfDot; + } + /// \brief Determine whether the output of this consumer is binary. bool isOutputBinary() const { return OutputIsBinary; } Index: include/clang/Sema/CodeCompleteOptions.h =================================================================== --- include/clang/Sema/CodeCompleteOptions.h +++ include/clang/Sema/CodeCompleteOptions.h @@ -35,9 +35,12 @@ /// Show brief documentation comments in code completion results. unsigned IncludeBriefComments : 1; + /// Show also results after dot to arrow correction if arrow operator can be applied. + unsigned TryArrowInsteadOfDot : 1; + CodeCompleteOptions() : IncludeMacros(0), IncludeCodePatterns(0), IncludeGlobals(1), - IncludeNamespaceLevelDecls(1), IncludeBriefComments(0) {} + IncludeNamespaceLevelDecls(1), IncludeBriefComments(0), TryArrowInsteadOfDot(0) {} }; } // namespace clang Index: lib/Frontend/ASTUnit.cpp =================================================================== --- lib/Frontend/ASTUnit.cpp +++ lib/Frontend/ASTUnit.cpp @@ -2068,6 +2068,7 @@ CodeCompleteOpts.IncludeCodePatterns = IncludeCodePatterns; CodeCompleteOpts.IncludeGlobals = CachedCompletionResults.empty(); CodeCompleteOpts.IncludeBriefComments = IncludeBriefComments; + CodeCompleteOpts.TryArrowInsteadOfDot = Consumer.tryArrowInsteadOfDot(); assert(IncludeBriefComments == this->IncludeBriefCommentsInCodeCompletion); Index: lib/Sema/CodeCompleteConsumer.cpp =================================================================== --- lib/Sema/CodeCompleteConsumer.cpp +++ lib/Sema/CodeCompleteConsumer.cpp @@ -194,10 +194,12 @@ const char **Annotations, unsigned NumAnnotations, StringRef ParentName, - const char *BriefComment) + const char *BriefComment, + bool RequiresDotToArrowCorrection) : NumChunks(NumChunks), NumAnnotations(NumAnnotations), Priority(Priority), Availability(Availability), - ParentName(ParentName), BriefComment(BriefComment) + ParentName(ParentName), BriefComment(BriefComment), + RequiresDotToArrowCorrection(RequiresDotToArrowCorrection) { assert(NumChunks <= 0xffff); assert(NumAnnotations <= 0xffff); @@ -335,7 +337,7 @@ = new (Mem) CodeCompletionString(Chunks.data(), Chunks.size(), Priority, Availability, Annotations.data(), Annotations.size(), - ParentName, BriefComment); + ParentName, BriefComment, RequiresDotToArrowCorrection); Chunks.clear(); return Result; } Index: lib/Sema/SemaCodeComplete.cpp =================================================================== --- lib/Sema/SemaCodeComplete.cpp +++ lib/Sema/SemaCodeComplete.cpp @@ -2740,7 +2740,8 @@ CodeCompletionAllocator &Allocator, CodeCompletionTUInfo &CCTUInfo, bool IncludeBriefComments) { - CodeCompletionBuilder Result(Allocator, CCTUInfo, Priority, Availability); + CodeCompletionBuilder Result(Allocator, CCTUInfo, Priority, Availability, + RequiresDotToArrowCorrection); PrintingPolicy Policy = getCompletionPrintingPolicy(Ctx, PP); if (Kind == RK_Pattern) { @@ -3961,62 +3962,41 @@ } } -void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base, - 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(); - - if (IsArrow) { - if (const PointerType *Ptr = BaseType->getAs()) - BaseType = Ptr->getPointeeType(); - else if (BaseType->isObjCObjectPointerType()) - /*Do nothing*/ ; - else - return; - } - +static ResultBuilder +GetCodeCompleteMemberReferenceResults(Sema &SemaRef, Scope *S, + QualType BaseType, bool IsArrow, + bool IsBaseExprStatement) { enum CodeCompletionContext::Kind contextKind; - + if (IsArrow) { contextKind = CodeCompletionContext::CCC_ArrowMemberAccess; - } - else { + } else { if (BaseType->isObjCObjectPointerType() || BaseType->isObjCObjectOrInterfaceType()) { contextKind = CodeCompletionContext::CCC_ObjCPropertyAccess; - } - else { + } else { contextKind = CodeCompletionContext::CCC_DotMemberAccess; } } CodeCompletionContext CCContext(contextKind, BaseType); - ResultBuilder Results(*this, CodeCompleter->getAllocator(), - CodeCompleter->getCodeCompletionTUInfo(), - CCContext, - &ResultBuilder::IsMember); + ResultBuilder Results(SemaRef, SemaRef.CodeCompleter->getAllocator(), + SemaRef.CodeCompleter->getCodeCompletionTUInfo(), + CCContext, &ResultBuilder::IsMember); Results.EnterNewScope(); if (const RecordType *Record = BaseType->getAs()) { - AddRecordMembersCompletionResults(*this, Results, S, BaseType, + AddRecordMembersCompletionResults(SemaRef, Results, S, BaseType, Record->getDecl()); } else if (const auto *TST = BaseType->getAs()) { TemplateName TN = TST->getTemplateName(); if (const auto *TD = dyn_cast_or_null(TN.getAsTemplateDecl())) { CXXRecordDecl *RD = TD->getTemplatedDecl(); - AddRecordMembersCompletionResults(*this, Results, S, BaseType, RD); + AddRecordMembersCompletionResults(SemaRef, Results, S, BaseType, RD); } } else if (const auto *ICNT = BaseType->getAs()) { if (auto *RD = ICNT->getDecl()) - AddRecordMembersCompletionResults(*this, Results, S, BaseType, RD); + AddRecordMembersCompletionResults(SemaRef, Results, S, BaseType, RD); } else if (!IsArrow && BaseType->isObjCObjectPointerType()) { // Objective-C property reference. AddedPropertiesSet AddedProperties; @@ -4026,42 +4006,120 @@ // Add property results based on our interface. assert(ObjCPtr && "Non-NULL pointer guaranteed above!"); AddObjCProperties(CCContext, ObjCPtr->getInterfaceDecl(), true, - /*AllowNullaryMethods=*/true, CurContext, + /*AllowNullaryMethods=*/true, SemaRef.CurContext, AddedProperties, Results, IsBaseExprStatement); } // Add properties from the protocols in a qualified interface. for (auto *I : BaseType->getAs()->quals()) AddObjCProperties(CCContext, I, true, /*AllowNullaryMethods=*/true, - CurContext, AddedProperties, Results, + SemaRef.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()) + if (const ObjCObjectPointerType *ObjCPtr = + BaseType->getAs()) Class = ObjCPtr->getInterfaceDecl(); else Class = BaseType->getAs()->getInterface(); - + // Add all ivars from this class and its superclasses. if (Class) { - CodeCompletionDeclConsumer Consumer(Results, CurContext); + CodeCompletionDeclConsumer Consumer(Results, SemaRef.CurContext); Results.setFilter(&ResultBuilder::IsObjCIvar); - LookupVisibleDecls(Class, LookupMemberName, Consumer, - CodeCompleter->includeGlobals()); + SemaRef.LookupVisibleDecls(Class, SemaRef.LookupMemberName, Consumer, + SemaRef.CodeCompleter->includeGlobals()); } } - + // FIXME: How do we cope with isa? - + Results.ExitScope(); + return Results; +} +static void TryCompleteArrowInsteadOfDot(Sema &SemaRef, Scope *S, Expr *Base, + SourceLocation OpLoc, + bool IsBaseExprStatement, + ResultBuilder &ExistingResults) { + QualType BaseType = Base->getType(); + if (BaseType->isPointerType()) { + BaseType = BaseType->getAs()->getPointeeType(); + } else { + const DeclarationName ArrowOpName = + SemaRef.Context.DeclarationNames.getCXXOperatorName(OO_Arrow); + bool HasArrowOp = false; + for (CodeCompletionResult *result = ExistingResults.data(); + result != ExistingResults.data() + ExistingResults.size(); ++result) { + if (result->Kind != CodeCompletionResult::RK_Declaration) + continue; + const NamedDecl *decl = result->getDeclaration(); + if (decl && decl->getDeclName() == ArrowOpName) { + HasArrowOp = true; + break; + } + } + if (!HasArrowOp) + return; + ParsedType ObjectType; + bool MayBePseudoDestructor = false; + ExprResult LHS = SemaRef.ActOnStartCXXMemberReference( + S, Base, OpLoc, tok::arrow, ObjectType, MayBePseudoDestructor); + ExprResult ConvertedBase = + SemaRef.PerformMemberExprBaseConversion(LHS.get(), true); + if (ConvertedBase.isInvalid()) + return; + Base = ConvertedBase.get(); + BaseType = Base->getType(); + if (const PointerType *Ptr = BaseType->getAs()) + BaseType = Ptr->getPointeeType(); + } + + ResultBuilder ArrowResults = GetCodeCompleteMemberReferenceResults( + SemaRef, S, BaseType, true, IsBaseExprStatement); + for (size_t i = 0; i < ArrowResults.size(); ++i) + ArrowResults.data()[i].RequiresDotToArrowCorrection = true; + HandleCodeCompleteResults(&SemaRef, SemaRef.CodeCompleter, + ArrowResults.getCompletionContext(), + ArrowResults.data(), ArrowResults.size()); +} + +void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base, + 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(); + + if (IsArrow) { + if (const PointerType *Ptr = BaseType->getAs()) + BaseType = Ptr->getPointeeType(); + else if (BaseType->isObjCObjectPointerType()) + /*Do nothing*/; + else + return; + } + + ResultBuilder Results = GetCodeCompleteMemberReferenceResults( + *this, S, BaseType, IsArrow, IsBaseExprStatement); + + if (CodeCompleter->tryArrowInsteadOfDot() && !IsArrow && + !BaseType->isObjCObjectPointerType() && + !BaseType->isObjCObjectOrInterfaceType()) { + TryCompleteArrowInsteadOfDot(*this, S, Base, OpLoc, IsBaseExprStatement, + Results); + } // 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, Index: test/Index/complete-arrow-for-dot.cpp =================================================================== --- /dev/null +++ test/Index/complete-arrow-for-dot.cpp @@ -0,0 +1,29 @@ +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_TRY_ARROW_INSTEAD_OF_DOT=1 c-index-test -code-completion-at=%s:10:8 %s | FileCheck -check-prefix=CHECK-WITH-CORRECTION %s + this.; +} + +// CHECK-NOT: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34) (requires correction: "." to "->") +// CHECK-NOT: FieldDecl:{ResultType int}{TypedText field} (35) (requires correction: "." to "->") +// CHECK-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires correction: "." to "->") +// CHECK-NOT: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires correction: "." to "->") +// CHECK-NOT: StructDecl:{TypedText X}{Text ::} (75) (requires correction: "." to "->") +// CHECK-NOT: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires correction: "." to "->") +// CHECK: Completion contexts: +// CHECK: Dot member access + +// CHECK-WITH-CORRECTION: CXXMethod:{ResultType void}{TypedText doSomething}{LeftParen (}{RightParen )} (34) (requires correction: "." to "->") +// CHECK-WITH-CORRECTION: FieldDecl:{ResultType int}{TypedText field} (35) (requires correction: "." to "->") +// CHECK-WITH-CORRECTION: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder const X &}{RightParen )} (79) (requires correction: "." to "->") +// CHECK-WITH-CORRECTION: CXXMethod:{ResultType X &}{TypedText operator=}{LeftParen (}{Placeholder X &&}{RightParen )} (79) (requires correction: "." to "->") +// CHECK-WITH-CORRECTION: StructDecl:{TypedText X}{Text ::} (75) (requires correction: "." to "->") +// CHECK-WITH-CORRECTION: CXXDestructor:{ResultType void}{TypedText ~X}{LeftParen (}{RightParen )} (79) (requires correction: "." to "->") +// CHECK-WITH-CORRECTION: Completion contexts: +// CHECK-WITH-CORRECTION: Dot member access 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 @@ -2227,7 +2227,13 @@ fprintf(file, "(brief comment: %s)", BriefCommentCString); } clang_disposeString(BriefComment); - + + unsigned RequiresDotToArrowCorrection = + clang_getCompletionRequiresDotToArrowCorrection( + completion_result->CompletionString); + if (RequiresDotToArrowCorrection) { + fprintf(file, " (requires correction: \".\" to \"->\")"); + } fprintf(file, "\n"); } @@ -2324,6 +2330,8 @@ completionOptions |= CXCodeComplete_IncludeCodePatterns; if (getenv("CINDEXTEST_COMPLETION_BRIEF_COMMENTS")) completionOptions |= CXCodeComplete_IncludeBriefComments; + if (getenv("CINDEXTEST_COMPLETION_TRY_ARROW_INSTEAD_OF_DOT")) + completionOptions |= CXCodeComplete_TryArrowInsteadOfDot; if (timing_only) input += strlen("-code-completion-timing="); Index: tools/libclang/CIndexCodeCompletion.cpp =================================================================== --- tools/libclang/CIndexCodeCompletion.cpp +++ tools/libclang/CIndexCodeCompletion.cpp @@ -238,6 +238,16 @@ return cxstring::createRef(CCStr->getBriefComment()); } +unsigned +clang_getCompletionRequiresDotToArrowCorrection(CXCompletionString completion_string) { + CodeCompletionString *CCStr = (CodeCompletionString *)completion_string; + + if (!CCStr) + return false; + + return CCStr->requiresDotToArrowCorrection(); +} + namespace { /// \brief The CXCodeCompleteResults structure we allocate internally; @@ -643,13 +653,13 @@ ArrayRef unsaved_files, unsigned options) { bool IncludeBriefComments = options & CXCodeComplete_IncludeBriefComments; + bool TryArrowInsteadOfDot = options & CXCodeComplete_TryArrowInsteadOfDot; #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)) { @@ -689,6 +699,7 @@ // Create a code-completion consumer to capture the results. CodeCompleteOptions Opts; Opts.IncludeBriefComments = IncludeBriefComments; + Opts.TryArrowInsteadOfDot = TryArrowInsteadOfDot; CaptureCompletionResults Capture(Opts, *Results, &TU); // Perform completion.