From: Douglas Gregor Date: Tue, 2 Nov 2010 20:36:02 +0000 (+0000) Subject: Teach code completion to provide property results when the property X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=ca45da0c6da20d9c0c903370f99af5e9a186e0da;p=clang Teach code completion to provide property results when the property can be used to automatically synthesize an ivar. git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@118052 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Sema/Sema.h b/include/clang/Sema/Sema.h index 6f52bbbb76..54357eb1da 100644 --- a/include/clang/Sema/Sema.h +++ b/include/clang/Sema/Sema.h @@ -1377,6 +1377,14 @@ public: ObjCIvarDecl **Fields, unsigned nIvars, SourceLocation Loc); + /// \brief Determine whether we can synthesize a provisional ivar for the + /// given name. + ObjCPropertyDecl *canSynthesizeProvisionalIvar(IdentifierInfo *II); + + /// \brief Determine whether we can synthesize a provisional ivar for the + /// given property. + bool canSynthesizeProvisionalIvar(ObjCPropertyDecl *Property); + /// ImplMethodsVsClassMethods - This is main routine to warn if any method /// remains unimplemented in the class or category @implementation. void ImplMethodsVsClassMethods(Scope *S, ObjCImplDecl* IMPDecl, diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index b07ae391f9..c245e38bb4 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -147,9 +147,13 @@ namespace { /// \brief The selector that we prefer. Selector PreferredSelector; - /// \brief The completion context in which + /// \brief The completion context in which we are gathering results. CodeCompletionContext CompletionContext; + /// \brief If we are in an instance method definition, the @implementation + /// object. + ObjCImplementationDecl *ObjCImplementation; + void AdjustResultPriorityForDecl(Result &R); void MaybeAddConstructorResults(Result R); @@ -160,7 +164,27 @@ namespace { LookupFilter Filter = 0) : SemaRef(SemaRef), Filter(Filter), AllowNestedNameSpecifiers(false), HasObjectTypeQualifiers(false), - CompletionContext(CompletionContext) { } + CompletionContext(CompletionContext), + ObjCImplementation(0) + { + // If this is an Objective-C instance method definition, dig out the + // corresponding implementation. + switch (CompletionContext.getKind()) { + case CodeCompletionContext::CCC_Expression: + case CodeCompletionContext::CCC_ObjCMessageReceiver: + case CodeCompletionContext::CCC_ParenthesizedExpression: + case CodeCompletionContext::CCC_Statement: + case CodeCompletionContext::CCC_Recovery: + if (ObjCMethodDecl *Method = SemaRef.getCurMethodDecl()) + if (Method->isInstanceMethod()) + if (ObjCInterfaceDecl *Interface = Method->getClassInterface()) + ObjCImplementation = Interface->getImplementation(); + break; + + default: + break; + } + } /// \brief Whether we should include code patterns in the completion /// results. @@ -203,7 +227,7 @@ namespace { void setPreferredSelector(Selector Sel) { PreferredSelector = Sel; } - + /// \brief Retrieve the code-completion context for which results are /// being collected. const CodeCompletionContext &getCompletionContext() const { @@ -919,9 +943,14 @@ bool ResultBuilder::IsOrdinaryName(NamedDecl *ND) const { unsigned IDNS = Decl::IDNS_Ordinary; if (SemaRef.getLangOptions().CPlusPlus) IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace | Decl::IDNS_Member; - else if (SemaRef.getLangOptions().ObjC1 && isa(ND)) - return true; - + else if (SemaRef.getLangOptions().ObjC1) { + if (isa(ND)) + return true; + if (isa(ND) && + SemaRef.canSynthesizeProvisionalIvar(cast(ND))) + return true; + } + return ND->getIdentifierNamespace() & IDNS; } @@ -935,9 +964,14 @@ bool ResultBuilder::IsOrdinaryNonTypeName(NamedDecl *ND) const { unsigned IDNS = Decl::IDNS_Ordinary; if (SemaRef.getLangOptions().CPlusPlus) IDNS |= Decl::IDNS_Tag | Decl::IDNS_Namespace | Decl::IDNS_Member; - else if (SemaRef.getLangOptions().ObjC1 && isa(ND)) - return true; - + else if (SemaRef.getLangOptions().ObjC1) { + if (isa(ND)) + return true; + if (isa(ND) && + SemaRef.canSynthesizeProvisionalIvar(cast(ND))) + return true; + } + return ND->getIdentifierNamespace() & IDNS; } diff --git a/lib/Sema/SemaExpr.cpp b/lib/Sema/SemaExpr.cpp index 9e28172c97..fdc4828cf1 100644 --- a/lib/Sema/SemaExpr.cpp +++ b/lib/Sema/SemaExpr.cpp @@ -1027,25 +1027,41 @@ bool Sema::DiagnoseEmptyLookup(Scope *S, CXXScopeSpec &SS, LookupResult &R, return true; } -static ObjCPropertyDecl *OkToSynthesizeProvisionalIvar(Sema &SemaRef, - IdentifierInfo *II, - SourceLocation NameLoc) { - ObjCMethodDecl *CurMeth = SemaRef.getCurMethodDecl(); +ObjCPropertyDecl *Sema::canSynthesizeProvisionalIvar(IdentifierInfo *II) { + ObjCMethodDecl *CurMeth = getCurMethodDecl(); ObjCInterfaceDecl *IDecl = CurMeth->getClassInterface(); if (!IDecl) return 0; ObjCImplementationDecl *ClassImpDecl = IDecl->getImplementation(); if (!ClassImpDecl) return 0; - ObjCPropertyDecl *property = SemaRef.LookupPropertyDecl(IDecl, II); + ObjCPropertyDecl *property = LookupPropertyDecl(IDecl, II); if (!property) return 0; if (ObjCPropertyImplDecl *PIDecl = ClassImpDecl->FindPropertyImplDecl(II)) - if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic) + if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic || + PIDecl->getPropertyIvarDecl()) return 0; return property; } +bool Sema::canSynthesizeProvisionalIvar(ObjCPropertyDecl *Property) { + ObjCMethodDecl *CurMeth = getCurMethodDecl(); + ObjCInterfaceDecl *IDecl = CurMeth->getClassInterface(); + if (!IDecl) + return false; + ObjCImplementationDecl *ClassImpDecl = IDecl->getImplementation(); + if (!ClassImpDecl) + return false; + if (ObjCPropertyImplDecl *PIDecl + = ClassImpDecl->FindPropertyImplDecl(Property->getIdentifier())) + if (PIDecl->getPropertyImplementation() == ObjCPropertyImplDecl::Dynamic || + PIDecl->getPropertyIvarDecl()) + return false; + + return true; +} + static ObjCIvarDecl *SynthesizeProvisionalIvar(Sema &SemaRef, LookupResult &Lookup, IdentifierInfo *II, @@ -1228,8 +1244,7 @@ ExprResult Sema::ActOnIdExpression(Scope *S, if (getLangOptions().ObjCNonFragileABI && IvarLookupFollowUp && !getLangOptions().ObjCNonFragileABI2 && Var->isFileVarDecl()) { - ObjCPropertyDecl *Property = - OkToSynthesizeProvisionalIvar(*this, II, NameLoc); + ObjCPropertyDecl *Property = canSynthesizeProvisionalIvar(II); if (Property) { Diag(NameLoc, diag::warn_ivar_variable_conflict) << Var->getDeclName(); Diag(Property->getLocation(), diag::note_property_declare); diff --git a/lib/Sema/SemaLookup.cpp b/lib/Sema/SemaLookup.cpp index acc5b354f8..f0c21f4396 100644 --- a/lib/Sema/SemaLookup.cpp +++ b/lib/Sema/SemaLookup.cpp @@ -2630,9 +2630,27 @@ static void LookupVisibleDecls(Scope *S, LookupResult &Result, // For instance methods, look for ivars in the method's interface. LookupResult IvarResult(Result.getSema(), Result.getLookupName(), Result.getNameLoc(), Sema::LookupMemberName); - if (ObjCInterfaceDecl *IFace = Method->getClassInterface()) + if (ObjCInterfaceDecl *IFace = Method->getClassInterface()) { LookupVisibleDecls(IFace, IvarResult, /*QualifiedNameLookup=*/false, /*InBaseClass=*/false, Consumer, Visited); + + // Look for properties from which we can synthesize ivars, if + // permitted. + if (Result.getSema().getLangOptions().ObjCNonFragileABI2 && + IFace->getImplementation() && + Result.getLookupKind() == Sema::LookupOrdinaryName) { + for (ObjCInterfaceDecl::prop_iterator + P = IFace->prop_begin(), + PEnd = IFace->prop_end(); + P != PEnd; ++P) { + if (Result.getSema().canSynthesizeProvisionalIvar(*P) && + !IFace->lookupInstanceVariable((*P)->getIdentifier())) { + Consumer.FoundDecl(*P, Visited.checkHidden(*P), false); + Visited.add(*P); + } + } + } + } } // We've already performed all of the name lookup that we need diff --git a/test/Index/complete-synthesized.m b/test/Index/complete-synthesized.m new file mode 100644 index 0000000000..a97eeab8cb --- /dev/null +++ b/test/Index/complete-synthesized.m @@ -0,0 +1,56 @@ +// Note: this test is line- and column-sensitive. Test commands are at +// the end. + + +@interface A +@property int prop1; +@end + +@interface B : A { + float _prop2; +} +@property float prop2; +@property short prop3; +@end + +@interface B () +@property double prop4; +@end + +@implementation B +@synthesize prop2 = _prop2; + +- (int)method { + return _prop2; +} + +@dynamic prop3; + +- (short)method2 { + return prop4; +} + +- (short)method3 { + return prop3; +} +@end + +// RUN: c-index-test -code-completion-at=%s:24:1 -Xclang -fobjc-nonfragile-abi2 %s | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: NotImplemented:{TypedText _Bool} (50) +// CHECK-CC1: ObjCIvarDecl:{ResultType float}{TypedText _prop2} (35) +// CHECK-CC1-NOT: prop2 +// CHECK-CC1: ObjCPropertyDecl:{ResultType short}{TypedText prop3} (35) +// CHECK-CC1: ObjCPropertyDecl:{ResultType double}{TypedText prop4} (35) + +// RUN: c-index-test -code-completion-at=%s:30:2 -Xclang -fobjc-nonfragile-abi2 %s | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: NotImplemented:{TypedText _Bool} (50) +// CHECK-CC2: ObjCIvarDecl:{ResultType float}{TypedText _prop2} (35) +// CHECK-CC2-NOT: prop3 +// CHECK-CC2: ObjCPropertyDecl:{ResultType double}{TypedText prop4} (35) + +// RUN: c-index-test -code-completion-at=%s:34:2 -Xclang -fobjc-nonfragile-abi2 %s | FileCheck -check-prefix=CHECK-CC3 %s +// CHECK-CC3: NotImplemented:{TypedText _Bool} (50) +// CHECK-CC3: ObjCIvarDecl:{ResultType float}{TypedText _prop2} (35) +// CHECK-CC3: ObjCIvarDecl:{ResultType double}{TypedText prop4} +// CHECK-CC3-NOT: ObjCPropertyDecl:{ResultType double}{TypedText prop4} (35) +// CHECK-CC1: restrict