mirror of
https://github.com/apple/swift.git
synced 2025-12-21 12:14:44 +01:00
Code completion: completion of type-ident in type contexts (like Foo.#^A^#)
This introduces the required code completion callbacks which pass partially parsed TypeReprs to code completion. These types can refer to generic function parameters. Because we need to typecheck these types, we need to typecheck generic parameters first. Because exposing fine-grained typechecker interface just for code completion is bad, we create a function declaration based on the limited information we have (i.e., just the function name and generic parameters) and pass that to the typechecker. This approach (in theory) should work uniformly for function decls and nominal type decls, but the nominal type decl case is not tested yet. Eventually we will also want to use a similar approach for normal parser recovery as well. Swift SVN r7313
This commit is contained in:
@@ -24,7 +24,10 @@ protected:
|
||||
Parser &P;
|
||||
ASTContext &Context;
|
||||
Parser::ParserPosition ExprBeginPosition;
|
||||
const GenericParamList *DeclFuncGenericParams = nullptr;
|
||||
|
||||
/// The declaration parsed during delayed parsing that was caused by code
|
||||
/// completion. This declaration contained the code completion token.
|
||||
Decl *DelayedParsedDecl = nullptr;
|
||||
|
||||
public:
|
||||
CodeCompletionCallbacks(Parser &P)
|
||||
@@ -37,25 +40,10 @@ public:
|
||||
ExprBeginPosition = PP;
|
||||
}
|
||||
|
||||
/// RAII object to inform code completion about function's generic parameters
|
||||
/// while parsing the function signature.
|
||||
class FunctionSignatureGenericParams {
|
||||
CodeCompletionCallbacks *CodeCompletion;
|
||||
|
||||
public:
|
||||
FunctionSignatureGenericParams(CodeCompletionCallbacks *CodeCompletion,
|
||||
const GenericParamList *GenericParams)
|
||||
: CodeCompletion(CodeCompletion) {
|
||||
if (CodeCompletion && GenericParams)
|
||||
CodeCompletion->DeclFuncGenericParams = GenericParams;
|
||||
void setDelayedParsedDecl(Decl *D) {
|
||||
DelayedParsedDecl = D;
|
||||
}
|
||||
|
||||
~FunctionSignatureGenericParams() {
|
||||
if (CodeCompletion)
|
||||
CodeCompletion->DeclFuncGenericParams = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
/// \brief Complete the whole expression. This is a fallback that should
|
||||
/// produce results when more specific completion methods failed.
|
||||
virtual void completeExpr() = 0;
|
||||
@@ -81,6 +69,9 @@ public:
|
||||
/// by user.
|
||||
virtual void completeTypeSimpleBeginning() = 0;
|
||||
|
||||
/// \brief Complete a given type-identifier after we have consumed the dot.
|
||||
virtual void completeTypeIdentifier(IdentTypeRepr *ITR) = 0;
|
||||
|
||||
/// \brief Signals that the AST for the all the delayed-parsed code was
|
||||
/// constructed. No \c complete*() callbacks will be done after this.
|
||||
virtual void doneParsing() = 0;
|
||||
|
||||
@@ -32,6 +32,7 @@ namespace llvm {
|
||||
namespace swift {
|
||||
class TranslationUnit;
|
||||
class Component;
|
||||
class Decl;
|
||||
class DeclContext;
|
||||
class Expr;
|
||||
class FuncExpr;
|
||||
@@ -113,14 +114,24 @@ namespace swift {
|
||||
/// main module.
|
||||
void performTypeChecking(TranslationUnit *TU, unsigned StartElem = 0);
|
||||
|
||||
/// performTypeLocChecking - recursively validate the specified type. This is
|
||||
/// used when dealing with partial translation units (e.g. SIL parsing).
|
||||
bool performTypeLocChecking(TranslationUnit *TU, TypeLoc &T);
|
||||
/// \brief Recursively validate the specified type.
|
||||
///
|
||||
/// This is used when dealing with partial translation units (e.g. SIL
|
||||
/// parsing, code completion).
|
||||
///
|
||||
/// \returns false on success, true on error.
|
||||
bool performTypeLocChecking(TranslationUnit *TU, TypeLoc &T,
|
||||
bool ProduceDiagnostics = true);
|
||||
|
||||
/// typeCheckCompletionContextExpr - Typecheck an expression parsed as a
|
||||
/// completion context.
|
||||
bool typeCheckCompletionContextExpr(TranslationUnit *TU,
|
||||
Expr *&parsedExpr);
|
||||
/// \brief Typecheck a declaration parsed during code completion.
|
||||
///
|
||||
/// \returns true on success, false on error.
|
||||
bool typeCheckCompletionDecl(TranslationUnit *TU, Decl *D);
|
||||
|
||||
/// \brief Typecheck an expression parsed during code completion.
|
||||
///
|
||||
/// \returns true on success, false on error.
|
||||
bool typeCheckCompletionContextExpr(TranslationUnit *TU, Expr *&parsedExpr);
|
||||
|
||||
/// Partially typecheck the specified function body.
|
||||
bool typeCheckFunctionBodyUntil(TranslationUnit *TU, DeclContext *DC,
|
||||
|
||||
@@ -346,17 +346,19 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks,
|
||||
SuperExpr,
|
||||
SuperExprDot,
|
||||
TypeSimpleBeginning,
|
||||
TypeIdentifier,
|
||||
};
|
||||
|
||||
CompletionKind Kind = CompletionKind::None;
|
||||
Expr *ParsedExpr = nullptr;
|
||||
TypeLoc ParsedTypeLoc;
|
||||
DeclContext *CurDeclContext = nullptr;
|
||||
const GenericParamList *DeclFuncGenericParams = nullptr;
|
||||
|
||||
/// \brief Set to true when we have delivered code completion results
|
||||
/// to the \c Consumer.
|
||||
bool DeliveredResults = false;
|
||||
|
||||
/// \returns true on success, false on failure.
|
||||
bool typecheckContext() {
|
||||
// Type check the function that contains the expression.
|
||||
if (CurDeclContext->getContextKind() == DeclContextKind::CapturingExpr) {
|
||||
@@ -373,6 +375,13 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks,
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \returns true on success, false on failure.
|
||||
bool typecheckDelayedParsedDecl() {
|
||||
assert(DelayedParsedDecl && "should have a delayed parsed decl");
|
||||
return typeCheckCompletionDecl(TU, DelayedParsedDecl);
|
||||
}
|
||||
|
||||
/// \returns true on success, false on failure.
|
||||
bool typecheckParsedExpr() {
|
||||
assert(ParsedExpr && "should have an expression");
|
||||
|
||||
@@ -395,6 +404,12 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks,
|
||||
return true;
|
||||
}
|
||||
|
||||
/// \returns true on success, false on failure.
|
||||
bool typecheckParsedType() {
|
||||
assert(ParsedTypeLoc.getTypeRepr() && "should have a TypeRepr");
|
||||
return !performTypeLocChecking(TU, ParsedTypeLoc, false);
|
||||
}
|
||||
|
||||
public:
|
||||
CodeCompletionCallbacksImpl(Parser &P,
|
||||
CodeCompletionContext &CompletionContext,
|
||||
@@ -416,6 +431,7 @@ public:
|
||||
void completeExprSuperDot(SuperRefExpr *SRE) override;
|
||||
|
||||
void completeTypeSimpleBeginning() override;
|
||||
void completeTypeIdentifier(IdentTypeRepr *ITR) override;
|
||||
|
||||
void doneParsing() override;
|
||||
|
||||
@@ -445,16 +461,22 @@ class CompletionLookup : swift::VisibleDeclConsumer {
|
||||
ASTContext &SwiftContext;
|
||||
const DeclContext *CurrDeclContext;
|
||||
|
||||
const GenericParamList *DeclFuncGenericParams = nullptr;
|
||||
|
||||
enum class LookupKind {
|
||||
ValueExpr,
|
||||
ValueInDeclContext,
|
||||
Type,
|
||||
TypeInDeclContext,
|
||||
};
|
||||
|
||||
LookupKind Kind;
|
||||
|
||||
/// Type of the user-provided expression for LookupKind::ValueExpr
|
||||
/// completions.
|
||||
Type ExprType;
|
||||
|
||||
/// User-provided base type for LookupKind::Type completions.
|
||||
Type BaseType;
|
||||
|
||||
bool HaveDot = false;
|
||||
bool NeedLeadingDot = false;
|
||||
bool IsSuperRefExpr = false;
|
||||
@@ -500,10 +522,6 @@ public:
|
||||
});
|
||||
}
|
||||
|
||||
void setDeclFuncGenericParameters(const GenericParamList *GenericParams) {
|
||||
DeclFuncGenericParams = GenericParams;
|
||||
}
|
||||
|
||||
void setHaveDot() {
|
||||
HaveDot = true;
|
||||
}
|
||||
@@ -600,6 +618,7 @@ public:
|
||||
FD->getDeclContext() == CurrMethodDC->getParent() &&
|
||||
InsideStaticMethod && !FD->isStatic();
|
||||
break;
|
||||
case LookupKind::Type:
|
||||
case LookupKind::TypeInDeclContext:
|
||||
llvm_unreachable("can not have a method call while doing a "
|
||||
"type completion");
|
||||
@@ -713,23 +732,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
void addDeclFuncGenericParams() {
|
||||
if (!DeclFuncGenericParams)
|
||||
return;
|
||||
|
||||
// Can not refer to generic parameters with a dot.
|
||||
if (HaveDot)
|
||||
return;
|
||||
|
||||
if (Kind != LookupKind::ValueInDeclContext &&
|
||||
Kind != LookupKind::TypeInDeclContext)
|
||||
return;
|
||||
|
||||
for (auto Param : *DeclFuncGenericParams) {
|
||||
addTypeAliasRef(Param.getAsTypeParam());
|
||||
}
|
||||
}
|
||||
|
||||
void addKeyword(StringRef Name, Type TypeAnnotation) {
|
||||
CodeCompletionResultBuilder Builder(
|
||||
CompletionContext,
|
||||
@@ -842,6 +844,7 @@ public:
|
||||
}
|
||||
return;
|
||||
|
||||
case LookupKind::Type:
|
||||
case LookupKind::TypeInDeclContext:
|
||||
if (auto *NTD = dyn_cast<NominalTypeDecl>(D)) {
|
||||
addNominalTypeRef(NTD);
|
||||
@@ -903,7 +906,6 @@ public:
|
||||
void getValueCompletionsInDeclContext(SourceLoc Loc) {
|
||||
Kind = LookupKind::ValueInDeclContext;
|
||||
lookupVisibleDecls(*this, CurrDeclContext, Loc);
|
||||
addDeclFuncGenericParams();
|
||||
|
||||
// FIXME: The pedantically correct way to find the type is to resolve the
|
||||
// swift.StringLiteralType type.
|
||||
@@ -915,10 +917,16 @@ public:
|
||||
CompletionContext.includeUnqualifiedClangResults();
|
||||
}
|
||||
|
||||
void getTypeCompletions(Type BaseType) {
|
||||
Kind = LookupKind::Type;
|
||||
NeedLeadingDot = !HaveDot;
|
||||
lookupVisibleDecls(*this, MetaTypeType::get(BaseType, SwiftContext),
|
||||
CurrDeclContext);
|
||||
}
|
||||
|
||||
void getTypeCompletionsInDeclContext(SourceLoc Loc) {
|
||||
Kind = LookupKind::TypeInDeclContext;
|
||||
lookupVisibleDecls(*this, CurrDeclContext, Loc);
|
||||
addDeclFuncGenericParams();
|
||||
}
|
||||
};
|
||||
|
||||
@@ -963,7 +971,12 @@ void CodeCompletionCallbacksImpl::completeExprSuperDot(SuperRefExpr *SRE) {
|
||||
void CodeCompletionCallbacksImpl::completeTypeSimpleBeginning() {
|
||||
Kind = CompletionKind::TypeSimpleBeginning;
|
||||
CurDeclContext = P.CurDeclContext;
|
||||
this->DeclFuncGenericParams = CodeCompletionCallbacks::DeclFuncGenericParams;
|
||||
}
|
||||
|
||||
void CodeCompletionCallbacksImpl::completeTypeIdentifier(IdentTypeRepr *ITR) {
|
||||
Kind = CompletionKind::TypeIdentifier;
|
||||
ParsedTypeLoc = TypeLoc(ITR);
|
||||
CurDeclContext = P.CurDeclContext;
|
||||
}
|
||||
|
||||
void CodeCompletionCallbacksImpl::doneParsing() {
|
||||
@@ -975,9 +988,18 @@ void CodeCompletionCallbacksImpl::doneParsing() {
|
||||
if (!typecheckContext())
|
||||
return;
|
||||
|
||||
if (DelayedParsedDecl && !typecheckDelayedParsedDecl())
|
||||
return;
|
||||
|
||||
if (auto *FD = dyn_cast_or_null<FuncDecl>(DelayedParsedDecl))
|
||||
CurDeclContext = FD->getBody();
|
||||
|
||||
if (ParsedExpr && !typecheckParsedExpr())
|
||||
return;
|
||||
|
||||
if (!ParsedTypeLoc.isNull() && !typecheckParsedType())
|
||||
return;
|
||||
|
||||
CompletionLookup Lookup(CompletionContext, TU->Ctx, CurDeclContext);
|
||||
|
||||
switch (Kind) {
|
||||
@@ -1016,11 +1038,16 @@ void CodeCompletionCallbacksImpl::doneParsing() {
|
||||
}
|
||||
|
||||
case CompletionKind::TypeSimpleBeginning: {
|
||||
Lookup.setDeclFuncGenericParameters(DeclFuncGenericParams);
|
||||
Lookup.getTypeCompletionsInDeclContext(
|
||||
TU->Ctx.SourceMgr.getCodeCompletionLoc());
|
||||
break;
|
||||
}
|
||||
|
||||
case CompletionKind::TypeIdentifier: {
|
||||
Lookup.setHaveDot();
|
||||
Lookup.getTypeCompletions(ParsedTypeLoc.getType());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
deliverCompletionResults();
|
||||
|
||||
@@ -661,8 +661,8 @@ void Parser::parseDeclDelayed() {
|
||||
Scope S(this, DelayedState->takeScope());
|
||||
ContextChange CC(*this, DelayedState->ParentContext);
|
||||
|
||||
SmallVector<ExprStmtOrDecl, 4> Entries;
|
||||
parseBraceItems(Entries, true, BraceItemListKind::TopLevelCode);
|
||||
SmallVector<Decl *, 2> Entries;
|
||||
parseDecl(Entries, DelayedState->Flags);
|
||||
}
|
||||
|
||||
/// Parse an 'import' declaration, returning true (and doing no token skipping)
|
||||
@@ -1456,12 +1456,31 @@ Parser::parseDeclFunc(SourceLoc StaticLoc, unsigned Flags) {
|
||||
}
|
||||
|
||||
TypeRepr *FuncRetTy;
|
||||
{
|
||||
CodeCompletionCallbacks::FunctionSignatureGenericParams CCInfo(
|
||||
CodeCompletion, GenericParams);
|
||||
|
||||
if (parseFunctionSignature(ArgParams, BodyParams, FuncRetTy))
|
||||
return 0;
|
||||
if (parseFunctionSignature(ArgParams, BodyParams, FuncRetTy)) {
|
||||
if (CodeCompletion) {
|
||||
// Create fake function signature.
|
||||
ArgParams.clear();
|
||||
BodyParams.clear();
|
||||
if (HasContainerType) {
|
||||
Pattern *thisPattern = buildImplicitThisParameter();
|
||||
ArgParams.push_back(thisPattern);
|
||||
BodyParams.push_back(thisPattern);
|
||||
}
|
||||
ArgParams.push_back(new (Context) AnyPattern(SourceLoc()));
|
||||
BodyParams.push_back(new (Context) AnyPattern(SourceLoc()));
|
||||
FuncRetTy = TupleTypeRepr::create(Context, {}, SourceRange(),
|
||||
SourceLoc());
|
||||
// Create function AST nodes.
|
||||
FuncExpr *FE =
|
||||
actOnFuncExprStart(FuncLoc, FuncRetTy, ArgParams, BodyParams);
|
||||
FuncDecl *FD = new (Context) FuncDecl(StaticLoc, FuncLoc, Name, NameLoc,
|
||||
GenericParams, Type(), FE,
|
||||
CurDeclContext);
|
||||
FE->setDecl(FD);
|
||||
FE->setBodySkipped(Tok.getLoc());
|
||||
CodeCompletion->setDelayedParsedDecl(FD);
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
// Enter the arguments for the function into a new function-body scope. We
|
||||
|
||||
@@ -214,18 +214,25 @@ IdentTypeRepr *Parser::parseTypeIdentifier() {
|
||||
}
|
||||
EndLoc = Loc;
|
||||
|
||||
ComponentsR.push_back(IdentTypeRepr::Component(Loc, Name,
|
||||
Context.AllocateCopy(GenericArgs),
|
||||
CurDeclContext));
|
||||
ComponentsR.push_back(IdentTypeRepr::Component(
|
||||
Loc, Name, Context.AllocateCopy(GenericArgs), CurDeclContext));
|
||||
|
||||
// Treat 'Foo.<anything>' as an attempt to write a dotted type
|
||||
// unless <anything> is 'metatype'.
|
||||
if ((Tok.is(tok::period) || Tok.is(tok::period_prefix)) &&
|
||||
peekToken().isNot(tok::kw_metatype)) {
|
||||
consumeToken();
|
||||
} else {
|
||||
break;
|
||||
if ((Tok.is(tok::period) || Tok.is(tok::period_prefix))) {
|
||||
if (peekToken().is(tok::code_complete) && CodeCompletion) {
|
||||
if (auto Entry = lookupInScope(ComponentsR[0].getIdentifier()))
|
||||
ComponentsR[0].setValue(Entry);
|
||||
CodeCompletion->completeTypeIdentifier(
|
||||
IdentTypeRepr::create(Context, ComponentsR));
|
||||
}
|
||||
|
||||
if (peekToken().isNot(tok::kw_metatype)) {
|
||||
consumeToken();
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// Lookup element #0 through our current scope chains in case it is some thing
|
||||
|
||||
@@ -716,10 +716,23 @@ void swift::performTypeChecking(TranslationUnit *TU, unsigned StartElem) {
|
||||
verify(TU);
|
||||
}
|
||||
|
||||
/// performTypeLocChecking - recursively validate the specified type. This is
|
||||
/// used when dealing with partial translation units (e.g. SIL parsing).
|
||||
bool swift::performTypeLocChecking(TranslationUnit *TU, TypeLoc &T) {
|
||||
bool swift::performTypeLocChecking(TranslationUnit *TU, TypeLoc &T,
|
||||
bool ProduceDiagnostics) {
|
||||
if (ProduceDiagnostics) {
|
||||
return TypeChecker(*TU).validateType(T);
|
||||
} else {
|
||||
// Set up a diagnostics engine that swallows diagnostics.
|
||||
DiagnosticEngine Diags(TU->Ctx.SourceMgr);
|
||||
return TypeChecker(*TU, Diags).validateType(T);
|
||||
}
|
||||
}
|
||||
|
||||
bool swift::typeCheckCompletionDecl(TranslationUnit *TU, Decl *D) {
|
||||
// Set up a diagnostics engine that swallows diagnostics.
|
||||
DiagnosticEngine Diags(TU->Ctx.SourceMgr);
|
||||
TypeChecker TC(*TU, Diags);
|
||||
TC.typeCheckDecl(D, true);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool swift::typeCheckCompletionContextExpr(TranslationUnit *TU,
|
||||
|
||||
Reference in New Issue
Block a user