Skip to content

Commit 46a4bc1

Browse files
committed
Code completion: preserve the AST for the parsed top-level code. This enables
us to find iteration variables while doing code completion in nested top-level code. Fixes part of rdar://15199468 Swift SVN r9343
1 parent d503c65 commit 46a4bc1

11 files changed

+85
-32
lines changed

Diff for: include/swift/Parse/Parser.h

+9-5
Original file line numberDiff line numberDiff line change
@@ -186,22 +186,24 @@ class Parser {
186186

187187
void restoreParserPosition(ParserPosition PP) {
188188
L->restoreState(PP.LS);
189-
PreviousLoc = PP.PreviousLoc;
190189

191190
// We might be at tok::eof now, so ensure that consumeToken() does not
192191
// assert about lexing past eof.
193192
Tok.setKind(tok::unknown);
194193
consumeToken();
194+
195+
PreviousLoc = PP.PreviousLoc;
195196
}
196197

197198
void backtrackToPosition(ParserPosition PP) {
198199
L->backtrackToState(PP.LS);
199-
PreviousLoc = PP.PreviousLoc;
200200

201201
// We might be at tok::eof now, so ensure that consumeToken() does not
202202
// assert about lexing past eof.
203203
Tok.setKind(tok::unknown);
204204
consumeToken();
205+
206+
PreviousLoc = PP.PreviousLoc;
205207
}
206208

207209
/// RAII object that, when it is destructed, restores the parser and lexer to
@@ -218,7 +220,7 @@ class Parser {
218220
P.backtrackToPosition(PP);
219221
}
220222
};
221-
223+
222224
/// RAII object for managing 'var' patterns. Inside a 'var' pattern, bare
223225
/// identifiers are parsed as new VarDecls instead of references to existing
224226
/// ones.
@@ -439,7 +441,8 @@ class Parser {
439441
tok SeparatorK, bool OptionalSep, Diag<> ErrorDiag,
440442
std::function<bool()> callback);
441443

442-
void consumeTopLevelDecl(ParserPosition BeginParserPosition);
444+
void consumeTopLevelDecl(ParserPosition BeginParserPosition,
445+
TopLevelCodeDecl *TLCD);
443446

444447
ParserStatus parseBraceItems(SmallVectorImpl<ExprStmtOrDecl> &Decls,
445448
bool IsTopLevel,
@@ -455,7 +458,8 @@ class Parser {
455458
static bool isStartOfOperatorDecl(const Token &Tok, const Token &Tok2);
456459

457460
bool parseTopLevel();
458-
void consumeDecl(ParserPosition BeginParserPosition, unsigned Flags);
461+
void consumeDecl(ParserPosition BeginParserPosition, unsigned Flags,
462+
bool IsTopLevel);
459463
ParserStatus parseDecl(SmallVectorImpl<Decl*> &Entries, unsigned Flags);
460464
void parseDeclDelayed();
461465
enum {

Diff for: include/swift/Parse/PersistentParserState.h

+3
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ class PersistentParserState {
103103
DeclContext *ParentContext,
104104
SourceRange BodyRange, SourceLoc PreviousLoc);
105105

106+
void delayTopLevel(TopLevelCodeDecl *TLCD, SourceRange BodyRange,
107+
SourceLoc PreviousLoc);
108+
106109
bool hasDelayedDecl() {
107110
return CodeCompletionDelayedDeclState.get() != nullptr;
108111
}

Diff for: include/swift/Sema/CodeCompletionTypeChecking.h

+7-3
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ namespace swift {
3131
/// \brief Typecheck a declaration parsed during code completion.
3232
///
3333
/// \returns true on success, false on error.
34-
bool typeCheckCompletionDecl(ASTContext &Ctx, Decl *D);
34+
bool typeCheckCompletionDecl(Decl *D);
3535

3636
/// \brief Typecheck an expression parsed during code completion.
3737
///
@@ -40,10 +40,14 @@ namespace swift {
4040
Expr *&parsedExpr);
4141

4242
/// Partially typecheck the specified function body.
43-
bool typeCheckAbstractFunctionBodyUntil(ASTContext &Ctx,
44-
AbstractFunctionDecl *AFD,
43+
bool typeCheckAbstractFunctionBodyUntil(AbstractFunctionDecl *AFD,
4544
SourceLoc EndTypeCheckLoc);
4645

46+
/// \brief Typecheck top-level code parsed during code completion.
47+
///
48+
/// \returns true on success, false on error.
49+
bool typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD);
50+
4751
/// A unique_ptr for LazyResolver that can perform additional cleanup.
4852
using OwnedResolver = std::unique_ptr<LazyResolver, void(*)(LazyResolver*)>;
4953

Diff for: lib/IDE/CodeCompletion.cpp

+6-4
Original file line numberDiff line numberDiff line change
@@ -426,8 +426,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks,
426426
typecheckContextImpl(DCToTypeCheck->getParent());
427427
// Then type check the function itself.
428428
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(DCToTypeCheck))
429-
return typeCheckAbstractFunctionBodyUntil(P.Context, AFD,
430-
EndTypeCheckLoc);
429+
return typeCheckAbstractFunctionBodyUntil(AFD, EndTypeCheckLoc);
431430
return false;
432431
}
433432
if (DC->getContextKind() == DeclContextKind::NominalTypeDecl) {
@@ -436,7 +435,10 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks,
436435
typecheckContextImpl(DC->getParent());
437436
if (NTD->hasType())
438437
return true;
439-
return typeCheckCompletionDecl(P.Context, cast<NominalTypeDecl>(DC));
438+
return typeCheckCompletionDecl(cast<NominalTypeDecl>(DC));
439+
}
440+
if (DC->getContextKind() == DeclContextKind::TopLevelCodeDecl) {
441+
return typeCheckTopLevelCodeDecl(dyn_cast<TopLevelCodeDecl>(DC));
440442
}
441443
return true;
442444
}
@@ -449,7 +451,7 @@ class CodeCompletionCallbacksImpl : public CodeCompletionCallbacks,
449451
/// \returns true on success, false on failure.
450452
bool typecheckDelayedParsedDecl() {
451453
assert(DelayedParsedDecl && "should have a delayed parsed decl");
452-
return typeCheckCompletionDecl(P.Context, DelayedParsedDecl);
454+
return typeCheckCompletionDecl(DelayedParsedDecl);
453455
}
454456

455457
/// \returns true on success, false on failure.

Diff for: lib/Parse/ParseDecl.cpp

+9-2
Original file line numberDiff line numberDiff line change
@@ -453,7 +453,8 @@ bool Parser::isStartOfOperatorDecl(const Token &Tok, const Token &Tok2) {
453453
|| Tok2.isContextualKeyword("infix"));
454454
}
455455

456-
void Parser::consumeDecl(ParserPosition BeginParserPosition, unsigned Flags) {
456+
void Parser::consumeDecl(ParserPosition BeginParserPosition, unsigned Flags,
457+
bool IsTopLevel) {
457458
backtrackToPosition(BeginParserPosition);
458459
SourceLoc BeginLoc = Tok.getLoc();
459460
// Consume tokens up to code completion token.
@@ -466,6 +467,12 @@ void Parser::consumeDecl(ParserPosition BeginParserPosition, unsigned Flags) {
466467
State->delayDecl(PersistentParserState::DelayedDeclKind::Decl, Flags,
467468
CurDeclContext, { BeginLoc, EndLoc },
468469
BeginParserPosition.PreviousLoc);
470+
471+
if (IsTopLevel) {
472+
// Skip the rest of the file to prevent the parser from constructing the
473+
// AST for it. Forward references are not allowed at the top level.
474+
skipUntil(tok::eof);
475+
}
469476
}
470477

471478
/// \brief Parse a single syntactic declaration and return a list of decl
@@ -586,7 +593,7 @@ ParserStatus Parser::parseDecl(SmallVectorImpl<Decl*> &Entries,
586593
if (Status.hasCodeCompletion() && isCodeCompletionFirstPass() &&
587594
!CurDeclContext->isModuleContext()) {
588595
// Only consume non-toplevel decls.
589-
consumeDecl(BeginParserPosition, Flags);
596+
consumeDecl(BeginParserPosition, Flags, /*IsTopLevel=*/false);
590597

591598
// Pretend that there was no error.
592599
return makeParserSuccess();

Diff for: lib/Parse/ParseStmt.cpp

+21-12
Original file line numberDiff line numberDiff line change
@@ -133,7 +133,8 @@ static bool isTerminatorForBraceItemListKind(const Token &Tok,
133133
}
134134
}
135135

136-
void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition) {
136+
void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition,
137+
TopLevelCodeDecl *TLCD) {
137138
backtrackToPosition(BeginParserPosition);
138139
SourceLoc BeginLoc = Tok.getLoc();
139140
// Consume tokens up to code completion token.
@@ -146,9 +147,8 @@ void Parser::consumeTopLevelDecl(ParserPosition BeginParserPosition) {
146147
// this decl that are past the code completion token.
147148
skipUntilDeclStmtRBrace(tok::l_brace);
148149
SourceLoc EndLoc = Tok.getLoc();
149-
State->delayDecl(PersistentParserState::DelayedDeclKind::TopLevelCodeDecl, 0,
150-
CurDeclContext, { BeginLoc, EndLoc },
151-
BeginParserPosition.PreviousLoc);
150+
State->delayTopLevel(TLCD, { BeginLoc, EndLoc },
151+
BeginParserPosition.PreviousLoc);
152152

153153
// Skip the rest of the file to prevent the parser from constructing the AST
154154
// for it. Forward references are not allowed at the top level.
@@ -239,7 +239,7 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl<ExprStmtOrDecl> &Entries,
239239
NeedParseErrorRecovery = true;
240240
if (Status.hasCodeCompletion() && IsTopLevel &&
241241
isCodeCompletionFirstPass()) {
242-
consumeTopLevelDecl(BeginParserPosition);
242+
consumeDecl(BeginParserPosition, 0U, IsTopLevel);
243243
return Status;
244244
}
245245
}
@@ -258,7 +258,10 @@ ParserStatus Parser::parseBraceItems(SmallVectorImpl<ExprStmtOrDecl> &Entries,
258258

259259
ParserStatus Status = parseExprOrStmt(Result);
260260
if (Status.hasCodeCompletion() && isCodeCompletionFirstPass()) {
261-
consumeTopLevelDecl(BeginParserPosition);
261+
consumeTopLevelDecl(BeginParserPosition, TLCD);
262+
auto Brace = BraceStmt::create(Context, StartLoc, {}, Tok.getLoc());
263+
TLCD->setBody(Brace);
264+
Entries.push_back(TLCD);
262265
return Status;
263266
}
264267
if (Status.isError())
@@ -339,14 +342,20 @@ void Parser::parseTopLevelCodeDeclDelayed() {
339342
// Rewind to the beginning of the top-level code.
340343
restoreParserPosition(BeginParserPosition);
341344

342-
// No need to re-enter the scope: parseBraceItems() will create a scope
343-
// anyway.
345+
// Re-enter the lexical scope.
346+
Scope S(this, DelayedState->takeScope());
344347

345348
// Re-enter the top-level decl context.
346-
ContextChange CC(*this, DelayedState->ParentContext);
347-
348-
SmallVector<ExprStmtOrDecl, 4> Entries;
349-
parseBraceItems(Entries, true, BraceItemListKind::TopLevelCode);
349+
auto *TLCD = cast<TopLevelCodeDecl>(DelayedState->ParentContext);
350+
ContextChange CC(*this, TLCD);
351+
352+
SourceLoc StartLoc = Tok.getLoc();
353+
ExprStmtOrDecl Result;
354+
parseExprOrStmt(Result);
355+
if (!Result.isNull()) {
356+
auto Brace = BraceStmt::create(Context, StartLoc, Result, Tok.getLoc());
357+
TLCD->setBody(Brace);
358+
}
350359
}
351360

352361
/// Recover from a 'case' or 'default' outside of a 'switch' by consuming up to

Diff for: lib/Parse/Parser.cpp

+1-1
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,7 @@ void parseDelayedDecl(TranslationUnit *TU, PersistentParserState &ParserState,
115115
TheParser.parseDeclDelayed();
116116
break;
117117
}
118-
118+
119119
if (CodeCompletion)
120120
CodeCompletion->doneParsing();
121121
}

Diff for: lib/Parse/PersistentParserState.cpp

+7
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,10 @@ void PersistentParserState::delayDecl(DelayedDeclKind Kind,
5353
ScopeInfo.saveCurrentScope()));
5454
}
5555

56+
void PersistentParserState::delayTopLevel(TopLevelCodeDecl *TLCD,
57+
SourceRange BodyRange,
58+
SourceLoc PreviousLoc) {
59+
delayDecl(DelayedDeclKind::TopLevelCodeDecl, 0U, TLCD, BodyRange,
60+
PreviousLoc);
61+
}
62+

Diff for: lib/Sema/TypeCheckDecl.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -2168,7 +2168,8 @@ void TypeChecker::validateDecl(ValueDecl *D, bool resolveTypeParams) {
21682168
// FIXME: This case is hit when code completion occurs in a function
21692169
// parameter list. Previous parameters are definitely in scope, but
21702170
// we don't really know how to type-check them.
2171-
assert(isa<AbstractFunctionDecl>(D->getDeclContext()));
2171+
assert(isa<AbstractFunctionDecl>(D->getDeclContext()) ||
2172+
isa<TopLevelCodeDecl>(D->getDeclContext()));
21722173
D->setType(ErrorType::get(Context));
21732174
}
21742175
break;

Diff for: lib/Sema/TypeCheckREPL.cpp

+2-1
Original file line numberDiff line numberDiff line change
@@ -715,7 +715,8 @@ void TypeChecker::processREPLTopLevel(SourceFile &SF, unsigned FirstDecl) {
715715
SF.Decls.push_back(D);
716716

717717
TopLevelCodeDecl *TLCD = dyn_cast<TopLevelCodeDecl>(D);
718-
if (TLCD == 0) continue;
718+
if (!TLCD || TLCD->getBody()->getElements().empty())
719+
continue;
719720

720721
auto Entry = TLCD->getBody()->getElements()[0];
721722

Diff for: lib/Sema/TypeChecker.cpp

+18-3
Original file line numberDiff line numberDiff line change
@@ -715,10 +715,13 @@ bool swift::performTypeLocChecking(ASTContext &Ctx, TypeLoc &T,
715715
}
716716
}
717717

718-
bool swift::typeCheckCompletionDecl(ASTContext &Ctx, Decl *D) {
718+
bool swift::typeCheckCompletionDecl(Decl *D) {
719+
auto &Ctx = D->getASTContext();
720+
719721
// Set up a diagnostics engine that swallows diagnostics.
720722
DiagnosticEngine Diags(Ctx.SourceMgr);
721723
TypeChecker TC(Ctx, Diags);
724+
722725
TC.typeCheckDecl(D, true);
723726
return true;
724727
}
@@ -736,19 +739,31 @@ bool swift::typeCheckCompletionContextExpr(ASTContext &Ctx, DeclContext *DC,
736739
&& !parsedExpr->getType()->is<ErrorType>();
737740
}
738741

739-
bool swift::typeCheckAbstractFunctionBodyUntil(ASTContext &Ctx,
740-
AbstractFunctionDecl *AFD,
742+
bool swift::typeCheckAbstractFunctionBodyUntil(AbstractFunctionDecl *AFD,
741743
SourceLoc EndTypeCheckLoc) {
742744
if (AFD->isInvalid())
743745
return false;
744746

747+
auto &Ctx = AFD->getASTContext();
748+
745749
// Set up a diagnostics engine that swallows diagnostics.
746750
DiagnosticEngine Diags(Ctx.SourceMgr);
747751

748752
TypeChecker TC(Ctx, Diags);
749753
return !TC.typeCheckAbstractFunctionBodyUntil(AFD, EndTypeCheckLoc);
750754
}
751755

756+
bool swift::typeCheckTopLevelCodeDecl(TopLevelCodeDecl *TLCD) {
757+
auto &Ctx = static_cast<Decl *>(TLCD)->getASTContext();
758+
759+
// Set up a diagnostics engine that swallows diagnostics.
760+
DiagnosticEngine Diags(Ctx.SourceMgr);
761+
762+
TypeChecker TC(Ctx, Diags);
763+
TC.typeCheckTopLevelCodeDecl(TLCD);
764+
return true;
765+
}
766+
752767
static void deleteTypeCheckerAndDiags(LazyResolver *resolver) {
753768
DiagnosticEngine &diags = static_cast<TypeChecker*>(resolver)->Diags;
754769
delete resolver;

0 commit comments

Comments
 (0)