blob: 43fd0a9130c24c986b93420e235b2e2cc5570542 [file] [log] [blame]
//===--- SyntaxParsingContext.cpp - Syntax Tree Parsing Support------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors
// Licensed under Apache License v2.0 with Runtime Library Exception
//
// See https://swift.org/LICENSE.txt for license information
// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors
//
//===----------------------------------------------------------------------===//
#include "swift/Parse/SyntaxParsingContext.h"
#include "swift/AST/ASTContext.h"
#include "swift/AST/DiagnosticEngine.h"
#include "swift/AST/DiagnosticsParse.h"
#include "swift/AST/Module.h"
#include "swift/Basic/Defer.h"
#include "swift/Parse/Token.h"
#include "swift/Syntax/RawSyntax.h"
#include "swift/Syntax/References.h"
#include "swift/Syntax/Syntax.h"
#include "swift/Syntax/SyntaxFactory.h"
#include "swift/Syntax/SyntaxVisitor.h"
#include "swift/Syntax/TokenKinds.h"
#include "swift/Syntax/TokenSyntax.h"
#include "swift/Syntax/Trivia.h"
using namespace swift;
using namespace swift::syntax;
using RootContextData = SyntaxParsingContext::RootContextData;
SyntaxParsingContext::SyntaxParsingContext(SyntaxParsingContext *&CtxtHolder,
SourceFile &SF, unsigned BufferID)
: RootDataOrParent(new RootContextData(SF, SF.getASTContext().Diags,
SF.getASTContext().SourceMgr,
BufferID)),
CtxtHolder(CtxtHolder), Arena(SF.getASTContext().getSyntaxArena()),
Storage(getRootData().Storage), Offset(0), Mode(AccumulationMode::Root),
Enabled(SF.shouldBuildSyntaxTree()) {
CtxtHolder = this;
Storage.reserve(128);
}
RC<RawSyntax>
SyntaxParsingContext::makeUnknownSyntax(SyntaxKind Kind,
ArrayRef<RC<RawSyntax>> Parts) {
assert(isUnknownKind(Kind));
return RawSyntax::make(Kind, Parts, SourcePresence::Present, &Arena);
}
RC<RawSyntax>
SyntaxParsingContext::createSyntaxAs(SyntaxKind Kind,
ArrayRef<RC<RawSyntax>> Parts) {
// Try to create the node of the given syntax.
if (auto Node = SyntaxFactory::createRaw(Kind, Parts, &Arena))
return Node;
// Fallback to unknown syntax for the category.
return makeUnknownSyntax(
getUnknownKind(Kind), Parts);
}
RC<RawSyntax> SyntaxParsingContext::bridgeAs(SyntaxContextKind Kind,
ArrayRef<RC<RawSyntax>> Parts) {
if (Parts.size() == 1) {
auto RawNode = Parts.front();
switch (Kind) {
case SyntaxContextKind::Stmt:
if (!RawNode->isStmt())
return makeUnknownSyntax(SyntaxKind::UnknownStmt, Parts);
break;
case SyntaxContextKind::Decl:
if (!RawNode->isDecl())
return makeUnknownSyntax(SyntaxKind::UnknownDecl, Parts);
break;
case SyntaxContextKind::Expr:
if (!RawNode->isExpr())
return makeUnknownSyntax(SyntaxKind::UnknownExpr, Parts);
break;
case SyntaxContextKind::Type:
if (!RawNode->isType())
return makeUnknownSyntax(SyntaxKind::UnknownType, Parts);
break;
case SyntaxContextKind::Pattern:
if (!RawNode->isPattern())
return makeUnknownSyntax(SyntaxKind::UnknownPattern, Parts);
break;
case SyntaxContextKind::Syntax:
// We don't need to coerce in this case.
break;
}
return RawNode;
} else {
SyntaxKind UnknownKind;
switch (Kind) {
case SyntaxContextKind::Stmt:
UnknownKind = SyntaxKind::UnknownStmt;
break;
case SyntaxContextKind::Decl:
UnknownKind = SyntaxKind::UnknownDecl;
break;
case SyntaxContextKind::Expr:
UnknownKind = SyntaxKind::UnknownExpr;
break;
case SyntaxContextKind::Type:
UnknownKind = SyntaxKind::UnknownType;
break;
case SyntaxContextKind::Pattern:
UnknownKind = SyntaxKind::UnknownPattern;
break;
case SyntaxContextKind::Syntax:
UnknownKind = SyntaxKind::Unknown;
break;
}
return makeUnknownSyntax(UnknownKind, Parts);
}
}
/// Add RawSyntax to the parts.
void SyntaxParsingContext::addRawSyntax(RC<RawSyntax> Raw) {
Storage.emplace_back(Raw);
}
SyntaxParsingContext *SyntaxParsingContext::getRoot() {
auto Curr = this;
while (!Curr->isRoot())
Curr = Curr->getParent();
return Curr;
}
/// Add Token with Trivia to the parts.
void SyntaxParsingContext::addToken(Token &Tok, Trivia &LeadingTrivia,
Trivia &TrailingTrivia) {
if (!Enabled)
return;
addRawSyntax(RawSyntax::getToken(Arena, Tok.getKind(), Tok.getText(),
LeadingTrivia.Pieces,
TrailingTrivia.Pieces));
}
/// Add Syntax to the parts.
void SyntaxParsingContext::addSyntax(Syntax Node) {
if (!Enabled)
return;
addRawSyntax(Node.getRaw());
}
void SyntaxParsingContext::createNodeInPlace(SyntaxKind Kind, size_t N) {
if (N == 0) {
Storage.push_back(createSyntaxAs(Kind, {}));
return;
}
auto I = Storage.end() - N;
*I = createSyntaxAs(Kind, getParts().take_back(N));
// Remove consumed parts.
if (N != 1)
Storage.erase(I + 1, Storage.end());
}
void SyntaxParsingContext::createNodeInPlace(SyntaxKind Kind) {
assert(isTopOfContextStack());
if (!Enabled)
return;
switch (Kind) {
case SyntaxKind::SuperRefExpr:
case SyntaxKind::OptionalChainingExpr:
case SyntaxKind::ForcedValueExpr:
case SyntaxKind::PostfixUnaryExpr:
case SyntaxKind::TernaryExpr:
case SyntaxKind::AvailabilityLabeledArgument: {
auto Pair = SyntaxFactory::countChildren(Kind);
assert(Pair.first == Pair.second);
createNodeInPlace(Kind, Pair.first);
break;
}
case SyntaxKind::CodeBlockItem:
case SyntaxKind::IdentifierExpr:
case SyntaxKind::SpecializeExpr:
case SyntaxKind::MemberAccessExpr:
case SyntaxKind::DotSelfExpr:
case SyntaxKind::ImplicitMemberExpr:
case SyntaxKind::SimpleTypeIdentifier:
case SyntaxKind::MemberTypeIdentifier:
case SyntaxKind::FunctionCallExpr:
case SyntaxKind::SubscriptExpr:
case SyntaxKind::ExprList: {
createNodeInPlace(Kind, getParts().size());
break;
}
default:
llvm_unreachable("Unrecognized node kind.");
}
}
void SyntaxParsingContext::collectNodesInPlace(SyntaxKind ColletionKind) {
assert(isCollectionKind(ColletionKind));
assert(isTopOfContextStack());
if (!Enabled)
return;
auto Parts = getParts();
auto Count = 0;
for (auto I = Parts.rbegin(), End = Parts.rend(); I != End; ++I) {
if (!SyntaxFactory::canServeAsCollectionMemberRaw(ColletionKind, *I))
break;
++Count;
}
if (Count)
createNodeInPlace(ColletionKind, Count);
}
/// This verifier traverses a syntax node to emit proper diagnostics.
class SyntaxVerifier: public SyntaxVisitor {
RootContextData &RootData;
template<class T>
SourceLoc getSourceLoc(T Node) {
return RootData.SourceMgr.getLocForOffset(RootData.BufferID,
Node.getAbsolutePosition().getOffset());
}
public:
SyntaxVerifier(RootContextData &RootData) : RootData(RootData) {}
void visit(UnknownDeclSyntax Node) override {
RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity,
"declaration");
visitChildren(Node);
}
void visit(UnknownExprSyntax Node) override {
RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity,
"expression");
visitChildren(Node);
}
void visit(UnknownStmtSyntax Node) override {
RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity,
"statement");
visitChildren(Node);
}
void visit(UnknownTypeSyntax Node) override {
RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity,
"type");
visitChildren(Node);
}
void visit(UnknownPatternSyntax Node) override {
RootData.Diags.diagnose(getSourceLoc(Node), diag::unknown_syntax_entity,
"pattern");
visitChildren(Node);
}
void verify(Syntax Node) {
Node.accept(*this);
}
};
namespace {
void finalizeSourceFile(RootContextData &RootData,
ArrayRef<RC<RawSyntax>> Parts) {
SourceFile &SF = RootData.SF;
std::vector<RC<RawSyntax>> AllTopLevel;
RC<RawSyntax> EOFToken;
if (SF.hasSyntaxRoot()) {
auto SourceRaw = SF.getSyntaxRoot().getRaw();
auto Decls =
SourceRaw->getChild(SourceFileSyntax::Cursor::Statements)->getLayout();
std::copy(Decls.begin(), Decls.end(), std::back_inserter(AllTopLevel));
EOFToken = SourceRaw->getChild(SourceFileSyntax::Cursor::EOFToken);
}
if (!Parts.empty() && Parts.back()->isToken(tok::eof)) {
EOFToken = Parts.back();
Parts = Parts.drop_back();
}
for (auto RawNode : Parts) {
if (RawNode->getKind() != SyntaxKind::CodeBlockItemList)
// FIXME: Skip toplevel garbage nodes for now. we shouldn't emit them in
// the first place.
continue;
auto Items = RawNode->getLayout();
std::copy(Items.begin(), Items.end(), std::back_inserter(AllTopLevel));
}
if (!EOFToken)
EOFToken = RawSyntax::missing(tok::eof, "");
auto newRaw = SyntaxFactory::createRaw(
SyntaxKind::SourceFile,
{
SyntaxFactory::createRaw(SyntaxKind::CodeBlockItemList, AllTopLevel),
EOFToken,
});
assert(newRaw);
SF.setSyntaxRoot(make<SourceFileSyntax>(newRaw));
// Verify the tree if specified.
// Do this only when we see the real EOF token because parseIntoSourceFile()
// can get called multiple times for single source file.
if (EOFToken->isPresent() && SF.getASTContext().LangOpts.VerifySyntaxTree) {
SyntaxVerifier Verifier(RootData);
Verifier.verify(SF.getSyntaxRoot());
}
}
} // End of anonymous namespace
void SyntaxParsingContext::finalizeRoot() {
if (!Enabled)
return;
assert(isTopOfContextStack() && "some sub-contexts are not destructed");
assert(isRoot() && "only root context can finalize the tree");
assert(Mode == AccumulationMode::Root);
finalizeSourceFile(getRootData(), getParts());
// Clear the parts because we will call this function again when destroying
// the root context.
getRootData().Storage.clear();
}
void SyntaxParsingContext::synthesize(tok Kind, StringRef Text) {
if (!Enabled)
return;
if (Text.empty())
Text = getTokenText(Kind);
Storage.push_back(RawSyntax::missing(Kind, Text));
}
void SyntaxParsingContext::synthesize(SyntaxKind Kind) {
if (!Enabled)
return;
Storage.push_back(RawSyntax::missing(Kind));
}
SyntaxParsingContext::~SyntaxParsingContext() {
assert(isTopOfContextStack() && "destructed in wrong order");
SWIFT_DEFER {
// Pop this context from the stack.
if (!isRoot())
CtxtHolder = getParent();
else
delete RootDataOrParent.get<RootContextData*>();
};
if (!Enabled)
return;
switch (Mode) {
// Create specified Syntax node from the parts and add it to the parent.
case AccumulationMode::CreateSyntax:
assert(!isRoot());
createNodeInPlace(SynKind, Storage.size() - Offset);
break;
// Ensure the result is specified Syntax category and add it to the parent.
case AccumulationMode::CoerceKind: {
assert(!isRoot());
if (Storage.size() == Offset) {
Storage.push_back(bridgeAs(CtxtKind, {}));
} else {
auto I = Storage.begin() + Offset;
*I = bridgeAs(CtxtKind, getParts());
// Remove used parts.
if (Storage.size() > Offset + 1)
Storage.erase(Storage.begin() + (Offset + 1), Storage.end());
}
break;
}
// Do nothing.
case AccumulationMode::Transparent:
assert(!isRoot());
break;
// Remove all parts in this context.
case AccumulationMode::Discard:
Storage.resize(Offset);
break;
// Accumulate parsed toplevel syntax onto the SourceFile.
case AccumulationMode::Root:
finalizeRoot();
break;
// Never.
case AccumulationMode::NotSet:
assert(!Enabled && "Cleanup mode must be specified before destruction");
break;
}
}