| //===--- 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: { |
| 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 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)); |
| |
| if (SF.getASTContext().LangOpts.VerifySyntaxTree) { |
| // Verify the added nodes if specified. |
| 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(); |
| } |
| |
| 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; |
| } |
| } |