//===--- ParseExpr.cpp - Swift Language Parser for Expressions ------------===// // // This source file is part of the Swift.org open source project // // Copyright (c) 2014 - 2016 Apple Inc. and the Swift project authors // Licensed under Apache License v2.0 with Runtime Library Exception // // See http://swift.org/LICENSE.txt for license information // See http://swift.org/CONTRIBUTORS.txt for the list of Swift project authors // //===----------------------------------------------------------------------===// // // Expression Parsing and AST Building // //===----------------------------------------------------------------------===// #include "swift/Parse/Parser.h" #include "swift/AST/DiagnosticsParse.h" #include "swift/Basic/EditorPlaceholder.h" #include "swift/Parse/CodeCompletionCallbacks.h" #include "llvm/ADT/SmallString.h" #include "llvm/ADT/StringSwitch.h" #include "llvm/ADT/Twine.h" #include "swift/Basic/Fallthrough.h" #include "swift/Basic/StringExtras.h" #include "llvm/Support/SaveAndRestore.h" #include "llvm/Support/raw_ostream.h" using namespace swift; /// \brief Create an argument with a trailing closure, with (optionally) /// the elements, names, and parentheses locations from an existing argument. static Expr *createArgWithTrailingClosure(ASTContext &context, SourceLoc leftParen, ArrayRef elementsIn, ArrayRef namesIn, ArrayRef nameLocsIn, SourceLoc rightParen, Expr *closure) { // If there are no elements, just build a parenthesized expression around // the closure. if (elementsIn.empty()) { return new (context) ParenExpr(leftParen, closure, rightParen, /*hasTrailingClosure=*/true); } // Create the list of elements, and add the trailing closure to the end. SmallVector elements(elementsIn.begin(), elementsIn.end()); elements.push_back(closure); SmallVector names; SmallVector nameLocs; if (!namesIn.empty()) { names.append(namesIn.begin(), namesIn.end()); names.push_back(Identifier()); nameLocs.append(nameLocsIn.begin(), nameLocsIn.end()); nameLocs.push_back(SourceLoc()); } // Form a full tuple expression. return TupleExpr::create(context, leftParen, elements, names, nameLocs, rightParen, /*hasTrailingClosure=*/true, /*Implicit=*/false); } /// \brief Add the given trailing closure argument to the call argument. static Expr *addTrailingClosureToArgument(ASTContext &context, Expr *arg, Expr *closure) { // Deconstruct the call argument to find its elements, element names, // and the locations of the left and right parentheses. if (auto tuple = dyn_cast(arg)) { // Deconstruct a tuple expression. return createArgWithTrailingClosure(context, tuple->getLParenLoc(), tuple->getElements(), tuple->getElementNames(), tuple->getElementNameLocs(), tuple->getRParenLoc(), closure); } // Deconstruct a parenthesized expression. auto paren = dyn_cast(arg); return createArgWithTrailingClosure(context, paren->getLParenLoc(), paren->getSubExpr(), { }, { }, paren->getRParenLoc(), closure); } /// parseExpr /// /// expr: /// expr-sequence(basic | trailing-closure) /// /// \param isExprBasic Whether we're only parsing an expr-basic. ParserResult Parser::parseExprImpl(Diag<> Message, bool isExprBasic) { // If we are parsing a refutable pattern, check to see if this is the start // of a let/var/is pattern. If so, parse it to an UnresolvedPatternExpr and // name binding will perform final validation. // // Only do this if we're parsing a pattern, to improve QoI on malformed // expressions followed by (e.g.) let/var decls. // if (InVarOrLetPattern && isOnlyStartOfMatchingPattern()) { ParserResult pattern = parseMatchingPattern(/*isExprBasic*/false); if (pattern.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (pattern.isNull()) return nullptr; return makeParserResult(new (Context) UnresolvedPatternExpr(pattern.get())); } ParserResult expr = parseExprSequence(Message, isExprBasic); if (expr.hasCodeCompletion()) return expr; if (expr.isNull()) return nullptr; return makeParserResult(expr.get()); } /// parseExprIs /// expr-is: /// 'is' type ParserResult Parser::parseExprIs() { SourceLoc isLoc = consumeToken(tok::kw_is); ParserResult type = parseType(diag::expected_type_after_is); if (type.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (type.isNull()) return nullptr; return makeParserResult(new (Context) IsExpr(isLoc, type.get())); } /// parseExprAs /// expr-as: /// 'as' type /// 'as?' type /// 'as!' type ParserResult Parser::parseExprAs() { // Parse the 'as'. SourceLoc asLoc = consumeToken(tok::kw_as); // Parse the postfix '?'. SourceLoc questionLoc; SourceLoc exclaimLoc; if (Tok.is(tok::question_postfix)) { questionLoc = consumeToken(tok::question_postfix); } else if (Tok.is(tok::exclaim_postfix)) { exclaimLoc = consumeToken(tok::exclaim_postfix); } ParserResult type = parseType(diag::expected_type_after_as); if (type.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (type.isNull()) return nullptr; Expr *parsed; if (questionLoc.isValid()) { parsed = new (Context) ConditionalCheckedCastExpr(asLoc, questionLoc, type.get()); } else if (exclaimLoc.isValid()) { parsed = new (Context) ForcedCheckedCastExpr(asLoc, exclaimLoc, type.get()); } else { parsed = new (Context) CoerceExpr(asLoc, type.get()); } return makeParserResult(parsed); } /// parseExprSequence /// /// expr-sequence(Mode): /// expr-sequence-element(Mode) expr-binary(Mode)* /// expr-binary(Mode): /// operator-binary expr-sequence-element(Mode) /// '?' expr-sequence(Mode) ':' expr-sequence-element(Mode) /// '=' expr-unary /// expr-is /// expr-as /// /// The sequencing for binary exprs is not structural, i.e., binary operators /// are not inherently right-associative. If present, '?' and ':' tokens must /// match. /// /// Similarly, the parsing of 'try' as part of expr-sequence-element /// is not structural. 'try' is not permitted at arbitrary points in /// a sequence; in the places it's permitted, it's hoisted out to /// apply to everything to its right. ParserResult Parser::parseExprSequence(Diag<> Message, bool isExprBasic, bool isConfigCondition) { SmallVector SequencedExprs; SourceLoc startLoc = Tok.getLoc(); bool HasCodeCompletion = false; while (true) { if (isConfigCondition && Tok.isAtStartOfLine()) break; // Parse a unary expression. ParserResult Primary = parseExprSequenceElement(Message, isExprBasic); HasCodeCompletion |= Primary.hasCodeCompletion(); if (Primary.isNull()) { if (Primary.hasCodeCompletion()) { if (CodeCompletion) { CodeCompletion->setLeadingSequenceExprs(SequencedExprs); } return Primary; } else { return nullptr; } } SequencedExprs.push_back(Primary.get()); parse_operator: switch (Tok.getKind()) { case tok::oper_binary_spaced: case tok::oper_binary_unspaced: { // If '>' is not an operator and this token starts with a '>', we're done. if (!GreaterThanIsOperator && startsWithGreater(Tok)) goto done; // Parse the operator. Expr *Operator = parseExprOperator(); SequencedExprs.push_back(Operator); // The message is only valid for the first subexpr. Message = diag::expected_expr_after_operator; break; } case tok::question_infix: { // Save the '?'. SourceLoc questionLoc = consumeToken(); // Parse the middle expression of the ternary. ParserResult middle = parseExprSequence(diag::expected_expr_after_if_question, isExprBasic); if (middle.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (middle.isNull()) return nullptr; // Make sure there's a matching ':' after the middle expr. if (!Tok.is(tok::colon)) { diagnose(questionLoc, diag::expected_colon_after_if_question); return makeParserErrorResult(new (Context) ErrorExpr( {startLoc, middle.get()->getSourceRange().End})); } SourceLoc colonLoc = consumeToken(); auto *unresolvedIf = new (Context) IfExpr(questionLoc, middle.get(), colonLoc); SequencedExprs.push_back(unresolvedIf); Message = diag::expected_expr_after_if_colon; break; } case tok::equal: { // If we're parsing an expression as the body of a refutable var/let // pattern, then an assignment doesn't make sense. In a "if let" // statement the equals is the start of the condition, so don't parse it // as a binary operator. if (InVarOrLetPattern) goto done; SourceLoc equalsLoc = consumeToken(); auto *assign = new (Context) AssignExpr(equalsLoc); SequencedExprs.push_back(assign); Message = diag::expected_expr_assignment; if (Tok.is(tok::code_complete)) { if (CodeCompletion) { auto RHS = new (Context) ErrorExpr( SourceRange(Tok.getRange().getStart(), Tok.getRange().getEnd())); assign->setSrc(RHS); SequencedExprs.pop_back(); assign->setDest(SequencedExprs.back()); SequencedExprs.pop_back(); SequencedExprs.push_back(assign); CodeCompletion->completeAssignmentRHS(assign); } consumeToken(); if (SequencedExprs.size() > 0 && (SequencedExprs.size() & 1) == 0) { // Make sure we have odd number of sequence exprs. SequencedExprs.pop_back(); } auto Result = SequencedExprs.size() == 1 ? makeParserResult(SequencedExprs[0]): makeParserResult(SequenceExpr::create(Context, SequencedExprs)); Result.setHasCodeCompletion(); return Result; } break; } case tok::kw_is: { // Parse a type after the 'is' token instead of an expression. ParserResult is = parseExprIs(); if (is.isNull() || is.hasCodeCompletion()) return nullptr; // Store the expr itself as a placeholder RHS. The real RHS is the // type parameter stored in the node itself. SequencedExprs.push_back(is.get()); SequencedExprs.push_back(is.get()); // We already parsed the right operand as part of the 'is' production. // Jump directly to parsing another operator. goto parse_operator; } case tok::kw_as: { ParserResult as = parseExprAs(); if (as.isNull() || as.hasCodeCompletion()) return nullptr; // Store the expr itself as a placeholder RHS. The real RHS is the // type parameter stored in the node itself. SequencedExprs.push_back(as.get()); SequencedExprs.push_back(as.get()); // We already parsed the right operand as part of the 'is' production. // Jump directly to parsing another operator. goto parse_operator; } default: // If the next token is not a binary operator, we're done. goto done; } } done: if (SequencedExprs.empty()) { if (isConfigCondition) { diagnose(startLoc, diag::expected_close_to_config_stmt); return makeParserError(); } else { // If we had semantic errors, just fail here. assert(!SequencedExprs.empty()); } } // If we saw no operators, don't build a sequence. if (SequencedExprs.size() == 1) { auto Result = makeParserResult(SequencedExprs[0]); if (HasCodeCompletion) Result.setHasCodeCompletion(); return Result; } auto Result = makeParserResult(SequenceExpr::create(Context, SequencedExprs)); if (HasCodeCompletion) Result.setHasCodeCompletion(); return Result; } /// parseExprSequenceElement /// /// expr-sequence-element(Mode): /// 'try' expr-unary(Mode) /// 'try' '?' expr-unary(Mode) /// 'try' '!' expr-unary(Mode) /// expr-unary(Mode) /// /// 'try' is not actually allowed at an arbitrary position of a /// sequence, but this isn't enforced until sequence-folding. ParserResult Parser::parseExprSequenceElement(Diag<> message, bool isExprBasic) { SourceLoc tryLoc; bool hadTry = consumeIf(tok::kw_try, tryLoc); Optional trySuffix; if (hadTry && Tok.isAny(tok::exclaim_postfix, tok::question_postfix)) { trySuffix = Tok; consumeToken(); } ParserResult sub = parseExprUnary(message, isExprBasic); if (hadTry && !sub.hasCodeCompletion() && !sub.isNull()) { switch (trySuffix ? trySuffix->getKind() : tok::NUM_TOKENS) { case tok::exclaim_postfix: sub = makeParserResult( new (Context) ForceTryExpr(tryLoc, sub.get(), trySuffix->getLoc())); break; case tok::question_postfix: sub = makeParserResult( new (Context) OptionalTryExpr(tryLoc, sub.get(), trySuffix->getLoc())); break; default: // If this is a simple "try expr" situation, where the expr is a closure // literal, and the next token is a 'catch', then the user wrote // try/catch instead of do/catch. Emit a fixit hint to rewrite to the // correct do/catch construct. if (Tok.is(tok::kw_catch) && isa(sub.get())) { diagnose(tryLoc, diag::docatch_not_trycatch) .fixItReplace(tryLoc, "do"); // Eat all of the catch clauses, so we don't trip over them in error // recovery. while (Tok.is(tok::kw_catch)) { ParserResult clause = parseStmtCatch(); if (clause.hasCodeCompletion() && clause.isNull()) break; } return makeParserResult(new (Context) ErrorExpr(tryLoc)); } sub = makeParserResult(new (Context) TryExpr(tryLoc, sub.get())); break; } } return sub; } /// parseExprUnary /// /// expr-unary(Mode): /// expr-postfix(Mode) /// operator-prefix expr-unary(Mode) /// '&' expr-unary(Mode) /// expr-selector /// ParserResult Parser::parseExprUnary(Diag<> Message, bool isExprBasic) { UnresolvedDeclRefExpr *Operator; switch (Tok.getKind()) { default: // If the next token is not an operator, just parse this as expr-postfix. return parseExprPostfix(Message, isExprBasic); case tok::amp_prefix: { SourceLoc Loc = consumeToken(tok::amp_prefix); ParserResult SubExpr = parseExprUnary(Message, isExprBasic); if (SubExpr.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (SubExpr.isNull()) return nullptr; return makeParserResult( new (Context) InOutExpr(Loc, SubExpr.get(), Type())); } case tok::pound_selector: return parseExprSelector(); case tok::oper_postfix: // Postfix operators cannot start a subexpression, but can happen // syntactically because the operator may just follow whatever precedes this // expression (and that may not always be an expression). diagnose(Tok, diag::invalid_postfix_operator); Tok.setKind(tok::oper_prefix); SWIFT_FALLTHROUGH; case tok::oper_prefix: Operator = parseExprOperator(); break; case tok::oper_binary_spaced: case tok::oper_binary_unspaced: { // For recovery purposes, accept an oper_binary here. SourceLoc OperEndLoc = Tok.getLoc().getAdvancedLoc(Tok.getLength()); Tok.setKind(tok::oper_prefix); Operator = parseExprOperator(); if (OperEndLoc == Tok.getLoc()) diagnose(PreviousLoc, diag::expected_expr_after_unary_operator); else diagnose(PreviousLoc, diag::expected_prefix_operator) .fixItRemoveChars(OperEndLoc, Tok.getLoc()); break; } } ParserResult SubExpr = parseExprUnary(Message, isExprBasic); if (SubExpr.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (SubExpr.isNull()) return nullptr; // Check if we have a unary '-' with number literal sub-expression, for // example, "-42" or "-1.25". if (auto *LE = dyn_cast(SubExpr.get())) { if (Operator->hasName() && Operator->getName().getBaseName().str() == "-") { LE->setNegative(Operator->getLoc()); return makeParserResult(LE); } } return makeParserResult( new (Context) PrefixUnaryExpr(Operator, SubExpr.get())); } /// parseExprSelector /// /// expr-selector: /// '#selector' '(' expr ')' /// ParserResult Parser::parseExprSelector() { // Consume '#selector'. SourceLoc keywordLoc = consumeToken(tok::pound_selector); // Parse the leading '('. if (!Tok.is(tok::l_paren)) { diagnose(Tok, diag::expr_selector_expected_lparen); return makeParserError(); } SourceLoc lParenLoc = consumeToken(tok::l_paren); // Parse the subexpression. ParserResult subExpr = parseExpr(diag::expr_selector_expected_expr); if (subExpr.hasCodeCompletion()) return subExpr; // Parse the closing ')' SourceLoc rParenLoc; if (subExpr.isParseError()) { skipUntilDeclStmtRBrace(tok::r_paren); if (Tok.is(tok::r_paren)) rParenLoc = consumeToken(); else rParenLoc = Tok.getLoc(); } else { parseMatchingToken(tok::r_paren, rParenLoc, diag::expr_selector_expected_rparen, lParenLoc); } // If the subexpression was in error, just propagate the error. if (subExpr.isParseError()) return makeParserResult( new (Context) ErrorExpr(SourceRange(keywordLoc, rParenLoc))); return makeParserResult( new (Context) ObjCSelectorExpr(keywordLoc, lParenLoc, subExpr.get(), rParenLoc)); } static DeclRefKind getDeclRefKindForOperator(tok kind) { switch (kind) { case tok::oper_binary_spaced: case tok::oper_binary_unspaced: return DeclRefKind::BinaryOperator; case tok::oper_postfix: return DeclRefKind::PostfixOperator; case tok::oper_prefix: return DeclRefKind::PrefixOperator; default: llvm_unreachable("bad operator token kind"); } } /// parseExprOperator - Parse an operator reference expression. These /// are not "proper" expressions; they can only appear in binary/unary /// operators. UnresolvedDeclRefExpr *Parser::parseExprOperator() { assert(Tok.isAnyOperator()); DeclRefKind refKind = getDeclRefKindForOperator(Tok.getKind()); SourceLoc loc = Tok.getLoc(); Identifier name = Context.getIdentifier(Tok.getText()); consumeToken(); // Bypass local lookup. return new (Context) UnresolvedDeclRefExpr(name, refKind, DeclNameLoc(loc)); } static VarDecl *getImplicitSelfDeclForSuperContext(Parser &P, DeclContext *DC, SourceLoc Loc) { auto *methodContext = DC->getInnermostMethodContext(); if (!methodContext) { P.diagnose(Loc, diag::super_not_in_class_method); return nullptr; } // Do an actual lookup for 'self' in case it shows up in a capture list. auto *methodSelf = methodContext->getImplicitSelfDecl(); auto *lookupSelf = P.lookupInScope(P.Context.Id_self); if (lookupSelf && lookupSelf != methodSelf) { // FIXME: This is the wrong diagnostic for if someone manually declares a // variable named 'self' using backticks. P.diagnose(Loc, diag::super_in_closure_with_capture); P.diagnose(lookupSelf->getLoc(), diag::super_in_closure_with_capture_here); return nullptr; } return methodSelf; } /// parseExprSuper /// /// expr-super: /// expr-super-member /// expr-super-init /// expr-super-subscript /// expr-super-member: /// 'super' '.' identifier /// expr-super-init: /// 'super' '.' 'init' /// expr-super-subscript: /// 'super' '[' expr ']' ParserResult Parser::parseExprSuper() { // Parse the 'super' reference. SourceLoc superLoc = consumeToken(tok::kw_super); VarDecl *selfDecl = getImplicitSelfDeclForSuperContext(*this, CurDeclContext, superLoc); bool ErrorOccurred = selfDecl == nullptr; Expr *superRef = !ErrorOccurred ? cast(new (Context) SuperRefExpr(selfDecl, superLoc, /*Implicit=*/false)) : cast(new (Context) ErrorExpr(superLoc)); if (Tok.is(tok::period)) { // 'super.' must be followed by a member or initializer ref. SourceLoc dotLoc = consumeToken(tok::period); if (Tok.is(tok::code_complete)) { if (CodeCompletion) { if (auto *SRE = dyn_cast(superRef)) CodeCompletion->completeExprSuperDot(SRE); } // Eat the code completion token because we handled it. consumeToken(tok::code_complete); return makeParserCodeCompletionResult(superRef); } DeclNameLoc nameLoc; DeclName name = parseUnqualifiedDeclName( /*allowInit=*/true, nameLoc, diag::expected_identifier_after_super_dot_expr); if (!name) return nullptr; return makeParserResult( new (Context) UnresolvedDotExpr(superRef, dotLoc, name, nameLoc, /*Implicit=*/false)); } if (Tok.isFollowingLSquare()) { // super[expr] ParserResult idx = parseExprList(tok::l_square, tok::r_square); if (idx.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (idx.isNull()) return nullptr; return makeParserResult(new (Context) SubscriptExpr(superRef, idx.get())); } if (Tok.is(tok::code_complete)) { if (CodeCompletion) { if (auto *SRE = dyn_cast(superRef)) CodeCompletion->completeExprSuper(SRE); } // Eat the code completion token because we handled it. consumeToken(tok::code_complete); return makeParserCodeCompletionResult(superRef); } if (consumeIf(tok::unknown)) return nullptr; diagnose(Tok, diag::expected_dot_or_subscript_after_super); return nullptr; } /// Copy a numeric literal value into AST-owned memory, stripping underscores /// so the semantic part of the value can be parsed by APInt/APFloat parsers. static StringRef copyAndStripUnderscores(ASTContext &C, StringRef orig) { char *start = static_cast(C.Allocate(orig.size(), 1)); char *p = start; for (char c : orig) if (c != '_') *p++ = c; return StringRef(start, p - start); } /// Disambiguate the parse after '{' token that is in a place that might be /// the start of a trailing closure, or start the variable accessor block. /// /// Check to see if the '{' is followed by a 'didSet' or a 'willSet' label, /// possibly preceded by attributes. If so, we disambiguate the parse as the /// start of a get-set block in a variable definition (not as a trailing /// closure). static bool isStartOfGetSetAccessor(Parser &P) { assert(P.Tok.is(tok::l_brace) && "not checking a brace?"); // The only case this can happen is if the accessor label is immediately after // a brace (possibly preceded by attributes). "get" is implicit, so it can't // be checked for. Conveniently however, get/set properties are not allowed // to have initializers, so we don't have an ambiguity, we just have to check // for observing accessors. // // If we have a 'didSet' or a 'willSet' label, disambiguate immediately as // an accessor block. Token NextToken = P.peekToken(); if (NextToken.isContextualKeyword("didSet") || NextToken.isContextualKeyword("willSet")) return true; // If we don't have attributes, then it cannot be an accessor block. if (NextToken.isNot(tok::at_sign)) return false; Parser::BacktrackingScope Backtrack(P); // Eat the "{". P.consumeToken(tok::l_brace); // Eat attributes, if present. if (!P.canParseAttributes()) return false; // Check if we have 'didSet'/'willSet' after attributes. return P.Tok.isContextualKeyword("didSet") || P.Tok.isContextualKeyword("willSet"); } /// Map magic literal tokens such as __FILE__ to their /// MagicIdentifierLiteralExpr kind. MagicIdentifierLiteralExpr::Kind getMagicIdentifierLiteralKind(tok Kind) { switch (Kind) { case tok::kw___COLUMN__: return MagicIdentifierLiteralExpr::Kind::Column; case tok::kw___FILE__: return MagicIdentifierLiteralExpr::Kind::File; case tok::kw___FUNCTION__: return MagicIdentifierLiteralExpr::Kind::Function; case tok::kw___LINE__: return MagicIdentifierLiteralExpr::Kind::Line; case tok::kw___DSO_HANDLE__: return MagicIdentifierLiteralExpr::Kind::DSOHandle; default: llvm_unreachable("not a magic literal"); } } /// parseExprPostfix /// /// expr-literal: /// integer_literal /// floating_literal /// string_literal /// nil /// true /// false /// '__FILE__' /// '__LINE__' /// '__COLUMN__' /// '__FUNCTION__' /// '__DSO_HANDLE__' /// /// expr-primary: /// expr-literal /// expr-identifier expr-call-suffix? /// expr-closure /// expr-anon-closure-argument /// expr-delayed-identifier /// expr-paren /// expr-super /// expr-discard /// /// expr-delayed-identifier: /// '.' identifier /// /// expr-discard: /// '_' /// /// expr-dot: /// expr-postfix '.' 'type' /// expr-postfix '.' identifier generic-args? expr-call-suffix? /// expr-postfix '.' integer_literal /// /// expr-subscript: /// expr-postfix '[' expr ']' /// /// expr-call: /// expr-postfix expr-paren /// /// expr-force-value: /// expr-postfix '!' /// /// expr-trailing-closure: /// expr-postfix(trailing-closure) expr-closure /// /// expr-postfix(Mode): /// expr-postfix(Mode) operator-postfix /// /// expr-postfix(basic): /// expr-primary /// expr-dot /// expr-metatype /// expr-init /// expr-subscript /// expr-call /// expr-force-value /// /// expr-postfix(trailing-closure): /// expr-postfix(basic) /// expr-trailing-closure /// ParserResult Parser::parseExprPostfix(Diag<> ID, bool isExprBasic) { ParserResult Result; switch (Tok.getKind()) { case tok::integer_literal: { StringRef Text = copyAndStripUnderscores(Context, Tok.getText()); SourceLoc Loc = consumeToken(tok::integer_literal); Result = makeParserResult(new (Context) IntegerLiteralExpr(Text, Loc, /*Implicit=*/false)); break; } case tok::floating_literal: { StringRef Text = copyAndStripUnderscores(Context, Tok.getText()); SourceLoc Loc = consumeToken(tok::floating_literal); Result = makeParserResult(new (Context) FloatLiteralExpr(Text, Loc, /*Implicit=*/false)); break; } case tok::at_sign: // Objective-C programmers habitually type @"foo", so recover gracefully // with a fixit. If this isn't @"foo", just handle it like an unknown // input. if (peekToken().isNot(tok::string_literal)) goto UnknownCharacter; diagnose(Tok.getLoc(), diag::string_literal_no_atsign) .fixItRemove(Tok.getLoc()); consumeToken(tok::at_sign); SWIFT_FALLTHROUGH; case tok::string_literal: // "foo" Result = makeParserResult(parseExprStringLiteral()); break; case tok::kw_nil: Result = makeParserResult( new (Context) NilLiteralExpr(consumeToken(tok::kw_nil))); break; case tok::kw_true: case tok::kw_false: { bool isTrue = Tok.is(tok::kw_true); Result = makeParserResult( new (Context) BooleanLiteralExpr(isTrue, consumeToken())); break; } case tok::kw___FILE__: case tok::kw___LINE__: case tok::kw___COLUMN__: case tok::kw___FUNCTION__: case tok::kw___DSO_HANDLE__: { auto Kind = getMagicIdentifierLiteralKind(Tok.getKind()); SourceLoc Loc = consumeToken(); Result = makeParserResult( new (Context) MagicIdentifierLiteralExpr(Kind, Loc, /*Implicit=*/false)); break; } case tok::identifier: // foo // If we are parsing a refutable pattern and are inside a let/var pattern, // the identifiers change to be value bindings instead of decl references. // Parse and return this as an UnresolvedPatternExpr around a binding. This // will be resolved (or rejected) by sema when the overall refutable pattern // it transformed from an expression into a pattern. if ((InVarOrLetPattern == IVOLP_ImplicitlyImmutable || InVarOrLetPattern == IVOLP_InVar || InVarOrLetPattern == IVOLP_InLet) && // If we have "case let x." or "case let x(", we parse x as a normal // name, not a binding, because it is the start of an enum pattern or // call pattern. peekToken().isNot(tok::period, tok::period_prefix, tok::l_paren)) { Identifier name; SourceLoc loc = consumeIdentifier(&name); auto pattern = createBindingFromPattern(loc, name, InVarOrLetPattern != IVOLP_InVar); Result = makeParserResult(new (Context) UnresolvedPatternExpr(pattern)); break; } SWIFT_FALLTHROUGH; case tok::kw_self: // self case tok::kw_Self: // Self Result = makeParserResult(parseExprIdentifier()); // If there is an expr-call-suffix, parse it and form a call. if (Tok.isFollowingLParen()) { Result = parseExprCallSuffix(Result); break; } break; case tok::dollarident: // $1 Result = makeParserResult(parseExprAnonClosureArg()); break; // If the next token is '_', parse a discard expression. case tok::kw__: Result = makeParserResult( new (Context) DiscardAssignmentExpr(consumeToken(), /*Implicit=*/false)); break; case tok::l_brace: // expr-closure Result = parseExprClosure(); break; case tok::period: //=.foo case tok::period_prefix: { // .foo SourceLoc DotLoc = consumeToken(); // Special case "." like ".4". This isn't valid, but the // developer almost certainly meant to use "0.4". Diagnose this, and // recover as if they wrote that. if (Tok.is(tok::integer_literal) && !Tok.isAtStartOfLine()) { diagnose(DotLoc, diag::invalid_float_literal_missing_leading_zero, Tok.getText()) .fixItInsert(DotLoc, "0") .highlight({DotLoc, Tok.getLoc()}); char *Ptr = (char*)Context.Allocate(Tok.getLength()+2, 1); memcpy(Ptr, "0.", 2); memcpy(Ptr+2, Tok.getText().data(), Tok.getLength()); auto FltText = StringRef(Ptr, Tok.getLength()+2); FltText = copyAndStripUnderscores(Context, FltText); consumeToken(tok::integer_literal); Result = makeParserResult(new (Context) FloatLiteralExpr(FltText, DotLoc, /*Implicit=*/false)); break; } DeclName Name; DeclNameLoc NameLoc; if (Tok.is(tok::code_complete)) { auto Expr = new (Context) UnresolvedMemberExpr( DotLoc, DeclNameLoc(DotLoc.getAdvancedLoc(1)), Context.getIdentifier("_"), nullptr); Result = makeParserResult(Expr); if (CodeCompletion) { std::vector Identifiers; // Move lexer to the start of the current line. L->backtrackToState(L->getStateForBeginningOfTokenLoc( L->getLocForStartOfLine(SourceMgr, Tok.getLoc()))); bool HasReturn = false; // Until we see the code completion token, collect identifiers. for (L->lex(Tok); !Tok.is(tok::code_complete); consumeToken()) { if (!HasReturn) HasReturn = Tok.is(tok::kw_return); if (Tok.is(tok::identifier)) { Identifiers.push_back(Tok.getText()); } } CodeCompletion->completeUnresolvedMember(Expr, Identifiers, HasReturn); } else { Result.setHasCodeCompletion(); } consumeToken(); return Result; } Name = parseUnqualifiedDeclName(/*allowInit=*/true, NameLoc, diag::expected_identifier_after_dot_expr); if (!Name) return nullptr; ParserResult Arg; // Check for a () suffix, which indicates a call when constructing // this member. Note that this cannot be the start of a new line. if (Tok.isFollowingLParen()) { Arg = parseExprList(tok::l_paren, tok::r_paren); if (Arg.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (Arg.isNull()) return nullptr; } // Handle .foo by just making an AST node. Result = makeParserResult( new (Context) UnresolvedMemberExpr(DotLoc, NameLoc, Name, Arg.getPtrOrNull())); break; } case tok::kw_super: { // super.foo or super[foo] Result = parseExprSuper(); break; } case tok::l_paren: Result = parseExprList(tok::l_paren, tok::r_paren); break; case tok::l_square: Result = parseExprCollection(); break; case tok::l_square_lit: // [#Color(...)#], [#Image(...)#] Result = parseExprObjectLiteral(); break; case tok::pound_available: { // For better error recovery, parse but reject #available in an expr // context. diagnose(Tok.getLoc(), diag::availability_query_outside_if_stmt_guard); auto res = parseStmtConditionPoundAvailable(); if (res.hasCodeCompletion()) return makeParserCodeCompletionStatus(); if (res.isParseError() || res.isNull()) return nullptr; Result = makeParserResult( new (Context) ErrorExpr(res.get()->getSourceRange())); break; } case tok::code_complete: Result = makeParserResult(new (Context) CodeCompletionExpr(Tok.getRange())); Result.setHasCodeCompletion(); if (CodeCompletion) CodeCompletion->completePostfixExprBeginning(dyn_cast( Result.get())); consumeToken(tok::code_complete); break; // Eat an invalid token in an expression context. Error tokens are diagnosed // by the lexer, so there is no reason to emit another diagnostic. case tok::unknown: consumeToken(tok::unknown); return nullptr; default: UnknownCharacter: checkForInputIncomplete(); // FIXME: offer a fixit: 'Self' -> 'self' diagnose(Tok, ID); return nullptr; } // If we had a parse error, don't attempt to parse suffixes. if (Result.isParseError()) return Result; bool hasBindOptional = false; // Handle suffix expressions. while (1) { // FIXME: Better recovery. if (Result.isNull()) return Result; // Check for a .foo suffix. SourceLoc TokLoc = Tok.getLoc(); if (consumeIf(tok::period) || consumeIf(tok::period_prefix)) { // Non-identifier cases. if (Tok.isNot(tok::identifier) && Tok.isNot(tok::integer_literal) && Tok.isNot(tok::kw_init)) { // A metatype expr. if (Tok.is(tok::kw_dynamicType)) { Result = makeParserResult( new (Context) DynamicTypeExpr(Result.get(), consumeToken(), Type())); continue; } // A '.self' expr. if (Tok.is(tok::kw_self)) { Result = makeParserResult( new (Context) DotSelfExpr(Result.get(), TokLoc, consumeToken())); continue; } // If we have '.', try to recover by creating // an identifier with the same spelling as the keyword. if (Tok.isKeyword() && peekToken().is(tok::code_complete)) { Identifier Name = Context.getIdentifier(Tok.getText()); Result = makeParserResult( new (Context) UnresolvedDotExpr(Result.get(), TokLoc, Name, DeclNameLoc(Tok.getLoc()), /*Implicit=*/false)); consumeToken(); } if (Tok.is(tok::code_complete)) { if (CodeCompletion && Result.isNonNull()) CodeCompletion->completeDotExpr(Result.get(), /*DotLoc=*/TokLoc); // Eat the code completion token because we handled it. consumeToken(tok::code_complete); Result.setHasCodeCompletion(); return Result; } checkForInputIncomplete(); diagnose(Tok, diag::expected_member_name); return nullptr; } // Don't allow '.' following a numeric literal // expression. if (Tok.is(tok::integer_literal) && Result.isNonNull() && (isa(Result.get()) || isa(Result.get()))) { diagnose(Tok, diag::numeric_literal_numeric_member) .highlight(Result.get()->getSourceRange()); consumeToken(); continue; } if (Result.isParseError()) continue; if (Tok.isAny(tok::identifier, tok::kw_init)) { DeclNameLoc NameLoc; DeclName Name = parseUnqualifiedDeclName(/*allowInit=*/true, NameLoc, diag::expected_member_name); if (!Name) return nullptr; Result = makeParserResult( new (Context) UnresolvedDotExpr(Result.get(), TokLoc, Name, NameLoc, /*Implicit=*/false)); if (canParseAsGenericArgumentList()) { SmallVector args; SourceLoc LAngleLoc, RAngleLoc; if (parseGenericArguments(args, LAngleLoc, RAngleLoc)) { diagnose(LAngleLoc, diag::while_parsing_as_left_angle_bracket); } SmallVector locArgs; for (auto ty : args) locArgs.push_back(ty); Result = makeParserResult(new (Context) UnresolvedSpecializeExpr( Result.get(), LAngleLoc, Context.AllocateCopy(locArgs), RAngleLoc)); } } else { DeclName name = Context.getIdentifier(Tok.getText()); SourceLoc nameLoc = consumeToken(tok::integer_literal); Result = makeParserResult( new (Context) UnresolvedDotExpr(Result.get(), TokLoc, name, DeclNameLoc(nameLoc), /*Implicit=*/false)); } // If there is an expr-call-suffix, parse it and form a call. if (Tok.isFollowingLParen()) Result = parseExprCallSuffix(Result); continue; } // Check for a () suffix, which indicates a call. // Note that this cannot be the start of a new line. if (Tok.isFollowingLParen()) { if (peekToken().is(tok::code_complete)) { consumeToken(tok::l_paren); auto SubResult = makeParserResult(new (Context) CodeCompletionExpr(Tok. getRange())); SubResult.setHasCodeCompletion(); if (CodeCompletion && Result.isNonNull()) CodeCompletion->completePostfixExprParen(Result.get(), SubResult.get()); // Eat the code completion token because we handled it. consumeToken(tok::code_complete); return SubResult; } ParserResult Arg = parseExprList(tok::l_paren, tok::r_paren); Result = makeParserResult(new (Context) CallExpr(Result.get(), Arg.get(), /*Implicit=*/false)); if (Arg.hasCodeCompletion()) Result.setHasCodeCompletion(); if (Arg.isParseError()) Result.setIsParseError(); continue; } // Check for a [expr] suffix. // Note that this cannot be the start of a new line. if (Tok.isFollowingLSquare()) { ParserResult Idx = parseExprList(tok::l_square, tok::r_square); if (Idx.hasCodeCompletion()) return makeParserCodeCompletionResult(); if (Idx.isNull() || Result.isNull()) return nullptr; Result = makeParserResult( new (Context) SubscriptExpr(Result.get(), Idx.get())); continue; } // Check for a trailing closure, if allowed. if (!isExprBasic && Tok.is(tok::l_brace) && !isStartOfGetSetAccessor(*this)) { SourceLoc braceLoc = Tok.getLoc(); // Parse the closure. ParserResult closure = parseExprClosure(); if (closure.isNull()) return nullptr; // Track the original end location of the expression we're trailing so // we can warn about excess newlines. auto origEndLoc = Result.get()->getEndLoc(); auto origLineCol = SourceMgr.getLineAndColumn(origEndLoc); auto braceLineCol = SourceMgr.getLineAndColumn(braceLoc); if (((int)braceLineCol.first - (int)origLineCol.first) > 1) { diagnose(braceLoc, diag::trailing_closure_excess_newlines); diagnose(Result.get()->getLoc(), diag::trailing_closure_call_here); } // Introduce the trailing closure into the call, or form a call, as // necessary. if (auto call = dyn_cast(Result.get())) { // When a closure follows a call, it becomes the last argument of // that call. Expr *arg = addTrailingClosureToArgument(Context, call->getArg(), closure.get()); call->setArg(arg); if (closure.hasCodeCompletion()) Result.setHasCodeCompletion(); } else { // Otherwise, the closure implicitly forms a call. Expr *arg = createArgWithTrailingClosure(Context, SourceLoc(), { }, { }, { }, SourceLoc(), closure.get()); Result = makeParserResult( ParserStatus(closure), new (Context) CallExpr(Result.get(), arg, /*Implicit=*/true)); } if (Result.hasCodeCompletion()) return Result; // We only allow a single trailing closure on a call. This could be // generalized in the future, but needs further design. if (Tok.is(tok::l_brace)) break; continue; } // Check for a ? suffix. if (consumeIf(tok::question_postfix)) { Result = makeParserResult( new (Context) BindOptionalExpr(Result.get(), TokLoc, /*depth*/ 0)); hasBindOptional = true; continue; } // Check for a ! suffix. if (consumeIf(tok::exclaim_postfix)) { Result = makeParserResult(new (Context) ForceValueExpr(Result.get(), TokLoc)); continue; } // Check for a postfix-operator suffix. if (Tok.is(tok::oper_postfix)) { // If '>' is not an operator and this token starts with a '>', we're done. if (!GreaterThanIsOperator && startsWithGreater(Tok)) return Result; Expr *oper = parseExprOperator(); Result = makeParserResult( new (Context) PostfixUnaryExpr(oper, Result.get())); continue; } if (Tok.is(tok::code_complete)) { if (Tok.isAtStartOfLine()) { // Postfix expression is located on a different line than the code // completion token, and thus they are not related. return Result; } if (CodeCompletion && Result.isNonNull()) { bool hasSpace = Tok.getLoc() != getEndOfPreviousLoc(); CodeCompletion->completePostfixExpr(Result.get(), hasSpace); } // Eat the code completion token because we handled it. consumeToken(tok::code_complete); return makeParserCodeCompletionResult(); } break; } // If we had a ? suffix expression, bind the entire postfix chain // within an OptionalEvaluationExpr. if (hasBindOptional) { Result = makeParserResult( new (Context) OptionalEvaluationExpr(Result.get())); } return Result; } static StringLiteralExpr * createStringLiteralExprFromSegment(ASTContext &Ctx, const Lexer *L, Lexer::StringSegment &Segment, SourceLoc TokenLoc) { assert(Segment.Kind == Lexer::StringSegment::Literal); // FIXME: Consider lazily encoding the string when needed. llvm::SmallString<256> Buf; StringRef EncodedStr = L->getEncodedStringSegment(Segment, Buf); if (!Buf.empty()) { assert(EncodedStr.begin() == Buf.begin() && "Returned string is not from buffer?"); EncodedStr = Ctx.AllocateCopy(EncodedStr); } return new (Ctx) StringLiteralExpr(EncodedStr, TokenLoc); } /// expr-literal: /// string_literal Expr *Parser::parseExprStringLiteral() { SmallVector Segments; L->getStringLiteralSegments(Tok, Segments); SourceLoc Loc = consumeToken(); // The simple case: just a single literal segment. if (Segments.size() == 1 && Segments.front().Kind == Lexer::StringSegment::Literal) { return createStringLiteralExprFromSegment(Context, L, Segments.front(), Loc); } SmallVector Exprs; bool First = true; for (auto Segment : Segments) { switch (Segment.Kind) { case Lexer::StringSegment::Literal: { auto TokenLoc = First ? Loc : Segment.Loc; Exprs.push_back( createStringLiteralExprFromSegment(Context, L, Segment, TokenLoc)); // Since the string is already parsed, Tok already points to the first // token after the whole string, but PreviousLoc is not exactly correct. PreviousLoc = TokenLoc; break; } case Lexer::StringSegment::Expr: { // We are going to mess with Tok to do reparsing for interpolated literals, // don't lose our 'next' token. llvm::SaveAndRestore SavedTok(Tok); // Create a temporary lexer that lexes from the body of the string. Lexer::State BeginState = L->getStateForBeginningOfTokenLoc(Segment.Loc); // We need to set the EOF at r_paren, to prevent the Lexer from eagerly // trying to lex the token beyond it. Parser::parseList() does a special // check for a tok::EOF that is spelled with a ')'. // FIXME: This seems like a hack, there must be a better way.. Lexer::State EndState = BeginState.advance(Segment.Length-1); Lexer LocalLex(*L, BeginState, EndState); // Temporarily swap out the parser's current lexer with our new one. llvm::SaveAndRestore T(L, &LocalLex); // Prime the new lexer with a '(' as the first token. // We might be at tok::eof now, so ensure that consumeToken() does not // assert about lexing past eof. Tok.setKind(tok::unknown); consumeToken(); assert(Tok.is(tok::l_paren)); ParserResult E = parseExprList(tok::l_paren, tok::r_paren); if (E.isNonNull()) { Exprs.push_back(E.get()); if (!Tok.is(tok::eof)) { diagnose(Tok, diag::string_interpolation_extra); } } break; } } First = false; } if (Exprs.empty()) return new (Context) ErrorExpr(Loc); return new (Context) InterpolatedStringLiteralExpr(Loc, Context.AllocateCopy(Exprs)); } void Parser::diagnoseEscapedArgumentLabel(const Token &tok) { assert(tok.isEscapedIdentifier() && "Only for escaped identifiers"); if (!canBeArgumentLabel(tok.getText())) return; SourceLoc start = tok.getLoc(); SourceLoc end = start.getAdvancedLoc(tok.getLength()); diagnose(tok, diag::escaped_parameter_name, tok.getText()) .fixItRemoveChars(start, start.getAdvancedLoc(1)) .fixItRemoveChars(end.getAdvancedLoc(-1), end); } DeclName Parser::parseUnqualifiedDeclName(bool allowInit, DeclNameLoc &loc, const Diagnostic &diag) { // Consume the base name. Identifier baseName; SourceLoc baseNameLoc; if (Tok.is(tok::kw_init) && allowInit) { baseName = Context.Id_init; baseNameLoc = consumeToken(tok::kw_init); } else if (Tok.is(tok::identifier) || Tok.is(tok::kw_Self) || Tok.is(tok::kw_self)) { baseNameLoc = consumeIdentifier(&baseName); } else { checkForInputIncomplete(); diagnose(Tok, diag); return DeclName(); } // If the next token isn't a following '(', we don't have a compound name. if (!Tok.isFollowingLParen()) { loc = DeclNameLoc(baseNameLoc); return baseName; } // If the token after that isn't an argument label or ':', we don't have a // compound name. if ((!peekToken().canBeArgumentLabel() && !peekToken().is(tok::colon)) || Identifier::isEditorPlaceholder(peekToken().getText())) { loc = DeclNameLoc(baseNameLoc); return baseName; } // Try to parse a compound name. BacktrackingScope backtrack(*this); SmallVector argumentLabels; SmallVector argumentLabelLocs; SourceLoc lparenLoc = consumeToken(tok::l_paren); SourceLoc rparenLoc; while (true) { // Terminate at ')'. if (Tok.is(tok::r_paren)) { rparenLoc = consumeToken(tok::r_paren); break; } // If we see a ':', the user forgot the '_'; if (Tok.is(tok::colon)) { diagnose(Tok, diag::empty_arg_label_underscore) .fixItInsert(Tok.getLoc(), "_"); argumentLabels.push_back(Identifier()); argumentLabelLocs.push_back(consumeToken(tok::colon)); } // If we see a potential argument label followed by a ':', consume // it. if (Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) { // If this was an escaped identifier that need not have been escaped, // say so. if (Tok.isEscapedIdentifier()) diagnoseEscapedArgumentLabel(Tok); if (Tok.is(tok::kw__)) argumentLabels.push_back(Identifier()); else argumentLabels.push_back(Context.getIdentifier(Tok.getText())); argumentLabelLocs.push_back(consumeToken()); (void)consumeToken(tok::colon); continue; } // This is not a compound name. // FIXME: Could recover better if we "know" it's a compound name. loc = DeclNameLoc(baseNameLoc); return baseName; } assert(!argumentLabels.empty() && "Logic above should prevent this"); assert(argumentLabels.size() == argumentLabelLocs.size()); // We have a compound name. Cancel backtracking and build that name. backtrack.cancelBacktrack(); loc = DeclNameLoc(Context, baseNameLoc, lparenLoc, argumentLabelLocs, rparenLoc); return DeclName(Context, baseName, argumentLabels); } /// expr-identifier: /// unqualified-decl-name generic-args? Expr *Parser::parseExprIdentifier() { assert(Tok.is(tok::identifier) || Tok.is(tok::kw_self) || Tok.is(tok::kw_Self)); Token IdentTok = Tok; // Parse the unqualified-decl-name. DeclNameLoc loc; DeclName name = parseUnqualifiedDeclName(/*allowInit=*/false, loc, diag::expected_expr); SmallVector args; SourceLoc LAngleLoc, RAngleLoc; bool hasGenericArgumentList = false; /// The generic-args case is ambiguous with an expression involving '<' /// and '>' operators. The operator expression is favored unless a generic /// argument list can be successfully parsed, and the closing bracket is /// followed by one of these tokens: /// lparen_following rparen lsquare_following rsquare lbrace rbrace /// period_following comma semicolon /// if (canParseAsGenericArgumentList()) { if (parseGenericArguments(args, LAngleLoc, RAngleLoc)) { diagnose(LAngleLoc, diag::while_parsing_as_left_angle_bracket); } // The result can be empty in error cases. hasGenericArgumentList = !args.empty(); } ValueDecl *D = lookupInScope(name.getBaseName()); // FIXME: We want this to work: "var x = { x() }", but for now it's better // to disallow it than to crash. if (D) { for (auto activeVar : DisabledVars) { if (activeVar == D) { diagnose(loc.getBaseNameLoc(), DisabledVarReason); return new (Context) ErrorExpr(loc.getSourceRange()); } } } else { for (auto activeVar : DisabledVars) { if (activeVar->getFullName() == name) { diagnose(loc.getBaseNameLoc(), DisabledVarReason); return new (Context) ErrorExpr(loc.getSourceRange()); } } } Expr *E; if (D == 0) { if (name.getBaseName().isEditorPlaceholder()) return parseExprEditorPlaceholder(IdentTok, name.getBaseName()); auto refKind = DeclRefKind::Ordinary; auto unresolved = new (Context) UnresolvedDeclRefExpr(name, refKind, loc); unresolved->setSpecialized(hasGenericArgumentList); E = unresolved; } else if (auto TD = dyn_cast(D)) { if (!hasGenericArgumentList) E = TypeExpr::createForDecl(loc.getBaseNameLoc(), TD, /*implicit*/false); else E = TypeExpr::createForSpecializedDecl(loc.getBaseNameLoc(), TD, Context.AllocateCopy(args), SourceRange(LAngleLoc, RAngleLoc)); } else { auto declRef = new (Context) DeclRefExpr(D, loc, /*Implicit=*/false); declRef->setGenericArgs(args); E = declRef; } if (hasGenericArgumentList) { SmallVector locArgs; for (auto ty : args) locArgs.push_back(ty); E = new (Context) UnresolvedSpecializeExpr(E, LAngleLoc, Context.AllocateCopy(locArgs), RAngleLoc); } return E; } Expr *Parser::parseExprEditorPlaceholder(Token PlaceholderTok, Identifier PlaceholderId) { assert(PlaceholderTok.is(tok::identifier)); assert(PlaceholderId.isEditorPlaceholder()); auto parseTypeForPlaceholder = [&](TypeLoc &TyLoc, TypeRepr *&ExpansionTyR) { Optional DataOpt = swift::parseEditorPlaceholder(PlaceholderTok.getText()); if (!DataOpt) return; StringRef TypeStr = DataOpt->Type; if (TypeStr.empty()) return; // Ensure that we restore the parser state at exit. ParserPositionRAII PPR(*this); auto parseTypeString = [&](StringRef TyStr) -> TypeRepr* { unsigned Offset = TyStr.data() - PlaceholderTok.getText().data(); SourceLoc TypeStartLoc = PlaceholderTok.getLoc().getAdvancedLoc(Offset); SourceLoc TypeEndLoc = TypeStartLoc.getAdvancedLoc(TyStr.size()); Lexer::State StartState = L->getStateForBeginningOfTokenLoc(TypeStartLoc); Lexer::State EndState = L->getStateForBeginningOfTokenLoc(TypeEndLoc); // Create a lexer for the type sub-string. Lexer LocalLex(*L, StartState, EndState); // Temporarily swap out the parser's current lexer with our new one. llvm::SaveAndRestore T(L, &LocalLex); Tok.setKind(tok::unknown); // we might be at tok::eof now. consumeToken(); return parseType().getPtrOrNull(); }; TypeRepr *TyR = parseTypeString(TypeStr); TyLoc = TyR; if (DataOpt->TypeForExpansion == TypeStr) { ExpansionTyR = TyR; } else { ExpansionTyR = parseTypeString(DataOpt->TypeForExpansion); } }; TypeLoc TyLoc; TypeRepr *ExpansionTyR = nullptr; parseTypeForPlaceholder(TyLoc, ExpansionTyR); return new (Context) EditorPlaceholderExpr(PlaceholderId, PlaceholderTok.getLoc(), TyLoc, ExpansionTyR); } bool Parser:: parseClosureSignatureIfPresent(SmallVectorImpl &captureList, ParameterList *¶ms, SourceLoc &throwsLoc, SourceLoc &arrowLoc, TypeRepr *&explicitResultType, SourceLoc &inLoc){ // Clear out result parameters. params = nullptr; throwsLoc = SourceLoc(); arrowLoc = SourceLoc(); explicitResultType = nullptr; inLoc = SourceLoc(); // If we have a leading token that may be part of the closure signature, do a // speculative parse to validate it and look for 'in'. if (Tok.isAny(tok::l_paren, tok::l_square, tok::identifier, tok::kw__)) { BacktrackingScope backtrack(*this); // Skip by a closure capture list if present. if (consumeIf(tok::l_square)) { skipUntil(tok::r_square); if (!consumeIf(tok::r_square)) return false; } // Parse pattern-tuple func-signature-result? 'in'. if (consumeIf(tok::l_paren)) { // Consume the ')'. // While we don't have '->' or ')', eat balanced tokens. while (!Tok.is(tok::r_paren) && !Tok.is(tok::eof)) skipSingle(); // Consume the ')', if it's there. if (consumeIf(tok::r_paren)) { consumeIf(tok::kw_throws) || consumeIf(tok::kw_rethrows); // Parse the func-signature-result, if present. if (consumeIf(tok::arrow)) { if (!canParseType()) return false; } } // Okay, we have a closure signature. } else if (Tok.isIdentifierOrUnderscore()) { // Parse identifier (',' identifier)* consumeToken(); while (consumeIf(tok::comma)) { if (Tok.isIdentifierOrUnderscore()) { consumeToken(); continue; } return false; } consumeIf(tok::kw_throws) || consumeIf(tok::kw_rethrows); // Parse the func-signature-result, if present. if (consumeIf(tok::arrow)) { if (!canParseType()) return false; } } // Parse the 'in' at the end. if (Tok.isNot(tok::kw_in)) return false; // Okay, we have a closure signature. } else { // No closure signature. return false; } // At this point, we know we have a closure signature. Parse the capture list // and parameters. if (consumeIf(tok::l_square) && !consumeIf(tok::r_square)) { do { // Check for the strength specifier: "weak", "unowned", or // "unowned(safe/unsafe)". SourceLoc loc; Ownership ownershipKind = Ownership::Strong; if (Tok.isContextualKeyword("weak")){ loc = consumeToken(tok::identifier); ownershipKind = Ownership::Weak; } else if (Tok.isContextualKeyword("unowned")) { loc = consumeToken(tok::identifier); ownershipKind = Ownership::Unowned; // Skip over "safe" and "unsafe" if present. if (consumeIf(tok::l_paren)) { if (Tok.getText() == "safe") ownershipKind = Ownership::Unowned; // FIXME: No "safe" variant. else if (Tok.getText() == "unsafe") ownershipKind = Ownership::Unmanaged; else diagnose(Tok, diag::attr_unowned_invalid_specifier); consumeIf(tok::identifier); if (!consumeIf(tok::r_paren)) diagnose(Tok, diag::attr_unowned_expected_rparen); } } else if (Tok.is(tok::identifier) && peekToken().isAny(tok::equal, tok::comma, tok::r_square)) { // "x = 42", "x," and "x]" are all strong captures of x. loc = Tok.getLoc(); } else { diagnose(Tok, diag::expected_capture_specifier); skipUntil(tok::comma, tok::r_square); continue; } if (Tok.isNot(tok::identifier, tok::kw_self)) { diagnose(Tok, diag::expected_capture_specifier_name); skipUntil(tok::comma, tok::r_square); continue; } // The thing being capture specified is an identifier, or as an identifier // followed by an expression. Expr *initializer; Identifier name; SourceLoc nameLoc = Tok.getLoc(); if (peekToken().isNot(tok::equal)) { // If this is the simple case, then the identifier is both the name and // the expression to capture. name = Context.getIdentifier(Tok.getText()); initializer = parseExprIdentifier(); // It is a common error to try to capture a nested field instead of just // a local name, reject it with a specific error message. if (Tok.isAny(tok::period, tok::exclaim_postfix,tok::question_postfix)){ diagnose(Tok, diag::cannot_capture_fields); skipUntil(tok::comma, tok::r_square); continue; } } else { // Otherwise, the name is a new declaration. consumeIdentifier(&name); consumeToken(tok::equal); auto ExprResult = parseExpr(diag::expected_init_capture_specifier); if (ExprResult.isNull()) continue; initializer = ExprResult.get(); } // Create the VarDecl and the PatternBindingDecl for the captured // expression. This uses the parent declcontext (not the closure) since // the initializer expression is evaluated before the closure is formed. auto *VD = new (Context) VarDecl(/*isStatic*/false, /*isLet*/ownershipKind !=Ownership::Weak, nameLoc, name, Type(), CurDeclContext); // Attributes. if (ownershipKind != Ownership::Strong) VD->getAttrs().add(new (Context) OwnershipAttr(ownershipKind)); auto pattern = new (Context) NamedPattern(VD, /*implicit*/true); auto *PBD = PatternBindingDecl::create(Context, /*staticloc*/SourceLoc(), StaticSpellingKind::None, nameLoc, pattern, initializer, CurDeclContext); captureList.push_back(CaptureListEntry(VD, PBD)); } while (consumeIf(tok::comma)); // The capture list needs to be closed off with a ']'. if (!consumeIf(tok::r_square)) { diagnose(Tok, diag::expected_capture_list_end_rsquare); skipUntil(tok::r_square); if (Tok.is(tok::r_square)) consumeToken(tok::r_square); } } bool invalid = false; if (Tok.isNot(tok::kw_in)) { if (Tok.is(tok::l_paren)) { // Parse the closure arguments. auto pattern = parseSingleParameterClause(ParameterContextKind::Closure); if (pattern.isNonNull()) params = pattern.get(); else invalid = true; } else { // Parse identifier (',' identifier)* SmallVector elements; do { if (Tok.isNot(tok::identifier, tok::kw__)) { diagnose(Tok, diag::expected_closure_parameter_name); invalid = true; break; } Identifier name = Tok.is(tok::identifier) ? Context.getIdentifier(Tok.getText()) : Identifier(); auto var = new (Context) ParamDecl(/*IsLet*/ true, SourceLoc(), Identifier(), Tok.getLoc(), name, Type(), nullptr); elements.push_back(var); consumeToken(); // Consume a comma to continue. } while (consumeIf(tok::comma)); params = ParameterList::create(Context, elements); } if (Tok.is(tok::kw_throws)) { throwsLoc = consumeToken(); } else if (Tok.is(tok::kw_rethrows)) { throwsLoc = consumeToken(); diagnose(throwsLoc, diag::rethrowing_function_type); } // Parse the optional explicit return type. if (Tok.is(tok::arrow)) { // Consume the '->'. arrowLoc = consumeToken(); // Parse the type. explicitResultType = parseType(diag::expected_closure_result_type).getPtrOrNull(); if (!explicitResultType) { // If we couldn't parse the result type, clear out the arrow location. arrowLoc = SourceLoc(); invalid = true; } } } // Parse the 'in'. if (Tok.is(tok::kw_in)) { inLoc = consumeToken(); } else { // Scan forward to see if we can find the 'in'. This re-synchronizes the // parser so we can at least parse the body correctly. SourceLoc startLoc = Tok.getLoc(); ParserPosition pos = getParserPosition(); while (Tok.isNot(tok::eof) && !Tok.is(tok::kw_in) && Tok.isNot(tok::r_brace)) { skipSingle(); } if (Tok.is(tok::kw_in)) { // We found the 'in'. If this is the first error, complain about the // junk tokens in-between but re-sync at the 'in'. if (!invalid) { diagnose(startLoc, diag::unexpected_tokens_before_closure_in); } inLoc = consumeToken(); } else { // We didn't find an 'in', backtrack to where we started. If this is the // first error, complain about the missing 'in'. backtrackToPosition(pos); if (!invalid) { diagnose(Tok, diag::expected_closure_in) .fixItInsert(Tok.getLoc(), "in "); } inLoc = Tok.getLoc(); } } return invalid; } ParserResult Parser::parseExprClosure() { assert(Tok.is(tok::l_brace) && "Not at a left brace?"); // We may be parsing this closure expr in a matching pattern context. If so, // reset our state to not be in a pattern for any recursive pattern parses. llvm::SaveAndRestore T(InVarOrLetPattern, IVOLP_NotInVarOrLet); // Parse the opening left brace. SourceLoc leftBrace = consumeToken(); // Parse the closure-signature, if present. ParameterList *params = nullptr; SourceLoc throwsLoc; SourceLoc arrowLoc; TypeRepr *explicitResultType; SourceLoc inLoc; SmallVector captureList; parseClosureSignatureIfPresent(captureList, params, throwsLoc, arrowLoc, explicitResultType, inLoc); // If the closure was created in the context of an array type signature's // size expression, there will not be a local context. A parse error will // be reported at the signature's declaration site. if (!CurLocalContext) { skipUntil(tok::r_brace); if (Tok.is(tok::r_brace)) consumeToken(); return makeParserError(); } unsigned discriminator = CurLocalContext->claimNextClosureDiscriminator(); // Create the closure expression and enter its context. auto *closure = new (Context) ClosureExpr(params, throwsLoc, arrowLoc, inLoc, explicitResultType, discriminator, CurDeclContext); // The arguments to the func are defined in their own scope. Scope S(this, ScopeKind::ClosureParams); ParseFunctionBody cc(*this, closure); // Handle parameters. if (params) { // Add the parameters into scope. addParametersToScope(params); } else { // There are no parameters; allow anonymous closure variables. // FIXME: We could do this all the time, and then provide Fix-Its // to map $i -> the appropriately-named argument. This might help // users who are refactoring code by adding names. AnonClosureVars.push_back({ leftBrace, {}}); } // Add capture list variables to scope. for (auto c : captureList) addToScope(c.Var); // Parse the body. SmallVector bodyElements; ParserStatus Status; Status |= parseBraceItems(bodyElements, BraceItemListKind::Brace); // Parse the closing '}'. SourceLoc rightBrace; parseMatchingToken(tok::r_brace, rightBrace, diag::expected_closure_rbrace, leftBrace); // If we didn't have any parameters, create a parameter list from the // anonymous closure arguments. if (!params) { // Create a parameter pattern containing the anonymous variables. auto &anonVars = AnonClosureVars.back().second; SmallVector elements; for (auto anonVar : anonVars) elements.push_back(anonVar); params = ParameterList::create(Context, leftBrace, elements, leftBrace); // Pop out of the anonymous closure variables scope. AnonClosureVars.pop_back(); // Attach the parameters to the closure. closure->setParameterList(params); closure->setHasAnonymousClosureVars(); } // If the body consists of a single expression, turn it into a return // statement. // // But don't do this transformation during code completion, as the source // may be incomplete and the type mismatch in return statement will just // confuse the type checker. bool hasSingleExpressionBody = false; if (!Status.hasCodeCompletion() && bodyElements.size() == 1) { // If the closure's only body element is a single return statement, // use that instead of creating a new wrapping return expression. Expr *returnExpr = nullptr; if (bodyElements[0].is()) { if (auto returnStmt = dyn_cast(bodyElements[0].get())) { if (!returnStmt->hasResult()) { returnExpr = TupleExpr::createEmpty(Context, SourceLoc(), SourceLoc(), /*implicit*/true); returnStmt->setResult(returnExpr); } hasSingleExpressionBody = true; } } // Otherwise, create the wrapping return. if (bodyElements[0].is()) { hasSingleExpressionBody = true; returnExpr = bodyElements[0].get(); bodyElements[0] = new (Context) ReturnStmt(SourceLoc(), returnExpr); } } // Set the body of the closure. closure->setBody(BraceStmt::create(Context, leftBrace, bodyElements, rightBrace), hasSingleExpressionBody); // If the closure includes a capture list, create an AST node for it as well. Expr *result = closure; if (!captureList.empty()) result = new (Context) CaptureListExpr(Context.AllocateCopy(captureList), closure); return makeParserResult(Status, result); } /// expr-anon-closure-argument: /// dollarident Expr *Parser::parseExprAnonClosureArg() { StringRef Name = Tok.getText(); SourceLoc Loc = consumeToken(tok::dollarident); assert(Name[0] == '$' && "Not a dollarident"); // We know from the lexer that this is all-numeric. unsigned ArgNo = 0; if (Name.substr(1).getAsInteger(10, ArgNo)) { diagnose(Loc.getAdvancedLoc(1), diag::dollar_numeric_too_large); return new (Context) ErrorExpr(Loc); } // If this is a closure expression that did not have any named parameters, // generate the anonymous variables we need. auto closure = dyn_cast_or_null( dyn_cast(CurDeclContext)); if (!closure || closure->getParameters()) { // FIXME: specialize diagnostic when there were closure parameters. // We can be fairly smart here. diagnose(Loc, closure ? diag::anon_closure_arg_in_closure_with_args : diag::anon_closure_arg_not_in_closure); return new (Context) ErrorExpr(Loc); } auto leftBraceLoc = AnonClosureVars.back().first; auto &decls = AnonClosureVars.back().second; while (ArgNo >= decls.size()) { unsigned nextIdx = decls.size(); SmallVector StrBuf; StringRef varName = ("$" + Twine(nextIdx)).toStringRef(StrBuf); Identifier ident = Context.getIdentifier(varName); SourceLoc varLoc = leftBraceLoc; auto *var = new (Context) ParamDecl(/*IsLet*/ true, SourceLoc(), Identifier(), varLoc, ident, Type(), closure); decls.push_back(var); } return new (Context) DeclRefExpr(decls[ArgNo], DeclNameLoc(Loc), /*Implicit=*/false); } /// parseExprList - Parse a list of expressions. /// /// expr-paren: /// lparen-any ')' /// lparen-any binary-op ')' /// lparen-any expr-paren-element (',' expr-paren-element)* ')' /// /// expr-paren-element: /// (identifier ':')? expr /// ParserResult Parser::parseExprList(tok LeftTok, tok RightTok) { StructureMarkerRAII ParsingExprList(*this, Tok); SourceLoc LLoc = consumeToken(LeftTok); SourceLoc RLoc; SmallVector SubExprs; SmallVector SubExprNames; SmallVector SubExprNameLocs; ParserStatus Status = parseList(RightTok, LLoc, RLoc, tok::comma, /*OptionalSep=*/false, /*AllowSepAfterLast=*/false, RightTok == tok::r_paren ? diag::expected_rparen_expr_list : diag::expected_rsquare_expr_list, [&] () -> ParserStatus { Identifier FieldName; SourceLoc FieldNameLoc; // Check to see if there is an argument label. if (Tok.canBeArgumentLabel() && peekToken().is(tok::colon)) { // If this was an escaped identifier that need not have been escaped, // say so. if (Tok.isEscapedIdentifier()) diagnoseEscapedArgumentLabel(Tok); if (!Tok.is(tok::kw__)) FieldName = Context.getIdentifier(Tok.getText()); FieldNameLoc = consumeToken(); consumeToken(tok::colon); } // See if we have an operator decl ref '()'. The operator token in // this case lexes as a binary operator because it neither leads nor // follows a proper subexpression. ParserStatus Status; Expr *SubExpr = nullptr; if (Tok.isBinaryOperator() && peekToken().isAny(RightTok, tok::comma)) { SourceLoc Loc; Identifier OperName; if (parseAnyIdentifier(OperName, Loc, diag::expected_operator_ref)) { return makeParserError(); } // Bypass local lookup. Use an 'Ordinary' reference kind so that the // reference may resolve to any unary or binary operator based on // context. SubExpr = new(Context) UnresolvedDeclRefExpr(OperName, DeclRefKind::Ordinary, DeclNameLoc(Loc)); } else { ParserResult ParsedSubExpr = parseExpr(diag::expected_expr_in_expr_list); SubExpr = ParsedSubExpr.getPtrOrNull(); Status = ParsedSubExpr; } // If we got a subexpression, add it. if (SubExpr) { // Update names and locations. if (!SubExprNames.empty()) { SubExprNames.push_back(FieldName); SubExprNameLocs.push_back(FieldNameLoc); } else if (FieldName.get()) { SubExprNames.resize(SubExprs.size()); SubExprNames.push_back(FieldName); SubExprNameLocs.resize(SubExprs.size()); SubExprNameLocs.push_back(FieldNameLoc); } // Add the subexpression. SubExprs.push_back(SubExpr); } return Status; }); // A tuple with a single, unlabelled element is just parentheses. if (SubExprs.size() == 1 && (SubExprNames.empty() || SubExprNames[0].empty())) { return makeParserResult( Status, new (Context) ParenExpr(LLoc, SubExprs[0], RLoc, /*hasTrailingClosure=*/false)); } return makeParserResult( Status, TupleExpr::create(Context, LLoc, SubExprs, SubExprNames, SubExprNameLocs, RLoc, /*hasTrailingClosure=*/false, /*Implicit=*/false)); } /// \brief Parse an object literal expression. /// /// expr-literal: /// '[#' identifier expr-paren '#]' ParserResult Parser::parseExprObjectLiteral() { SourceLoc LLitLoc = consumeToken(tok::l_square_lit); Identifier Name; SourceLoc NameLoc; if (parseIdentifier(Name, NameLoc, diag::expected_identifier_after_l_square_lit)) { return makeParserError(); } // Parse a tuple of args if (!Tok.is(tok::l_paren)) { diagnose(Tok, diag::expected_arg_list_in_object_literal); return makeParserError(); } ParserResult Arg; Arg = parseExprList(tok::l_paren, tok::r_paren); if (Arg.hasCodeCompletion()) { return Arg; } if (Arg.isParseError()) { return makeParserError(); } if (!Tok.is(tok::r_square_lit)) { diagnose(Tok, diag::expected_r_square_lit_after_object_literal); return makeParserError(); } SourceLoc RLitLoc = consumeToken(tok::r_square_lit); return makeParserResult( new (Context) ObjectLiteralExpr(LLitLoc, Name, NameLoc, Arg.get(), RLitLoc, /*implicit=*/false)); } /// \brief Parse an expression call suffix. /// /// expr-call-suffix: /// expr-paren selector-arg* /// expr-closure selector-arg* (except in expr-basic) /// /// selector-arg: /// identifier expr-paren ParserResult Parser::parseExprCallSuffix(ParserResult fn, Identifier firstSelectorPiece, SourceLoc firstSelectorPieceLoc) { assert(Tok.isFollowingLParen() && "Not a call suffix?"); // Parse the first argument. // If there is a code completion token right after the '(', do a special case // callback. if (peekToken().is(tok::code_complete) && CodeCompletion) { consumeToken(tok::l_paren); auto CCE = new (Context) CodeCompletionExpr(Tok.getRange()); auto Result = makeParserResult(new (Context) CallExpr(fn.get(), new (Context) ParenExpr(SourceLoc(), CCE, SourceLoc(), /*hasTrailingClosure=*/false), /*Implicit=*/false)); CodeCompletion->completePostfixExprParen(fn.get(), CCE); // Eat the code completion token because we handled it. consumeToken(tok::code_complete); Result.setHasCodeCompletion(); return Result; } ParserResult firstArg = parseExprList(Tok.getKind(), tok::r_paren); // Form the call. auto Result = makeParserResult(new (Context) CallExpr(fn.get(), firstArg.get(), /*Implicit=*/false)); if (fn.isParseError() || firstArg.isParseError()) Result.setIsParseError(); if (fn.hasCodeCompletion() || firstArg.hasCodeCompletion()) { if (CodeCompletion) { CodeCompletion->completeCallArg(Result.get()); } Result.setHasCodeCompletion(); } return Result; } /// parseExprCollection - Parse a collection literal expression. /// /// expr-collection: /// expr-array /// expr-dictionary // lsquare-starting ']' ParserResult Parser::parseExprCollection() { Parser::StructureMarkerRAII ParsingCollection(*this, Tok); SourceLoc LSquareLoc = consumeToken(tok::l_square); // [] is always an array. if (Tok.is(tok::r_square)) { SourceLoc RSquareLoc = consumeToken(tok::r_square); return makeParserResult( ArrayExpr::create(Context, LSquareLoc, {}, {}, RSquareLoc)); } // [:] is always an empty dictionary. if (Tok.is(tok::colon) && peekToken().is(tok::r_square)) { consumeToken(tok::colon); SourceLoc RSquareLoc = consumeToken(tok::r_square); return makeParserResult( DictionaryExpr::create(Context, LSquareLoc, {}, RSquareLoc)); } // Parse the first expression. ParserResult FirstExpr = parseExpr(diag::expected_expr_in_collection_literal); if (FirstExpr.isNull() || FirstExpr.hasCodeCompletion()) { skipUntil(tok::r_square); if (Tok.is(tok::r_square)) consumeToken(); if (FirstExpr.hasCodeCompletion()) return makeParserCodeCompletionResult(); return nullptr; } // If we have a ':', this is a dictionary literal. if (Tok.is(tok::colon)) { return parseExprDictionary(LSquareLoc, FirstExpr.get()); } // Otherwise, we have an array literal. return parseExprArray(LSquareLoc, FirstExpr.get()); } /// parseExprArray - Parse an array literal expression. /// /// The lsquare-starting and first expression have already been /// parsed, and are passed in as parameters. /// /// expr-array: /// '[' expr (',' expr)* ','? ']' /// '[' ']' ParserResult Parser::parseExprArray(SourceLoc LSquareLoc, Expr *FirstExpr) { SmallVector SubExprs; SmallVector CommaLocs; SubExprs.push_back(FirstExpr); SourceLoc CommaLoc, RSquareLoc; ParserStatus Status; if (Tok.isNot(tok::r_square) && !consumeIf(tok::comma, CommaLoc)) { diagnose(Tok, diag::expected_separator, ",") .fixItInsertAfter(PreviousLoc, ","); Status.setIsParseError(); } CommaLocs.push_back(CommaLoc); Status |= parseList(tok::r_square, LSquareLoc, RSquareLoc, tok::comma, /*OptionalSep=*/false, /*AllowSepAfterLast=*/true, diag::expected_rsquare_array_expr, [&] () -> ParserStatus { ParserResult Element = parseExpr(diag::expected_expr_in_collection_literal); if (Element.isNonNull()) SubExprs.push_back(Element.get()); if (Tok.is(tok::comma)) CommaLocs.push_back(Tok.getLoc()); return Element; }); if (Status.hasCodeCompletion()) return makeParserCodeCompletionResult(); assert(SubExprs.size() >= 1); return makeParserResult(Status, ArrayExpr::create(Context, LSquareLoc, SubExprs, CommaLocs, RSquareLoc)); } /// parseExprDictionary - Parse a dictionary literal expression. /// /// The lsquare-starting and first key have already been parsed, and /// are passed in as parameters. /// /// expr-dictionary: /// '[' expr ':' expr (',' expr ':' expr)* ','? ']' /// '[' ':' ']' ParserResult Parser::parseExprDictionary(SourceLoc LSquareLoc, Expr *FirstKey) { assert(Tok.is(tok::colon)); // Each subexpression is a (key, value) tuple. // FIXME: We're not tracking the colon locations in the AST. SmallVector SubExprs; SourceLoc RSquareLoc; // Function that adds a new key/value pair. auto addKeyValuePair = [&](Expr *Key, Expr *Value) -> void { Expr *Exprs[] = {Key, Value}; SubExprs.push_back(TupleExpr::createImplicit(Context, Exprs, { })); }; bool FirstPair = true; ParserStatus Status = parseList(tok::r_square, LSquareLoc, RSquareLoc, tok::comma, /*OptionalSep=*/false, /*AllowSepAfterLast=*/true, diag::expected_rsquare_array_expr, [&]() -> ParserStatus { // Parse the next key. ParserResult Key; if (FirstPair) { Key = makeParserResult(FirstKey); FirstPair = false; } else { Key = parseExpr(diag::expected_key_in_dictionary_literal); if (Key.isNull() || Key.hasCodeCompletion()) return Key; } // Parse the ':'. if (Tok.isNot(tok::colon)) { diagnose(Tok, diag::expected_colon_in_dictionary_literal); return makeParserError(); } consumeToken(); // Parse the next value. ParserResult Value = parseExpr(diag::expected_value_in_dictionary_literal); if (Value.hasCodeCompletion()) return Value; if (Value.isNull()) Value = makeParserResult(Value, new (Context) ErrorExpr(PreviousLoc)); // Add this key/value pair. addKeyValuePair(Key.get(), Value.get()); return Value; }); if (Status.hasCodeCompletion()) return makeParserCodeCompletionResult(); assert(SubExprs.size() >= 1); return makeParserResult(DictionaryExpr::create(Context, LSquareLoc, SubExprs, RSquareLoc)); } void Parser::addPatternVariablesToScope(ArrayRef Patterns) { for (Pattern *Pat : Patterns) { Pat->forEachVariable([&](VarDecl *VD) { if (VD->hasName()) { // Add any variable declarations to the current scope. addToScope(VD); } }); } } void Parser::addParametersToScope(ParameterList *PL) { for (auto param : *PL) if (param->hasName()) addToScope(param); } /// Parse availability query specification. /// /// availability-spec: /// '*' /// version-constraint-spec ParserResult Parser::parseAvailabilitySpec() { if (Tok.isBinaryOperator() && Tok.getText() == "*") { SourceLoc StarLoc = Tok.getLoc(); consumeToken(); return makeParserResult(new (Context) OtherPlatformAvailabilitySpec(StarLoc)); } return parseVersionConstraintSpec(); } /// Parse version constraint specification. /// /// version-constraint-spec: /// identifier version-comparison version-tuple ParserResult Parser::parseVersionConstraintSpec() { Identifier PlatformIdentifier; SourceLoc PlatformLoc; if (Tok.is(tok::code_complete)) { consumeToken(); if (CodeCompletion) { CodeCompletion->completePoundAvailablePlatform(); } return makeParserCodeCompletionStatus(); } if (parseIdentifier(PlatformIdentifier, PlatformLoc, diag::avail_query_expected_platform_name)) { return nullptr; } if (Tok.isBinaryOperator() && Tok.getText() == ">=") { diagnose(Tok, diag::avail_query_version_comparison_not_needed) .fixItRemove(Tok.getLoc()); consumeToken(); } clang::VersionTuple Version; SourceRange VersionRange; if (parseVersionTuple(Version, VersionRange, diag::avail_query_expected_version_number)) { return nullptr; } Optional Platform = platformFromString(PlatformIdentifier.str()); if (!Platform.hasValue() || Platform.getValue() == PlatformKind::none) { diagnose(Tok, diag::avail_query_unrecognized_platform_name, PlatformIdentifier); return nullptr; } return makeParserResult(new (Context) VersionConstraintAvailabilitySpec( Platform.getValue(), PlatformLoc, Version, VersionRange)); }