Teach code completion to adjust its completion priorities based on the

type that we expect to see at a given point in the grammar, e.g., when
initializing a variable, returning a result, or calling a function. We
don't prune the candidate set at all, just adjust priorities to favor
things that should type-check, using an ultra-simplified type system.

llvm-svn: 105128
This commit is contained in:
Douglas Gregor 2010-05-30 01:49:25 +00:00
parent 0e15c9aef6
commit 7aa6b229fe
9 changed files with 426 additions and 36 deletions

View File

@ -2729,7 +2729,27 @@ public:
/// \param NumArgs the number of arguments in \p Args.
virtual void CodeCompleteCall(Scope *S, ExprTy *Fn,
ExprTy **Args, unsigned NumArgs) { }
/// \brief Code completion for the initializer of a variable declaration.
///
/// \param S The scope in which the initializer occurs.
///
/// \param D The declaration being initialized.
virtual void CodeCompleteInitializer(Scope *S, DeclPtrTy D) { }
/// \brief Code completion after the "return" keyword within a function.
///
/// \param S The scope in which the return statement occurs.
virtual void CodeCompleteReturn(Scope *S) { }
/// \brief Code completion for the right-hand side of an assignment or
/// compound assignment operator.
///
/// \param S The scope in which the assignment occurs.
///
/// \param LHS The left-hand side of the assignment expression.
virtual void CodeCompleteAssignmentRHS(Scope *S, ExprTy *LHS) { }
/// \brief Code completion for a C++ nested-name-specifier that precedes a
/// qualified-id of some form.
///

View File

@ -59,6 +59,18 @@ enum {
CCD_InBaseClass = 2
};
/// \brief Priority value factors by which we will divide or multiply the
/// priority of a code-completion result.
enum {
/// \brief Divide by this factor when a code-completion result's type exactly
/// matches the type we expect.
CCF_ExactTypeMatch = 4,
/// \brief Divide by this factor when a code-completion result's type is
/// similar to the type we expect (e.g., both arithmetic types, both
/// Objective-C object pointer types).
CCF_SimilarTypeMatch = 2
};
class FunctionDecl;
class FunctionType;
class FunctionTemplateDecl;

View File

@ -556,6 +556,13 @@ Parser::DeclPtrTy Parser::ParseDeclarationAfterDeclarator(Declarator &D,
Actions.ActOnCXXEnterDeclInitializer(CurScope, ThisDecl);
}
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteInitializer(CurScope, ThisDecl);
ConsumeCodeCompletionToken();
SkipUntil(tok::comma, true, true);
return ThisDecl;
}
OwningExprResult Init(ParseInitializer());
if (getLang().CPlusPlus && D.getCXXScopeSpec().isSet()) {

View File

@ -343,6 +343,14 @@ Parser::ParseRHSOfBinaryExpression(OwningExprResult LHS, prec::Level MinPrec) {
}
}
// Code completion for the right-hand side of an assignment expression
// goes through a special hook that takes the left-hand side into account.
if (Tok.is(tok::code_completion) && NextTokPrec == prec::Assignment) {
Actions.CodeCompleteAssignmentRHS(CurScope, LHS.get());
ConsumeCodeCompletionToken();
return ExprError();
}
// Parse another leaf here for the RHS of the operator.
// ParseCastExpression works here because all RHS expressions in C have it
// as a prefix, at least. However, in C++, an assignment-expression could

View File

@ -1199,6 +1199,13 @@ Parser::OwningStmtResult Parser::ParseReturnStatement(AttributeList *Attr) {
OwningExprResult R(Actions);
if (Tok.isNot(tok::semi)) {
if (Tok.is(tok::code_completion)) {
Actions.CodeCompleteReturn(CurScope);
ConsumeCodeCompletionToken();
SkipUntil(tok::semi, false, true);
return StmtError();
}
R = ParseExpression();
if (R.isInvalid()) { // Skip to the semicolon, but don't consume it.
SkipUntil(tok::semi, false, true);

View File

@ -4417,6 +4417,7 @@ public:
//@{
virtual void CodeCompleteOrdinaryName(Scope *S,
CodeCompletionContext CompletionContext);
virtual void CodeCompleteExpression(Scope *S, QualType T);
virtual void CodeCompleteMemberReferenceExpr(Scope *S, ExprTy *Base,
SourceLocation OpLoc,
bool IsArrow);
@ -4424,6 +4425,10 @@ public:
virtual void CodeCompleteCase(Scope *S);
virtual void CodeCompleteCall(Scope *S, ExprTy *Fn,
ExprTy **Args, unsigned NumArgs);
virtual void CodeCompleteInitializer(Scope *S, DeclPtrTy D);
virtual void CodeCompleteReturn(Scope *S);
virtual void CodeCompleteAssignmentRHS(Scope *S, ExprTy *LHS);
virtual void CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS,
bool EnteringContext);
virtual void CodeCompleteUsing(Scope *S);

View File

@ -119,6 +119,13 @@ namespace {
/// nested-name-specifiers that would otherwise be filtered out.
bool AllowNestedNameSpecifiers;
/// \brief If set, the type that we would prefer our resulting value
/// declarations to have.
///
/// Closely matching the preferred type gives a boost to a result's
/// priority.
CanQualType PreferredType;
/// \brief A list of shadow maps, which is used to model name hiding at
/// different levels of, e.g., the inheritance hierarchy.
std::list<ShadowMap> ShadowMaps;
@ -147,6 +154,11 @@ namespace {
unsigned size() const { return Results.size(); }
bool empty() const { return Results.empty(); }
/// \brief Specify the preferred type.
void setPreferredType(QualType T) {
PreferredType = SemaRef.Context.getCanonicalType(T);
}
/// \brief Specify whether nested-name-specifiers are allowed.
void allowNestedNameSpecifiers(bool Allow = true) {
AllowNestedNameSpecifiers = Allow;
@ -565,6 +577,118 @@ void ResultBuilder::MaybeAddResult(Result R, DeclContext *CurContext) {
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) {
@ -618,6 +742,21 @@ void ResultBuilder::AddResult(Result R, DeclContext *CurContext,
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;
}
}
// Insert this result into the set of results.
Results.push_back(R);
}
@ -755,35 +894,6 @@ bool ResultBuilder::IsMember(NamedDecl *ND) const {
isa<ObjCPropertyDecl>(ND);
}
/// \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();
}
static bool isObjCReceiverType(ASTContext &C, QualType T) {
T = C.getCanonicalType(T);
switch (T->getTypeClass()) {
@ -2131,6 +2241,31 @@ void Sema::CodeCompleteOrdinaryName(Scope *S,
HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
}
/// \brief Perform code-completion in an expression context when we know what
/// type we're looking for.
void Sema::CodeCompleteExpression(Scope *S, QualType T) {
typedef CodeCompleteConsumer::Result Result;
ResultBuilder Results(*this);
if (WantTypesInContext(CCC_Expression, getLangOptions()))
Results.setFilter(&ResultBuilder::IsOrdinaryName);
else
Results.setFilter(&ResultBuilder::IsOrdinaryNonTypeName);
Results.setPreferredType(T.getNonReferenceType());
CodeCompletionDeclConsumer Consumer(Results, CurContext);
LookupVisibleDecls(S, LookupOrdinaryName, Consumer);
Results.EnterNewScope();
AddOrdinaryNameResults(CCC_Expression, S, *this, Results);
Results.ExitScope();
if (CodeCompleter->includeMacros())
AddMacroResults(PP, Results);
HandleCodeCompleteResults(this, CodeCompleter, Results.data(),Results.size());
}
static void AddObjCProperties(ObjCContainerDecl *Container,
bool AllowCategories,
DeclContext *CurContext,
@ -2447,6 +2582,8 @@ void Sema::CodeCompleteCall(Scope *S, ExprTy *FnIn,
}
}
QualType ParamType;
if (!CandidateSet.empty()) {
// Sort the overload candidate set by placing the best overloads first.
std::stable_sort(CandidateSet.begin(), CandidateSet.end(),
@ -2459,14 +2596,85 @@ void Sema::CodeCompleteCall(Scope *S, ExprTy *FnIn,
if (Cand->Viable)
Results.push_back(ResultCandidate(Cand->Function));
}
// From the viable candidates, try to determine the type of this parameter.
for (unsigned I = 0, N = Results.size(); I != N; ++I) {
if (const FunctionType *FType = Results[I].getFunctionType())
if (const FunctionProtoType *Proto = dyn_cast<FunctionProtoType>(FType))
if (NumArgs < Proto->getNumArgs()) {
if (ParamType.isNull())
ParamType = Proto->getArgType(NumArgs);
else if (!Context.hasSameUnqualifiedType(
ParamType.getNonReferenceType(),
Proto->getArgType(NumArgs).getNonReferenceType())) {
ParamType = QualType();
break;
}
}
}
} else {
// Try to determine the parameter type from the type of the expression
// being called.
QualType FunctionType = Fn->getType();
if (const PointerType *Ptr = FunctionType->getAs<PointerType>())
FunctionType = Ptr->getPointeeType();
else if (const BlockPointerType *BlockPtr
= FunctionType->getAs<BlockPointerType>())
FunctionType = BlockPtr->getPointeeType();
else if (const MemberPointerType *MemPtr
= FunctionType->getAs<MemberPointerType>())
FunctionType = MemPtr->getPointeeType();
if (const FunctionProtoType *Proto
= FunctionType->getAs<FunctionProtoType>()) {
if (NumArgs < Proto->getNumArgs())
ParamType = Proto->getArgType(NumArgs);
}
}
CodeCompleteOrdinaryName(S, CCC_Expression);
if (ParamType.isNull())
CodeCompleteOrdinaryName(S, CCC_Expression);
else
CodeCompleteExpression(S, ParamType);
if (!Results.empty())
CodeCompleter->ProcessOverloadCandidates(*this, NumArgs, Results.data(),
Results.size());
}
void Sema::CodeCompleteInitializer(Scope *S, DeclPtrTy D) {
ValueDecl *VD = dyn_cast_or_null<ValueDecl>(D.getAs<Decl>());
if (!VD) {
CodeCompleteOrdinaryName(S, CCC_Expression);
return;
}
CodeCompleteExpression(S, VD->getType());
}
void Sema::CodeCompleteReturn(Scope *S) {
QualType ResultType;
if (isa<BlockDecl>(CurContext)) {
if (BlockScopeInfo *BSI = getCurBlock())
ResultType = BSI->ReturnType;
} else if (FunctionDecl *Function = dyn_cast<FunctionDecl>(CurContext))
ResultType = Function->getResultType();
else if (ObjCMethodDecl *Method = dyn_cast<ObjCMethodDecl>(CurContext))
ResultType = Method->getResultType();
if (ResultType.isNull())
CodeCompleteOrdinaryName(S, CCC_Expression);
else
CodeCompleteExpression(S, ResultType);
}
void Sema::CodeCompleteAssignmentRHS(Scope *S, ExprTy *LHS) {
if (LHS)
CodeCompleteExpression(S, static_cast<Expr *>(LHS)->getType());
else
CodeCompleteOrdinaryName(S, CCC_Expression);
}
void Sema::CodeCompleteQualifiedId(Scope *S, CXXScopeSpec &SS,
bool EnteringContext) {
if (!SS.getScopeRep() || !CodeCompleter)

View File

@ -9,13 +9,19 @@ int test(int i, int j, int k, int l) {
// RUN: c-index-test -code-completion-at=%s:7:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
// CHECK-CC1: macro definition:{TypedText __VERSION__} (70)
// CHECK-CC1: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (50)
// CHECK-CC1: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (12)
// CHECK-CC1-NOT: NotImplemented:{TypedText float} (40)
// CHECK-CC1: ParmDecl:{ResultType int}{TypedText j} (8)
// CHECK-CC1: ParmDecl:{ResultType int}{TypedText j} (2)
// CHECK-CC1: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
// RUN: c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
// RUN: c-index-test -code-completion-at=%s:7:18 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
// RUN: c-index-test -code-completion-at=%s:7:22 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
// RUN: c-index-test -code-completion-at=%s:7:14 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s
// CHECK-CC3: macro definition:{TypedText __VERSION__} (70)
// CHECK-CC3: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (50)
// CHECK-CC3-NOT: NotImplemented:{TypedText float} (40)
// CHECK-CC3: ParmDecl:{ResultType int}{TypedText j} (8)
// CHECK-CC3: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expressio
// RUN: c-index-test -code-completion-at=%s:7:18 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s
// RUN: c-index-test -code-completion-at=%s:7:22 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s
// RUN: c-index-test -code-completion-at=%s:7:2 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC2 %s
// CHECK-CC2: macro definition:{TypedText __VERSION__} (70)
// CHECK-CC2: FunctionDecl:{ResultType int}{TypedText f}{LeftParen (}{Placeholder int}{RightParen )} (50)

View File

@ -0,0 +1,117 @@
/* Run lines are at the end, since line/column matter in this test. */
enum Color {
Red, Green, Blue
};
enum Priority {
Low,
High
};
int func1(enum Color);
enum Priority func2(int);
void func3(float);
enum Priority test1(enum Priority priority, enum Color color, int integer) {
int i = integer;
enum Color c = color;
return priority;
func1(c);
void (^block)(enum Color, int);
block(c, 17);
c = color;
}
// FIXME: It would be great for message sends to have the same
// benefits as function calls, but we don't quite have the
// infrastructure yet.
// RUN: c-index-test -code-completion-at=%s:16:11 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC1 %s
// CHECK-CC1: EnumConstantDecl:{ResultType enum Color}{TypedText Blue} (30)
// CHECK-CC1: ParmDecl:{ResultType enum Color}{TypedText color} (4)
// CHECK-CC1: FunctionDecl:{ResultType int}{TypedText func1}{LeftParen (}{Placeholder enum Color}{RightParen )} (12)
// CHECK-CC1: FunctionDecl:{ResultType enum Priority}{TypedText func2}{LeftParen (}{Placeholder int}{RightParen )} (25)
// CHECK-CC1: EnumConstantDecl:{ResultType enum Color}{TypedText Green} (30)
// CHECK-CC1: EnumConstantDecl:{ResultType enum Priority}{TypedText High} (30)
// CHECK-CC1: VarDecl:{ResultType int}{TypedText i} (2)
// CHECK-CC1: ParmDecl:{ResultType int}{TypedText integer} (2)
// CHECK-CC1: EnumConstantDecl:{ResultType enum Priority}{TypedText Low} (30)
// CHECK-CC1: macro definition:{TypedText OBJC_NEW_PROPERTIES} (70)
// CHECK-CC1: macro definition:{TypedText OBJC_ZEROCOST_EXCEPTIONS} (70)
// CHECK-CC1: ParmDecl:{ResultType enum Priority}{TypedText priority} (4)
// CHECK-CC1: EnumConstantDecl:{ResultType enum Color}{TypedText Red} (30)
// CHECK-CC1: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
// CHECK-CC1: FunctionDecl:{ResultType enum Priority}{TypedText test1}{LeftParen (}{Placeholder enum Priority priority}{Comma , }{Placeholder enum Color color}{Comma , }{Placeholder int integer}{RightParen )} (25)
// RUN: c-index-test -code-completion-at=%s:17:18 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC2 %s
// CHECK-CC2: EnumConstantDecl:{ResultType enum Color}{TypedText Blue} (15)
// CHECK-CC2: VarDecl:{ResultType enum Color}{TypedText c} (2)
// CHECK-CC2: ParmDecl:{ResultType enum Color}{TypedText color} (2)
// CHECK-CC2: FunctionDecl:{ResultType int}{TypedText func1}{LeftParen (}{Placeholder enum Color}{RightParen )} (25)
// CHECK-CC2: FunctionDecl:{ResultType enum Priority}{TypedText func2}{LeftParen (}{Placeholder int}{RightParen )} (50)
// CHECK-CC2: EnumConstantDecl:{ResultType enum Color}{TypedText Green} (15)
// CHECK-CC2: EnumConstantDecl:{ResultType enum Priority}{TypedText High} (60)
// CHECK-CC2: VarDecl:{ResultType int}{TypedText i} (4)
// CHECK-CC2: ParmDecl:{ResultType int}{TypedText integer} (4)
// CHECK-CC2: EnumConstantDecl:{ResultType enum Priority}{TypedText Low} (60)
// CHECK-CC2: macro definition:{TypedText OBJC_NEW_PROPERTIES} (70)
// CHECK-CC2: macro definition:{TypedText OBJC_ZEROCOST_EXCEPTIONS} (70)
// CHECK-CC2: ParmDecl:{ResultType enum Priority}{TypedText priority} (8)
// CHECK-CC2: EnumConstantDecl:{ResultType enum Color}{TypedText Red} (15)
// CHECK-CC2: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
// CHECK-CC2: FunctionDecl:{ResultType enum Priority}{TypedText test1}{LeftParen (}{Placeholder enum Priority priority}{Comma , }{Placeholder enum Color color}{Comma , }{Placeholder int integer}{RightParen )} (50)
// RUN: c-index-test -code-completion-at=%s:18:10 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC3 %s
// CHECK-CC3: EnumConstantDecl:{ResultType enum Color}{TypedText Blue} (60)
// CHECK-CC3: VarDecl:{ResultType enum Color}{TypedText c} (8)
// CHECK-CC3: ParmDecl:{ResultType enum Color}{TypedText color} (8)
// CHECK-CC3: FunctionDecl:{ResultType int}{TypedText func1}{LeftParen (}{Placeholder enum Color}{RightParen )} (25)
// CHECK-CC3: FunctionDecl:{ResultType enum Priority}{TypedText func2}{LeftParen (}{Placeholder int}{RightParen )} (12)
// CHECK-CC3: FunctionDecl:{ResultType void}{TypedText func3}{LeftParen (}{Placeholder float}{RightParen )} (50)
// CHECK-CC3: EnumConstantDecl:{ResultType enum Color}{TypedText Green} (60)
// CHECK-CC3: EnumConstantDecl:{ResultType enum Priority}{TypedText High} (15)
// CHECK-CC3: VarDecl:{ResultType int}{TypedText i} (4)
// CHECK-CC3: ParmDecl:{ResultType int}{TypedText integer} (4)
// CHECK-CC3: EnumConstantDecl:{ResultType enum Priority}{TypedText Low} (15)
// CHECK-CC3: macro definition:{TypedText OBJC_NEW_PROPERTIES} (70)
// CHECK-CC3: macro definition:{TypedText OBJC_ZEROCOST_EXCEPTIONS} (70)
// CHECK-CC3: ParmDecl:{ResultType enum Priority}{TypedText priority} (2)
// CHECK-CC3: EnumConstantDecl:{ResultType enum Color}{TypedText Red} (60)
// CHECK-CC3: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
// CHECK-CC3: FunctionDecl:{ResultType enum Priority}{TypedText test1}{LeftParen (}{Placeholder enum Priority priority}{Comma , }{Placeholder enum Color color}{Comma , }{Placeholder int integer}{RightParen )} (12)
// RUN: c-index-test -code-completion-at=%s:19:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC4 %s
// CHECK-CC4: EnumConstantDecl:{ResultType enum Color}{TypedText Blue} (15)
// CHECK-CC4: VarDecl:{ResultType enum Color}{TypedText c} (2)
// CHECK-CC4: ParmDecl:{ResultType enum Color}{TypedText color} (2)
// CHECK-CC4: FunctionDecl:{ResultType int}{TypedText func1}{LeftParen (}{Placeholder enum Color}{RightParen )} (25)
// CHECK-CC4: FunctionDecl:{ResultType enum Priority}{TypedText func2}{LeftParen (}{Placeholder int}{RightParen )} (50)
// CHECK-CC4: FunctionDecl:{ResultType void}{TypedText func3}{LeftParen (}{Placeholder float}{RightParen )} (50)
// CHECK-CC4: EnumConstantDecl:{ResultType enum Color}{TypedText Green} (15)
// CHECK-CC4: EnumConstantDecl:{ResultType enum Priority}{TypedText High} (60)
// CHECK-CC4: VarDecl:{ResultType int}{TypedText i} (4)
// CHECK-CC4: ParmDecl:{ResultType int}{TypedText integer} (4)
// CHECK-CC4: EnumConstantDecl:{ResultType enum Priority}{TypedText Low} (60)
// CHECK-CC4: macro definition:{TypedText OBJC_NEW_PROPERTIES} (70)
// CHECK-CC4: macro definition:{TypedText OBJC_ZEROCOST_EXCEPTIONS} (70)
// CHECK-CC4: ParmDecl:{ResultType enum Priority}{TypedText priority} (8)
// CHECK-CC4: EnumConstantDecl:{ResultType enum Color}{TypedText Red} (15)
// CHECK-CC4: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
// CHECK-CC4: FunctionDecl:{ResultType enum Priority}{TypedText test1}{LeftParen (}{Placeholder enum Priority priority}{Comma , }{Placeholder enum Color color}{Comma , }{Placeholder int integer}{RightParen )} (50)
// RUN: c-index-test -code-completion-at=%s:21:9 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC4 %s
// RUN: c-index-test -code-completion-at=%s:22:7 -Xclang -code-completion-patterns %s | FileCheck -check-prefix=CHECK-CC6 %s
// CHECK-CC6: VarDecl:{ResultType void (^)(enum Color, int)}{TypedText block} (8)
// CHECK-CC6: EnumConstantDecl:{ResultType enum Color}{TypedText Blue} (15)
// CHECK-CC6: VarDecl:{ResultType enum Color}{TypedText c} (2)
// CHECK-CC6: ParmDecl:{ResultType enum Color}{TypedText color} (2)
// CHECK-CC6: FunctionDecl:{ResultType int}{TypedText func1}{LeftParen (}{Placeholder enum Color}{RightParen )} (25)
// CHECK-CC6: FunctionDecl:{ResultType enum Priority}{TypedText func2}{LeftParen (}{Placeholder int}{RightParen )} (50)
// CHECK-CC6: FunctionDecl:{ResultType void}{TypedText func3}{LeftParen (}{Placeholder float}{RightParen )} (50)
// CHECK-CC6: EnumConstantDecl:{ResultType enum Color}{TypedText Green} (15)
// CHECK-CC6: EnumConstantDecl:{ResultType enum Priority}{TypedText High} (60)
// CHECK-CC6: VarDecl:{ResultType int}{TypedText i} (4)
// CHECK-CC6: ParmDecl:{ResultType int}{TypedText integer} (4)
// CHECK-CC6: EnumConstantDecl:{ResultType enum Priority}{TypedText Low} (60)
// CHECK-CC6: macro definition:{TypedText OBJC_NEW_PROPERTIES} (70)
// CHECK-CC6: macro definition:{TypedText OBJC_ZEROCOST_EXCEPTIONS} (70)
// CHECK-CC6: ParmDecl:{ResultType enum Priority}{TypedText priority} (8)
// CHECK-CC6: EnumConstantDecl:{ResultType enum Color}{TypedText Red} (15)
// CHECK-CC6: NotImplemented:{TypedText sizeof}{LeftParen (}{Placeholder expression-or-type}{RightParen )} (30)
// CHECK-CC6: FunctionDecl:{ResultType enum Priority}{TypedText test1}{LeftParen (}{Placeholder enum Priority priority}{Comma , }{Placeholder enum Color color}{Comma , }{Placeholder int integer}{RightParen )} (50)