//===--- Parser.h - Swift Language Parser -----------------------*- C++ -*-===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2024 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 // //===----------------------------------------------------------------------===// // // This file defines the Parser interface. // //===----------------------------------------------------------------------===// #ifndef SWIFT_PARSER_H #define SWIFT_PARSER_H #include "swift/AST/ASTContext.h" #include "swift/AST/ASTNode.h" #include "swift/AST/Expr.h" #include "swift/AST/LayoutConstraint.h" #include "swift/AST/LifetimeDependence.h" #include "swift/AST/ParseRequests.h" #include "swift/AST/Pattern.h" #include "swift/AST/SourceFile.h" #include "swift/AST/Stmt.h" #include "swift/Basic/OptionSet.h" #include "swift/Config.h" #include "swift/Parse/Lexer.h" #include "swift/Parse/ParserPosition.h" #include "swift/Parse/ParserResult.h" #include "swift/Parse/PatternBindingState.h" #include "swift/Parse/PersistentParserState.h" #include "swift/Parse/Token.h" #include "llvm/ADT/IntrusiveRefCntPtr.h" namespace llvm { template class PointerUnion; } namespace swift { class AvailabilitySpec; class CodeCompletionCallbacks; class DoneParsingCallback; class DefaultArgumentInitializer; class DiagnosticEngine; class Expr; class Lexer; class PersistentParserState; class RequirementRepr; class SILParserStateBase; class SourceManager; class UUID; struct EnumElementInfo; /// Different contexts in which BraceItemList are parsed. enum class BraceItemListKind { /// A statement list terminated by a closing brace. The default. Brace, /// A statement list in a case block. The list is terminated /// by a closing brace or a 'case' or 'default' label. Case, /// The top-level of a file, when not in parse-as-library mode (i.e. the /// repl or a script). TopLevelCode, /// The top-level of a file, when in parse-as-library mode. TopLevelLibrary, /// The body of the inactive clause of an #if/#else/#endif block InactiveConditionalBlock, /// The body of the active clause of an #if/#else/#endif block ActiveConditionalBlock, /// The top-level of a macro expansion "file". MacroExpansion, }; /// The receiver will be fed with consumed tokens while parsing. The main purpose /// is to generate a corrected token stream for tooling support like syntax /// coloring. class ConsumeTokenReceiver { public: /// This is called when a token is consumed. virtual void receive(const Token &Tok) {} /// This is called to update the kind of a token whose start location is Loc. virtual void registerTokenKindChange(SourceLoc Loc, tok NewKind) {}; /// This is called when a source file is fully parsed. It returns the /// finalized vector of tokens, or \c None if the receiver isn't configured to /// record them. virtual std::optional> finalize() { return std::nullopt; } virtual ~ConsumeTokenReceiver() = default; }; /// The role of the elements in a given #if enum class IfConfigElementsRole { // Parse normally. Normal, // Parse, but only for syntax. Throw away the results. SyntaxOnly, // Already skipped; no need to parse anything. Skipped }; /// Describes the context in which the '#if' is being parsed. enum class IfConfigContext { BraceItems, DeclItems, SwitchStmt, PostfixExpr, DeclAttrs }; /// The main class used for parsing a source file (.swift or .sil). /// /// Rather than instantiating a Parser yourself, use one of the parsing APIs /// provided in Subsystems.h. class Parser { Parser(const Parser&) = delete; void operator=(const Parser&) = delete; bool IsInputIncomplete = false; bool EnableParameterizedNonisolated = true; // HACK: for closures std::vector SplitTokens; public: SourceManager &SourceMgr; DiagnosticEngine &Diags; SourceFile &SF; Lexer *L; SILParserStateBase *SIL; // Non-null when parsing SIL decls. PersistentParserState *State; std::unique_ptr OwnedState; DeclContext *CurDeclContext; ASTContext &Context; CodeCompletionCallbacks *CodeCompletionCallbacks = nullptr; DoneParsingCallback *DoneParsingCallback = nullptr; std::vector>> AnonClosureVars; /// The current token hash, or \c None if the parser isn't computing a hash /// for the token stream. std::optional CurrentTokenHash; void recordTokenHash(const Token Tok) { if (!Tok.getText().empty()) recordTokenHash(Tok.getText()); } void recordTokenHash(StringRef token); PatternBindingState InBindingPattern = PatternBindingState::NotInBinding; /// Whether this context has an async attribute. bool InPatternWithAsyncAttribute = false; /// Whether a '#sourceLocation' is currently active. /// NOTE: Do not use this outside of `parseLineDirective`, it doesn't have /// its state restored when parsing delayed bodies (we avoid skipping bodies /// if we see a `#sourceLocation` in them though). Instead, query the /// SourceManager. bool InPoundLineEnvironment = false; bool InPoundIfEnvironment = false; /// ASTScopes are not created in inactive clauses and lookups to decls will fail. bool InInactiveClauseEnvironment = false; bool InSwiftKeyPath = false; bool InFreestandingMacroArgument = false; /// Whether we should delay parsing nominal type, extension, and function /// bodies. bool isDelayedParsingEnabled() const; /// Whether to evaluate the conditions of #if decls, meaning that the bodies /// of any active clauses are hoisted such that they become sibling nodes with /// the #if decl. bool shouldEvaluatePoundIfDecls() const; void setCodeCompletionCallbacks(class CodeCompletionCallbacks *Callbacks) { CodeCompletionCallbacks = Callbacks; } void setDoneParsingCallback(class DoneParsingCallback *Callback) { this->DoneParsingCallback = Callback; } bool isIDEInspectionFirstPass() const { return SourceMgr.hasIDEInspectionTargetBuffer() && !DoneParsingCallback; } bool allowTopLevelCode() const; bool isInMacroExpansion(SourceLoc loc) const; const std::vector &getSplitTokens() const { return SplitTokens; } void markSplitToken(tok Kind, StringRef Txt); /// Returns true if the parser reached EOF with incomplete source input, due /// for example, a missing right brace. bool isInputIncomplete() const { return IsInputIncomplete; } void checkForInputIncomplete() { IsInputIncomplete = IsInputIncomplete || // Check whether parser reached EOF but the real EOF, not the end of a // string interpolation segment. (Tok.is(tok::eof) && Tok.getText() != ")"); } /// This is the current token being considered by the parser. Token Tok; /// The receiver to collect all consumed tokens. ConsumeTokenReceiver *TokReceiver; /// The location of the previous token. SourceLoc PreviousLoc; /// Use this to assert that the parser has advanced the lexing location, e.g. /// before a specific parser function has returned. class AssertParserMadeProgressBeforeLeavingScopeRAII { Parser &P; SourceLoc InitialLoc; public: AssertParserMadeProgressBeforeLeavingScopeRAII(Parser &parser) : P(parser) { InitialLoc = P.Tok.getLoc(); } ~AssertParserMadeProgressBeforeLeavingScopeRAII() { assert(InitialLoc != P.Tok.getLoc() && "parser did not make progress, this can result in infinite loop"); } }; /// A RAII object for temporarily changing CurDeclContext. class ContextChange { protected: Parser &P; DeclContext *OldContext; // null signals that this has been popped ContextChange(const ContextChange &) = delete; ContextChange &operator=(const ContextChange &) = delete; public: ContextChange(Parser &P, DeclContext *DC) : P(P), OldContext(P.CurDeclContext) { assert(DC && "pushing null context?"); P.CurDeclContext = DC; } /// Prematurely pop the DeclContext installed by the constructor. /// Makes the destructor a no-op. void pop() { assert(OldContext && "already popped context!"); popImpl(); OldContext = nullptr; } ~ContextChange() { if (OldContext) popImpl(); } private: void popImpl() { P.CurDeclContext = OldContext; } }; /// A RAII object for parsing a new local context. class ParseFunctionBody { private: ContextChange CC; public: ParseFunctionBody(Parser &P, DeclContext *DC) : CC(P, DC) { assert(!isa(DC) && "top-level code should be parsed using TopLevelCodeContext!"); } void pop() { CC.pop(); } }; /// Describes the kind of a lexical structure marker, indicating /// what kind of structural element we started parsing at a /// particular location. enum class StructureMarkerKind : uint8_t { /// The start of a declaration. Declaration, /// The start of a statement. Statement, /// An open parentheses. OpenParen, /// An open brace. OpenBrace, /// An open square bracket. OpenSquare, /// An #if conditional clause. IfConfig, }; /// A structure marker, which identifies the location at which the /// parser saw an entity it is parsing. struct StructureMarker { /// The location at which the marker occurred. SourceLoc Loc; /// The kind of marker. StructureMarkerKind Kind; /// The leading whitespace for this marker, if it has already been /// computed. std::optional LeadingWhitespace; }; /// An RAII object that notes when we have seen a structure marker. class StructureMarkerRAII { Parser &P; /// Max nesting level // TODO: customizable. enum { MaxDepth = 256 }; StructureMarkerRAII(Parser &parser) : P(parser) {} public: StructureMarkerRAII(Parser &parser, SourceLoc loc, StructureMarkerKind kind); StructureMarkerRAII(Parser &parser, const Token &tok); ~StructureMarkerRAII() { P.StructureMarkers.pop_back(); } }; friend class StructureMarkerRAII; /// The stack of structure markers indicating the locations of /// structural elements actively being parsed, including the start /// of declarations, statements, and opening operators of various /// kinds. /// /// This vector is managed by \c StructureMarkerRAII objects. llvm::SmallVector StructureMarkers; public: Parser(unsigned BufferID, SourceFile &SF, DiagnosticEngine* LexerDiags, SILParserStateBase *SIL, PersistentParserState *PersistentState); Parser(unsigned BufferID, SourceFile &SF, SILParserStateBase *SIL, PersistentParserState *PersistentState = nullptr); Parser(std::unique_ptr Lex, SourceFile &SF, SILParserStateBase *SIL = nullptr, PersistentParserState *PersistentState = nullptr); ~Parser(); /// Returns true if the buffer being parsed is allowed to contain SIL. bool isInSILMode() const; /// Retrieve the token receiver from the parser once it has finished parsing. std::unique_ptr takeTokenReceiver() { assert(Tok.is(tok::eof) && "not done parsing yet"); auto *receiver = TokReceiver; TokReceiver = nullptr; return std::unique_ptr(receiver); } //===--------------------------------------------------------------------===// // Routines to save and restore parser state. ParserPosition getParserPosition() { return ParserPosition(L->getStateForBeginningOfToken(Tok), PreviousLoc); } ParserPosition getParserPosition(SourceLoc loc, SourceLoc previousLoc) { return ParserPosition(L->getStateForBeginningOfTokenLoc(loc), previousLoc); } void restoreParserPosition(ParserPosition PP, bool enableDiagnostics = false) { L->restoreState(PP.LS, enableDiagnostics); L->lex(Tok); PreviousLoc = PP.PreviousLoc; } void backtrackToPosition(ParserPosition PP) { assert(PP.isValid()); L->backtrackToState(PP.LS); L->lex(Tok); PreviousLoc = PP.PreviousLoc; } /// RAII object that, when it is destructed, restores the parser and lexer to /// their positions at the time the object was constructed. Will not jump /// forward in the token stream. /// Actual uses of the backtracking scope should choose either \c /// BacktrackingScope, which will always backtrack or \c /// CancellableBacktrackingScope which can be cancelled. class BacktrackingScopeImpl { protected: Parser &P; ParserPosition PP; DiagnosticTransaction DT; bool Backtrack = true; /// A token receiver used by the parser in the back tracking scope. This /// receiver will save any consumed tokens during this back tracking scope. /// After the scope ends, it either transfers the saved tokens to the old receiver /// or discard them. struct DelayedTokenReceiver: ConsumeTokenReceiver { /// Keep track of the old token receiver in the parser so that we can recover /// after the backtracking sope ends. llvm::SaveAndRestore savedConsumer; // Whether the tokens should be transferred to the original receiver. // When the back tracking scope will actually back track, this should be false; // otherwise true. bool shouldTransfer = false; std::vector delayedTokens; DelayedTokenReceiver(ConsumeTokenReceiver *&receiver): savedConsumer(receiver, this) {} void receive(const Token &tok) override { delayedTokens.push_back(tok); } std::optional> finalize() override { llvm_unreachable("Cannot finalize a DelayedTokenReceiver"); } ~DelayedTokenReceiver() { if (!shouldTransfer) return; for (auto tok: delayedTokens) { savedConsumer.get()->receive(tok); } } } TempReceiver; BacktrackingScopeImpl(Parser &P) : P(P), PP(P.getParserPosition()), DT(P.Diags), TempReceiver(P.TokReceiver) { } public: ~BacktrackingScopeImpl(); bool willBacktrack() const { return Backtrack; } }; /// A backtracking scope that will always backtrack when destructed. class BacktrackingScope final : public BacktrackingScopeImpl { public: BacktrackingScope(Parser &P) : BacktrackingScopeImpl(P) { } }; /// A backtracking scope whose backtracking can be disabled by calling /// \c cancelBacktrack. class CancellableBacktrackingScope final : public BacktrackingScopeImpl { public: CancellableBacktrackingScope(Parser &P) : BacktrackingScopeImpl(P) { } void cancelBacktrack(); }; /// RAII object that, when it is destructed, restores the parser and lexer to /// their positions at the time the object was constructed. struct ParserPositionRAII { private: Parser &P; ParserPosition PP; public: ParserPositionRAII(Parser &P) : P(P), PP(P.getParserPosition()) {} ~ParserPositionRAII() { P.restoreParserPosition(PP); } }; //===--------------------------------------------------------------------===// // Utilities /// Return the next token that will be installed by \c consumeToken. const Token &peekToken(); /// Consumes K tokens within a backtracking scope before calling \c f and /// providing it with the backtracking scope. Unless if the backtracking is /// explicitly cancelled, the parser's token state is restored after \c f /// returns. /// /// \param K The number of tokens ahead to skip. Zero is the current token. /// \param f The function to apply after skipping K tokens ahead. /// The value returned by \c f will be returned by \c peekToken /// after the parser is rolled back. /// \returns the value returned by \c f /// \note When calling, you may need to specify the \c Val type /// explicitly as a type parameter. template decltype(auto) lookahead(unsigned char K, Fn f) { CancellableBacktrackingScope backtrackScope(*this); for (unsigned char i = 0; i < K; ++i) consumeToken(); return f(backtrackScope); } /// Discard the current token. This will avoid interface hashing or updating /// the previous loc. Only should be used if you've completely re-lexed /// a different token at that position. SourceLoc discardToken(); /// Consume a token that we created on the fly to correct the original token /// stream from lexer. void consumeExtraToken(Token K); SourceLoc consumeTokenWithoutFeedingReceiver(); SourceLoc consumeToken(); SourceLoc consumeToken(tok K) { assert(Tok.is(K) && "Consuming wrong token kind"); return consumeToken(); } SourceLoc consumeIdentifier(Identifier &Result, bool diagnoseDollarPrefix) { assert(Tok.isAny(tok::identifier, tok::kw_self, tok::kw_Self)); assert(Result.empty()); Result = Context.getIdentifier(Tok.getText()); if (Tok.getText()[0] == '$') diagnoseDollarIdentifier(Tok, diagnoseDollarPrefix); return consumeToken(); } SourceLoc consumeArgumentLabel(Identifier &Result, bool diagnoseDollarPrefix) { assert(Tok.canBeArgumentLabel()); assert(Result.empty()); if (!Tok.is(tok::kw__)) { Tok.setKind(tok::identifier); Result = Context.getIdentifier(Tok.getText()); if (Tok.getText()[0] == '$') diagnoseDollarIdentifier(Tok, diagnoseDollarPrefix); } return consumeToken(); } /// When we have a token that is an identifier starting with '$', /// diagnose it if not permitted in this mode. /// \param diagnoseDollarPrefix Whether to diagnose dollar-prefixed /// identifiers in addition to a standalone '$'. void diagnoseDollarIdentifier(const Token &tok, bool diagnoseDollarPrefix); /// Retrieve the location just past the end of the previous /// source location. SourceLoc getEndOfPreviousLoc() const; /// If the current token is the specified kind, consume it and /// return true. Otherwise, return false without consuming it. bool consumeIf(tok K) { if (Tok.isNot(K)) return false; consumeToken(K); return true; } /// If the current token is the specified kind, consume it and /// return true. Otherwise, return false without consuming it. bool consumeIf(tok K, SourceLoc &consumedLoc) { if (Tok.isNot(K)) return false; consumedLoc = consumeToken(K); return true; } /// Consume a '('. If it is not directly following the previous token, emit an /// error (Swift 6) or warning (Swift <6) that attribute name and parentheses /// must not be separated by a space. SourceLoc consumeAttributeLParen(); /// If the next token is a '(' that's not on a new line consume it, and error /// (Swift 6) or warn (Swift <6) that the attribute must not be separted from /// the '(' by a space. /// /// If the next token is not '(' or it's on a new line, return false. bool consumeIfAttributeLParen(bool isCustomAttr = false); /// Check if the current token is '(' and it looks like a start of an /// attribute argument list. bool isAtAttributeLParen(bool isCustomAttr = false); bool consumeIfNotAtStartOfLine(tok K) { if (Tok.isAtStartOfLine()) return false; return consumeIf(K); } bool isContextualYieldKeyword() { return (Tok.isContextualKeyword("yield") && isa(CurDeclContext) && cast(CurDeclContext)->isCoroutine()); } /// Whether the current token is the contextual keyword for a \c then /// statement. bool isContextualThenKeyword(bool preferExpr); /// `discard self` is the only valid phrase, but we peek ahead for just any /// identifier after `discard` to determine if it's the statement. This helps /// us avoid interpreting `discard(self)` as the statement and not a call. /// We also want to be mindful of statements like `discard ++ something` where /// folks have defined a custom operator returning void. /// /// Later, type checking will verify that you're discarding the right thing /// so that when people make a mistake, thinking they can `discard x` we give /// a nice diagnostic. bool isContextualDiscardKeyword() { // must be `discard` ... if (!Tok.isContextualKeyword("discard")) return false; // followed by either an identifier, `self`, or `Self`. return !peekToken().isAtStartOfLine() && peekToken().isAny(tok::identifier, tok::kw_self, tok::kw_Self); } /// Read tokens until we get to one of the specified tokens, then /// return without consuming it. Because we cannot guarantee that the token /// will ever occur, this skips to some likely good stopping point. ParserStatus skipUntil(tok T1, tok T2 = tok::NUM_TOKENS); void skipUntilAnyOperator(); /// Skip until a token that starts with '>', and consume it if found. /// Applies heuristics that are suitable when trying to find the end of a list /// of generic parameters, generic arguments, or list of types in a protocol /// composition. SourceLoc skipUntilGreaterInTypeList(bool protocolComposition = false); /// skipUntilDeclStmtRBrace - Skip to the next decl or '}'. void skipUntilDeclRBrace(); template void skipUntilDeclStmtRBrace(T... K) { while (Tok.isNot(K..., tok::eof, tok::r_brace, tok::pound_endif, tok::pound_else, tok::pound_elseif, tok::code_complete) && !isStartOfStmt(/*preferExpr*/ false) && !isStartOfSwiftDecl(/*allowPoundIfAttributes=*/true)) { skipSingle(); } } void skipUntilDeclRBrace(tok T1, tok T2); void skipListUntilDeclRBrace(SourceLoc startLoc, tok T1, tok T2); /// Skip a single token, but match parentheses, braces, and square brackets. /// /// Note: this does \em not match angle brackets ("<" and ">")! These are /// matched in the source when they refer to a generic type, /// but not when used as comparison operators. /// /// Returns a parser status that can capture whether a code completion token /// was returned. ParserStatus skipSingle(); /// Skip until the next '#else', '#endif' or until eof. void skipUntilConditionalBlockClose(); /// Skip until either finding \c T1 or reaching the end of the line. /// /// This uses \c skipSingle and so matches parens etc. After calling, one or /// more of the following will be true: Tok.is(T1), Tok.isStartOfLine(), /// Tok.is(tok::eof). The "or more" case is the first two: if the next line /// starts with T1. /// /// \returns true if there is an instance of \c T1 on the current line (this /// avoids the foot-gun of not considering T1 starting the next line for a /// plain Tok.is(T1) check). bool skipUntilTokenOrEndOfLine(tok T1, tok T2 = tok::NUM_TOKENS); /// Skip over SIL decls until we encounter the start of a Swift decl or eof. void skipSILUntilSwiftDecl(); /// Skip over any attribute. void skipAnyAttribute(); /// Parse an #endif. bool parseEndIfDirective(SourceLoc &Loc); /// Given that the current token is a string literal, /// - if it is not interpolated, returns the contents; /// - otherwise, diagnoses and returns None. /// /// \param Loc where to diagnose. /// \param DiagText name for the string literal in the diagnostic. std::optional getStringLiteralIfNotInterpolated(SourceLoc Loc, StringRef DiagText); /// Returns true to indicate that experimental concurrency syntax should be /// parsed if the parser is generating only a syntax tree or if the user has /// passed the `-enable-experimental-concurrency` flag to the frontend. bool shouldParseExperimentalConcurrency() const { return Context.LangOpts.EnableExperimentalConcurrency; } /// Returns true if a Swift declaration starts after the current token, /// otherwise returns false. bool isNextStartOfSwiftDecl() { BacktrackingScope backtrack(*this); consumeToken(); return isStartOfSwiftDecl(); } public: InFlightDiagnostic diagnose(SourceLoc Loc, DiagRef Diag) { if (Diags.isDiagnosticPointsToFirstBadToken(Diag.getID()) && Loc == Tok.getLoc() && Tok.isAtStartOfLine()) Loc = getEndOfPreviousLoc(); return Diags.diagnose(Loc, Diag); } InFlightDiagnostic diagnose(Token Tok, DiagRef Diag) { return diagnose(Tok.getLoc(), Diag); } template InFlightDiagnostic diagnose(SourceLoc Loc, Diag DiagID, ArgTypes &&...Args) { return diagnose(Loc, {DiagID, {std::forward(Args)...}}); } template InFlightDiagnostic diagnose(Token Tok, Diag DiagID, ArgTypes &&...Args) { return diagnose(Tok.getLoc(), {DiagID, {std::forward(Args)...}}); } /// Add a fix-it to remove the space in consecutive identifiers. /// Add a camel-cased option if it is different than the first option. void diagnoseConsecutiveIDs(StringRef First, SourceLoc FirstLoc, StringRef DeclKindName); bool startsWithSymbol(Token Tok, char symbol) { return (Tok.isAnyOperator() || Tok.isPunctuation()) && Tok.getText()[0] == symbol; } /// Check whether the current token starts with '<'. bool startsWithLess(Token Tok) { return startsWithSymbol(Tok, '<'); } /// Check whether the current token starts with '>'. bool startsWithGreater(Token Tok) { return startsWithSymbol(Tok, '>'); } /// Check whether the current token starts with '...'. bool startsWithEllipsis(Token Tok); /// Check whether the current token starts with a multi-line string delimiter. bool startsWithMultilineStringDelimiter(Token Tok) { return Tok.getText().ltrim('#').starts_with("\"\"\""); } /// Returns true if token is an identifier with the given value. bool isIdentifier(Token Tok, StringRef value) { return Tok.is(tok::identifier) && Tok.getText() == value; } /// Consume the starting '<' of the current token, which may either /// be a complete '<' token or some kind of operator token starting with '<', /// e.g., '<>'. SourceLoc consumeStartingLess(); /// Consume the starting '>' of the current token, which may either /// be a complete '>' token or some kind of operator token starting with '>', /// e.g., '>>'. SourceLoc consumeStartingGreater(); /// Consume the starting '...' of the current token, which may either be a /// complete '...' token or some kind of operator token starting with '...', /// e.g '...>'. SourceLoc consumeStartingEllipsis(); /// Consume the starting character of the current token, and split the /// remainder of the token into a new token (or tokens). SourceLoc consumeStartingCharacterOfCurrentToken(tok Kind = tok::oper_binary_unspaced, size_t Len = 1); /// If the next token is \c tok::colon, consume it; if the next token is /// \c tok::colon_colon, split it into two \c tok::colons and consume the /// first; otherwise, do nothing and return false. bool consumeIfColonSplittingDoubles() { if (!Tok.isAny(tok::colon, tok::colon_colon)) return false; consumeStartingCharacterOfCurrentToken(tok::colon); return true; } //===--------------------------------------------------------------------===// // Primitive Parsing /// Consume an identifier (but not an operator) if present and return /// its name in \p Result. Otherwise, emit an error. /// /// \returns false on success, true on error. bool parseIdentifier(Identifier &Result, SourceLoc &Loc, DiagRef D, bool diagnoseDollarPrefix); /// Consume an identifier with a specific expected name. This is useful for /// contextually sensitive keywords that must always be present. bool parseSpecificIdentifier(StringRef expected, SourceLoc &Loc, DiagRef D); template bool parseIdentifier(Identifier &Result, SourceLoc &L, bool diagnoseDollarPrefix, Diag ID, ArgTypes... Args) { return parseIdentifier(Result, L, {ID, {Args...}}, diagnoseDollarPrefix); } template bool parseSpecificIdentifier(StringRef expected, Diag ID, ArgTypes... Args) { SourceLoc L; return parseSpecificIdentifier(expected, L, {ID, {Args...}}); } /// Consume an identifier or operator if present and return its name /// in \p Result. Otherwise, emit an error and return true. bool parseAnyIdentifier(Identifier &Result, SourceLoc &Loc, DiagRef D, bool diagnoseDollarPrefix); template bool parseAnyIdentifier(Identifier &Result, bool diagnoseDollarPrefix, Diag ID, ArgTypes... Args) { SourceLoc L; return parseAnyIdentifier(Result, L, {ID, {Args...}}, diagnoseDollarPrefix); } /// \brief Parse an unsigned integer and returns it in \p Result. On failure /// emit the specified error diagnostic, and a note at the specified note /// location. bool parseUnsignedInteger(unsigned &Result, SourceLoc &Loc, DiagRef D); /// The parser expects that \p K is next token in the input. If so, /// it is consumed and false is returned. /// /// If the input is malformed, this emits the specified error diagnostic. bool parseToken(tok K, SourceLoc &TokLoc, DiagRef D); template bool parseToken(tok K, Diag ID, ArgTypes... Args) { SourceLoc L; return parseToken(K, L, {ID, {Args...}}); } template bool parseToken(tok K, SourceLoc &L, Diag ID, ArgTypes... Args) { return parseToken(K, L, {ID, {Args...}}); } /// Parse the specified expected token and return its location on success. On failure, emit the specified /// error diagnostic, a note at the specified note location, and return the location of the previous token. bool parseMatchingToken(tok K, SourceLoc &TokLoc, DiagRef ErrorDiag, SourceLoc OtherLoc); /// Returns the proper location for a missing right brace, parenthesis, etc. SourceLoc getLocForMissingMatchingToken() const; /// When encountering an error or a missing matching token (e.g. '}'), return /// the location to use for it. This value should be at the last token in /// the ASTNode being parsed so that it nests within any enclosing nodes, and, /// for ASTScope lookups, it does not precede any identifiers to be looked up. /// However, the latter case does not hold when parsing an interpolated /// string literal because there may be identifiers to be looked up in the /// literal and their locations will not precede the location of a missing /// close brace. SourceLoc getErrorOrMissingLoc() const; enum class ParseListItemResult { /// There are more list items to parse. Continue, /// The list ended inside a string literal interpolation context. FinishedInStringInterpolation, /// The list ended for another reason. Finished, }; /// Parses a single item from a comma separated list and updates `Status`. ParseListItemResult parseListItem(ParserStatus &Status, tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, bool AllowSepAfterLast, llvm::function_ref callback); /// Parse a comma separated list of some elements. ParserStatus parseList(tok RightK, SourceLoc LeftLoc, SourceLoc &RightLoc, bool AllowSepAfterLast, DiagRef RightErrorDiag, llvm::function_ref callback); void consumeTopLevelDecl(ParserPosition BeginParserPosition, TopLevelCodeDecl *TLCD); ParserStatus parseBraceItems(SmallVectorImpl &Decls, BraceItemListKind Kind, BraceItemListKind ConditionalBlockKind, bool &IsFollowingGuard); ParserStatus parseBraceItems(SmallVectorImpl &Decls, BraceItemListKind Kind = BraceItemListKind::Brace, BraceItemListKind ConditionalBlockKind = BraceItemListKind::Brace) { bool IsFollowingGuard = false; return parseBraceItems(Decls, Kind, ConditionalBlockKind, IsFollowingGuard); } ParserResult parseBraceItemList(Diag<> ID); //===--------------------------------------------------------------------===// // Decl Parsing /// Returns true if parser is at the start of a Swift decl or decl-import. bool isStartOfSwiftDecl(bool allowPoundIfAttributes = true, bool hadAttrsOrModifiers = false); /// Returns true if the parser is at the start of a SIL decl. bool isStartOfSILDecl(); /// Returns true if the parser is at a freestanding macro expansion. bool isStartOfFreestandingMacroExpansion(); /// Parse the top-level Swift items into the provided vector. /// /// Each item will be a declaration, statement, or expression. void parseTopLevelItems(SmallVectorImpl &items); /// Parse the top-level SIL decls into the SIL module. /// \returns \c true if there was a parsing error. bool parseTopLevelSIL(); bool isStartOfGetSetAccessor(); /// Flags that control the parsing of declarations. enum ParseDeclFlags : uint16_t { PD_Default = 0, PD_AllowTopLevel = 1 << 1, PD_HasContainerType = 1 << 2, PD_DisallowInit = 1 << 3, PD_AllowEnumElement = 1 << 4, PD_InProtocol = 1 << 5, PD_InClass = 1 << 6, PD_InExtension = 1 << 7, PD_InStruct = 1 << 8, PD_InEnum = 1 << 9, PD_StubOnly = 1 << 10, }; /// Options that control the parsing of declarations. using ParseDeclOptions = OptionSet; void consumeDecl(ParserPosition BeginParserPosition, bool IsTopLevel); ParserStatus parseDecl(bool IsAtStartOfLineOrPreviousHadSemi, bool IfConfigsAreDeclAttrs, llvm::function_ref Handler, bool stubOnly = false); std::pair, std::optional> parseDeclListDelayed(IterableDeclContext *IDC); bool parseMemberDeclList(SourceLoc &LBLoc, SourceLoc &RBLoc, Diag<> LBraceDiag, Diag<> RBraceDiag, IterableDeclContext *IDC, ParseDeclOptions Flags); bool canDelayMemberDeclParsing(bool &HasOperatorDeclarations, bool &HasNestedClassDeclarations, bool &HasDerivativeDeclarations, ParseDeclOptions Flags); bool canDelayFunctionBodyParsing(bool &HasNestedTypeDeclarations, ParseDeclOptions Flags); bool delayParsingDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, IterableDeclContext *IDC); ParserResult parseDeclTypeAlias(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclAssociatedType(ParseDeclOptions Flags, DeclAttributes &Attributes); /// Parse a #if ... #endif directive. /// Delegate callback function to parse elements in the blocks and record /// them however they wish. The parsing function will be provided with the /// location of the clause token (`#if`, `#else`, etc.), the condition, /// whether this is the active clause, and the role of the elements. template Result parseIfConfigRaw( IfConfigContext ifConfigContext, llvm::function_ref parseElements, llvm::function_ref finish); /// Parse a #if ... #endif directive. /// Delegate callback function to parse elements in the blocks. ParserStatus parseIfConfig( IfConfigContext ifConfigContext, llvm::function_ref parseElements); /// Parse an #if ... #endif containing only attributes. ParserStatus parseIfConfigAttributes( DeclAttributes &attributes, bool ifConfigsAreDeclAttrs); /// Parse a #error or #warning diagnostic. ParserStatus parseDeclPoundDiagnostic(); /// Parse a #line/#sourceLocation directive. /// 'isLine = true' indicates parsing #line instead of #sourcelocation ParserStatus parseLineDirective(bool isLine = false); /// Skip an `#if` configuration block containing only attributes. /// /// \returns true if the skipping was successful, false otherwise. bool skipIfConfigOfAttributes(bool &sawAnyAttributes); /// Determine whether the `#if` at which the parser occurs only contains /// attributes (in all branches), in which case it is treated as part of /// an attribute list. bool ifConfigContainsOnlyAttributes(); /// Parse the optional attributes before a declaration. ParserStatus parseDeclAttributeList(DeclAttributes &Attributes, bool IfConfigsAreDeclAttrs = false); /// Parse the optional attributes before a closure declaration. ParserStatus parseClosureDeclAttributeList(DeclAttributes &Attributes); /// Parse the optional modifiers before a declaration. ParserStatus parseDeclModifierList(DeclAttributes &Attributes, SourceLoc &StaticLoc, StaticSpellingKind &StaticSpelling, bool isFromClangAttribute = false); /// Parse an availability attribute of the form /// @available(*, introduced: 1.0, deprecated: 3.1). /// \return \p nullptr if the platform name is invalid ParserResult parseExtendedAvailabilitySpecList(SourceLoc AtLoc, SourceLoc AttrLoc, StringRef AttrName); /// Parse a string literal whose contents can be interpreted as a UUID. /// /// \returns false on success, true on error. bool parseUUIDString(UUID &uuid, Diag<> diag, bool justChecking = false); /// Parse the Objective-C selector inside @objc void parseObjCSelector(SmallVector &Names, SmallVector &NameLocs, bool &IsNullarySelector); /// Parse a parenthesized and comma-separated list of attribute arguments. /// /// \returns false on success, true on error. ParserStatus parseAttributeArguments(SourceLoc attrLoc, StringRef attrName, bool isAttrModifier, SourceRange &parensRange, llvm::function_ref parseAttr); /// Parse the @_specialize/@specialized attribute. /// \p closingBrace is the expected closing brace, which can be either ) or ] /// \p Attr is where to store the parsed attribute bool parseSpecializeAttribute( swift::tok ClosingBrace, SourceLoc AtLoc, SourceLoc Loc, bool isPublic, AbstractSpecializeAttr *&Attr, AvailabilityRange *SILAvailability, llvm::function_ref parseSILTargetName = [](Parser &) { return false; }, llvm::function_ref parseSILSIPModule = [](Parser &) { return false; }); /// Parse the arguments inside the @_specialize/@specialized attribute bool parseSpecializeAttributeArguments( swift::tok ClosingBrace, bool isPublic, bool &DiscardAttribute, std::optional &Exported, std::optional &Kind, TrailingWhereClause *&TrailingWhereClause, DeclNameRef &targetFunction, DeclNameLoc &targetFunctionLoc, AvailabilityRange *SILAvailability, SmallVectorImpl &spiGroups, SmallVectorImpl &availableAttrs, llvm::function_ref parseSILTargetName, llvm::function_ref parseSILSIPModule); /// Parse the @storageRestrictions(initializes:accesses:) attribute. /// \p Attr is where to store the parsed attribute ParserResult parseStorageRestrictionsAttribute(SourceLoc AtLoc, SourceLoc Loc); /// Parse the @_implements attribute. /// \p Attr is where to store the parsed attribute ParserResult parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc); /// Parse the @differentiable attribute. ParserResult parseDifferentiableAttribute(SourceLoc AtLoc, SourceLoc Loc); /// Parse the @_extern attribute. bool parseExternAttribute(DeclAttributes &Attributes, bool &DiscardAttribute, StringRef AttrName, SourceLoc AtLoc, SourceLoc Loc); /// Parse the arguments inside the @differentiable attribute. bool parseDifferentiableAttributeArguments( DifferentiabilityKind &diffKind, SmallVectorImpl ¶ms, TrailingWhereClause *&whereClause); /// Parse a differentiability parameters clause, i.e. the 'wrt:' clause in /// `@differentiable`, `@derivative`, and `@transpose` attributes. /// /// If `allowNamedParameters` is false, allow only index parameters and /// 'self'. Used for `@transpose` attributes. bool parseDifferentiabilityParametersClause( SmallVectorImpl ¶meters, StringRef attrName, bool allowNamedParameters = true); /// Parse the @derivative attribute. ParserResult parseDerivativeAttribute(SourceLoc AtLoc, SourceLoc Loc); /// Parse the @transpose attribute. ParserResult parseTransposeAttribute(SourceLoc AtLoc, SourceLoc Loc); /// Parse the @backDeployed attribute. bool parseBackDeployedAttribute(DeclAttributes &Attributes, StringRef AttrName, SourceLoc AtLoc, SourceLoc Loc); /// Parse the @_documentation attribute. ParserResult parseDocumentationAttribute(SourceLoc AtLoc, SourceLoc Loc); /// Parse a single argument from a @_documentation attribute. bool parseDocumentationAttributeArgument(std::optional &Metadata, std::optional &Visibility); ParserResult parseAllowFeatureSuppressionAttribute(bool inverted, SourceLoc atLoc, SourceLoc loc); /// Parse the @attached or @freestanding attribute that specifies a macro /// role. ParserResult parseMacroRoleAttribute( MacroSyntax syntax, SourceLoc AtLoc, SourceLoc Loc ); /// Parse the @lifetime attribute. ParserResult parseLifetimeAttribute(StringRef attrName, SourceLoc atLoc, SourceLoc loc); /// Common utility to parse swift @lifetime decl attribute and SIL @lifetime /// type modifier. ParserResult parseLifetimeEntry(SourceLoc loc); /// Parse a specific attribute. ParserStatus parseDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, SourceLoc AtEndLoc, bool isFromClangAttribute = false); bool canParseCustomAttribute(); /// Parse a custom attribute after the initial '@'. /// /// \param atLoc The location of the already-parsed '@'. ParserResult parseCustomAttribute(SourceLoc atLoc); ParserStatus parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc, DeclAttrKind DK, bool isFromClangAttribute = false); /// Parse a version tuple of the form x[.y[.z]]. Returns true if there was /// an error parsing. bool parseVersionTuple(llvm::VersionTuple &Version, SourceRange &Range, DiagRef D); bool isParameterSpecifier() { if (Tok.is(tok::kw_inout)) return true; if ((Context.LangOpts.hasFeature(Feature::LifetimeDependence) || Context.LangOpts.hasFeature(Feature::Lifetimes)) && isSILLifetimeDependenceToken()) return true; if (!canHaveParameterSpecifierContextualKeyword()) return false; if (Tok.isContextualKeyword("__shared") || Tok.isContextualKeyword("__owned") || Tok.isContextualKeyword("borrowing") || Tok.isContextualKeyword("consuming") || Tok.isContextualKeyword("isolated") || Tok.isContextualKeyword("_const")) return true; if (isCallerIsolatedSpecifier()) return true; if (Tok.isContextualKeyword("sending")) return true; return false; } /// Given that we just returned true from isParameterSpecifier(), skip /// over the specifier. void skipParameterSpecifier() { // These are all currently single tokens. consumeToken(); } bool isSILLifetimeDependenceToken() { return isInSILMode() && Tok.is(tok::at_sign) && (peekToken().isContextualKeyword("lifetime")); } bool isCallerIsolatedSpecifier() { if (!Tok.isContextualKeyword("nonisolated")) return false; return peekToken().isFollowingLParen(); } bool canHaveParameterSpecifierContextualKeyword() { // The parameter specifiers like `isolated`, `consuming`, `borrowing` are // also valid identifiers and could be the name of a type. Check whether // the following token is something that can introduce a type. Thankfully // none of these tokens overlap with the set of tokens that can follow an // identifier in a type production. if (Tok.is(tok::identifier)) { auto next = peekToken(); if (next.isAny(tok::at_sign, tok::kw_inout, tok::l_paren, tok::identifier, tok::l_square, tok::kw_Any, tok::kw_Self, tok::kw__, tok::kw_var, tok::kw_let, tok::code_complete)) return true; if (next.is(tok::oper_prefix) && next.getText() == "~") return true; } return false; } bool parseConventionAttributeInternal(SourceLoc atLoc, SourceLoc attrLoc, ConventionTypeAttr *&result, bool justChecking); ParserResult parseDeclImport(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclUsing(ParseDeclOptions Flags, DeclAttributes &Attributes); /// Parse an inheritance clause into a vector of InheritedEntry's. /// /// \param allowClassRequirement whether to permit parsing of 'class' /// \param allowAnyObject whether to permit parsing of 'AnyObject' ParserStatus parseInheritance(SmallVectorImpl &Inherited, bool allowClassRequirement, bool allowAnyObject); ParserStatus parseDeclItem(bool &PreviousHadSemi, llvm::function_ref handler); std::pair, std::optional> parseDeclList(SourceLoc LBLoc, SourceLoc &RBLoc, Diag<> ErrorDiag, bool &hadError); ParserResult parseDeclExtension(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclEnum(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclEnumCase(ParseDeclOptions Flags, DeclAttributes &Attributes, SmallVectorImpl &decls); ParserResult parseDeclStruct(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclClass(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclVar(ParseDeclOptions Flags, DeclAttributes &Attributes, SmallVectorImpl &Decls, SourceLoc StaticLoc, StaticSpellingKind StaticSpelling, SourceLoc TryLoc, bool HasLetOrVarKeyword = true); struct ParsedAccessors; bool parseAccessorAfterIntroducer( SourceLoc Loc, AccessorKind Kind, ParsedAccessors &accessors, bool &hasEffectfulGet, bool &parsingLimitedSyntax, DeclAttributes &Attributes, ParseDeclOptions Flags, AbstractStorageDecl *storage, ParserStatus &Status); ParserStatus parseGetSet(ParseDeclOptions Flags, ParameterList *Indices, TypeRepr *ResultType, ParsedAccessors &accessors, AbstractStorageDecl *storage); ParserResult parseDeclVarGetSet(PatternBindingEntry &entry, ParseDeclOptions Flags, SourceLoc StaticLoc, StaticSpellingKind StaticSpelling, SourceLoc VarLoc, bool hasInitializer, const DeclAttributes &Attributes, SmallVectorImpl &Decls); ParserStatus parseGetEffectSpecifier(ParsedAccessors &accessors, SourceLoc &asyncLoc, SourceLoc &throwsLoc, TypeRepr *&thrownTy, bool &hasEffectfulGet, AccessorKind currentKind, SourceLoc const& currentLoc); /// Parse accessors provided as a separate list, for use in macro /// expansions. void parseTopLevelAccessors( AbstractStorageDecl *storage, SmallVectorImpl &items ); /// Parse the result of attribute macro expansion, which is a floating /// attribute list. /// /// Parsing a floating attribute list will produce a `MissingDecl` with /// the attribute list attached. /// /// If isFromClangAttribute, we also parse modifiers and suppress any /// diagnostics about bad modifiers. void parseExpandedAttributeList(SmallVectorImpl &items, bool isFromClangAttribute); /// Parse the result of member macro expansion, which is a floating /// member list. void parseExpandedMemberList(SmallVectorImpl &items); ParserResult parseDeclFunc(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling, ParseDeclOptions Flags, DeclAttributes &Attributes, bool HasFuncKeyword = true); BodyAndFingerprint parseAbstractFunctionBodyImpl(AbstractFunctionDecl *AFD); void parseAbstractFunctionBody(AbstractFunctionDecl *AFD, ParseDeclOptions Flags); BodyAndFingerprint parseAbstractFunctionBodyDelayed(AbstractFunctionDecl *AFD); ParserResult parseDeclMacro(DeclAttributes &Attributes); ParserStatus parsePrimaryAssociatedTypes( SmallVectorImpl &AssocTypeNames); ParserStatus parsePrimaryAssociatedTypeList( SmallVectorImpl &AssocTypeNames); ParserResult parseDeclProtocol(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclSubscript(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling, ParseDeclOptions Flags, DeclAttributes &Attributes, SmallVectorImpl &Decls); ParserResult parseDeclInit(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclDeinit(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclOperator(ParseDeclOptions Flags, DeclAttributes &Attributes); ParserResult parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name, SourceLoc NameLoc, DeclAttributes &Attrs); ParserResult parseDeclPrecedenceGroup(ParseDeclOptions flags, DeclAttributes &attributes); ParserResult parseDeclMacroExpansion(ParseDeclOptions flags, DeclAttributes &attributes); ParserResult parseDeclResultType(Diag<> MessageID); /// Get the location for a type error. SourceLoc getTypeErrorLoc() const; //===--------------------------------------------------------------------===// // Type Parsing enum class ParseTypeReason { /// Any type parsing context. Unspecified, /// Whether the type is for a closure attribute. CustomAttribute, /// A type in an inheritance clause. InheritanceClause, }; ParserResult parseTypeScalar( Diag<> MessageID, ParseTypeReason reason); ParserResult parseType(); ParserResult parseType(Diag<> MessageID, ParseTypeReason reason = ParseTypeReason::Unspecified); /// Parse a type optionally prefixed by a list of named opaque parameters. If /// no params present, return 'type'. Otherwise, return 'type-named-opaque'. /// /// type-named-opaque: /// generic-params type ParserResult parseTypeWithOpaqueParams(Diag<> MessageID); ParserResult parseTypeSimpleOrComposition(Diag<> MessageID, ParseTypeReason reason); ParserResult parseTypeSimple( Diag<> MessageID, ParseTypeReason reason); ParserResult parseTypeOrValue(); ParserResult parseTypeOrValue(Diag<> MessageID, ParseTypeReason reason = ParseTypeReason::Unspecified); /// Parse layout constraint. LayoutConstraint parseLayoutConstraint(Identifier LayoutConstraintID); ParserStatus parseGenericArguments(SmallVectorImpl &Args, SourceLoc &LAngleLoc, SourceLoc &RAngleLoc); /// Parses and returns the base type for a qualified declaration name, /// positioning the parser at the '.' before the final declaration name. This /// position is important for parsing final declaration names like '.init' via /// `parseUnqualifiedDeclName`. For example, 'Foo.Bar.f' parses as 'Foo.Bar' /// and the parser is positioned at '.f'. If there is no base type qualifier /// (e.g. when parsing just 'f'), returns an empty parser error. /// /// \verbatim /// qualified-decl-name-base-type: /// identifier generic-args? ('.' identifier generic-args?)* /// \endverbatim ParserResult parseQualifiedDeclNameBaseType(); /// Parse a single type identifier, possibly followed by a generic argument /// list, e.g `Foo` or `Bar`. /// /// \verbatim /// type-identifier: identifier generic-args? /// \endverbatim ParserResult parseTypeIdentifier(TypeRepr *Base); /// Parse a dotted type, e.g. 'Foo.Y.Z', 'P.Type', '[X].Y'. ParserResult parseTypeDotted(ParserResult Base); struct ParsedTypeAttributeList { ParseTypeReason ParseReason; ParamDecl::Specifier Specifier = ParamDecl::Specifier::Default; SourceLoc SpecifierLoc; SourceLoc IsolatedLoc; SourceLoc ConstLoc; SourceLoc SendingLoc; SourceLoc CallerIsolatedLoc; SmallVector Attributes; LifetimeEntry *lifetimeEntry = nullptr; ParsedTypeAttributeList(ParseTypeReason reason) : ParseReason(reason) {} /// Main entry point for parsing. /// /// Inline we just have the fast path of failing to match. We call slowParse /// that contains the outline of more complex implementation. This is HOT /// code! ParserStatus parse(Parser &P) { auto &Tok = P.Tok; if (Tok.is(tok::at_sign) || P.isParameterSpecifier()) return slowParse(P); return makeParserSuccess(); } TypeRepr *applyAttributesToType(Parser &P, TypeRepr *Type) const; private: /// An out of line implementation of the more complicated cases. This /// ensures on the inlined fast path we handle the case of not matching. ParserStatus slowParse(Parser &P); }; ParserStatus parseTypeAttribute(TypeOrCustomAttr &result, SourceLoc AtLoc, SourceLoc AtEndLoc, ParseTypeReason reason, bool justChecking = false); ParserResult parseOldStyleProtocolComposition(); ParserResult parseAnyType(); ParserResult parseSILBoxType(GenericParamList *generics, ParsedTypeAttributeList &attrs); ParserResult parseTypeTupleBody(); ParserResult parseTypeArray(ParserResult Base); /// Whether the parser is at the start of an InlineArray type body. bool isStartOfInlineArrayTypeBody(); /// Parse an InlineArray type '[' integer 'x' type ']'. /// /// NOTE: 'isStartOfInlineArrayTypeBody' must be true. ParserResult parseTypeInlineArray(SourceLoc lSquare); /// Parse a collection type. /// type-simple: /// '[' type ']' /// '[' type ':' type ']' ParserResult parseTypeCollection(); ParserResult parseTypeOptional(ParserResult Base); ParserResult parseTypeImplicitlyUnwrappedOptional(ParserResult Base); bool isOptionalToken(const Token &T) const; SourceLoc consumeOptionalToken(); bool isImplicitlyUnwrappedOptionalToken(const Token &T) const; SourceLoc consumeImplicitlyUnwrappedOptionalToken(); //===--------------------------------------------------------------------===// // Pattern Parsing /// A structure for collecting information about the default /// arguments of a context. struct DefaultArgumentInfo { unsigned NextIndex : 31; /// Track whether or not one of the parameters in a signature's argument /// list accepts a default argument. unsigned HasDefaultArgument : 1; /// Claim the next argument index. It's important to do this for /// all the arguments, not just those that have default arguments. unsigned claimNextIndex() { return NextIndex++; } DefaultArgumentInfo() { NextIndex = 0; HasDefaultArgument = false; } }; /// Describes a parsed parameter. struct ParsedParameter { /// Any declaration attributes attached to the parameter. DeclAttributes Attrs; /// The location of the 'inout' keyword, if present. SourceLoc SpecifierLoc; /// The parsed specifier kind, if present. ParamDecl::Specifier SpecifierKind = ParamDecl::Specifier::Default; /// The location of the first name. /// /// \c FirstName is the name. SourceLoc FirstNameLoc; /// The location of the second name, if present. /// /// \p SecondName is the name. SourceLoc SecondNameLoc; /// The first name. Identifier FirstName; /// The second name, the presence of which is indicated by \c SecondNameLoc. Identifier SecondName; /// The location of the 'isolated' keyword, if present. SourceLoc IsolatedLoc; /// The location of the '_const' keyword, if present. SourceLoc CompileConstLoc; /// The location of the 'sending' keyword if present. SourceLoc SendingLoc; /// The type following the ':'. TypeRepr *Type = nullptr; /// The default argument for this parameter. Expr *DefaultArg = nullptr; /// The default argument for this parameter. DefaultArgumentInitializer *DefaultArgInitContext = nullptr; /// True if we emitted a parse error about this parameter. bool isInvalid = false; /// True if this parameter is potentially destructuring a tuple argument. bool isPotentiallyDestructured = false; }; /// Describes the context in which the given parameter is being parsed. enum class ParameterContextKind { /// An operator. Operator, /// A function. Function, /// An initializer. Initializer, /// A closure. Closure, /// A subscript. Subscript, /// A curried argument clause. Curried, /// An enum element. EnumElement, /// A macro. Macro, }; /// Whether we are at the start of a parameter name when parsing a parameter. bool startsParameterName(bool isClosure); /// Attempts to perform code completion for the possible start of a function /// parameter type, returning the source location of the consumed completion /// token, or a null location if there is no completion token. SourceLoc tryCompleteFunctionParamTypeBeginning(); /// Parse a parameter-clause. /// /// \verbatim /// parameter-clause: /// '(' ')' /// '(' parameter (',' parameter)* '...'? )' /// /// parameter: /// 'inout'? ('let' | 'var')? '`'? identifier-or-none identifier-or-none? /// (':' type)? ('...' | '=' expr)? /// /// identifier-or-none: /// identifier /// '_' /// \endverbatim ParserStatus parseParameterClause(SourceLoc &leftParenLoc, SmallVectorImpl ¶ms, SourceLoc &rightParenLoc, DefaultArgumentInfo *defaultArgs, ParameterContextKind paramContext); ParserResult parseSingleParameterClause( ParameterContextKind paramContext, SmallVectorImpl *namePieces = nullptr, DefaultArgumentInfo *defaultArgs = nullptr); ParserStatus parseFunctionArguments(SmallVectorImpl &NamePieces, ParameterList *&BodyParams, ParameterContextKind paramContext, DefaultArgumentInfo &defaultArgs); ParserStatus parseFunctionSignature(DeclBaseName functionName, DeclName &fullName, ParameterList *&bodyParams, DefaultArgumentInfo &defaultArgs, SourceLoc &asyncLoc, bool &reasync, SourceLoc &throws, bool &rethrows, TypeRepr *&thrownType, TypeRepr *&retType); /// Parse 'async' and 'throws', if present, putting the locations of the /// keywords into the \c SourceLoc parameters. /// /// \param existingArrowLoc The location of an existing '->', if there is /// one. Parsing 'async' or 'throws' after the `->` is an error we /// correct for. /// /// \param reasync If non-NULL, will also parse the 'reasync' keyword in /// lieu of 'async'. /// /// \param rethrows If non-NULL, will also parse the 'rethrows' keyword in /// lieu of 'throws'. ParserStatus parseEffectsSpecifiers(SourceLoc existingArrowLoc, SourceLoc &asyncLoc, bool *reasync, SourceLoc &throwsLoc, bool *rethrows, TypeRepr *&thrownType); /// Returns 'true' if \p T is consider a throwing effect specifier. static bool isThrowsEffectSpecifier(const Token &T); /// Returns 'true' if \p T is considered effects specifier. static bool isEffectsSpecifier(const Token &T); //===--------------------------------------------------------------------===// // Pattern Parsing ParserResult parseTypedPattern(); ParserResult parsePattern(); /// Parse a tuple pattern element. /// /// \code /// pattern-tuple-element: /// pattern ('=' expr)? /// \endcode /// /// \returns The tuple pattern element, if successful. std::pair> parsePatternTupleElement(); ParserResult parsePatternTuple(); ParserResult parseOptionalPatternTypeAnnotation(ParserResult P); ParserResult parseMatchingPattern(bool isExprBasic); ParserResult parseMatchingPatternAsBinding(PatternBindingState newState, SourceLoc VarLoc, bool isExprBasic); Pattern *createBindingFromPattern(SourceLoc loc, Identifier name, VarDecl::Introducer introducer); /// Determine whether this token can only start a matching pattern /// production and not an expression. bool isOnlyStartOfMatchingPattern(); //===--------------------------------------------------------------------===// // Speculative type list parsing //===--------------------------------------------------------------------===// /// Returns true if we can parse a generic argument list at the current /// location in expression context. This parses types without generating /// AST nodes from the '<' at the current location up to a matching '>'. If /// the type list parse succeeds, and the closing '>' is followed by one /// of the following tokens: /// lparen_following rparen lsquare_following rsquare lbrace rbrace /// period_following comma semicolon /// then this function returns true, and the expression will parse as a /// generic parameter list. If the parse fails, or the closing '>' is not /// followed by one of the above tokens, then this function returns false, /// and the expression will parse with the '<' as an operator. bool canParseAsGenericArgumentList(); bool canParseTypeSimple(); bool canParseTypeSimpleOrComposition(); bool canParseTypeScalar(); bool canParseType(); bool canParseStartOfInlineArrayType(); bool canParseCollectionType(); /// Returns true if a simple type identifier can be parsed. /// /// \verbatim /// simple-type-identifier: identifier generic-argument-list? /// \endverbatim bool canParseTypeIdentifier(); bool canParseOldStyleProtocolComposition(); bool canParseTypeTupleBody(); bool canParseTypeAttribute(); bool canParseGenericArguments(); bool canParseTypedPattern(); /// Returns true if a qualified declaration name base type can be parsed. bool canParseBaseTypeForQualifiedDeclName(); /// Returns true if `nonisolated` contextual keyword could be parsed /// as part of the type a the current location. bool canParseNonisolatedAsTypeModifier(); /// Returns true if the current token is '->' or effects specifiers followed /// by '->'. /// /// e.g. /// throws -> // true /// async throws -> // true /// throws { // false bool isAtFunctionTypeArrow(); //===--------------------------------------------------------------------===// // Expression Parsing ParserResult parseExpr(Diag<> ID) { return parseExprImpl(ID, /*isExprBasic=*/false); } ParserResult parseExprBasic(Diag<> ID) { return parseExprImpl(ID, /*isExprBasic=*/true); } ParserResult parseExprImpl(Diag<> ID, bool isExprBasic); ParserResult parseExprIs(); ParserResult parseExprAs(); ParserResult parseExprArrow(); ParserResult parseExprSequence(Diag<> ID, bool isExprBasic, bool isForConditionalDirective = false); ParserResult parseExprSequenceElement(Diag<> ID, bool isExprBasic); ParserResult parseExprPostfixSuffix(ParserResult inner, bool isExprBasic, bool periodHasKeyPathBehavior); ParserResult parseExprPostfix(Diag<> ID, bool isExprBasic); ParserResult parseExprPrimary(Diag<> ID, bool isExprBasic); ParserResult parseExprUnary(Diag<> ID, bool isExprBasic); ParserResult parseExprKeyPathObjC(); ParserResult parseExprKeyPath(); ParserResult parseExprSelector(); ParserResult parseExprSuper(); ParserResult parseExprStringLiteral(); ParserResult parseExprRegexLiteral(); StringRef copyAndStripUnderscores(StringRef text); StringRef stripUnderscoresIfNeeded(StringRef text, SmallVectorImpl &buffer); ParserStatus parseStringSegments(SmallVectorImpl &Segments, Token EntireTok, VarDecl *InterpolationVar, SmallVectorImpl &Stmts, unsigned &LiteralCapacity, unsigned &InterpolationCount); /// Parse an argument label `identifier ':'`, if it exists. /// /// \param name The parsed name of the label (empty if it doesn't exist, or is /// _) /// \param loc The location of the label (empty if it doesn't exist) /// \param isAttr True if this is an argument label for an attribute (allows, but deprecates, use of /// \c '=' instead of \c ':'). /// \param splittingColonColon True if \c :: tokens should be treated as two /// adjacent colons. void parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc, bool isAttr = false, bool splittingColonColon = false); /// If a \c module-selector is present, returns a true value (specifically, /// 1 or 2 depending on how many tokens should be consumed to skip it). unsigned isAtModuleSelector(); /// Attempts to parse a \c module-selector if one is present. /// /// \verbatim /// module-selector: identifier '::' /// \endverbatim /// /// \return \c None if no selector is present or a selector is present but /// is not allowed; an instance with an empty \c Identifier if a /// selector is present but has no valid identifier; an instance with /// a valid \c Identifier if a selector is present and includes a /// module name. std::optional> parseModuleSelector(); enum class DeclNameFlag : uint8_t { /// If passed, operator basenames are allowed. AllowOperators = 1 << 0, /// If passed, names that coincide with keywords are allowed. Used after a /// dot to enable things like '.init' and '.default'. AllowKeywords = 1 << 1, /// If passed, 'deinit' and 'subscript' should be parsed as special names, /// not ordinary identifiers. AllowKeywordsUsingSpecialNames = AllowKeywords | 1 << 2, /// If passed, module selectors are not permitted on this declaration name. ModuleSelectorUnsupported = 1 << 3, /// If passed, compound names with argument lists are allowed, unless they /// have empty argument lists. AllowCompoundNames = 1 << 4, /// If passed, compound names with empty argument lists are allowed. AllowZeroArgCompoundNames = AllowCompoundNames | 1 << 5, /// If passed, \c self and \c Self are allowed as basenames. In a lot of /// cases this doesn't actually make sense but we need to accept them for /// backwards compatibility. AllowLowercaseAndUppercaseSelf = 1 << 6, }; using DeclNameOptions = OptionSet; friend DeclNameOptions operator|(DeclNameFlag flag1, DeclNameFlag flag2) { return DeclNameOptions(flag1) | flag2; } /// Parse a declaration name that results in a `DeclNameRef` in the syntax /// tree. /// /// Without \c DeclNameFlag::AllowCompoundNames, parse an /// unqualified-decl-base-name. /// /// unqualified-decl-base-name: identifier /// /// With \c DeclNameFlag::AllowCompoundNames, parse an unqualified-base-name. /// /// unqualified-decl-name: /// unqualified-decl-base-name /// unqualified-decl-base-name '(' ((identifier | '_') ':') + ')' DeclNameRef parseDeclNameRef(DeclNameLoc &loc, DiagRef diag, DeclNameOptions flags); /// Parse macro expansion. /// /// macro-expansion: /// '#' identifier generic-arguments? expr-paren? trailing-closure? ParserStatus parseFreestandingMacroExpansion( SourceLoc £Loc, DeclNameLoc ¯oNameLoc, DeclNameRef ¯oNameRef, SourceLoc &leftAngleLoc, SmallVectorImpl &genericArgs, SourceLoc &rightAngleLoc, ArgumentList *&argList, bool isExprBasic, DiagRef diag); ParserResult parseExprIdentifier(bool allowKeyword, bool allowModuleSelector = true); Expr *parseExprEditorPlaceholder(Token PlaceholderTok, Identifier PlaceholderId); /// Parse a closure expression after the opening brace. /// /// \verbatim /// expr-closure: /// '{' closure-signature? brace-item-list* '}' /// /// closure-signature: /// '|' closure-signature-arguments? '|' closure-signature-result? /// /// closure-signature-arguments: /// pattern-tuple-element (',' pattern-tuple-element)* /// /// closure-signature-result: /// '->' type /// \endverbatim ParserResult parseExprClosure(); /// Parse the closure signature, if present. /// /// \verbatim /// closure-signature: /// parameter-clause func-signature-result? 'in' /// identifier (',' identifier)* func-signature-result? 'in' /// \endverbatim /// /// \param bracketRange The range of the brackets enclosing a capture list, if /// present. Needed to offer fix-its for inserting 'self' into a capture list. /// \param captureList The entries in the capture list. /// \param params The parsed parameter list, or null if none was provided. /// \param arrowLoc The location of the arrow, if present. /// \param explicitResultType The explicit result type, if specified. /// \param inLoc The location of the 'in' keyword, if present. /// /// \returns ParserStatus error if an error occurred. Success if no signature /// is present or successfully parsed. ParserStatus parseClosureSignatureIfPresent( DeclAttributes &attributes, SourceRange &bracketRange, SmallVectorImpl &captureList, VarDecl *&capturedSelfParamDecl, ParameterList *¶ms, SourceLoc &asyncLoc, SourceLoc &throwsLoc, TypeExpr *&thrownType, SourceLoc &arrowLoc, TypeExpr *&explicitResultType, SourceLoc &inLoc); Expr *parseExprAnonClosureArg(); /// An element of an expression list, which may become e.g a tuple element or /// argument list argument. struct ExprListElt { SourceLoc LabelLoc; Identifier Label; Expr *E; }; /// Parse a tuple or paren expr. ParserResult parseTupleOrParenExpr(tok leftTok, tok rightTok); /// Parse an argument list. ParserResult parseArgumentList(tok leftTok, tok rightTok, bool isExprBasic, bool allowTrailingClosure = true); ParserStatus parseExprListElement(tok rightTok, bool isArgumentList, SourceLoc leftLoc, SmallVectorImpl &elts); /// Parse one or more trailing closures after an argument list. ParserStatus parseTrailingClosures(bool isExprBasic, SourceRange calleeRange, SmallVectorImpl &closures); /// Parse an expression list. ParserStatus parseExprList(tok leftTok, tok rightTok, bool isArgumentList, SourceLoc &leftLoc, SmallVectorImpl &elts, SourceLoc &rightLoc); /// Parse an object literal. /// /// \param LK The literal kind as determined by the first token. ParserResult parseExprObjectLiteral(ObjectLiteralExpr::LiteralKind LK, bool isExprBasic); ParserResult parseExprCallSuffix(ParserResult fn, bool isExprBasic); ParserResult parseExprMacroExpansion(bool isExprBasic); ParserResult parseExprCollection(); ParserResult parseExprCollectionElement(std::optional &isDictionary); ParserResult parseExprPoundCodeCompletion(std::optional ParentKind); UnresolvedDeclRefExpr *parseExprOperator(); /// Try re-lex a '/' operator character as a regex literal. This should be /// called when parsing in an expression position to ensure a regex literal is /// correctly parsed. void tryLexRegexLiteral(bool forUnappliedOperator); void validateCollectionElement(ParserResult element); //===--------------------------------------------------------------------===// // Statement Parsing /// Whether we are at the start of a statement. /// /// \param preferExpr If either an expression or statement could be parsed and /// this parameter is \c true, the function returns \c false such that an /// expression can be parsed. bool isStartOfStmt(bool preferExpr); bool isStartOfConditionalStmtBody(); bool isTerminatorForBraceItemListKind(BraceItemListKind Kind, ArrayRef ParsedDecls); ParserResult parseStmt(); ParserStatus parseExprOrStmt(ASTNode &Result); ParserResult parseStmtBreak(); ParserResult parseStmtContinue(); ParserResult parseStmtReturn(SourceLoc tryLoc); ParserResult parseStmtYield(SourceLoc tryLoc); ParserResult parseStmtThen(SourceLoc tryLoc); ParserResult parseStmtThrow(SourceLoc tryLoc); ParserResult parseStmtDiscard(); ParserResult parseStmtDefer(); ParserStatus parseStmtConditionElement(SmallVectorImpl &result, Diag<> DefaultID, StmtKind ParentKind, StringRef &BindingKindStr); ParserStatus parseStmtCondition(StmtCondition &Result, Diag<> ID, StmtKind ParentKind); ParserResult parseStmtConditionPoundAvailable(); ParserResult parseStmtConditionPoundHasSymbol(); ParserResult parseStmtIf(LabeledStmtInfo LabelInfo, bool IfWasImplicitlyInserted = false); ParserResult parseStmtGuard(); ParserResult parseStmtWhile(LabeledStmtInfo LabelInfo); ParserResult parseStmtRepeat(LabeledStmtInfo LabelInfo); ParserResult parseStmtDo(LabeledStmtInfo LabelInfo, bool shouldSkipDoTokenConsume = false); ParserResult parseStmtCatch(); ParserResult parseStmtForEach(LabeledStmtInfo LabelInfo); ParserResult parseStmtSwitch(LabeledStmtInfo LabelInfo); ParserStatus parseStmtCases(SmallVectorImpl &cases, bool IsActive); ParserResult parseStmtCase(bool IsActive); ParserResult parseStmtPoundAssert(); //===--------------------------------------------------------------------===// // Generics Parsing ParserResult parseGenericParameters(); ParserResult parseGenericParameters(SourceLoc LAngleLoc); ParserStatus parseGenericParametersBeforeWhere(SourceLoc LAngleLoc, SmallVectorImpl &GenericParams); ParserResult maybeParseGenericParams(); void diagnoseWhereClauseInGenericParamList(const GenericParamList *GenericParams); ParserStatus parseFreestandingGenericWhereClause(GenericContext *genCtx); ParserStatus parseGenericWhereClause( SourceLoc &WhereLoc, SourceLoc &EndLoc, SmallVectorImpl &Requirements, bool AllowLayoutConstraints = false); ParserStatus parseProtocolOrAssociatedTypeWhereClause(TrailingWhereClause *&trailingWhere, bool isProtocol); //===--------------------------------------------------------------------===// // Availability Specification Parsing /// The source of an availability spec list. enum class AvailabilitySpecSource: uint8_t { /// A spec from '@available(, ...)' or '#available(, ...)'. Available, /// A spec from '#unavailable(, ...)'. Unavailable, /// A spec from a '-define-availability "Name:, ..."' frontend arg. Macro, }; /// Parse a comma-separated list of availability specifications. Try to /// expand availability macros when \p Source is not a command line macro. ParserStatus parseAvailabilitySpecList(SmallVectorImpl &Specs, AvailabilitySpecSource Source); /// Does the current matches an argument macro name? Parsing compiler /// arguments as required without consuming tokens from the source file /// parser. bool peekAvailabilityMacroName(); /// Try to parse a reference to an availability macro and append its result /// to \p Specs. If the current token doesn't match a macro name, return /// a success without appending anything to \c Specs. ParserStatus parseAvailabilityMacro(SmallVectorImpl &Specs); /// Parse an availability macro definition from a command line argument. /// This function should only be called on a Parser set up on the command line /// argument code. ParserStatus parseAvailabilityMacroDefinition(std::string &Name, llvm::VersionTuple &Version, SmallVectorImpl &Specs); ParserResult parseAvailabilitySpec(); bool parseAvailability(bool parseAsPartOfSpecializeAttr, StringRef AttrName, bool &DiscardAttribute, SourceRange &attrRange, SourceLoc AtLoc, SourceLoc Loc, llvm::function_ref addAttribute); using PlatformAndVersion = std::pair; /// Parse a platform and version tuple (e.g. "macOS 12.0") and append it to /// the given vector. Wildcards ('*') parse successfully but are ignored. /// Unrecognized platform names also parse successfully but are ignored. /// Assumes that the tuples are part of a comma separated list ending with a /// trailing ')'. ParserStatus parsePlatformVersionInList( StringRef AttrName, llvm::SmallVector & PlatformAndVersions, bool &ParsedUnrecognizedPlatformName); //===--------------------------------------------------------------------===// // Code completion second pass. void performIDEInspectionSecondPassImpl(IDEInspectionDelayedDeclState &info); }; /// To assist debugging parser crashes, tell us the location of the /// current token. class PrettyStackTraceParser : public llvm::PrettyStackTraceEntry { Parser &P; public: explicit PrettyStackTraceParser(Parser &P) : P(P) {} void print(llvm::raw_ostream &out) const override; }; /// Whether a given token can be the start of a decl. bool isKeywordPossibleDeclStart(const LangOptions &options, const Token &Tok); } // end namespace swift #endif