blob: dea6dabbca7741463b10728bd9bde992e99e57f4 [file] [log] [blame]
//===--- 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 "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(/*includeTrivia=*/false);
return SourceMgr.extractText(SourceRange);
};
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>();
SmallVector<ParsedSyntax, 0> Junk;
auto LParenLoc = Tok.getLoc();
auto LParen = consumeTokenSyntax(tok::l_paren);
Junk.push_back(LParen.copyDeferred());
SmallVector<ParsedTupleTypeElementSyntax, 4> Elements;
SmallVector<std::tuple<SourceLoc, SourceLoc, SourceLoc>, 4> ElementsLoc;
SourceLoc FirstEllipsisLoc;
Optional<ParsedTokenSyntax> Comma;
Optional<ParsedTokenSyntax> RParen;
ParserStatus Status =
parseListSyntax(tok::r_paren, LParenLoc, Comma, RParen, Junk,
false, diag::expected_rparen_tuple_type_list, [&]() {
Optional<BacktrackingScope> Backtracking;
SmallVector<ParsedSyntax, 0> LocalJunk;
// '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;
LocalJunk.push_back(InOut->copyDeferred());
}
// 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)
LocalJunk.push_back(Name->copyDeferred());
if (SecondName)
LocalJunk.push_back(SecondName->copyDeferred());
if (Colon)
LocalJunk.push_back(Colon->copyDeferred());
} else if (Backtracking && Backtracking->willBacktrack()) {
Name.reset();
SecondName.reset();
assert(!Colon.hasValue());
}
Backtracking.reset();
// Parse the type annotation.
auto TypeLoc = Tok.getLoc();
auto ty = parseTypeSyntax(diag::expected_type);
if (ty.isError()) {
std::move(LocalJunk.begin(), LocalJunk.end(), std::back_inserter(Junk));
if (!ty.isNull())
Junk.push_back(ty.get());
skipListUntilDeclRBraceSyntax(Junk, LParenLoc, tok::r_paren, tok::comma);
return ty.getStatus();
}
if (InOut) {
if (IsInOutObsoleted) {
bool IsTypeAlreadyAttributed = false;
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);
InOut.reset();
}
}
Optional<ParsedTokenSyntax> ElementEllipsis;
if (Tok.isEllipsis()) {
Tok.setKind(tok::ellipsis);
auto ElementEllipsisLoc = Tok.getLoc();
ElementEllipsis = consumeTokenSyntax();
if (!FirstEllipsisLoc.isValid()) {
FirstEllipsisLoc = ElementEllipsisLoc;
} else {
diagnose(ElementEllipsisLoc, diag::multiple_ellipsis_in_tuple)
.highlight(FirstEllipsisLoc)
.fixItRemove(ElementEllipsisLoc);
}
}
Optional<ParsedTokenSyntax> Equal;
Optional<ParsedInitializerClauseSyntax> Initializer;
if (Tok.is(tok::equal)) {
auto EqualLoc = Tok.getLoc();
Equal = consumeTokenSyntax(tok::equal);
auto Init = parseExpr(diag::expected_init_value);
auto InFlight = diagnose(EqualLoc, diag::tuple_type_init);
if (Init.isNonNull())
InFlight.fixItRemove(SourceRange(EqualLoc, Init.get()->getEndLoc()));
Initializer = ParsedSyntaxRecorder::makeInitializerClause(
std::move(*Equal),
std::move(*SyntaxContext->popIf<ParsedExprSyntax>()),
*SyntaxContext);
}
Comma = consumeTokenSyntaxIf(tok::comma);
auto Element = ParsedSyntaxRecorder::makeTupleTypeElement(
std::move(InOut), std::move(Name), std::move(SecondName),
std::move(Colon), ty.get(), std::move(ElementEllipsis),
std::move(Initializer), std::move(Comma), *SyntaxContext);
Junk.push_back(Element.copyDeferred());
Elements.push_back(std::move(Element));
ElementsLoc.emplace_back(NameLoc, SecondNameLoc, TypeLoc);
return makeParserSuccess();
});
if (!Status.isSuccess()) {
if (RParen)
Junk.push_back(std::move(*RParen));
auto ty = ParsedSyntaxRecorder::makeUnknownType(Junk, *SyntaxContext);
return makeParsedResult(std::move(ty), Status);
}
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), "_");
}
}
}
}
auto ElementList =
ParsedSyntaxRecorder::makeTupleTypeElementList(Elements, *SyntaxContext);
auto TupleType = ParsedSyntaxRecorder::makeTupleType(
std::move(LParen), std::move(ElementList), std::move(*RParen),
*SyntaxContext);
return makeParsedResult(std::move(TupleType));
}
/// 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);
}