summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIvan Donchevskii <ivan.donchevskii@qt.io>2018-05-17 10:52:13 +0200
committerIvan Donchevskii <ivan.donchevskii@qt.io>2018-05-29 08:11:03 +0000
commit12a3d091adc5a3a730904ade40a4eb74ffec7663 (patch)
tree4af3bf14c70de0c228d5c9162532ae2ddf6ca62a
parentcbe51a943adebe43cfd8f20a190670a1d6a0e908 (diff)
[backported/clang-7] Optionally add code completion results for arrow instead of dot
-------------------------------------------------------------------------- * https://reviews.llvm.org/D41537 -------------------------------------------------------------------------- Currently getting such completions requires source correction, reparsing and calling completion again. And if it shows no results and rollback is required then it costs one more reparse. With this change it's possible to get all results which can be later filtered to split changes which require correction. Change-Id: Id2fc3f0518fd53f2a6d0097da3ee723ddeb372ab Reviewed-by: Nikolai Kosjar <nikolai.kosjar@qt.io>
-rw-r--r--include/clang/Driver/CC1Options.td2
-rw-r--r--include/clang/Sema/CodeCompleteConsumer.h41
-rw-r--r--include/clang/Sema/CodeCompleteOptions.h7
-rw-r--r--include/clang/Sema/Sema.h2
-rw-r--r--lib/Frontend/ASTUnit.cpp1
-rw-r--r--lib/Frontend/CompilerInvocation.cpp2
-rw-r--r--lib/Parse/ParseExpr.cpp26
-rw-r--r--lib/Sema/CodeCompleteConsumer.cpp19
-rw-r--r--lib/Sema/SemaCodeComplete.cpp194
-rw-r--r--test/CodeCompletion/member-access.cpp44
10 files changed, 248 insertions, 90 deletions
diff --git a/include/clang/Driver/CC1Options.td b/include/clang/Driver/CC1Options.td
index 7dea88b62c..86dc2e4c12 100644
--- a/include/clang/Driver/CC1Options.td
+++ b/include/clang/Driver/CC1Options.td
@@ -447,6 +447,8 @@ def no_code_completion_ns_level_decls : Flag<["-"], "no-code-completion-ns-level
HelpText<"Do not include declarations inside namespaces (incl. global namespace) in the code-completion results.">;
def code_completion_brief_comments : Flag<["-"], "code-completion-brief-comments">,
HelpText<"Include brief documentation comments in code-completion results.">;
+def code_completion_with_fixits : Flag<["-"], "code-completion-with-fixits">,
+ HelpText<"Include code completion results which require small fix-its.">;
def disable_free : Flag<["-"], "disable-free">,
HelpText<"Disable freeing of memory on exit">;
def discard_value_names : Flag<["-"], "discard-value-names">,
diff --git a/include/clang/Sema/CodeCompleteConsumer.h b/include/clang/Sema/CodeCompleteConsumer.h
index 5d280b5608..720a150f51 100644
--- a/include/clang/Sema/CodeCompleteConsumer.h
+++ b/include/clang/Sema/CodeCompleteConsumer.h
@@ -680,6 +680,33 @@ public:
/// \brief The availability of this result.
CXAvailabilityKind Availability;
+ /// \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;
+ /// In 'vec_ptr.^', one of completion items is 'push_back', it requires
+ /// replacing '.' with '->'.
+ /// In 'vec_ptr->^', one of completion items is 'release', it requires
+ /// replacing '->' with '.'.
+ std::vector<FixItHint> FixIts;
+
/// \brief Whether this result is hidden by another name.
bool Hidden : 1;
@@ -704,17 +731,19 @@ public:
NestedNameSpecifier *Qualifier;
/// \brief Build a result that refers to a declaration.
- CodeCompletionResult(const NamedDecl *Declaration,
- unsigned Priority,
+ CodeCompletionResult(const NamedDecl *Declaration, unsigned Priority,
NestedNameSpecifier *Qualifier = nullptr,
bool QualifierIsInformative = false,
- bool Accessible = true)
+ bool Accessible = true,
+ std::vector<FixItHint> FixIts = std::vector<FixItHint>())
: Declaration(Declaration), Priority(Priority),
StartParameter(0), Kind(RK_Declaration),
Availability(CXAvailability_Available), Hidden(false),
QualifierIsInformative(QualifierIsInformative),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
- DeclaringEntity(false), Qualifier(Qualifier) {
+ DeclaringEntity(false), Qualifier(Qualifier),
+ FixIts(std::move(FixIts)) {
+ //FIXME: Add assert to check FixIts range requirements.
computeCursorKindAndAvailability(Accessible);
}
@@ -934,6 +963,10 @@ public:
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 Determine whether the output of this consumer is binary.
bool isOutputBinary() const { return OutputIsBinary; }
diff --git a/include/clang/Sema/CodeCompleteOptions.h b/include/clang/Sema/CodeCompleteOptions.h
index 091d8ca605..bf2e3f40c5 100644
--- a/include/clang/Sema/CodeCompleteOptions.h
+++ b/include/clang/Sema/CodeCompleteOptions.h
@@ -35,9 +35,14 @@ public:
/// Show brief documentation comments in code completion results.
unsigned IncludeBriefComments : 1;
+ /// Include 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) {}
+ IncludeNamespaceLevelDecls(1), IncludeBriefComments(0),
+ IncludeFixIts(0) {}
};
} // namespace clang
diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h
index 055d14f2d0..f3c351a161 100644
--- a/include/clang/Sema/Sema.h
+++ b/include/clang/Sema/Sema.h
@@ -10159,7 +10159,7 @@ public:
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);
diff --git a/lib/Frontend/ASTUnit.cpp b/lib/Frontend/ASTUnit.cpp
index 84598fa4d5..c334e3e6db 100644
--- a/lib/Frontend/ASTUnit.cpp
+++ b/lib/Frontend/ASTUnit.cpp
@@ -2079,6 +2079,7 @@ void ASTUnit::CodeComplete(
CodeCompleteOpts.IncludeCodePatterns = IncludeCodePatterns;
CodeCompleteOpts.IncludeGlobals = CachedCompletionResults.empty();
CodeCompleteOpts.IncludeBriefComments = IncludeBriefComments;
+ CodeCompleteOpts.IncludeFixIts = Consumer.includeFixIts();
assert(IncludeBriefComments == this->IncludeBriefCommentsInCodeCompletion);
diff --git a/lib/Frontend/CompilerInvocation.cpp b/lib/Frontend/CompilerInvocation.cpp
index 6ce719aac6..833039e693 100644
--- a/lib/Frontend/CompilerInvocation.cpp
+++ b/lib/Frontend/CompilerInvocation.cpp
@@ -1416,6 +1416,8 @@ static InputKind ParseFrontendArgs(FrontendOptions &Opts, ArgList &Args,
= !Args.hasArg(OPT_no_code_completion_ns_level_decls);
Opts.CodeCompleteOpts.IncludeBriefComments
= Args.hasArg(OPT_code_completion_brief_comments);
+ Opts.CodeCompleteOpts.IncludeFixIts
+ = Args.hasArg(OPT_code_completion_with_fixits);
Opts.OverrideRecordLayoutsFile
= Args.getLastArgValue(OPT_foverride_record_layout_EQ);
diff --git a/lib/Parse/ParseExpr.cpp b/lib/Parse/ParseExpr.cpp
index 1b8865edb7..cb28fcf9bd 100644
--- a/lib/Parse/ParseExpr.cpp
+++ b/lib/Parse/ParseExpr.cpp
@@ -1686,8 +1686,10 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
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() ||
@@ -1712,11 +1714,25 @@ Parser::ParsePostfixExpressionSuffix(ExprResult LHS) {
}
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();
diff --git a/lib/Sema/CodeCompleteConsumer.cpp b/lib/Sema/CodeCompleteConsumer.cpp
index 3431ddcf70..ecec7f4434 100644
--- a/lib/Sema/CodeCompleteConsumer.cpp
+++ b/lib/Sema/CodeCompleteConsumer.cpp
@@ -477,7 +477,24 @@ PrintingCodeCompleteConsumer::ProcessCodeCompleteResults(Sema &SemaRef,
if (const char *BriefComment = CCS->getBriefComment())
OS << " : " << BriefComment;
}
-
+ for (const FixItHint &FixIt : Results[I].FixIts) {
+ const SourceLocation BLoc = FixIt.RemoveRange.getBegin();
+ const SourceLocation ELoc = FixIt.RemoveRange.getEnd();
+
+ SourceManager &SM = SemaRef.SourceMgr;
+ std::pair<FileID, unsigned> BInfo = SM.getDecomposedLoc(BLoc);
+ std::pair<FileID, unsigned> EInfo = SM.getDecomposedLoc(ELoc);
+ // Adjust for token ranges.
+ if (FixIt.RemoveRange.isTokenRange())
+ EInfo.second += Lexer::MeasureTokenLength(ELoc, SM, SemaRef.LangOpts);
+
+ OS << " (requires fix-it:"
+ << " {" << SM.getLineNumber(BInfo.first, BInfo.second) << ':'
+ << SM.getColumnNumber(BInfo.first, BInfo.second) << '-'
+ << SM.getLineNumber(EInfo.first, EInfo.second) << ':'
+ << SM.getColumnNumber(EInfo.first, EInfo.second) << "}"
+ << " to \"" << FixIt.CodeToInsert << "\")";
+ }
OS << '\n';
break;
diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp
index 2acc896d53..aa86fb3bac 100644
--- a/lib/Sema/SemaCodeComplete.cpp
+++ b/lib/Sema/SemaCodeComplete.cpp
@@ -1280,10 +1280,13 @@ namespace {
class CodeCompletionDeclConsumer : public VisibleDeclConsumer {
ResultBuilder &Results;
DeclContext *CurContext;
+ std::vector<FixItHint> FixIts;
public:
- CodeCompletionDeclConsumer(ResultBuilder &Results, DeclContext *CurContext)
- : Results(Results), CurContext(CurContext) { }
+ CodeCompletionDeclConsumer(
+ ResultBuilder &Results, DeclContext *CurContext,
+ std::vector<FixItHint> FixIts = std::vector<FixItHint>())
+ : Results(Results), CurContext(CurContext), FixIts(std::move(FixIts)) {}
void FoundDecl(NamedDecl *ND, NamedDecl *Hiding, DeclContext *Ctx,
bool InBaseClass) override {
@@ -1292,7 +1295,7 @@ namespace {
Accessible = Results.getSema().IsSimplyAccessible(ND, Ctx);
ResultBuilder::Result Result(ND, Results.getBasePriority(ND), nullptr,
- false, Accessible);
+ false, Accessible, FixIts);
Results.AddResult(Result, CurContext, Hiding, InBaseClass);
}
};
@@ -3929,14 +3932,18 @@ static void AddObjCProperties(
static void AddRecordMembersCompletionResults(Sema &SemaRef,
ResultBuilder &Results, Scope *S,
QualType BaseType,
- RecordDecl *RD) {
+ RecordDecl *RD,
+ Optional<FixItHint> AccessOpFixIt) {
// Indicate that we are performing a member access, and the cv-qualifiers
// for the base object type.
Results.setObjectTypeQualifiers(BaseType.getQualifiers());
// Access to a C/C++ class, struct, or union.
Results.allowNestedNameSpecifiers();
- CodeCompletionDeclConsumer Consumer(Results, SemaRef.CurContext);
+ std::vector<FixItHint> FixIts;
+ if (AccessOpFixIt)
+ FixIts.emplace_back(AccessOpFixIt.getValue());
+ CodeCompletionDeclConsumer Consumer(Results, SemaRef.CurContext, std::move(FixIts));
SemaRef.LookupVisibleDecls(RD, Sema::LookupMemberName, Consumer,
SemaRef.CodeCompleter->includeGlobals(),
/*IncludeDependentBases=*/true);
@@ -3962,6 +3969,7 @@ static void AddRecordMembersCompletionResults(Sema &SemaRef,
}
void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
+ Expr *OtherOpBase,
SourceLocation OpLoc, bool IsArrow,
bool IsBaseExprStatement) {
if (!Base || !CodeCompleter)
@@ -3970,98 +3978,128 @@ void Sema::CodeCompleteMemberReferenceExpr(Scope *S, Expr *Base,
ExprResult ConvertedBase = PerformMemberExprBaseConversion(Base, IsArrow);
if (ConvertedBase.isInvalid())
return;
- Base = ConvertedBase.get();
+ QualType ConvertedBaseType = ConvertedBase.get()->getType();
- QualType BaseType = Base->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);
- 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());
+
+ auto DoCompletion = [&](Expr *Base, bool IsArrow, Optional<FixItHint> AccessOpFixIt) -> 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(),
+ std::move(AccessOpFixIt));
+ } 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,
+ std::move(AccessOpFixIt));
+ }
+ } else if (const auto *ICNT = BaseType->getAs<InjectedClassNameType>()) {
+ if (auto *RD = ICNT->getDecl())
+ AddRecordMembersCompletionResults(*this, Results, S, BaseType, RD,
+ std::move(AccessOpFixIt));
+ } 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());
+ }
}
+
+ // FIXME: How do we cope with isa?
+ return true;
+ };
+
+ Results.EnterNewScope();
+
+ bool CompletionSucceded = DoCompletion(Base, IsArrow, None);
+ if (CodeCompleter->includeFixIts()) {
+ const CharSourceRange OpRange =
+ CharSourceRange::getTokenRange(OpLoc, OpLoc);
+ CompletionSucceded |= DoCompletion(
+ OtherOpBase, !IsArrow,
+ FixItHint::CreateReplacement(OpRange, IsArrow ? "." : "->"));
}
-
- // 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,
diff --git a/test/CodeCompletion/member-access.cpp b/test/CodeCompletion/member-access.cpp
index a0dc7b4856..a5b973f8b3 100644
--- a/test/CodeCompletion/member-access.cpp
+++ b/test/CodeCompletion/member-access.cpp
@@ -164,3 +164,47 @@ void dependentColonColonCompletion() {
typename Template<T>::Nested m;
// RUN: %clang_cc1 -fsyntax-only -code-completion-at=%s:164:25 %s -o - | FileCheck -check-prefix=CHECK-CC7 %s
}
+
+class Proxy2 {
+public:
+ Derived *operator->() const;
+ int member5;
+};
+
+void test2(const Proxy2 &p) {
+ p->
+}
+
+void test3(const Proxy2 &p) {
+ p.
+}
+
+// RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:175:6 %s -o - | FileCheck -check-prefix=CHECK-CC8 --implicit-check-not="Derived : Derived(" %s
+// CHECK-CC8: Base1 : Base1::
+// CHECK-CC8: member1 : [#int#][#Base1::#]member1
+// CHECK-CC8: member1 : [#int#][#Base2::#]member1
+// CHECK-CC8: member2 : [#float#][#Base1::#]member2
+// CHECK-CC8: member3 : [#double#][#Base2::#]member3
+// CHECK-CC8: member4 : [#int#]member4
+// CHECK-CC8: member5 : [#int#]member5 (requires fix-it: {175:4-175:6} to ".")
+// CHECK-CC8: memfun1 : [#void#][#Base3::#]memfun1(<#float#>)
+// CHECK-CC8: memfun1 : [#void#][#Base3::#]memfun1(<#double#>)[# const#]
+// CHECK-CC8: memfun1 (Hidden) : [#void#]Base2::memfun1(<#int#>)
+// CHECK-CC8: memfun2 : [#void#][#Base3::#]memfun2(<#int#>)
+// CHECK-CC8: memfun3 : [#int#]memfun3(<#int#>)
+// CHECK-CC8: operator-> : [#Derived *#]operator->()[# const#] (requires fix-it: {175:4-175:6} to ".")
+
+// RUN: %clang_cc1 -fsyntax-only -code-completion-with-fixits -code-completion-at=%s:179:6 %s -o - | FileCheck -check-prefix=CHECK-CC9 --implicit-check-not="Derived : Derived(" %s
+// CHECK-CC9: Base1 : Base1::
+// CHECK-CC9: member1 : [#int#][#Base1::#]member1 (requires fix-it: {179:4-179:5} to "->")
+// CHECK-CC9: member1 : [#int#][#Base2::#]member1 (requires fix-it: {179:4-179:5} to "->")
+// CHECK-CC9: member2 : [#float#][#Base1::#]member2 (requires fix-it: {179:4-179:5} to "->")
+// CHECK-CC9: member3 : [#double#][#Base2::#]member3 (requires fix-it: {179:4-179:5} to "->")
+// CHECK-CC9: member4 : [#int#]member4 (requires fix-it: {179:4-179:5} to "->")
+// CHECK-CC9: member5 : [#int#]member5
+// CHECK-CC9: memfun1 : [#void#][#Base3::#]memfun1(<#float#>) (requires fix-it: {179:4-179:5} to "->")
+// CHECK-CC9: memfun1 : [#void#][#Base3::#]memfun1(<#double#>)[# const#] (requires fix-it: {179:4-179:5} to "->")
+// CHECK-CC9: memfun1 (Hidden) : [#void#]Base2::memfun1(<#int#>) (requires fix-it: {179:4-179:5} to "->")
+// CHECK-CC9: memfun2 : [#void#][#Base3::#]memfun2(<#int#>) (requires fix-it: {179:4-179:5} to "->")
+// CHECK-CC9: memfun3 : [#int#]memfun3(<#int#>) (requires fix-it: {179:4-179:5} to "->")
+// CHECK-CC9: operator-> : [#Derived *#]operator->()[# const#]