| //===--- ParseType.cpp - Swift Language Parser for Types ------------------===// |
| // |
| // This source file is part of the Swift.org open source project |
| // |
| // Copyright (c) 2014 - 2017 Apple Inc. and the Swift project authors |
| // Licensed under Apache License v2.0 with Runtime Library Exception |
| // |
| // See https://swift.org/LICENSE.txt for license information |
| // See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors |
| // |
| //===----------------------------------------------------------------------===// |
| // |
| // Type Parsing and AST Building |
| // |
| //===----------------------------------------------------------------------===// |
| |
| #include "ParseList.h" |
| #include "swift/Parse/Parser.h" |
| #include "swift/AST/ASTWalker.h" |
| #include "swift/AST/Attr.h" |
| #include "swift/AST/TypeLoc.h" |
| #include "swift/AST/TypeRepr.h" |
| #include "swift/Parse/Lexer.h" |
| #include "swift/Parse/CodeCompletionCallbacks.h" |
| #include "swift/Parse/SyntaxParsingContext.h" |
| #include "swift/Parse/ParsedSyntaxBuilders.h" |
| #include "swift/Parse/ParsedSyntaxRecorder.h" |
| #include "llvm/ADT/APInt.h" |
| #include "llvm/ADT/SmallString.h" |
| #include "llvm/ADT/Twine.h" |
| #include "llvm/Support/Compiler.h" |
| #include "llvm/Support/SaveAndRestore.h" |
| |
| using namespace swift; |
| using namespace swift::syntax; |
| |
| TypeRepr *Parser::applyAttributeToType(TypeRepr *ty, |
| const TypeAttributes &attrs, |
| ParamDecl::Specifier specifier, |
| SourceLoc specifierLoc) { |
| // Apply those attributes that do apply. |
| if (!attrs.empty()) { |
| ty = new (Context) AttributedTypeRepr(attrs, ty); |
| } |
| |
| // Apply 'inout' or '__shared' or '__owned' |
| if (specifierLoc.isValid()) { |
| switch (specifier) { |
| case ParamDecl::Specifier::Owned: |
| ty = new (Context) OwnedTypeRepr(ty, specifierLoc); |
| break; |
| case ParamDecl::Specifier::InOut: |
| ty = new (Context) InOutTypeRepr(ty, specifierLoc); |
| break; |
| case ParamDecl::Specifier::Shared: |
| ty = new (Context) SharedTypeRepr(ty, specifierLoc); |
| break; |
| case ParamDecl::Specifier::Default: |
| break; |
| } |
| } |
| |
| return ty; |
| } |
| |
| /// Apply specifier and attributes to the parsed type. |
| ParsedSyntaxResult<ParsedTypeSyntax> |
| Parser::applyAttributeToTypeSyntax(ParsedSyntaxResult<ParsedTypeSyntax> &&ty, |
| Optional<ParsedTokenSyntax> specifier, |
| Optional<ParsedAttributeListSyntax> attrs) { |
| if (!attrs && !specifier) |
| return std::move(ty); |
| |
| if (ty.isNull()) { |
| SmallVector<ParsedSyntax, 2> junk; |
| if (specifier) |
| junk.emplace_back(std::move(*specifier)); |
| if (attrs) |
| junk.emplace_back(std::move(*attrs)); |
| return makeParsedResult( |
| ParsedSyntaxRecorder::makeUnknownType(junk, *SyntaxContext), |
| ty.getStatus()); |
| } |
| |
| return makeParsedResult( |
| ParsedSyntaxRecorder::makeAttributedType( |
| std::move(specifier), std::move(attrs), ty.get(), *SyntaxContext), |
| ty.getStatus()); |
| } |
| |
| /// Parse layout constraint for 'where' clause in '@_specialize' attribute |
| /// and in SIL. |
| /// |
| /// layout-constraint: |
| /// identifier |
| /// identifier '(' integer-literal ')' |
| /// identifier '(' integer-literal ',' integer-literal ')' |
| ParsedSyntaxResult<ParsedLayoutConstraintSyntax> |
| Parser::parseLayoutConstraintSyntax() { |
| assert(Tok.is(tok::identifier)); |
| ParsedLayoutConstraintSyntaxBuilder builder(*SyntaxContext); |
| |
| builder.useName(consumeTokenSyntax(tok::identifier)); |
| |
| if (!Tok.isFollowingLParen()) |
| return makeParsedResult(builder.build()); |
| |
| auto lParenLoc = Tok.getLoc(); |
| builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); |
| |
| auto parseTrivialConstraintBody = [&]() -> bool { |
| int value; |
| |
| if (!Tok.is(tok::integer_literal) || |
| Tok.getText().getAsInteger(10, value) || value < 0) { |
| diagnose(Tok, diag::layout_size_should_be_positive); |
| return true; |
| } |
| builder.useSize(consumeTokenSyntax(tok::integer_literal)); |
| |
| if (Tok.is(tok::comma)) { |
| builder.useComma(consumeTokenSyntax(tok::comma)); |
| |
| if (!Tok.is(tok::integer_literal) || |
| Tok.getText().getAsInteger(10, value) || value < 0) { |
| diagnose(Tok, diag::layout_alignment_should_be_positive); |
| return true; |
| } |
| builder.useAlignment(consumeTokenSyntax(tok::integer_literal)); |
| } |
| return false; |
| }; |
| |
| auto hasError = parseTrivialConstraintBody(); |
| if (hasError) |
| ignoreUntil(tok::r_paren); |
| auto rParen = parseMatchingTokenSyntax( |
| tok::r_paren, diag::expected_rparen_layout_constraint, lParenLoc, |
| /*silenceDiag=*/hasError); |
| if (!rParen.isNull()) |
| builder.useRightParen(rParen.get()); |
| |
| return makeParsedResult(builder.build()); |
| } |
| |
| /// parseTypeSimple |
| /// type-simple: |
| /// type-identifier |
| /// type-tuple |
| /// type-composition-deprecated |
| /// 'Any' |
| /// type-simple '.Type' |
| /// type-simple '.Protocol' |
| /// type-simple '?' |
| /// type-simple '!' |
| /// type-collection |
| /// type-array |
| ParsedSyntaxResult<ParsedTypeSyntax> |
| Parser::parseTypeSimple(Diag<> MessageID, bool HandleCodeCompletion) { |
| if (Tok.is(tok::kw_inout) || |
| (Tok.is(tok::identifier) && (Tok.getRawText().equals("__shared") || |
| Tok.getRawText().equals("__owned")))) { |
| // Type specifier should already be parsed before here. This only happens |
| // for construct like 'P1 & inout P2'. |
| diagnose(Tok.getLoc(), diag::attr_only_on_parameters, Tok.getRawText()); |
| ignoreToken(); |
| } |
| |
| auto TypeLoc = leadingTriviaLoc(); |
| |
| ParsedSyntaxResult<ParsedTypeSyntax> Result; |
| switch (Tok.getKind()) { |
| case tok::kw_Self: |
| case tok::kw_Any: |
| case tok::identifier: |
| Result = parseTypeIdentifier(); |
| break; |
| case tok::l_paren: |
| Result = parseTypeTupleBody(); |
| break; |
| case tok::code_complete: { |
| if (!HandleCodeCompletion) |
| break; |
| ParsedTypeSyntax ty = ParsedSyntaxRecorder::makeCodeCompletionType( |
| None, None, consumeTokenSyntax(), *SyntaxContext); |
| return makeParsedCodeCompletion(std::move(ty)); |
| } |
| case tok::l_square: |
| Result = parseTypeCollection(); |
| break; |
| case tok::kw_protocol: |
| if (startsWithLess(peekToken())) { |
| Result = parseOldStyleProtocolComposition(); |
| break; |
| } |
| LLVM_FALLTHROUGH; |
| default: |
| { |
| auto diag = diagnose(Tok, MessageID); |
| // If the next token is closing or separating, the type was likely forgotten |
| if (Tok.isAny(tok::r_paren, tok::r_brace, tok::r_square, tok::arrow, |
| tok::equal, tok::comma, tok::semi)) |
| diag.fixItInsert(getEndOfPreviousLoc(), " <#type#>"); |
| } |
| |
| if (Tok.isKeyword() && !Tok.isAtStartOfLine()) { |
| auto token = consumeTokenSyntax(); |
| ParsedTypeSyntax ty = ParsedSyntaxRecorder::makeUnknownType( |
| {&token, 1}, *SyntaxContext); |
| // Return success result because we recovered. |
| return makeParsedResult(std::move(ty)); |
| } |
| |
| checkForInputIncomplete(); |
| return makeParsedError<ParsedTypeSyntax>(); |
| } |
| |
| // '.Type', '.Protocol', '?', '!', and '[]' still leave us with type-simple. |
| while (Result.isSuccess()) { |
| if ((Tok.is(tok::period) || Tok.is(tok::period_prefix)) && |
| (peekToken().isContextualKeyword("Type") || |
| peekToken().isContextualKeyword("Protocol"))) { |
| Result = parseMetatypeType(Result.get()); |
| continue; |
| } |
| |
| if (!Tok.isAtStartOfLine()) { |
| if (isOptionalToken(Tok)) { |
| Result = parseOptionalType(Result.get()); |
| continue; |
| } |
| if (isImplicitlyUnwrappedOptionalToken(Tok)) { |
| Result = parseImplicitlyUnwrappedOptionalType(Result.get()); |
| continue; |
| } |
| // Parse legacy array types for migration. |
| if (Tok.is(tok::l_square)) { |
| Result = parseTypeArray(Result.get(), TypeLoc); |
| continue; |
| } |
| } |
| break; |
| } |
| |
| return Result; |
| } |
| |
| ParsedSyntaxResult<ParsedTypeSyntax> Parser::parseSILBoxTypeSyntax( |
| Optional<ParsedGenericParameterClauseListSyntax> generics) { |
| ParsedSILBoxTypeSyntaxBuilder builder(*SyntaxContext); |
| ParserStatus status; |
| |
| if (generics) |
| builder.useGenericParameterClauses(std::move(*generics)); |
| |
| // Parse '{'. |
| builder.useLeftBrace(consumeTokenSyntax(tok::l_brace)); |
| |
| // Parse comma separated field list. |
| if (!Tok.is(tok::r_brace)) { |
| bool hasNext = true; |
| do { |
| ParsedSILBoxTypeFieldSyntaxBuilder fieldBuilder(*SyntaxContext); |
| |
| // Parse 'let' or 'var'. |
| if (!Tok.isAny(tok::kw_var, tok::kw_let)) { |
| diagnose(Tok, diag::sil_box_expected_var_or_let); |
| break; |
| } |
| fieldBuilder.useSpecifier(consumeTokenSyntax()); |
| |
| // Parse the type. |
| auto ty = parseTypeSyntax(); |
| status |= ty.getStatus(); |
| if (!ty.isNull()) |
| fieldBuilder.useType(ty.get()); |
| else |
| fieldBuilder.useType( |
| ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); |
| |
| // Parse ','. |
| hasNext = (status.isSuccess() && Tok.is(tok::comma)); |
| if (hasNext) |
| fieldBuilder.useTrailingComma(consumeTokenSyntax()); |
| |
| builder.addFieldsMember(fieldBuilder.build()); |
| } while (hasNext); |
| } |
| |
| // Parse '}'. |
| if (!Tok.is(tok::r_brace)) { |
| diagnose(Tok, diag::sil_box_expected_r_brace); |
| return makeParsedError(builder.build()); |
| } |
| builder.useRightBrace(consumeTokenSyntax(tok::r_brace)); |
| |
| // Parse the generic argument. |
| if (startsWithLess(Tok)) { |
| auto genericArgs = parseGenericArgumentClauseSyntax(); |
| status |= genericArgs.getStatus(); |
| if (!genericArgs.isNull()) |
| builder.useGenericArgumentClause(genericArgs.get()); |
| } |
| |
| return makeParsedResult(builder.build()); |
| } |
| |
| /// parseType |
| /// type: |
| /// attribute-list type-composition |
| /// attribute-list type-function |
| /// attribute-list sil-generic-function-type |
| /// sil-box-type |
| /// |
| /// type-function: |
| /// '(' tuple-type-element-list ')' 'throws'? '->' type |
| /// |
| /// sil-generic-function-type: |
| /// generic-parameter-clause-list type-function |
| ParsedSyntaxResult<ParsedTypeSyntax> |
| Parser::parseTypeSyntax(Diag<> MessageID, bool HandleCodeCompletion, |
| bool IsSILFuncDecl) { |
| ParserStatus status; |
| |
| // Parse attributes. |
| Optional<ParsedTokenSyntax> specifier; |
| Optional<ParsedAttributeListSyntax> attrs; |
| if (Tok.isAny(tok::at_sign, tok::kw_inout) || |
| (Tok.is(tok::identifier) && (Tok.getRawText().equals("__shared") || |
| Tok.getRawText().equals("__owned")))) |
| status |= parseTypeAttributeListSyntax(specifier, attrs); |
| |
| // Parse generic parameters in SIL mode. |
| Optional<ParsedGenericParameterClauseListSyntax> genericParams; |
| SourceLoc genericParamsLoc = Tok.getLoc(); |
| if (isInSILMode()) |
| (void)parseSILGenericParamsSyntax(genericParams); |
| |
| // In SIL mode, parse box types { ... }. |
| if (isInSILMode() && Tok.is(tok::l_brace)) { |
| auto ty = parseSILBoxTypeSyntax(std::move(genericParams)); |
| return applyAttributeToTypeSyntax(std::move(ty), std::move(specifier), |
| std::move(attrs)); |
| } |
| |
| auto startLoc = Tok.getLoc(); |
| // Parse the type. |
| auto ty = parseTypeSimpleOrComposition(MessageID, HandleCodeCompletion); |
| status |= ty.getStatus(); |
| auto endLoc = PreviousLoc; |
| |
| // Don't consume 'throws', if the next token is not '->', so we can emit a |
| // more useful diagnostic when parsing a function decl. |
| bool canBeFunctionTy = |
| Tok.is(tok::arrow) || |
| (Tok.isAny(tok::kw_throws, tok::kw_rethrows, tok::kw_throw) && |
| peekToken().is(tok::arrow)); |
| |
| // If the first type has an error, or this is not a function type, return the |
| // first result. |
| if (ty.isNull() || !canBeFunctionTy) { |
| // Diagnose generic parameter for non-function types. |
| if (!genericParams && !specifier && !attrs) |
| return ty; |
| |
| if (genericParams) { |
| SmallVector<ParsedSyntax, 2> junk; |
| diagnose(genericParamsLoc, diag::generic_non_function); |
| junk.push_back(std::move(*genericParams)); |
| if (!ty.isNull()) |
| junk.emplace_back(ty.get()); |
| ty = makeParsedResult( |
| ParsedSyntaxRecorder::makeUnknownType(junk, *SyntaxContext), |
| status); |
| } |
| |
| return applyAttributeToTypeSyntax(std::move(ty), std::move(specifier), |
| std::move(attrs)); |
| } |
| |
| // Parse a throws specifier. |
| Optional<ParsedTokenSyntax> throws; |
| if (Tok.isAny(tok::kw_throws, tok::kw_rethrows, tok::kw_throw) && |
| peekToken().is(tok::arrow)) { |
| if (Tok.isNot(tok::kw_throws)) { |
| // 'rethrows' is only allowed on function declarations for now. |
| // 'throw' is probably a typo for 'throws'. |
| Diag<> DiagID = Tok.is(tok::kw_rethrows) ? diag::rethrowing_function_type |
| : diag::throw_in_function_type; |
| diagnose(Tok.getLoc(), DiagID).fixItReplace(Tok.getLoc(), "throws"); |
| ignoreToken(); |
| } else { |
| throws = consumeTokenSyntax(); |
| } |
| } |
| |
| auto arrowLoc = Tok.getLoc(); |
| auto arrow = consumeTokenSyntax(tok::arrow); |
| if (Tok.is(tok::kw_throws)) { |
| Diag<> DiagID = diag::throws_in_wrong_position; |
| diagnose(Tok.getLoc(), DiagID) |
| .fixItInsert(arrowLoc, "throws ") |
| .fixItRemove(Tok.getLoc()); |
| ignoreToken(); |
| } |
| |
| auto input = ty.get(); |
| auto result = parseTypeSyntax(diag::expected_type_function_result); |
| status |= result.getStatus(); |
| |
| ParsedFunctionTypeSyntaxBuilder builder(*SyntaxContext); |
| if (auto tuple = input.getAs<ParsedTupleTypeSyntax>()) { |
| assert(tuple->getRaw().isDeferredLayout()); |
| builder.useLeftParen(tuple->getDeferredLeftParen()); |
| builder.useArguments(tuple->getDeferredElements()); |
| builder.useRightParen(tuple->getDeferredRightParen()); |
| } else { |
| builder.addArgumentsMember(ParsedSyntaxRecorder::makeTupleTypeElement( |
| std::move(input), /*TrailingComma=*/None, *SyntaxContext)); |
| |
| // Diagnose only if the result type is successfully parsed, to reduce the |
| // noisy diagnostics. |
| if (result.isSuccess()) { |
| auto charRange = Lexer::getCharSourceRangeFromSourceRange( |
| SourceMgr, {startLoc, endLoc}); |
| auto diag = diagnose(startLoc, diag::function_type_no_parens); |
| if (SourceMgr.extractText(charRange) == "Void") { |
| diag.fixItReplace(startLoc, "()"); |
| } else { |
| diag.highlight(SourceRange(startLoc, endLoc)); |
| diag.fixItInsert(startLoc, "("); |
| diag.fixItInsertAfter(endLoc, ")"); |
| } |
| } |
| } |
| |
| if (throws) |
| builder.useThrowsOrRethrowsKeyword(std::move(*throws)); |
| builder.useArrow(std::move(arrow)); |
| if (!result.isNull()) |
| builder.useReturnType(result.get()); |
| else |
| builder.useReturnType( |
| ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext)); |
| |
| ParsedFunctionTypeSyntax funcTy = builder.build(); |
| |
| // Apply generic parameters if exists in SIL mode. |
| if (genericParams) { |
| auto silTy = ParsedSyntaxRecorder::makeSILFunctionType( |
| std::move(*genericParams), std::move(funcTy), *SyntaxContext); |
| return applyAttributeToTypeSyntax( |
| makeParsedResult(std::move(silTy), status), std::move(specifier), |
| std::move(attrs)); |
| } |
| |
| return applyAttributeToTypeSyntax(makeParsedResult(std::move(funcTy), status), |
| std::move(specifier), std::move(attrs)); |
| } |
| |
| ParsedSyntaxResult<ParsedTypeSyntax> Parser::parseTypeSyntax() { |
| return parseTypeSyntax(diag::expected_type); |
| } |
| |
| ParserResult<TypeRepr> Parser::parseType(Diag<> MessageID, |
| bool HandleCodeCompletion, |
| bool IsSILFuncDecl) { |
| auto leadingLoc = leadingTriviaLoc(); |
| auto result = parseTypeSyntax(MessageID, HandleCodeCompletion, IsSILFuncDecl); |
| auto status = result.getStatus(); |
| if (result.isNull()) |
| return status; |
| |
| SyntaxContext->addSyntax(result.get()); |
| auto syntax = SyntaxContext->topNode<TypeSyntax>(); |
| auto tyR = Generator.generate(syntax, leadingLoc, IsSILFuncDecl); |
| if (!tyR) |
| status.setIsParseError(); |
| return makeParserResult(status, tyR); |
| } |
| |
| ParserResult<TypeRepr> Parser::parseType() { |
| return parseType(diag::expected_type); |
| } |
| |
| |
| ParserResult<TypeRepr> Parser::parseDeclResultType(Diag<> MessageID) { |
| if (Tok.is(tok::code_complete)) { |
| if (CodeCompletion) |
| CodeCompletion->completeTypeDeclResultBeginning(); |
| consumeToken(tok::code_complete); |
| return makeParserCodeCompletionStatus(); |
| } |
| |
| auto result = parseType(MessageID); |
| |
| if (!result.isParseError() && Tok.is(tok::r_square)) { |
| auto diag = diagnose(Tok, diag::extra_rbracket); |
| diag.fixItInsert(result.get()->getStartLoc(), getTokenText(tok::l_square)); |
| consumeToken(); |
| return makeParserErrorResult(new (Context) |
| ErrorTypeRepr(getTypeErrorLoc())); |
| } else if (!result.isParseError() && Tok.is(tok::colon)) { |
| auto colonTok = consumeToken(); |
| auto secondType = parseType(diag::expected_dictionary_value_type); |
| |
| auto diag = diagnose(colonTok, diag::extra_colon); |
| diag.fixItInsert(result.get()->getStartLoc(), getTokenText(tok::l_square)); |
| if (!secondType.isParseError()) { |
| if (Tok.is(tok::r_square)) { |
| consumeToken(); |
| } else { |
| diag.fixItInsertAfter(secondType.get()->getEndLoc(), getTokenText(tok::r_square)); |
| } |
| } |
| return makeParserErrorResult(new (Context) |
| ErrorTypeRepr(getTypeErrorLoc())); |
| } |
| return result; |
| } |
| |
| SourceLoc Parser::getTypeErrorLoc() const { |
| // Use the same location as a missing close brace, etc. |
| return getErrorOrMissingLoc(); |
| } |
| |
| ParsedSyntaxResult<ParsedGenericArgumentClauseSyntax> |
| Parser::parseGenericArgumentClauseSyntax() { |
| assert(startsWithLess(Tok) && "Generic parameter list must start with '<'"); |
| auto LAngleLoc = Tok.getLoc(); |
| ParsedGenericArgumentClauseSyntaxBuilder builder(*SyntaxContext); |
| ParserStatus status; |
| |
| // Parse '<'. |
| builder.useLeftAngleBracket(consumeStartingLessSyntax()); |
| |
| bool hasNext = true; |
| do { |
| // Parse argument type. |
| auto ty = parseTypeSyntax(diag::expected_type); |
| status |= ty.getStatus(); |
| if (ty.isNull()) |
| break; |
| ParsedGenericArgumentSyntaxBuilder argBuilder(*SyntaxContext); |
| argBuilder.useArgumentType(ty.get()); |
| |
| // Parse trailing comma: ','. |
| if (Tok.is(tok::comma)) { |
| argBuilder.useTrailingComma(consumeTokenSyntax()); |
| } else { |
| hasNext = false; |
| } |
| builder.addArgumentsMember(argBuilder.build()); |
| } while (hasNext); |
| |
| // Parse '>'. |
| if (startsWithGreater(Tok)) { |
| builder.useRightAngleBracket(consumeStartingGreaterSyntax()); |
| } else { |
| if (status.isSuccess()) { |
| diagnose(Tok, diag::expected_rangle_generic_arg_list); |
| diagnose(LAngleLoc, diag::opening_angle); |
| } |
| checkForInputIncomplete(); |
| status.setIsParseError(); |
| if (ignoreUntilGreaterInTypeList()) |
| builder.useRightAngleBracket(consumeStartingGreaterSyntax()); |
| } |
| |
| return makeParsedResult(builder.build(), status); |
| } |
| |
| ParserStatus Parser::parseGenericArguments(SmallVectorImpl<TypeRepr *> &ArgsAST, |
| SourceLoc &LAngleLoc, |
| SourceLoc &RAngleLoc) { |
| auto leadingLoc = leadingTriviaLoc(); |
| auto ParsedClauseResult = parseGenericArgumentClauseSyntax(); |
| if (ParsedClauseResult.isNull()) |
| return ParsedClauseResult.getStatus(); |
| |
| SyntaxContext->addSyntax(ParsedClauseResult.get()); |
| if (ParsedClauseResult.isError()) |
| return ParsedClauseResult.getStatus(); |
| |
| auto Clause = SyntaxContext->topNode<GenericArgumentClauseSyntax>(); |
| Generator.generate(Clause, leadingLoc, LAngleLoc, RAngleLoc, ArgsAST); |
| return makeParserSuccess(); |
| } |
| |
| /// SWIFT_ENABLE_TENSORFLOW |
| bool Parser::canParseTypeQualifierForDeclName() { |
| BacktrackingScope backtrack(*this); |
| |
| // First, parse a single type identifier component. |
| if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) |
| return false; |
| consumeToken(); |
| |
| if (startsWithLess(Tok)) { |
| if (!canParseGenericArguments()) |
| return false; |
| } |
| |
| // If the next token is a period or starts with a period, then this can be |
| // parsed as a type qualifier. |
| return startsWithSymbol(Tok, '.'); |
| } |
| |
| /// parseTypeIdentifier |
| /// |
| /// type-identifier: |
| /// identifier generic-args? ('.' identifier generic-args?)* |
| /// |
| // SWIFT_ENABLE_TENSORFLOW: Added `isParsingQualifiedDeclName` flag. |
| ParsedSyntaxResult<ParsedTypeSyntax> Parser::parseTypeIdentifier(bool isParsingQualifiedDeclName) { |
| if (isParsingQualifiedDeclName && !canParseTypeQualifierForDeclName()) |
| return makeParsedError<ParsedTypeSyntax>(); |
| |
| // SWIFT_ENABLE_TENSORFLOW: Condition body intentionally not indented, to |
| // reduce merge conflicts. |
| if (!isParsingQualifiedDeclName || Tok.isNotAnyOperator()) { |
| if (Tok.isNot(tok::identifier) && Tok.isNot(tok::kw_Self)) { |
| // is this the 'Any' type |
| if (Tok.is(tok::kw_Any)) |
| return parseAnyType(); |
| |
| if (Tok.is(tok::code_complete)) { |
| auto CCTok = consumeTokenSyntax(tok::code_complete); |
| auto ty = ParsedSyntaxRecorder::makeCodeCompletionType( |
| None, None, std::move(CCTok), *SyntaxContext); |
| return makeParsedCodeCompletion(std::move(ty)); |
| } |
| |
| diagnose(Tok, diag::expected_identifier_for_type); |
| |
| // If there is a keyword at the start of a new line, we won't want to |
| // skip it as a recovery but rather keep it. |
| if (Tok.isKeyword() && !Tok.isAtStartOfLine()) { |
| auto kwTok = consumeTokenSyntax(); |
| ParsedTypeSyntax ty = |
| ParsedSyntaxRecorder::makeUnknownType({&kwTok, 1}, *SyntaxContext); |
| return makeParsedError(std::move(ty)); |
| } |
| |
| return makeParsedError<ParsedTypeSyntax>(); |
| } |
| } |
| |
| SmallVector<ParsedSyntax, 0> Junk; |
| |
| auto parseComponent = |
| [&](Optional<ParsedTokenSyntax> &Identifier, |
| Optional<ParsedGenericArgumentClauseSyntax> &GenericArgs) { |
| if (Tok.is(tok::kw_Self)) { |
| Identifier = consumeIdentifierSyntax(); |
| } else { |
| // FIXME: specialize diagnostic for 'Type': type cannot start with |
| // 'metatype' |
| // FIXME: offer a fixit: 'self' -> 'Self' |
| Identifier = |
| parseIdentifierSyntax(diag::expected_identifier_in_dotted_type); |
| } |
| |
| if (!Identifier) |
| return makeParserError(); |
| |
| if (!startsWithLess(Tok)) |
| return makeParserSuccess(); |
| |
| SmallVector<TypeRepr *, 4> GenericArgsAST; |
| SourceLoc LAngleLoc, RAngleLoc; |
| auto GenericArgsResult = parseGenericArgumentClauseSyntax(); |
| if (!GenericArgsResult.isNull()) |
| GenericArgs = GenericArgsResult.get(); |
| return GenericArgsResult.getStatus(); |
| }; |
| |
| ParsedSyntaxResult<ParsedTypeSyntax> result; |
| |
| // Parse the base identifier. |
| result = [&]() { |
| Optional<ParsedTokenSyntax> identifier; |
| Optional<ParsedGenericArgumentClauseSyntax> genericArgs; |
| auto status = parseComponent(identifier, genericArgs); |
| assert(identifier); |
| return makeParsedResult( |
| ParsedSyntaxRecorder::makeSimpleTypeIdentifier( |
| std::move(*identifier), std::move(genericArgs), *SyntaxContext), |
| status); |
| }(); |
| |
| // Parse member identifiers. |
| while (result.isSuccess() && Tok.isAny(tok::period, tok::period_prefix)) { |
| if (peekToken().isContextualKeyword("Type") || |
| peekToken().isContextualKeyword("Protocol")) |
| break; |
| |
| if (isParsingQualifiedDeclName) { |
| // If we're parsing a qualified decl name, break out before parsing the |
| // last period. |
| |
| BacktrackingScope backtrack(*this); |
| |
| if (Tok.is(tok::period) || Tok.is(tok::period_prefix)) |
| consumeToken(); |
| else if (startsWithSymbol(Tok, '.')) |
| consumeStartingCharacterOfCurrentToken(tok::period); |
| |
| if (!canParseTypeQualifierForDeclName()) |
| break; |
| } |
| |
| // Parse '.'. |
| auto period = consumeTokenSyntax(); |
| |
| if (isParsingQualifiedDeclName && Tok.isAnyOperator()) { |
| // If an operator is encountered, break and do not backtrack later. |
| break; |
| } |
| |
| // Parse component; |
| Optional<ParsedTokenSyntax> identifier; |
| Optional<ParsedGenericArgumentClauseSyntax> genericArgs; |
| auto status = parseComponent(identifier, genericArgs); |
| if (identifier) { |
| ParsedMemberTypeIdentifierSyntaxBuilder builder(*SyntaxContext); |
| builder.useBaseType(result.get()); |
| builder.usePeriod(std::move(period)); |
| builder.useName(std::move(*identifier)); |
| if (genericArgs) |
| builder.useGenericArgumentClause(std::move(*genericArgs)); |
| result = makeParsedResult(builder.build(), status); |
| continue; |
| } |
| |
| assert(!genericArgs); |
| |
| if (Tok.is(tok::code_complete)) { |
| auto ty = ParsedSyntaxRecorder::makeCodeCompletionType( |
| result.get(), std::move(period), consumeTokenSyntax(), |
| *SyntaxContext); |
| return makeParsedCodeCompletion(std::move(ty)); |
| } |
| |
| ParsedSyntax parts[] = {result.get(), std::move(period)}; |
| return makeParsedResult( |
| ParsedSyntaxRecorder::makeUnknownType({parts, 2}, *SyntaxContext), |
| status); |
| } |
| |
| if (result.isSuccess() && Tok.is(tok::code_complete) && |
| !Tok.isAtStartOfLine()) { |
| auto ty = ParsedSyntaxRecorder::makeCodeCompletionType( |
| result.get(), None, consumeTokenSyntax(), *SyntaxContext); |
| return makeParsedCodeCompletion(std::move(ty)); |
| } |
| |
| // Don't propagate malformed type as valid type. |
| if (!result.isSuccess()) { |
| auto ty = result.get(); |
| return makeParsedResult( |
| ParsedSyntaxRecorder::makeUnknownType({&ty, 1}, *SyntaxContext), |
| result.getStatus()); |
| } |
| |
| return result; |
| } |
| |
| /// parseTypeSimpleOrComposition |
| /// |
| /// type-composition: |
| /// 'some'? type-simple |
| /// type-composition '&' type-simple |
| ParsedSyntaxResult<ParsedTypeSyntax> |
| Parser::parseTypeSimpleOrComposition(Diag<> MessageID, |
| bool HandleCodeCompletion) { |
| // Check for the opaque modifier. |
| // This is only semantically allowed in certain contexts, but we parse it |
| // generally for diagnostics and recovery. |
| Optional<ParsedTokenSyntax> FirstSome; |
| if (Tok.is(tok::identifier) && Tok.getRawText() == "some") { |
| // Treat some as a keyword. |
| TokReceiver->registerTokenKindChange(Tok.getLoc(), tok::contextual_keyword); |
| FirstSome = consumeTokenSyntax(); |
| } |
| |
| auto ApplySome = [this](ParsedTypeSyntax Type, |
| Optional<ParsedTokenSyntax> Some) { |
| return Some ? ParsedSyntaxRecorder::makeSomeType( |
| std::move(*Some), std::move(Type), *SyntaxContext) |
| : std::move(Type); |
| }; |
| |
| // Parse the first type |
| auto FirstTypeResult = parseTypeSimple(MessageID, HandleCodeCompletion); |
| |
| if (FirstTypeResult.isError()) |
| return FirstTypeResult; |
| |
| auto FirstType = FirstTypeResult.get(); |
| |
| if (!Tok.isContextualPunctuator("&")) |
| return makeParsedResult( |
| ApplySome(std::move(FirstType), std::move(FirstSome))); |
| |
| SmallVector<ParsedCompositionTypeElementSyntax, 4> Elements; |
| |
| Optional<ParsedTokenSyntax> Ampersand = consumeTokenSyntax(); |
| auto FirstElement = ParsedSyntaxRecorder::makeCompositionTypeElement( |
| std::move(FirstType), std::move(*Ampersand), *SyntaxContext); |
| Elements.push_back(std::move(FirstElement)); |
| |
| ParserStatus Status; |
| |
| do { |
| // Diagnose invalid `some` after an ampersand. |
| Optional<ParsedTokenSyntax> NextSome; |
| if (Tok.is(tok::identifier) && Tok.getRawText() == "some") { |
| auto NextSomeLoc = Tok.getLoc(); |
| NextSome = consumeTokenSyntax(); |
| // TODO: Fixit to move to beginning of composition. |
| diagnose(NextSomeLoc, diag::opaque_mid_composition); |
| } |
| |
| auto NextTypeResult = parseTypeSimple(diag::expected_identifier_for_type, |
| HandleCodeCompletion); |
| |
| if (!NextTypeResult.isSuccess()) { |
| Status |= NextTypeResult.getStatus(); |
| if (NextTypeResult.isNull()) |
| break; |
| |
| SmallVector<ParsedSyntax, 0> nodes; |
| if (FirstSome) |
| nodes.push_back(std::move(*FirstSome)); |
| std::move(Elements.begin(), Elements.end(), std::back_inserter(nodes)); |
| if (NextSome) |
| nodes.push_back(std::move(*NextSome)); |
| nodes.push_back(NextTypeResult.get()); |
| |
| auto ty = ParsedSyntaxRecorder::makeUnknownType(nodes, *SyntaxContext); |
| return makeParsedResult(std::move(ty), Status); |
| } |
| |
| auto NextType = ApplySome(NextTypeResult.get(), std::move(NextSome)); |
| Ampersand = Tok.isContextualPunctuator("&") |
| ? consumeTokenSyntax() |
| : llvm::Optional<ParsedTokenSyntax>(); |
| auto NextElement = ParsedSyntaxRecorder::makeCompositionTypeElement( |
| std::move(NextType), std::move(Ampersand), *SyntaxContext); |
| Elements.push_back(std::move(NextElement)); |
| } while (Ampersand); |
| |
| auto ElementList = ParsedSyntaxRecorder::makeCompositionTypeElementList( |
| Elements, *SyntaxContext); |
| auto Composition = ParsedSyntaxRecorder::makeCompositionType( |
| std::move(ElementList), *SyntaxContext); |
| return makeParsedResult( |
| ApplySome(std::move(Composition), std::move(FirstSome)), Status); |
| } |
| |
| ParserResult<TypeRepr> Parser::parseAnyTypeAST() { |
| auto AnyLoc = leadingTriviaLoc(); |
| auto ParsedAny = parseAnyType().get(); |
| SyntaxContext->addSyntax(std::move(ParsedAny)); |
| auto Any = SyntaxContext->topNode<SimpleTypeIdentifierSyntax>(); |
| return makeParserResult(Generator.generate(Any, AnyLoc)); |
| } |
| |
| ParsedSyntaxResult<ParsedTypeSyntax> Parser::parseAnyType() { |
| auto Any = consumeTokenSyntax(tok::kw_Any); |
| auto Type = ParsedSyntaxRecorder::makeSimpleTypeIdentifier( |
| std::move(Any), llvm::None, *SyntaxContext); |
| return makeParsedResult(std::move(Type)); |
| } |
| |
| /// parseOldStyleProtocolComposition |
| /// type-composition-deprecated: |
| /// 'protocol' '<' '>' |
| /// 'protocol' '<' type-composition-list-deprecated '>' |
| /// |
| /// type-composition-list-deprecated: |
| /// type-identifier |
| /// type-composition-list-deprecated ',' type-identifier |
| ParsedSyntaxResult<ParsedTypeSyntax> |
| Parser::parseOldStyleProtocolComposition() { |
| // Defer all nodes so that we can de-structure the composed types in case we |
| // need to emit a diagnostic (below). |
| DeferringContextRAII Deferring(*SyntaxContext); |
| |
| SmallVector<ParsedSyntax, 0> Junk; |
| |
| auto ProtocolLoc = Tok.getLoc(); |
| auto Protocol = consumeTokenSyntax(); |
| auto LAngleLoc = Tok.getLoc(); |
| auto LAngle = consumeStartingLessSyntax(); |
| |
| Junk.push_back(Protocol.copyDeferred()); |
| Junk.push_back(LAngle.copyDeferred()); |
| |
| // Parse the type-composition-list. |
| ParserStatus Status; |
| SmallVector<ParsedTypeSyntax, 4> Protocols; |
| Optional<ParsedTokenSyntax> Comma; |
| bool IsEmpty = startsWithGreater(Tok); |
| if (!IsEmpty) { |
| do { |
| bool IsAny = Tok.getKind() == tok::kw_Any; |
| auto TypeResult = parseTypeIdentifier(); |
| Status |= TypeResult.getStatus(); |
| if (!TypeResult.isNull()) { |
| auto Type = TypeResult.get(); |
| Junk.push_back(Type.copyDeferred()); |
| if (!IsAny) |
| Protocols.push_back(std::move(Type)); |
| } |
| Comma = consumeTokenSyntaxIf(tok::comma); |
| if (Comma) |
| Junk.push_back(Comma->copyDeferred()); |
| } while (Comma); |
| } |
| |
| // Check for the terminating '>'. |
| Optional<SourceLoc> RAngleLoc; |
| if (startsWithGreater(Tok)) { |
| RAngleLoc = Tok.getLoc(); |
| auto RAngle = consumeStartingGreaterSyntax(); |
| Junk.push_back(RAngle.copyDeferred()); |
| } else { |
| if (Status.isSuccess()) { |
| diagnose(Tok, diag::expected_rangle_protocol); |
| diagnose(LAngleLoc, diag::opening_angle); |
| Status.setIsParseError(); |
| } |
| |
| SmallVector<ParsedSyntax, 4> RAngleJunk; |
| // Skip until we hit the '>'. |
| skipUntilGreaterInTypeListSyntax(RAngleJunk, /*protocolComposition=*/true); |
| for (auto &&Piece : RAngleJunk) |
| Junk.push_back(Piece.copyDeferred()); |
| } |
| |
| if (Status.isSuccess()) { |
| SmallString<32> replacement; |
| if (Protocols.empty()) { |
| replacement = "Any"; |
| } else { |
| auto extractText = [&](ParsedTypeSyntax &Type) -> StringRef { |
| auto SourceRange = Type.getRaw().getDeferredRange(); |
| return SourceMgr.extractText(SourceRange).trim(); |
| }; |
| auto Begin = Protocols.begin(); |
| replacement += extractText(*Begin); |
| while (++Begin != Protocols.end()) { |
| replacement += " & "; |
| replacement += extractText(*Begin); |
| } |
| } |
| |
| if (Protocols.size() > 1) { |
| // Need parenthesis if the next token looks like postfix TypeRepr. |
| // i.e. '?', '!', '.Type', '.Protocol' |
| bool needParen = false; |
| needParen |= !Tok.isAtStartOfLine() && |
| (isOptionalToken(Tok) || isImplicitlyUnwrappedOptionalToken(Tok)); |
| needParen |= Tok.isAny(tok::period, tok::period_prefix); |
| if (needParen) { |
| replacement.insert(replacement.begin(), '('); |
| replacement += ")"; |
| } |
| } |
| |
| // Copy split token after '>' to the replacement string. |
| // FIXME: lexer should smartly separate '>' and trailing contents like '?'. |
| StringRef TrailingContent = L->getTokenAt(*RAngleLoc).getRange().str(). |
| substr(1); |
| if (!TrailingContent.empty()) |
| replacement += TrailingContent; |
| |
| // Replace 'protocol<T1, T2>' with 'T1 & T2' |
| diagnose(ProtocolLoc, |
| IsEmpty ? diag::deprecated_any_composition : |
| Protocols.size() > 1 ? diag::deprecated_protocol_composition : |
| diag::deprecated_protocol_composition_single) |
| .highlight({ProtocolLoc, *RAngleLoc}) |
| .fixItReplace({ProtocolLoc, *RAngleLoc}, replacement); |
| } |
| |
| auto Unknown = ParsedSyntaxRecorder::makeUnknownType(Junk, *SyntaxContext); |
| return makeParsedResult(std::move(Unknown)); |
| } |
| |
| /// parseTypeTupleBody |
| /// type-tuple: |
| /// '(' type-tuple-body? ')' |
| /// type-tuple-body: |
| /// type-tuple-element (',' type-tuple-element)* '...'? |
| /// type-tuple-element: |
| /// identifier? identifier ':' type |
| /// type |
| ParsedSyntaxResult<ParsedTypeSyntax> Parser::parseTypeTupleBody() { |
| // Force the context to create deferred nodes, as we might need to |
| // de-structure the tuple type to create a function type. |
| DeferringContextRAII Deferring(*SyntaxContext); |
| Parser::StructureMarkerRAII ParsingTypeTuple(*this, Tok); |
| |
| if (ParsingTypeTuple.isFailed()) |
| return makeParsedError<ParsedTypeSyntax>(); |
| |
| ParsedTupleTypeSyntaxBuilder builder(*SyntaxContext); |
| |
| // Parse '('. |
| auto LParenLoc = Tok.getLoc(); |
| builder.useLeftParen(consumeTokenSyntax(tok::l_paren)); |
| |
| // Parse the elements. |
| SmallVector<ParsedTupleTypeElementSyntax, 4> Elements; |
| SmallVector<std::tuple<SourceLoc, SourceLoc, SourceLoc>, 4> ElementsLoc; |
| SourceLoc FirstEllipsisLoc; |
| auto Status = parseListSyntax( |
| Elements, /*AllowEmpty=*/true, /*AllowSepAfterLast=*/false, |
| [&] { return Tok.is(tok::r_paren); }, |
| [&](ParsedTupleTypeElementSyntaxBuilder &elemBuilder) { |
| Optional<BacktrackingScope> Backtracking; |
| |
| // 'inout' here can be a obsoleted use of the marker in an argument |
| // list, consume it in backtracking context so we can determine it's |
| // really a deprecated use of it. |
| SourceLoc InOutLoc; |
| Optional<ParsedTokenSyntax> InOut; |
| bool IsInOutObsoleted = false; |
| if (Tok.is(tok::kw_inout)) { |
| InOutLoc = Tok.getLoc(); |
| InOut = consumeTokenSyntax(tok::kw_inout); |
| IsInOutObsoleted = true; |
| } |
| |
| // If the label is "some", this could end up being an opaque type |
| // description if there's `some <identifier>` without a following colon, |
| // so we may need to backtrack as well. |
| if (Tok.getText().equals("some")) |
| Backtracking.emplace(*this); |
| |
| // If the tuple element starts with a potential argument label followed |
| // by a ':' or another potential argument label, then the identifier is |
| // an element tag, and it is followed by a type annotation. |
| Optional<ParsedTokenSyntax> Name; |
| Optional<ParsedTokenSyntax> SecondName; |
| Optional<ParsedTokenSyntax> Colon; |
| SourceLoc NameLoc; |
| SourceLoc SecondNameLoc; |
| if (Tok.canBeArgumentLabel() && |
| (peekToken().is(tok::colon) || peekToken().canBeArgumentLabel())) { |
| // Consume a name. |
| NameLoc = Tok.getLoc(); |
| Name = consumeArgumentLabelSyntax(); |
| |
| // If there is a second name, consume it as well. |
| if (Tok.canBeArgumentLabel()) { |
| SecondNameLoc = Tok.getLoc(); |
| SecondName = consumeArgumentLabelSyntax(); |
| } |
| |
| // Consume the ':'. |
| if ((Colon = consumeTokenSyntaxIf(tok::colon))) { |
| // If we succeed, then we successfully parsed a label. |
| if (Backtracking) |
| Backtracking->cancelBacktrack(); |
| // Otherwise, if we can't backtrack to parse this as a type, |
| // this is a syntax error. |
| } else { |
| if (!Backtracking) |
| diagnose(Tok, diag::expected_parameter_colon); |
| NameLoc = SourceLoc(); |
| SecondNameLoc = SourceLoc(); |
| } |
| } else if (InOut) { |
| // If we don't have labels, 'inout' is not a obsoleted use. |
| IsInOutObsoleted = false; |
| } |
| |
| if (!Backtracking || !Backtracking->willBacktrack()) { |
| if (Name) |
| elemBuilder.useName(std::move(*Name)); |
| if (SecondName) |
| elemBuilder.useSecondName(std::move(*SecondName)); |
| if (Colon) |
| elemBuilder.useColon(std::move(*Colon)); |
| } else if (Backtracking && Backtracking->willBacktrack()) { |
| NameLoc = SourceLoc(); |
| SecondNameLoc = SourceLoc(); |
| Name.reset(); |
| SecondName.reset(); |
| assert(!Colon.hasValue()); |
| } |
| Backtracking.reset(); |
| |
| // Parse the type. |
| auto TypeLoc = Tok.getLoc(); |
| auto ty = parseTypeSyntax(diag::expected_type); |
| if (ty.isNull()) { |
| ty = makeParsedResult( |
| ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext), |
| ty.getStatus()); |
| } |
| |
| // Handle pre-parsed 'inout'. |
| if (InOut) { |
| if (IsInOutObsoleted) { |
| elemBuilder.useInOut(std::move(*InOut)); |
| bool IsTypeAlreadyAttributed = false; |
| if (!ty.isNull()) { |
| if (auto AttributedType = |
| ty.getAs<ParsedAttributedTypeSyntax>()) { |
| IsTypeAlreadyAttributed = |
| AttributedType->getDeferredSpecifier().hasValue(); |
| ty = makeParsedResult(std::move(*AttributedType), |
| ty.getStatus()); |
| } |
| } |
| if (IsTypeAlreadyAttributed) { |
| // If the parsed type is already attributed, suggest removing |
| // `inout`. |
| diagnose(Tok, diag::parameter_specifier_repeated) |
| .fixItRemove(InOutLoc); |
| } else { |
| diagnose(InOutLoc, diag::parameter_specifier_as_attr_disallowed, |
| "inout") |
| .fixItRemove(InOutLoc) |
| .fixItInsert(TypeLoc, "inout "); |
| } |
| } else { |
| // Apply 'inout' to the parsed type. |
| ParsedAttributedTypeSyntaxBuilder builder(*SyntaxContext); |
| ty = applyAttributeToTypeSyntax(std::move(ty), std::move(InOut), |
| None); |
| TypeLoc = InOutLoc; |
| InOutLoc = SourceLoc(); |
| InOut.reset(); |
| } |
| } |
| if (!ty.isNull()) |
| elemBuilder.useType(ty.get()); |
| ElementsLoc.emplace_back(NameLoc, SecondNameLoc, TypeLoc); |
| if (ty.isError()) |
| return ty.getStatus(); |
| |
| // Parse '...'. |
| if (Tok.isEllipsis()) { |
| auto ElementEllipsisLoc = Tok.getLoc(); |
| Tok.setKind(tok::ellipsis); |
| elemBuilder.useEllipsis(consumeTokenSyntax(tok::ellipsis)); |
| if (!FirstEllipsisLoc.isValid()) { |
| FirstEllipsisLoc = ElementEllipsisLoc; |
| } else { |
| diagnose(ElementEllipsisLoc, diag::multiple_ellipsis_in_tuple) |
| .highlight(FirstEllipsisLoc) |
| .fixItRemove(ElementEllipsisLoc); |
| } |
| } |
| |
| // Parse the initializer ('=' expr). |
| if (Tok.is(tok::equal)) { |
| ParsedInitializerClauseSyntaxBuilder initBuilder(*SyntaxContext); |
| auto EqualLoc = Tok.getLoc(); |
| initBuilder.useEqual(consumeTokenSyntax(tok::equal)); |
| SyntaxParsingContext tmpCtxt(SyntaxContext); |
| tmpCtxt.setTransparent(); |
| auto Init = parseExpr(diag::expected_init_value); |
| auto InFlight = diagnose(EqualLoc, diag::tuple_type_init); |
| if (Init.isNonNull()) |
| InFlight.fixItRemove(SourceRange(EqualLoc, PreviousLoc)); |
| if (auto expr = SyntaxContext->popIf<ParsedExprSyntax>()) |
| initBuilder.useValue(std::move(*expr)); |
| else |
| initBuilder.useValue( |
| ParsedSyntaxRecorder::makeUnknownExpr({}, *SyntaxContext)); |
| elemBuilder.useInitializer(initBuilder.build()); |
| } |
| |
| return makeParserSuccess(); |
| }); |
| |
| // Parse ')'. |
| auto RParen = parseMatchingTokenSyntax( |
| tok::r_paren, diag::expected_rparen_tuple_type_list, LParenLoc, |
| /*silenceDiag=*/Status.isError()); |
| Status |= RParen.getStatus(); |
| |
| bool IsFunctionType = Tok.isAny(tok::arrow, tok::kw_throws, tok::kw_rethrows); |
| |
| auto GetNameText = [this](Optional<ParsedTokenSyntax> Name) { |
| return !Name ? StringRef() |
| : SourceMgr.extractText( |
| Name->getRaw().getDeferredTokenRange(), |
| L->getBufferID()); |
| }; |
| |
| if (!IsFunctionType) { |
| for (unsigned i = 0; i < Elements.size(); i++) { |
| // true tuples have labels |
| auto &Element = Elements[i]; |
| SourceLoc NameLoc, SecondNameLoc, TypeLoc; |
| std::tie(NameLoc, SecondNameLoc, TypeLoc) = ElementsLoc[i]; |
| // If there were two names, complain. |
| if (NameLoc.isValid() && SecondNameLoc.isValid()) { |
| auto Diag = diagnose(NameLoc, diag::tuple_type_multiple_labels); |
| auto Name = Element.getDeferredName(); |
| auto NameText = SourceMgr.extractText( |
| Name->getRaw().getDeferredTokenRange(), |
| L->getBufferID()); |
| if (NameText == "_") { |
| Diag.fixItRemoveChars(NameLoc, TypeLoc); |
| } else { |
| Diag.fixItRemove(SourceRange( |
| Lexer::getLocForEndOfToken(SourceMgr, NameLoc), SecondNameLoc)); |
| } |
| } |
| } |
| } else { |
| for (unsigned i = 0; i < Elements.size(); i++) { |
| // If there was a first name, complain; arguments in function types are |
| // always unlabeled. |
| auto &Element = Elements[i]; |
| SourceLoc NameLoc, SecondNameLoc, TypeLoc; |
| std::tie(NameLoc, SecondNameLoc, TypeLoc) = ElementsLoc[i]; |
| if (NameLoc.isValid()) { |
| auto NameText = GetNameText(Element.getDeferredName()); |
| if (NameText != "_") { |
| auto NameIdentifier = Context.getIdentifier(NameText); |
| auto Diag = diagnose(NameLoc, diag::function_type_argument_label, |
| NameIdentifier); |
| auto SecondNameText = GetNameText(Element.getDeferredSecondName()); |
| if (SecondNameLoc.isInvalid()) |
| Diag.fixItInsert(NameLoc, "_ "); |
| else if (SecondNameText == "_") |
| Diag.fixItRemoveChars(NameLoc, TypeLoc); |
| else |
| Diag.fixItReplace(SourceRange(NameLoc), "_"); |
| } |
| } |
| } |
| } |
| for (auto &elem : Elements) |
| builder.addElementsMember(std::move(elem)); |
| if (!RParen.isNull()) |
| builder.useRightParen(RParen.get()); |
| |
| return makeParsedResult(builder.build(), Status); |
| } |
| |
| /// parseTypeArray - Parse the type-array production, given that we |
| /// are looking at the initial l_square. Note that this index |
| /// clause is actually the outermost (first-indexed) clause. |
| /// |
| /// type-array: |
| /// type-simple |
| /// type-array '[' ']' |
| /// type-array '[' expr ']' |
| /// |
| ParsedSyntaxResult<ParsedTypeSyntax> |
| Parser::parseTypeArray(ParsedTypeSyntax Base, SourceLoc BaseLoc) { |
| assert(Tok.isFollowingLSquare()); |
| auto LSquareLoc = Tok.getLoc(); |
| ignoreToken(tok::l_square); |
| |
| // Ignore integer literal between '[' and ']' |
| ignoreIf(tok::integer_literal); |
| |
| auto RSquareLoc = Tok.getLoc(); |
| auto RSquare = parseMatchingTokenSyntax( |
| tok::r_square, diag::expected_rbracket_array_type, LSquareLoc); |
| |
| if (!RSquare.isNull()) { |
| // If we parsed something valid, diagnose it with a fixit to rewrite it to |
| // Swift syntax. |
| diagnose(LSquareLoc, diag::new_array_syntax) |
| .fixItInsert(BaseLoc, "[") |
| .fixItRemoveChars(LSquareLoc, RSquareLoc); |
| } |
| |
| ParsedArrayTypeSyntaxBuilder builder(*SyntaxContext); |
| ParserStatus status; |
| |
| builder.useElementType(std::move(Base)); |
| if (!RSquare.isNull()) |
| builder.useRightSquareBracket(RSquare.get()); |
| status |= RSquare.getStatus(); |
| |
| return makeParsedResult(builder.build(), status); |
| } |
| |
| /// Parse a collection type. |
| /// type-simple: |
| /// '[' type ']' |
| /// '[' type ':' type ']' |
| ParsedSyntaxResult<ParsedTypeSyntax> Parser::parseTypeCollection() { |
| ParserStatus Status; |
| assert(Tok.is(tok::l_square)); |
| Parser::StructureMarkerRAII parsingCollection(*this, Tok); |
| auto LSquareLoc = Tok.getLoc(); |
| auto LSquare = consumeTokenSyntax(tok::l_square); |
| |
| auto ElementTypeResult = parseTypeSyntax(diag::expected_element_type); |
| Status |= ElementTypeResult.getStatus(); |
| auto ElementType = ElementTypeResult.getOrNull(); |
| if (!ElementType) |
| ElementType = ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext); |
| |
| Optional<ParsedTokenSyntax> Colon; |
| Optional<ParsedTypeSyntax> ValueType; |
| |
| if (Tok.is(tok::colon)) { |
| Colon = consumeTokenSyntax(tok::colon); |
| auto ValueTypeResult = |
| parseTypeSyntax(diag::expected_dictionary_value_type); |
| ValueType = ValueTypeResult.getOrNull(); |
| if (!ValueType) |
| ValueType = ParsedSyntaxRecorder::makeUnknownType({}, *SyntaxContext); |
| Status |= ValueTypeResult.getStatus(); |
| } |
| |
| auto Diag = Colon ? diag::expected_rbracket_dictionary_type |
| : diag::expected_rbracket_array_type; |
| |
| auto RSquare = parseMatchingTokenSyntax(tok::r_square, Diag, LSquareLoc); |
| Status |= RSquare.getStatus(); |
| |
| if (Colon) { |
| ParsedDictionaryTypeSyntaxBuilder builder(*SyntaxContext); |
| builder.useLeftSquareBracket(std::move(LSquare)); |
| builder.useKeyType(std::move(*ElementType)); |
| builder.useColon(std::move(*Colon)); |
| builder.useValueType(std::move(*ValueType)); |
| if (!RSquare.isNull()) |
| builder.useRightSquareBracket(RSquare.get()); |
| return makeParsedResult(builder.build(), Status); |
| } else { |
| ParsedArrayTypeSyntaxBuilder builder(*SyntaxContext); |
| builder.useLeftSquareBracket(std::move(LSquare)); |
| builder.useElementType(std::move(*ElementType)); |
| if (!RSquare.isNull()) |
| builder.useRightSquareBracket(RSquare.get()); |
| return makeParsedResult(builder.build(), Status); |
| } |
| } |
| |
| ParsedSyntaxResult<ParsedTypeSyntax> |
| Parser::parseMetatypeType(ParsedTypeSyntax Base) { |
| auto Period = consumeTokenSyntax(); // tok::period or tok::period_prefix |
| auto Keyword = consumeTokenSyntax(tok::identifier); // "Type" or "Protocol" |
| auto MetatypeType = ParsedSyntaxRecorder::makeMetatypeType( |
| std::move(Base), std::move(Period), std::move(Keyword), *SyntaxContext); |
| return makeParsedResult(std::move(MetatypeType)); |
| } |
| |
| bool Parser::isOptionalToken(const Token &T) const { |
| // A postfix '?' by itself is obviously optional. |
| if (T.is(tok::question_postfix)) |
| return true; |
| |
| // A postfix or bound infix operator token that begins with '?' can be |
| // optional too. We'll munch off the '?', so long as it is left-bound with |
| // the type (i.e., parsed as a postfix or unspaced binary operator). |
| if ((T.is(tok::oper_postfix) || T.is(tok::oper_binary_unspaced)) && |
| T.getText().startswith("?")) |
| return true; |
| return false; |
| } |
| |
| bool Parser::isImplicitlyUnwrappedOptionalToken(const Token &T) const { |
| // A postfix '!' by itself, or a '!' in SIL mode, is obviously implicitly |
| // unwrapped optional. |
| if (T.is(tok::exclaim_postfix) || T.is(tok::sil_exclamation)) |
| return true; |
| // A postfix or bound infix operator token that begins with '!' can be |
| // implicitly unwrapped optional too. We'll munch off the '!', so long as it |
| // is left-bound with the type (i.e., parsed as a postfix or unspaced binary |
| // operator). |
| if ((T.is(tok::oper_postfix) || T.is(tok::oper_binary_unspaced)) && |
| T.getText().startswith("!")) |
| return true; |
| return false; |
| } |
| |
| ParsedTokenSyntax Parser::consumeOptionalTokenSyntax() { |
| assert(isOptionalToken(Tok) && "not a '?' token?!"); |
| return consumeStartingCharacterOfCurrentTokenSyntax(tok::question_postfix, 1); |
| } |
| |
| SourceLoc Parser::consumeOptionalToken() { |
| assert(isOptionalToken(Tok) && "not a '?' token?!"); |
| return consumeStartingCharacterOfCurrentToken(tok::question_postfix); |
| } |
| |
| ParsedTokenSyntax Parser::consumeImplicitlyUnwrappedOptionalTokenSyntax() { |
| assert(isImplicitlyUnwrappedOptionalToken(Tok) && "not a '!' token?!"); |
| // If the text of the token is just '!', grab the next token. |
| return consumeStartingCharacterOfCurrentTokenSyntax(tok::exclaim_postfix, 1); |
| } |
| |
| SourceLoc Parser::consumeImplicitlyUnwrappedOptionalToken() { |
| assert(isImplicitlyUnwrappedOptionalToken(Tok) && "not a '!' token?!"); |
| // If the text of the token is just '!', grab the next token. |
| return consumeStartingCharacterOfCurrentToken(tok::exclaim_postfix); |
| } |
| |
| ParsedSyntaxResult<ParsedTypeSyntax> |
| Parser::parseOptionalType(ParsedTypeSyntax Base) { |
| auto Question = consumeOptionalTokenSyntax(); |
| auto Optional = ParsedSyntaxRecorder::makeOptionalType( |
| std::move(Base), std::move(Question), *SyntaxContext); |
| return makeParsedResult(std::move(Optional)); |
| } |
| |
| ParsedSyntaxResult<ParsedTypeSyntax> |
| Parser::parseImplicitlyUnwrappedOptionalType(ParsedTypeSyntax Base) { |
| auto Exclamation = consumeImplicitlyUnwrappedOptionalTokenSyntax(); |
| auto Unwrapped = ParsedSyntaxRecorder::makeImplicitlyUnwrappedOptionalType( |
| std::move(Base), std::move(Exclamation), *SyntaxContext); |
| return makeParsedResult(std::move(Unwrapped)); |
| } |
| |
| //===----------------------------------------------------------------------===// |
| // Speculative type list parsing |
| //===----------------------------------------------------------------------===// |
| |
| static bool isGenericTypeDisambiguatingToken(Parser &P) { |
| auto &tok = P.Tok; |
| switch (tok.getKind()) { |
| default: |
| return false; |
| case tok::r_paren: |
| case tok::r_square: |
| case tok::l_brace: |
| case tok::r_brace: |
| case tok::period: |
| case tok::period_prefix: |
| case tok::comma: |
| case tok::semi: |
| case tok::eof: |
| case tok::code_complete: |
| case tok::exclaim_postfix: |
| case tok::question_postfix: |
| case tok::colon: |
| return true; |
| |
| case tok::oper_binary_spaced: |
| if (tok.getText() == "&") |
| return true; |
| |
| LLVM_FALLTHROUGH; |
| case tok::oper_binary_unspaced: |
| case tok::oper_postfix: |
| // These might be '?' or '!' type modifiers. |
| return P.isOptionalToken(tok) || P.isImplicitlyUnwrappedOptionalToken(tok); |
| |
| case tok::l_paren: |
| case tok::l_square: |
| // These only apply to the generic type if they don't start a new line. |
| return !tok.isAtStartOfLine(); |
| } |
| } |
| |
| bool Parser::canParseAsGenericArgumentList() { |
| if (!Tok.isAnyOperator() || !Tok.getText().equals("<")) |
| return false; |
| |
| BacktrackingScope backtrack(*this); |
| |
| if (canParseGenericArguments()) |
| // 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 dis-ambiguating tokens. |
| return isGenericTypeDisambiguatingToken(*this); |
| |
| return false; |
| } |
| |
| bool Parser::canParseGenericArguments() { |
| // Parse the opening '<'. |
| if (!startsWithLess(Tok)) |
| return false; |
| consumeStartingLess(); |
| |
| do { |
| if (!canParseType()) |
| return false; |
| // Parse the comma, if the list continues. |
| } while (consumeIf(tok::comma)); |
| |
| if (!startsWithGreater(Tok)) { |
| return false; |
| } else { |
| consumeStartingGreater(); |
| return true; |
| } |
| } |
| |
| bool Parser::canParseType() { |
| // Accept 'inout' at for better recovery. |
| consumeIf(tok::kw_inout); |
| |
| switch (Tok.getKind()) { |
| case tok::kw_Self: |
| case tok::kw_Any: |
| if (!canParseTypeIdentifier()) |
| return false; |
| break; |
| case tok::kw_protocol: // Deprecated composition syntax |
| case tok::identifier: |
| if (!canParseTypeIdentifierOrTypeComposition()) |
| return false; |
| break; |
| case tok::l_paren: { |
| consumeToken(); |
| if (!canParseTypeTupleBody()) |
| return false; |
| break; |
| } |
| case tok::at_sign: { |
| consumeToken(); |
| if (!canParseTypeAttribute()) |
| return false; |
| return canParseType(); |
| } |
| case tok::l_square: |
| consumeToken(); |
| if (!canParseType()) |
| return false; |
| if (consumeIf(tok::colon)) { |
| if (!canParseType()) |
| return false; |
| } |
| if (!consumeIf(tok::r_square)) |
| return false; |
| break; |
| |
| |
| default: |
| return false; |
| } |
| |
| // '.Type', '.Protocol', '?', and '!' still leave us with type-simple. |
| while (true) { |
| if ((Tok.is(tok::period) || Tok.is(tok::period_prefix)) && |
| (peekToken().isContextualKeyword("Type") |
| || peekToken().isContextualKeyword("Protocol"))) { |
| consumeToken(); |
| consumeToken(tok::identifier); |
| continue; |
| } |
| if (isOptionalToken(Tok)) { |
| consumeOptionalToken(); |
| continue; |
| } |
| if (isImplicitlyUnwrappedOptionalToken(Tok)) { |
| consumeImplicitlyUnwrappedOptionalToken(); |
| continue; |
| } |
| break; |
| } |
| |
| // Handle type-function if we have an arrow or 'throws'/'rethrows' modifier. |
| if (Tok.isAny(tok::kw_throws, tok::kw_rethrows)) { |
| consumeToken(); |
| // "throws" or "rethrows" isn't a valid type without being followed by |
| // a return. |
| if (!Tok.is(tok::arrow)) |
| return false; |
| } |
| |
| if (consumeIf(tok::arrow)) { |
| if (!canParseType()) |
| return false; |
| return true; |
| } |
| |
| return true; |
| } |
| |
| bool Parser::canParseTypeIdentifierOrTypeComposition() { |
| if (Tok.is(tok::kw_protocol)) |
| return canParseOldStyleProtocolComposition(); |
| |
| while (true) { |
| if (!canParseTypeIdentifier()) |
| return false; |
| |
| if (Tok.isContextualPunctuator("&")) { |
| consumeToken(); |
| continue; |
| } else { |
| return true; |
| } |
| } |
| } |
| |
| bool Parser::canParseTypeIdentifier() { |
| while (true) { |
| if (!Tok.isAny(tok::identifier, tok::kw_Self, tok::kw_Any)) |
| return false; |
| consumeToken(); |
| |
| if (startsWithLess(Tok)) { |
| if (!canParseGenericArguments()) |
| return false; |
| } |
| |
| // Treat 'Foo.<anything>' as an attempt to write a dotted type |
| // unless <anything> is 'Type'. |
| if ((Tok.is(tok::period) || Tok.is(tok::period_prefix)) && |
| !peekToken().isContextualKeyword("Type") && |
| !peekToken().isContextualKeyword("Protocol")) { |
| consumeToken(); |
| } else { |
| return true; |
| } |
| } |
| } |
| |
| bool Parser::canParseOldStyleProtocolComposition() { |
| consumeToken(tok::kw_protocol); |
| |
| // Check for the starting '<'. |
| if (!startsWithLess(Tok)) { |
| return false; |
| } |
| consumeStartingLess(); |
| |
| // Check for empty protocol composition. |
| if (startsWithGreater(Tok)) { |
| consumeStartingGreater(); |
| return true; |
| } |
| |
| // Parse the type-composition-list. |
| do { |
| if (!canParseTypeIdentifier()) { |
| return false; |
| } |
| } while (consumeIf(tok::comma)); |
| |
| // Check for the terminating '>'. |
| if (!startsWithGreater(Tok)) { |
| return false; |
| } |
| consumeStartingGreater(); |
| |
| return true; |
| } |
| |
| bool Parser::canParseTypeTupleBody() { |
| if (Tok.isNot(tok::r_paren) && Tok.isNot(tok::r_brace) && |
| Tok.isNotEllipsis() && !isStartOfDecl()) { |
| do { |
| // The contextual inout marker is part of argument lists. |
| consumeIf(tok::kw_inout); |
| |
| // If the tuple element starts with "ident :", then it is followed |
| // by a type annotation. |
| if (Tok.canBeArgumentLabel() && |
| (peekToken().is(tok::colon) || peekToken().canBeArgumentLabel())) { |
| consumeToken(); |
| if (Tok.canBeArgumentLabel()) { |
| consumeToken(); |
| if (!Tok.is(tok::colon)) return false; |
| } |
| consumeToken(tok::colon); |
| |
| // Parse a type. |
| if (!canParseType()) |
| return false; |
| |
| // Parse default values. This aren't actually allowed, but we recover |
| // better if we skip over them. |
| if (consumeIf(tok::equal)) { |
| while (Tok.isNot(tok::eof) && Tok.isNot(tok::r_paren) && |
| Tok.isNot(tok::r_brace) && Tok.isNotEllipsis() && |
| Tok.isNot(tok::comma) && |
| !isStartOfDecl()) { |
| skipSingle(); |
| } |
| } |
| |
| continue; |
| } |
| |
| // Otherwise, this has to be a type. |
| if (!canParseType()) |
| return false; |
| |
| if (Tok.isEllipsis()) |
| consumeToken(); |
| |
| } while (consumeIf(tok::comma)); |
| } |
| |
| return consumeIf(tok::r_paren); |
| } |