diff options
-rw-r--r-- | include/clang/AST/DeclObjC.h | 2 | ||||
-rw-r--r-- | include/clang/Parse/Action.h | 19 | ||||
-rw-r--r-- | lib/Parse/ParseObjc.cpp | 10 | ||||
-rw-r--r-- | lib/Sema/Sema.h | 4 | ||||
-rw-r--r-- | lib/Sema/SemaCodeComplete.cpp | 68 | ||||
-rw-r--r-- | test/Index/complete-categories.m | 39 |
6 files changed, 140 insertions, 2 deletions
diff --git a/include/clang/AST/DeclObjC.h b/include/clang/AST/DeclObjC.h index 13193ffab5..897776cdb9 100644 --- a/include/clang/AST/DeclObjC.h +++ b/include/clang/AST/DeclObjC.h @@ -808,7 +808,7 @@ public: /// - myMethod; /// @end /// -/// Cateogries also allow you to split the implementation of a class across +/// Categories also allow you to split the implementation of a class across /// several files (a feature more naturally supported in C++). /// /// Categories were originally inspired by dynamic languages such as Common diff --git a/include/clang/Parse/Action.h b/include/clang/Parse/Action.h index 62f7221a88..64d97dc015 100644 --- a/include/clang/Parse/Action.h +++ b/include/clang/Parse/Action.h @@ -2389,6 +2389,25 @@ public: /// \brief Code completion for an Objective-C implementation, after the /// @implementation but before any identifier. virtual void CodeCompleteObjCImplementationDecl(Scope *S) { } + + /// \brief Code completion for the category name in an Objective-C interface + /// declaration. + /// + /// This code completion action is invoked after the '(' that indicates + /// a category name within an Objective-C interface declaration. + virtual void CodeCompleteObjCInterfaceCategory(Scope *S, + IdentifierInfo *ClassName) { + } + + /// \brief Code completion for the category name in an Objective-C category + /// implementation. + /// + /// This code completion action is invoked after the '(' that indicates + /// the category name within an Objective-C category implementation. + virtual void CodeCompleteObjCImplementationCategory(Scope *S, + IdentifierInfo *ClassName) { + } + //@} }; diff --git a/lib/Parse/ParseObjc.cpp b/lib/Parse/ParseObjc.cpp index a60c89a0f1..e1f045bd8e 100644 --- a/lib/Parse/ParseObjc.cpp +++ b/lib/Parse/ParseObjc.cpp @@ -143,6 +143,11 @@ Parser::DeclPtrTy Parser::ParseObjCAtInterfaceDeclaration( SourceLocation categoryLoc, rparenLoc; IdentifierInfo *categoryId = 0; + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteObjCInterfaceCategory(CurScope, nameId); + ConsumeToken(); + } + // For ObjC2, the category name is optional (not an error). if (Tok.is(tok::identifier)) { categoryId = Tok.getIdentifierInfo(); @@ -1111,6 +1116,11 @@ Parser::DeclPtrTy Parser::ParseObjCAtImplementationDeclaration( SourceLocation categoryLoc, rparenLoc; IdentifierInfo *categoryId = 0; + if (Tok.is(tok::code_completion)) { + Actions.CodeCompleteObjCImplementationCategory(CurScope, nameId); + ConsumeToken(); + } + if (Tok.is(tok::identifier)) { categoryId = Tok.getIdentifierInfo(); categoryLoc = ConsumeToken(); diff --git a/lib/Sema/Sema.h b/lib/Sema/Sema.h index abef317d62..a13ffc54da 100644 --- a/lib/Sema/Sema.h +++ b/lib/Sema/Sema.h @@ -3649,6 +3649,10 @@ public: virtual void CodeCompleteObjCSuperclass(Scope *S, IdentifierInfo *ClassName); virtual void CodeCompleteObjCImplementationDecl(Scope *S); + virtual void CodeCompleteObjCInterfaceCategory(Scope *S, + IdentifierInfo *ClassName); + virtual void CodeCompleteObjCImplementationCategory(Scope *S, + IdentifierInfo *ClassName); //@} //===--------------------------------------------------------------------===// diff --git a/lib/Sema/SemaCodeComplete.cpp b/lib/Sema/SemaCodeComplete.cpp index eaa4f1514a..93aa08c4da 100644 --- a/lib/Sema/SemaCodeComplete.cpp +++ b/lib/Sema/SemaCodeComplete.cpp @@ -1933,7 +1933,7 @@ void Sema::CodeCompleteObjCSuperclass(Scope *S, IdentifierInfo *ClassName) { // Make sure that we ignore the class we're currently defining. NamedDecl *CurClass = LookupSingleName(TUScope, ClassName, LookupOrdinaryName); - if (isa<ObjCInterfaceDecl>(CurClass)) + if (CurClass && isa<ObjCInterfaceDecl>(CurClass)) Results.Ignore(CurClass); // Add all classes. @@ -1955,3 +1955,69 @@ void Sema::CodeCompleteObjCImplementationDecl(Scope *S) { Results.ExitScope(); HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); } + +void Sema::CodeCompleteObjCInterfaceCategory(Scope *S, + IdentifierInfo *ClassName) { + typedef CodeCompleteConsumer::Result Result; + + ResultBuilder Results(*this); + + // Ignore any categories we find that have already been implemented by this + // interface. + llvm::SmallPtrSet<IdentifierInfo *, 16> CategoryNames; + NamedDecl *CurClass + = LookupSingleName(TUScope, ClassName, LookupOrdinaryName); + if (ObjCInterfaceDecl *Class = dyn_cast_or_null<ObjCInterfaceDecl>(CurClass)) + for (ObjCCategoryDecl *Category = Class->getCategoryList(); Category; + Category = Category->getNextClassCategory()) + CategoryNames.insert(Category->getIdentifier()); + + // Add all of the categories we know about. + Results.EnterNewScope(); + TranslationUnitDecl *TU = Context.getTranslationUnitDecl(); + for (DeclContext::decl_iterator D = TU->decls_begin(), + DEnd = TU->decls_end(); + D != DEnd; ++D) + if (ObjCCategoryDecl *Category = dyn_cast<ObjCCategoryDecl>(*D)) + if (CategoryNames.insert(Category->getIdentifier())) + Results.MaybeAddResult(Result(Category, 0), CurContext); + Results.ExitScope(); + + HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); +} + +void Sema::CodeCompleteObjCImplementationCategory(Scope *S, + IdentifierInfo *ClassName) { + typedef CodeCompleteConsumer::Result Result; + + // Find the corresponding interface. If we couldn't find the interface, the + // program itself is ill-formed. However, we'll try to be helpful still by + // providing the list of all of the categories we know about. + NamedDecl *CurClass + = LookupSingleName(TUScope, ClassName, LookupOrdinaryName); + ObjCInterfaceDecl *Class = dyn_cast_or_null<ObjCInterfaceDecl>(CurClass); + if (!Class) + return CodeCompleteObjCInterfaceCategory(S, ClassName); + + ResultBuilder Results(*this); + + // Add all of the categories that have have corresponding interface + // declarations in this class and any of its superclasses, except for + // already-implemented categories in the class itself. + llvm::SmallPtrSet<IdentifierInfo *, 16> CategoryNames; + Results.EnterNewScope(); + bool IgnoreImplemented = true; + while (Class) { + for (ObjCCategoryDecl *Category = Class->getCategoryList(); Category; + Category = Category->getNextClassCategory()) + if ((!IgnoreImplemented || !Category->getImplementation()) && + CategoryNames.insert(Category->getIdentifier())) + Results.MaybeAddResult(Result(Category, 0), CurContext); + + Class = Class->getSuperClass(); + IgnoreImplemented = false; + } + Results.ExitScope(); + + HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size()); +} diff --git a/test/Index/complete-categories.m b/test/Index/complete-categories.m new file mode 100644 index 0000000000..92b14db936 --- /dev/null +++ b/test/Index/complete-categories.m @@ -0,0 +1,39 @@ +/* Note: the RUN lines are near the end of the file, since line/column + matter for this test. */ + +@interface I1 @end +@interface I2 @end +@interface I3 : I2 @end + +@interface I1(Cat1) @end +@interface I1(Cat2) @end +@interface I1(Cat3) @end + +@interface I2 (Cat2) @end +@interface I2 (Cat3) @end +@interface I2 (Cat2) @end +@interface I3 (Cat1) @end +@interface I3 (Cat2) @end + +@implementation I1(Cat2) @end +@implementation I1(Cat3) @end +@implementation I3(Cat2) @end + +// RUN: c-index-test -code-completion-at=%s:12:16 %s | FileCheck -check-prefix=CHECK-CC1 %s +// CHECK-CC1: ObjCCategoryDecl:{TypedText Cat1} +// CHECK-CC1: ObjCCategoryDecl:{TypedText Cat2} +// CHECK-CC1: ObjCCategoryDecl:{TypedText Cat3} +// RUN: c-index-test -code-completion-at=%s:13:16 %s | FileCheck -check-prefix=CHECK-CC2 %s +// CHECK-CC2: ObjCCategoryDecl:{TypedText Cat1} +// CHECK-CC2-NEXT: ObjCCategoryDecl:{TypedText Cat3} +// RUN: c-index-test -code-completion-at=%s:18:20 %s | FileCheck -check-prefix=CHECK-CC3 %s +// CHECK-CC3: ObjCCategoryDecl:{TypedText Cat1} +// CHECK-CC3: ObjCCategoryDecl:{TypedText Cat2} +// CHECK-CC3: ObjCCategoryDecl:{TypedText Cat3} +// RUN: c-index-test -code-completion-at=%s:19:20 %s | FileCheck -check-prefix=CHECK-CC4 %s +// CHECK-CC4: ObjCCategoryDecl:{TypedText Cat1} +// CHECK-CC4-NEXT: ObjCCategoryDecl:{TypedText Cat3} +// RUN: c-index-test -code-completion-at=%s:20:20 %s | FileCheck -check-prefix=CHECK-CC5 %s +// CHECK-CC5: ObjCCategoryDecl:{TypedText Cat1} +// CHECK-CC5-NEXT: ObjCCategoryDecl:{TypedText Cat2} +// CHECK-CC5-NEXT: ObjCCategoryDecl:{TypedText Cat3} |