[cfe-commits] r107929 - in /cfe/trunk: include/clang/Parse/Action.h include/clang/Sema/CodeCompleteConsumer.h lib/Parse/ParseObjc.cpp lib/Sema/Sema.h lib/Sema/SemaCodeComplete.cpp test/Index/complete-method-decls.m
Douglas Gregor
dgregor at apple.com
Thu Jul 8 16:20:03 PDT 2010
Author: dgregor
Date: Thu Jul 8 18:20:03 2010
New Revision: 107929
URL: http://llvm.org/viewvc/llvm-project?rev=107929&view=rev
Log:
Introduce a new code-completion point prior to an identifier in the
selector of an Objective-C method declaration, e.g., given
- (int)first:(int)x second:(int)y;
this code completion point triggers at the location of "second". It
will provide completions that fill out the method declaration for any
known method, anywhere in the translation unit.
Modified:
cfe/trunk/include/clang/Parse/Action.h
cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
cfe/trunk/lib/Parse/ParseObjc.cpp
cfe/trunk/lib/Sema/Sema.h
cfe/trunk/lib/Sema/SemaCodeComplete.cpp
cfe/trunk/test/Index/complete-method-decls.m
Modified: cfe/trunk/include/clang/Parse/Action.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Parse/Action.h?rev=107929&r1=107928&r2=107929&view=diff
==============================================================================
--- cfe/trunk/include/clang/Parse/Action.h (original)
+++ cfe/trunk/include/clang/Parse/Action.h Thu Jul 8 18:20:03 2010
@@ -3043,7 +3043,7 @@
/// parsed.
virtual void CodeCompleteObjCProtocolReferences(IdentifierLocPair *Protocols,
unsigned NumProtocols) { }
-
+
/// \brief Code completion for a protocol declaration or definition, after
/// the @protocol but before any identifier.
///
@@ -3133,6 +3133,28 @@
TypeTy *ReturnType,
DeclPtrTy IDecl) {
}
+
+ /// \brief Code completion for a selector identifier within an Objective-C
+ /// method declaration.
+ ///
+ /// \param S The scope in which this code completion occurs.
+ ///
+ /// \param IsInstanceMethod Whether we are parsing an instance method (or,
+ /// if false, a class method).
+ ///
+ /// \param ReturnType If non-NULL, the specified return type of the method
+ /// being declared or defined.
+ ///
+ /// \param SelIdents The identifiers that occurred in the selector for the
+ /// method declaration prior to the code completion point.
+ ///
+ /// \param NumSelIdents The number of identifiers provided by SelIdents.
+ virtual void CodeCompleteObjCMethodDeclSelector(Scope *S,
+ bool IsInstanceMethod,
+ TypeTy *ReturnType,
+ IdentifierInfo **SelIdents,
+ unsigned NumSelIdents) { }
+
//@}
};
Modified: cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h?rev=107929&r1=107928&r2=107929&view=diff
==============================================================================
--- cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h (original)
+++ cfe/trunk/include/clang/Sema/CodeCompleteConsumer.h Thu Jul 8 18:20:03 2010
@@ -355,6 +355,10 @@
/// method, etc.) should be considered "informative".
bool AllParametersAreInformative : 1;
+ /// \brief Whether we're completing a declaration of the given entity,
+ /// rather than a use of that entity.
+ bool DeclaringEntity : 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.
@@ -368,7 +372,7 @@
Priority(getPriorityFromDecl(Declaration)), StartParameter(0),
Hidden(false), QualifierIsInformative(QualifierIsInformative),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
- Qualifier(Qualifier) {
+ DeclaringEntity(false), Qualifier(Qualifier) {
}
/// \brief Build a result that refers to a keyword or symbol.
@@ -376,21 +380,21 @@
: Kind(RK_Keyword), Keyword(Keyword), Priority(Priority),
StartParameter(0), Hidden(false), QualifierIsInformative(0),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
- Qualifier(0) { }
+ DeclaringEntity(false), Qualifier(0) { }
/// \brief Build a result that refers to a macro.
Result(IdentifierInfo *Macro, unsigned Priority = CCP_Macro)
: Kind(RK_Macro), Macro(Macro), Priority(Priority), StartParameter(0),
Hidden(false), QualifierIsInformative(0),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
- Qualifier(0) { }
+ DeclaringEntity(false), Qualifier(0) { }
/// \brief Build a result that refers to a pattern.
Result(CodeCompletionString *Pattern, unsigned Priority = CCP_CodePattern)
: Kind(RK_Pattern), Pattern(Pattern), Priority(Priority),
StartParameter(0), Hidden(false), QualifierIsInformative(0),
StartsNestedNameSpecifier(false), AllParametersAreInformative(false),
- Qualifier(0) { }
+ DeclaringEntity(false), Qualifier(0) { }
/// \brief Retrieve the declaration stored in this result.
NamedDecl *getDeclaration() const {
Modified: cfe/trunk/lib/Parse/ParseObjc.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Parse/ParseObjc.cpp?rev=107929&r1=107928&r2=107929&view=diff
==============================================================================
--- cfe/trunk/lib/Parse/ParseObjc.cpp (original)
+++ cfe/trunk/lib/Parse/ParseObjc.cpp Thu Jul 8 18:20:03 2010
@@ -868,6 +868,17 @@
ArgInfos.push_back(ArgInfo);
KeyIdents.push_back(SelIdent);
+ // Code completion for the next piece of the selector.
+ if (Tok.is(tok::code_completion)) {
+ ConsumeCodeCompletionToken();
+ Actions.CodeCompleteObjCMethodDeclSelector(getCurScope(),
+ mType == tok::minus,
+ ReturnType,
+ KeyIdents.data(),
+ KeyIdents.size());
+ break;
+ }
+
// Check for another keyword selector.
SourceLocation Loc;
SelIdent = ParseObjCSelectorPiece(Loc);
Modified: cfe/trunk/lib/Sema/Sema.h
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/Sema.h?rev=107929&r1=107928&r2=107929&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/Sema.h (original)
+++ cfe/trunk/lib/Sema/Sema.h Thu Jul 8 18:20:03 2010
@@ -4572,6 +4572,12 @@
bool IsInstanceMethod,
TypeTy *ReturnType,
DeclPtrTy IDecl);
+ virtual void CodeCompleteObjCMethodDeclSelector(Scope *S,
+ bool IsInstanceMethod,
+ TypeTy *ReturnType,
+ IdentifierInfo **SelIdents,
+ unsigned NumSelIdents);
+
//@}
//===--------------------------------------------------------------------===//
Modified: cfe/trunk/lib/Sema/SemaCodeComplete.cpp
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/lib/Sema/SemaCodeComplete.cpp?rev=107929&r1=107928&r2=107929&view=diff
==============================================================================
--- cfe/trunk/lib/Sema/SemaCodeComplete.cpp (original)
+++ cfe/trunk/lib/Sema/SemaCodeComplete.cpp Thu Jul 8 18:20:03 2010
@@ -130,6 +130,8 @@
/// different levels of, e.g., the inheritance hierarchy.
std::list<ShadowMap> ShadowMaps;
+ void AdjustResultPriorityForPreferredType(Result &R);
+
public:
explicit ResultBuilder(Sema &SemaRef, LookupFilter Filter = 0)
: SemaRef(SemaRef), Filter(Filter), AllowNestedNameSpecifiers(false) { }
@@ -470,6 +472,134 @@
return false;
}
+enum SimplifiedTypeClass {
+ STC_Arithmetic,
+ STC_Array,
+ STC_Block,
+ STC_Function,
+ STC_ObjectiveC,
+ STC_Other,
+ STC_Pointer,
+ STC_Record,
+ STC_Void
+};
+
+/// \brief A simplified classification of types used to determine whether two
+/// types are "similar enough" when adjusting priorities.
+static SimplifiedTypeClass getSimplifiedTypeClass(CanQualType T) {
+ switch (T->getTypeClass()) {
+ case Type::Builtin:
+ switch (cast<BuiltinType>(T)->getKind()) {
+ case BuiltinType::Void:
+ return STC_Void;
+
+ case BuiltinType::NullPtr:
+ return STC_Pointer;
+
+ case BuiltinType::Overload:
+ case BuiltinType::Dependent:
+ case BuiltinType::UndeducedAuto:
+ return STC_Other;
+
+ case BuiltinType::ObjCId:
+ case BuiltinType::ObjCClass:
+ case BuiltinType::ObjCSel:
+ return STC_ObjectiveC;
+
+ default:
+ return STC_Arithmetic;
+ }
+ return STC_Other;
+
+ case Type::Complex:
+ return STC_Arithmetic;
+
+ case Type::Pointer:
+ return STC_Pointer;
+
+ case Type::BlockPointer:
+ return STC_Block;
+
+ case Type::LValueReference:
+ case Type::RValueReference:
+ return getSimplifiedTypeClass(T->getAs<ReferenceType>()->getPointeeType());
+
+ case Type::ConstantArray:
+ case Type::IncompleteArray:
+ case Type::VariableArray:
+ case Type::DependentSizedArray:
+ return STC_Array;
+
+ case Type::DependentSizedExtVector:
+ case Type::Vector:
+ case Type::ExtVector:
+ return STC_Arithmetic;
+
+ case Type::FunctionProto:
+ case Type::FunctionNoProto:
+ return STC_Function;
+
+ case Type::Record:
+ return STC_Record;
+
+ case Type::Enum:
+ return STC_Arithmetic;
+
+ case Type::ObjCObject:
+ case Type::ObjCInterface:
+ case Type::ObjCObjectPointer:
+ return STC_ObjectiveC;
+
+ default:
+ return STC_Other;
+ }
+}
+
+/// \brief Get the type that a given expression will have if this declaration
+/// is used as an expression in its "typical" code-completion form.
+static QualType getDeclUsageType(ASTContext &C, NamedDecl *ND) {
+ ND = cast<NamedDecl>(ND->getUnderlyingDecl());
+
+ if (TypeDecl *Type = dyn_cast<TypeDecl>(ND))
+ return C.getTypeDeclType(Type);
+ if (ObjCInterfaceDecl *Iface = dyn_cast<ObjCInterfaceDecl>(ND))
+ return C.getObjCInterfaceType(Iface);
+
+ QualType T;
+ if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND))
+ T = Function->getResultType();
+ else if (ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(ND))
+ T = Method->getResultType();
+ else if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND))
+ T = FunTmpl->getTemplatedDecl()->getResultType();
+ else if (EnumConstantDecl *Enumerator = dyn_cast<EnumConstantDecl>(ND))
+ T = C.getTypeDeclType(cast<EnumDecl>(Enumerator->getDeclContext()));
+ else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND))
+ T = Property->getType();
+ else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND))
+ T = Value->getType();
+ else
+ return QualType();
+
+ return T.getNonReferenceType();
+}
+
+void ResultBuilder::AdjustResultPriorityForPreferredType(Result &R) {
+ QualType T = getDeclUsageType(SemaRef.Context, R.Declaration);
+ if (T.isNull())
+ return;
+
+ CanQualType TC = SemaRef.Context.getCanonicalType(T);
+ // Check for exactly-matching types (modulo qualifiers).
+ if (SemaRef.Context.hasSameUnqualifiedType(PreferredType, TC))
+ R.Priority /= CCF_ExactTypeMatch;
+ // Check for nearly-matching types, based on classification of each.
+ else if ((getSimplifiedTypeClass(PreferredType)
+ == getSimplifiedTypeClass(TC)) &&
+ !(PreferredType->isEnumeralType() && TC->isEnumeralType()))
+ R.Priority /= CCF_SimilarTypeMatch;
+}
+
void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) {
assert(!ShadowMaps.empty() && "Must enter into a results scope");
@@ -554,8 +684,9 @@
if (AsNestedNameSpecifier) {
R.StartsNestedNameSpecifier = true;
R.Priority = CCP_NestedNameSpecifier;
- }
-
+ } else if (!PreferredType.isNull())
+ AdjustResultPriorityForPreferredType(R);
+
// If this result is supposed to have an informative qualifier, add one.
if (R.QualifierIsInformative && !R.Qualifier &&
!R.StartsNestedNameSpecifier) {
@@ -575,118 +706,6 @@
Results.push_back(R);
}
-enum SimplifiedTypeClass {
- STC_Arithmetic,
- STC_Array,
- STC_Block,
- STC_Function,
- STC_ObjectiveC,
- STC_Other,
- STC_Pointer,
- STC_Record,
- STC_Void
-};
-
-/// \brief A simplified classification of types used to determine whether two
-/// types are "similar enough" when adjusting priorities.
-static SimplifiedTypeClass getSimplifiedTypeClass(CanQualType T) {
- switch (T->getTypeClass()) {
- case Type::Builtin:
- switch (cast<BuiltinType>(T)->getKind()) {
- case BuiltinType::Void:
- return STC_Void;
-
- case BuiltinType::NullPtr:
- return STC_Pointer;
-
- case BuiltinType::Overload:
- case BuiltinType::Dependent:
- case BuiltinType::UndeducedAuto:
- return STC_Other;
-
- case BuiltinType::ObjCId:
- case BuiltinType::ObjCClass:
- case BuiltinType::ObjCSel:
- return STC_ObjectiveC;
-
- default:
- return STC_Arithmetic;
- }
- return STC_Other;
-
- case Type::Complex:
- return STC_Arithmetic;
-
- case Type::Pointer:
- return STC_Pointer;
-
- case Type::BlockPointer:
- return STC_Block;
-
- case Type::LValueReference:
- case Type::RValueReference:
- return getSimplifiedTypeClass(T->getAs<ReferenceType>()->getPointeeType());
-
- case Type::ConstantArray:
- case Type::IncompleteArray:
- case Type::VariableArray:
- case Type::DependentSizedArray:
- return STC_Array;
-
- case Type::DependentSizedExtVector:
- case Type::Vector:
- case Type::ExtVector:
- return STC_Arithmetic;
-
- case Type::FunctionProto:
- case Type::FunctionNoProto:
- return STC_Function;
-
- case Type::Record:
- return STC_Record;
-
- case Type::Enum:
- return STC_Arithmetic;
-
- case Type::ObjCObject:
- case Type::ObjCInterface:
- case Type::ObjCObjectPointer:
- return STC_ObjectiveC;
-
- default:
- return STC_Other;
- }
-}
-
-/// \brief Get the type that a given expression will have if this declaration
-/// is used as an expression in its "typical" code-completion form.
-static QualType getDeclUsageType(ASTContext &C, NamedDecl *ND) {
- ND = cast<NamedDecl>(ND->getUnderlyingDecl());
-
- if (TypeDecl *Type = dyn_cast<TypeDecl>(ND))
- return C.getTypeDeclType(Type);
- if (ObjCInterfaceDecl *Iface = dyn_cast<ObjCInterfaceDecl>(ND))
- return C.getObjCInterfaceType(Iface);
-
- QualType T;
- if (FunctionDecl *Function = dyn_cast<FunctionDecl>(ND))
- T = Function->getResultType();
- else if (ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(ND))
- T = Method->getResultType();
- else if (FunctionTemplateDecl *FunTmpl = dyn_cast<FunctionTemplateDecl>(ND))
- T = FunTmpl->getTemplatedDecl()->getResultType();
- else if (EnumConstantDecl *Enumerator = dyn_cast<EnumConstantDecl>(ND))
- T = C.getTypeDeclType(cast<EnumDecl>(Enumerator->getDeclContext()));
- else if (ObjCPropertyDecl *Property = dyn_cast<ObjCPropertyDecl>(ND))
- T = Property->getType();
- else if (ValueDecl *Value = dyn_cast<ValueDecl>(ND))
- T = Value->getType();
- else
- return QualType();
-
- return T.getNonReferenceType();
-}
-
void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
NamedDecl *Hiding, bool InBaseClass = false) {
if (R.Kind != Result::RK_Declaration) {
@@ -740,20 +759,8 @@
if (InBaseClass)
R.Priority += CCD_InBaseClass;
- if (!PreferredType.isNull()) {
- if (ValueDecl *Value = dyn_cast<ValueDecl>(R.Declaration)) {
- CanQualType T = SemaRef.Context.getCanonicalType(
- getDeclUsageType(SemaRef.Context, Value));
- // Check for exactly-matching types (modulo qualifiers).
- if (SemaRef.Context.hasSameUnqualifiedType(PreferredType, T))
- R.Priority /= CCF_ExactTypeMatch;
- // Check for nearly-matching types, based on classification of each.
- else if ((getSimplifiedTypeClass(PreferredType)
- == getSimplifiedTypeClass(T)) &&
- !(PreferredType->isEnumeralType() && T->isEnumeralType()))
- R.Priority /= CCF_SimilarTypeMatch;
- }
- }
+ if (!PreferredType.isNull())
+ AdjustResultPriorityForPreferredType(R);
// Insert this result into the set of results.
Results.push_back(R);
@@ -1955,9 +1962,9 @@
if (IdentifierInfo *II = Sel.getIdentifierInfoForSlot(Idx))
Keyword += II->getName().str();
Keyword += ":";
- if (Idx < StartParameter || AllParametersAreInformative) {
+ if (Idx < StartParameter || AllParametersAreInformative)
Result->AddInformativeChunk(Keyword);
- } else if (Idx == StartParameter)
+ else if (Idx == StartParameter)
Result->AddTypedTextChunk(Keyword);
else
Result->AddTextChunk(Keyword);
@@ -1972,14 +1979,18 @@
Arg = "(" + Arg + ")";
if (IdentifierInfo *II = (*P)->getIdentifier())
Arg += II->getName().str();
- if (AllParametersAreInformative)
+ if (DeclaringEntity)
+ Result->AddTextChunk(Arg);
+ else if (AllParametersAreInformative)
Result->AddInformativeChunk(Arg);
else
Result->AddPlaceholderChunk(Arg);
}
if (Method->isVariadic()) {
- if (AllParametersAreInformative)
+ if (DeclaringEntity)
+ Result->AddTextChunk(", ...");
+ else if (AllParametersAreInformative)
Result->AddInformativeChunk(", ...");
else
Result->AddPlaceholderChunk(", ...");
@@ -4133,3 +4144,55 @@
HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
}
+
+void Sema::CodeCompleteObjCMethodDeclSelector(Scope *S,
+ bool IsInstanceMethod,
+ TypeTy *ReturnTy,
+ IdentifierInfo **SelIdents,
+ unsigned NumSelIdents) {
+ llvm::DenseMap<Selector, ObjCMethodList> &Pool
+ = IsInstanceMethod? InstanceMethodPool : FactoryMethodPool;
+
+ // If we have an external source, load the entire class method
+ // pool from the PCH file.
+ if (ExternalSource) {
+ for (uint32_t I = 0, N = ExternalSource->GetNumExternalSelectors();
+ I != N; ++I) {
+ Selector Sel = ExternalSource->GetExternalSelector(I);
+ if (Sel.isNull() || InstanceMethodPool.count(Sel) ||
+ FactoryMethodPool.count(Sel))
+ continue;
+
+ ReadMethodPool(Sel, IsInstanceMethod);
+ }
+ }
+
+ // Build the set of methods we can see.
+ typedef CodeCompleteConsumer::Result Result;
+ ResultBuilder Results(*this);
+
+ if (ReturnTy)
+ Results.setPreferredType(GetTypeFromParser(ReturnTy).getNonReferenceType());
+
+ Results.EnterNewScope();
+ for (llvm::DenseMap<Selector, ObjCMethodList>::iterator M = Pool.begin(),
+ MEnd = Pool.end();
+ M != MEnd;
+ ++M) {
+ for (ObjCMethodList *MethList = &M->second; MethList && MethList->Method;
+ MethList = MethList->Next) {
+ if (!isAcceptableObjCMethod(MethList->Method, MK_Any, SelIdents,
+ NumSelIdents))
+ continue;
+
+ Result R(MethList->Method, 0);
+ R.StartParameter = NumSelIdents;
+ R.AllParametersAreInformative = false;
+ R.DeclaringEntity = true;
+ Results.MaybeAddResult(R, CurContext);
+ }
+ }
+
+ Results.ExitScope();
+ HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
+}
Modified: cfe/trunk/test/Index/complete-method-decls.m
URL: http://llvm.org/viewvc/llvm-project/cfe/trunk/test/Index/complete-method-decls.m?rev=107929&r1=107928&r2=107929&view=diff
==============================================================================
--- cfe/trunk/test/Index/complete-method-decls.m (original)
+++ cfe/trunk/test/Index/complete-method-decls.m Thu Jul 8 18:20:03 2010
@@ -42,6 +42,16 @@
- (id)categoryFunction:(int)x { return self; }
@end
+ at interface C
+- (int)first:(int)x second:(float)y third:(double)z;
+- (id)first:(int)x second2:(float)y third:(double)z;
+- (void*)first:(int)x second3:(float)y third:(double)z;
+ at end
+
+ at interface D
+- (int)first:(int)x second4:(float)y third:(double)z;
+ at end
+
// RUN: c-index-test -code-completion-at=%s:17:3 %s | FileCheck -check-prefix=CHECK-CC1 %s
// CHECK-CC1: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText abc}
// CHECK-CC1: NotImplemented:{LeftParen (}{Text int}{RightParen )}{TypedText getInt}
@@ -79,4 +89,9 @@
// CHECK-CC6: NotImplemented:{TypedText initWithTwoInts}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace }{Text second}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text y}{HorizontalSpace }{LeftBrace {}{VerticalSpace
// RUN: c-index-test -code-completion-at=%s:42:3 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC7 %s
// CHECK-CC7: NotImplemented:{LeftParen (}{Text id}{RightParen )}{TypedText categoryFunction}{Colon :}{LeftParen (}{Text int}{RightParen )}{Text x}{HorizontalSpace }{LeftBrace {}{VerticalSpace
+// RUN: c-index-test -code-completion-at=%s:52:21 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC8 %s
+// CHECK-CC8: ObjCInstanceMethodDecl:{ResultType id}{Informative first:}{TypedText second2:}{Text (float)y}{HorizontalSpace }{Text third:}{Text (double)z} (20)
+// CHECK-CC8: ObjCInstanceMethodDecl:{ResultType void *}{Informative first:}{TypedText second3:}{Text (float)y}{HorizontalSpace }{Text third:}{Text (double)z} (20)
+// CHECK-CC8: ObjCInstanceMethodDecl:{ResultType int}{Informative first:}{TypedText second:}{Text (float)y}{HorizontalSpace }{Text third:}{Text (double)z} (5)
+
More information about the cfe-commits
mailing list