Files
swift-mirror/lib/Parse/ParsePattern.cpp
Sam Lazarus 2a38b48eea Parse / AST: Allow let / var as argument labels with a warning.
The diagnostic is now a warning and the new message alerts the user that
though it is valid to have let and var as argument label names,
they are interpreted as argument labels, not keywords.
2019-04-26 04:08:01 -04:00

1263 lines
46 KiB
C++

//===--- ParsePattern.cpp - Swift Language Parser for Patterns ------------===//
//
// This source file is part of the Swift.org open source project
//
// Copyright (c) 2014 - 2018 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
//
//===----------------------------------------------------------------------===//
//
// Pattern Parsing and AST Building
//
//===----------------------------------------------------------------------===//
#include "swift/Parse/Parser.h"
#include "swift/AST/ASTWalker.h"
#include "swift/AST/Initializer.h"
#include "swift/AST/Module.h"
#include "swift/Basic/StringExtras.h"
#include "swift/Parse/CodeCompletionCallbacks.h"
#include "swift/Parse/ParsedSyntaxRecorder.h"
#include "swift/Parse/SyntaxParsingContext.h"
#include "swift/Syntax/SyntaxKind.h"
#include "llvm/ADT/SmallString.h"
#include "llvm/ADT/StringMap.h"
#include "llvm/Support/SaveAndRestore.h"
using namespace swift;
using namespace swift::syntax;
/// Determine the kind of a default argument given a parsed
/// expression that has not yet been type-checked.
static DefaultArgumentKind getDefaultArgKind(Expr *init) {
if (!init)
return DefaultArgumentKind::None;
auto magic = dyn_cast<MagicIdentifierLiteralExpr>(init);
if (!magic)
return DefaultArgumentKind::Normal;
switch (magic->getKind()) {
case MagicIdentifierLiteralExpr::Column:
return DefaultArgumentKind::Column;
case MagicIdentifierLiteralExpr::File:
return DefaultArgumentKind::File;
case MagicIdentifierLiteralExpr::Line:
return DefaultArgumentKind::Line;
case MagicIdentifierLiteralExpr::Function:
return DefaultArgumentKind::Function;
case MagicIdentifierLiteralExpr::DSOHandle:
return DefaultArgumentKind::DSOHandle;
}
llvm_unreachable("Unhandled MagicIdentifierLiteralExpr in switch.");
}
void Parser::DefaultArgumentInfo::setFunctionContext(
DeclContext *DC, ParameterList *paramList){
for (auto context : ParsedContexts) {
context->changeFunction(DC, paramList);
}
}
static ParserStatus parseDefaultArgument(
Parser &P, Parser::DefaultArgumentInfo *defaultArgs, unsigned argIndex,
Expr *&init, bool &hasInheritedDefaultArg,
Parser::ParameterContextKind paramContext) {
SyntaxParsingContext DefaultArgContext(P.SyntaxContext,
SyntaxKind::InitializerClause);
SourceLoc equalLoc = P.consumeToken(tok::equal);
if (P.SF.Kind == SourceFileKind::Interface) {
// Swift module interfaces don't synthesize inherited intializers and
// instead include them explicitly in subclasses. Since the
// \c DefaultArgumentKind of these initializers is \c Inherited, this is
// represented textually as `= super` in the interface.
// If we're in a module interface and the default argument is exactly
// `super` (i.e. the token after that is `,` or `)` which end a parameter)
// report an inherited default argument to the caller and return.
if (P.Tok.is(tok::kw_super) && P.peekToken().isAny(tok::comma, tok::r_paren)) {
hasInheritedDefaultArg = true;
P.consumeToken(tok::kw_super);
P.SyntaxContext->createNodeInPlace(SyntaxKind::SuperRefExpr);
defaultArgs->HasDefaultArgument = true;
return ParserStatus();
}
}
// Enter a fresh default-argument context with a meaningless parent.
// We'll change the parent to the function later after we've created
// that declaration.
auto initDC = new (P.Context) DefaultArgumentInitializer(P.CurDeclContext,
argIndex);
Parser::ParseFunctionBody initScope(P, initDC);
ParserResult<Expr> initR = P.parseExpr(diag::expected_init_value);
// Record the default-argument context if we're supposed to accept default
// arguments here.
if (defaultArgs) {
defaultArgs->ParsedContexts.push_back(initDC);
}
Diag<> diagID = { DiagID() };
switch (paramContext) {
case Parser::ParameterContextKind::Function:
case Parser::ParameterContextKind::Operator:
case Parser::ParameterContextKind::Initializer:
case Parser::ParameterContextKind::EnumElement:
case Parser::ParameterContextKind::Subscript:
break;
case Parser::ParameterContextKind::Closure:
diagID = diag::no_default_arg_closure;
break;
case Parser::ParameterContextKind::Curried:
diagID = diag::no_default_arg_curried;
break;
}
assert((diagID.ID != DiagID()) == !defaultArgs &&
"Default arguments specified for an unexpected parameter list kind");
if (diagID.ID != DiagID()) {
auto inFlight = P.diagnose(equalLoc, diagID);
if (initR.isNonNull())
inFlight.fixItRemove(SourceRange(equalLoc, initR.get()->getEndLoc()));
return ParserStatus();
}
defaultArgs->HasDefaultArgument = true;
if (initR.hasCodeCompletion())
return makeParserCodeCompletionStatus();
if (initR.isNull())
return makeParserError();
init = initR.get();
return ParserStatus();
}
/// Determine whether we are at the start of a parameter name when
/// parsing a parameter.
static bool startsParameterName(Parser &parser, bool isClosure) {
// '_' cannot be a type, so it must be a parameter name.
if (parser.Tok.is(tok::kw__))
return true;
// To have a parameter name here, we need a name.
if (!parser.Tok.canBeArgumentLabel())
return false;
// If the next token can be an argument label or is ':', this is a name.
const auto &nextTok = parser.peekToken();
if (nextTok.is(tok::colon) || nextTok.canBeArgumentLabel())
return true;
// The identifier could be a name or it could be a type. In a closure, we
// assume it's a name, because the type can be inferred. Elsewhere, we
// assume it's a type.
return isClosure;
}
ParserStatus
Parser::parseParameterClause(SourceLoc &leftParenLoc,
SmallVectorImpl<ParsedParameter> &params,
SourceLoc &rightParenLoc,
DefaultArgumentInfo *defaultArgs,
ParameterContextKind paramContext) {
assert(params.empty() && leftParenLoc.isInvalid() &&
rightParenLoc.isInvalid() && "Must start with empty state");
SyntaxParsingContext ParamClauseCtx(SyntaxContext, SyntaxKind::ParameterClause);
// Consume the starting '(';
leftParenLoc = consumeToken(tok::l_paren);
// Trivial case: empty parameter list.
if (Tok.is(tok::r_paren)) {
{
SyntaxParsingContext EmptyPLContext(SyntaxContext,
SyntaxKind::FunctionParameterList);
}
rightParenLoc = consumeToken(tok::r_paren);
// Per SE-0155, enum elements may not have empty parameter lists.
if (paramContext == ParameterContextKind::EnumElement) {
decltype(diag::enum_element_empty_arglist) diagnostic;
if (Context.isSwiftVersionAtLeast(5)) {
diagnostic = diag::enum_element_empty_arglist;
} else {
diagnostic = diag::enum_element_empty_arglist_swift4;
}
diagnose(leftParenLoc, diagnostic)
.highlight({leftParenLoc, rightParenLoc});
diagnose(leftParenLoc, diag::enum_element_empty_arglist_delete)
.fixItRemoveChars(leftParenLoc, rightParenLoc);
diagnose(leftParenLoc, diag::enum_element_empty_arglist_add_void)
.fixItInsert(leftParenLoc, "Void");
}
return ParserStatus();
}
// Parse the parameter list.
bool isClosure = paramContext == ParameterContextKind::Closure;
return parseList(tok::r_paren, leftParenLoc, rightParenLoc,
/*AllowSepAfterLast=*/false,
diag::expected_rparen_parameter,
SyntaxKind::FunctionParameterList,
[&]() -> ParserStatus {
ParsedParameter param;
ParserStatus status;
SourceLoc StartLoc = Tok.getLoc();
unsigned defaultArgIndex = defaultArgs ? defaultArgs->NextIndex++ : 0;
// Attributes.
bool FoundCCToken = false;
if (paramContext != ParameterContextKind::EnumElement)
parseDeclAttributeList(param.Attrs, FoundCCToken);
if (FoundCCToken) {
if (CodeCompletion) {
CodeCompletion->completeDeclAttrKeyword(nullptr, isInSILMode(), true);
} else {
status |= makeParserCodeCompletionStatus();
}
}
// ('inout' | '__shared' | '__owned')?
bool hasSpecifier = false;
while (Tok.is(tok::kw_inout) ||
(Tok.is(tok::identifier) &&
(Tok.getRawText().equals("__shared") ||
Tok.getRawText().equals("__owned")))) {
if (!hasSpecifier) {
if (Tok.is(tok::kw_inout)) {
// This case is handled later when mapping to ParamDecls for
// better fixits.
param.SpecifierKind = VarDecl::Specifier::InOut;
param.SpecifierLoc = consumeToken();
} else if (Tok.is(tok::identifier) &&
Tok.getRawText().equals("__shared")) {
// This case is handled later when mapping to ParamDecls for
// better fixits.
param.SpecifierKind = VarDecl::Specifier::Shared;
param.SpecifierLoc = consumeToken();
} else if (Tok.is(tok::identifier) &&
Tok.getRawText().equals("__owned")) {
// This case is handled later when mapping to ParamDecls for
// better fixits.
param.SpecifierKind = VarDecl::Specifier::Owned;
param.SpecifierLoc = consumeToken();
}
hasSpecifier = true;
} else {
// Redundant specifiers are fairly common, recognize, reject, and
// recover from this gracefully.
diagnose(Tok, diag::parameter_specifier_repeated)
.fixItRemove(Tok.getLoc());
consumeToken();
}
}
// If let or var is being used as an argument label, allow it but
// generate a warning.
if (!isClosure && Tok.isAny(tok::kw_let, tok::kw_var)) {
diagnose(Tok, diag::parameter_let_var_as_attr, Tok.getText())
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
}
if (startsParameterName(*this, isClosure)) {
// identifier-or-none for the first name
param.FirstNameLoc = consumeArgumentLabel(param.FirstName);
// identifier-or-none? for the second name
if (Tok.canBeArgumentLabel())
param.SecondNameLoc = consumeArgumentLabel(param.SecondName);
// Operators, closures, and enum elements cannot have API names.
if ((paramContext == ParameterContextKind::Operator ||
paramContext == ParameterContextKind::Closure ||
paramContext == ParameterContextKind::EnumElement) &&
!param.FirstName.empty() &&
param.SecondNameLoc.isValid()) {
enum KeywordArgumentDiagnosticContextKind {
Operator = 0,
Closure = 1,
EnumElement = 2,
} diagContextKind;
switch (paramContext) {
case ParameterContextKind::Operator:
diagContextKind = Operator;
break;
case ParameterContextKind::Closure:
diagContextKind = Closure;
break;
case ParameterContextKind::EnumElement:
diagContextKind = EnumElement;
break;
default:
llvm_unreachable("Unhandled parameter context kind!");
}
diagnose(param.FirstNameLoc, diag::parameter_operator_keyword_argument,
unsigned(diagContextKind))
.fixItRemoveChars(param.FirstNameLoc, param.SecondNameLoc);
param.FirstName = param.SecondName;
param.FirstNameLoc = param.SecondNameLoc;
param.SecondName = Identifier();
param.SecondNameLoc = SourceLoc();
}
// (':' type)?
if (consumeIf(tok::colon)) {
auto type = parseType(diag::expected_parameter_type);
status |= type;
param.Type = type.getPtrOrNull();
// If we didn't parse a type, then we already diagnosed that the type
// was invalid. Remember that.
if (type.isParseError() && !type.hasCodeCompletion())
param.isInvalid = true;
}
} else {
// Otherwise, we have invalid code. Check to see if this looks like a
// type. If so, diagnose it as a common error.
bool isBareType = false;
{
BacktrackingScope backtrack(*this);
isBareType = canParseType() && Tok.isAny(tok::comma, tok::r_paren,
tok::equal);
}
if (isBareType && paramContext == ParameterContextKind::EnumElement) {
auto type = parseType(diag::expected_parameter_type, false);
status |= type;
param.Type = type.getPtrOrNull();
param.FirstName = Identifier();
param.FirstNameLoc = SourceLoc();
param.SecondName = Identifier();
param.SecondNameLoc = SourceLoc();
} else if (isBareType) {
// Otherwise, if this is a bare type, then the user forgot to name the
// parameter, e.g. "func foo(Int) {}"
SourceLoc typeStartLoc = Tok.getLoc();
auto type = parseType(diag::expected_parameter_type, false);
status |= type;
param.Type = type.getPtrOrNull();
// If this is a closure declaration, what is going
// on is most likely argument destructuring, we are going
// to diagnose that after all of the parameters are parsed.
if (param.Type) {
// Mark current parameter as invalid so it is possible
// to diagnose it as destructuring of the closure parameter list.
param.isInvalid = true;
if (!isClosure) {
// Unnamed parameters must be written as "_: Type".
diagnose(typeStartLoc, diag::parameter_unnamed)
.fixItInsert(typeStartLoc, "_: ");
}
}
} else {
// Otherwise, we're not sure what is going on, but this doesn't smell
// like a parameter.
diagnose(Tok, diag::expected_parameter_name);
param.isInvalid = true;
param.FirstNameLoc = Tok.getLoc();
TokReceiver->registerTokenKindChange(param.FirstNameLoc,
tok::identifier);
status.setIsParseError();
}
}
// '...'?
if (Tok.isEllipsis()) {
Tok.setKind(tok::ellipsis);
param.EllipsisLoc = consumeToken();
}
// ('=' expr)?
if (Tok.is(tok::equal)) {
SourceLoc EqualLoc = Tok.getLoc();
status |= parseDefaultArgument(*this, defaultArgs, defaultArgIndex,
param.DefaultArg,
param.hasInheritedDefaultArg,
paramContext);
if (param.EllipsisLoc.isValid() && param.DefaultArg) {
// The range of the complete default argument.
SourceRange defaultArgRange;
if (auto init = param.DefaultArg)
defaultArgRange = SourceRange(param.EllipsisLoc, init->getEndLoc());
diagnose(EqualLoc, diag::parameter_vararg_default)
.highlight(param.EllipsisLoc)
.fixItRemove(defaultArgRange);
param.isInvalid = true;
param.DefaultArg = nullptr;
}
}
// If we haven't made progress, don't add the parameter.
if (Tok.getLoc() == StartLoc) {
// If we took a default argument index for this parameter, but didn't add
// one, then give it back.
if (defaultArgs) defaultArgs->NextIndex--;
return status;
}
params.push_back(param);
return status;
});
}
template <typename T>
static TypeRepr *
validateParameterWithSpecifier(Parser &parser,
Parser::ParsedParameter &paramInfo,
StringRef specifierName,
bool parsingEnumElt) {
auto type = paramInfo.Type;
auto loc = paramInfo.SpecifierLoc;
// If we're validating an enum element, 'inout' is not allowed
// at all - Sema will catch this for us. In all other contexts, we
// assume the user put 'inout' in the wrong place and offer a fixit.
if (parsingEnumElt) {
return new (parser.Context) T(type, loc);
}
if (isa<SpecifierTypeRepr>(type)) {
parser.diagnose(loc, diag::parameter_specifier_repeated).fixItRemove(loc);
} else {
llvm::SmallString<128> replacement(specifierName);
replacement += " ";
parser
.diagnose(loc, diag::parameter_specifier_as_attr_disallowed,
specifierName)
.fixItRemove(loc)
.fixItInsert(type->getStartLoc(), replacement);
type = new (parser.Context) T(type, loc);
}
return type;
}
/// Map parsed parameters to a ParameterList.
static ParameterList *
mapParsedParameters(Parser &parser,
SourceLoc leftParenLoc,
MutableArrayRef<Parser::ParsedParameter> params,
SourceLoc rightParenLoc,
SmallVectorImpl<Identifier> *argNames,
Parser::ParameterContextKind paramContext) {
auto &ctx = parser.Context;
// Local function to create a pattern for a single parameter.
auto createParam = [&](Parser::ParsedParameter &paramInfo,
Identifier argName, SourceLoc argNameLoc,
Identifier paramName, SourceLoc paramNameLoc)
-> ParamDecl * {
auto param = new (ctx) ParamDecl(VarDecl::Specifier::Default,
paramInfo.SpecifierLoc,
argNameLoc, argName,
paramNameLoc, paramName,
parser.CurDeclContext);
param->getAttrs() = paramInfo.Attrs;
auto setInvalid = [&]{
if (param->isInvalid())
return;
param->getTypeLoc().setInvalidType(ctx);
param->setInvalid();
};
bool parsingEnumElt
= (paramContext == Parser::ParameterContextKind::EnumElement);
// If we're not parsing an enum case, lack of a SourceLoc for both
// names indicates the parameter is synthetic.
if (!parsingEnumElt && argNameLoc.isInvalid() && paramNameLoc.isInvalid())
param->setImplicit();
// If we diagnosed this parameter as a parse error, propagate to the decl.
if (paramInfo.isInvalid)
setInvalid();
// If a type was provided, create the type for the parameter.
if (auto type = paramInfo.Type) {
// If 'inout' was specified, turn the type into an in-out type.
if (paramInfo.SpecifierKind == VarDecl::Specifier::InOut) {
type = validateParameterWithSpecifier<InOutTypeRepr>(parser, paramInfo,
"inout",
parsingEnumElt);
} else if (paramInfo.SpecifierKind == VarDecl::Specifier::Shared) {
type = validateParameterWithSpecifier<SharedTypeRepr>(parser, paramInfo,
"__shared",
parsingEnumElt);
} else if (paramInfo.SpecifierKind == VarDecl::Specifier::Owned) {
type = validateParameterWithSpecifier<OwnedTypeRepr>(parser, paramInfo,
"__owned",
parsingEnumElt);
}
param->getTypeLoc() = TypeLoc(type);
// If there is `@autoclosure` attribute associated with the type
// let's mark that in the declaration as well, because it
// belongs to both type flags and declaration.
if (auto *ATR = dyn_cast<AttributedTypeRepr>(type)) {
auto &attrs = ATR->getAttrs();
// At this point we actually don't know if that's valid to mark
// this parameter declaration as `autoclosure` because type has
// not been resolved yet - it should either be a function type
// or typealias with underlying function type.
param->setAutoClosure(attrs.has(TypeAttrKind::TAK_autoclosure));
}
} else if (paramContext != Parser::ParameterContextKind::Closure) {
// Non-closure parameters require a type.
if (!param->isInvalid())
parser.diagnose(param->getLoc(), diag::missing_parameter_type);
setInvalid();
} else if (paramInfo.SpecifierLoc.isValid()) {
StringRef specifier;
switch (paramInfo.SpecifierKind) {
case VarDecl::Specifier::InOut:
specifier = "'inout'";
break;
case VarDecl::Specifier::Shared:
specifier = "'shared'";
break;
case VarDecl::Specifier::Owned:
specifier = "'owned'";
break;
case VarDecl::Specifier::Let:
case VarDecl::Specifier::Var:
llvm_unreachable("can't have let or var here");
break;
}
parser.diagnose(paramInfo.SpecifierLoc, diag::specifier_must_have_type,
specifier);
paramInfo.SpecifierLoc = SourceLoc();
paramInfo.SpecifierKind = VarDecl::Specifier::Default;
}
return param;
};
// Collect the elements of the tuple patterns for argument and body
// parameters.
SmallVector<ParamDecl*, 4> elements;
SourceLoc ellipsisLoc;
for (auto &param : params) {
// Whether the provided name is API by default depends on the parameter
// context.
bool isKeywordArgumentByDefault;
switch (paramContext) {
case Parser::ParameterContextKind::Closure:
case Parser::ParameterContextKind::Subscript:
case Parser::ParameterContextKind::Operator:
isKeywordArgumentByDefault = false;
break;
case Parser::ParameterContextKind::EnumElement:
case Parser::ParameterContextKind::Curried:
case Parser::ParameterContextKind::Initializer:
case Parser::ParameterContextKind::Function:
isKeywordArgumentByDefault = true;
break;
}
// Create the pattern.
ParamDecl *result = nullptr;
Identifier argName;
Identifier paramName;
if (param.SecondNameLoc.isValid()) {
argName = param.FirstName;
paramName = param.SecondName;
// Both names were provided, so pass them in directly.
result = createParam(param, argName, param.FirstNameLoc,
paramName, param.SecondNameLoc);
// If the first and second names are equivalent and non-empty, and we
// would have an argument label by default, complain.
if (isKeywordArgumentByDefault && param.FirstName == param.SecondName
&& !param.FirstName.empty()) {
parser.diagnose(param.FirstNameLoc,
diag::parameter_extraneous_double_up,
param.FirstName)
.fixItRemoveChars(param.FirstNameLoc, param.SecondNameLoc);
}
} else {
if (isKeywordArgumentByDefault)
argName = param.FirstName;
paramName = param.FirstName;
result = createParam(param, argName, SourceLoc(),
param.FirstName, param.FirstNameLoc);
}
// Warn when an unlabeled parameter follows a variadic parameter
if (ellipsisLoc.isValid() && elements.back()->isVariadic() &&
param.FirstName.empty()) {
parser.diagnose(param.FirstNameLoc,
diag::unlabeled_parameter_following_variadic_parameter);
}
// If this parameter had an ellipsis, check whether it's the last parameter.
if (param.EllipsisLoc.isValid()) {
if (ellipsisLoc.isValid()) {
parser.diagnose(param.EllipsisLoc, diag::multiple_parameter_ellipsis)
.highlight(ellipsisLoc)
.fixItRemove(param.EllipsisLoc);
param.EllipsisLoc = SourceLoc();
} else if (!result->getTypeLoc().getTypeRepr()) {
parser.diagnose(param.EllipsisLoc, diag::untyped_pattern_ellipsis)
.highlight(result->getSourceRange());
param.EllipsisLoc = SourceLoc();
} else {
ellipsisLoc = param.EllipsisLoc;
result->setVariadic();
}
}
assert (((!param.DefaultArg &&
!param.hasInheritedDefaultArg) ||
paramContext == Parser::ParameterContextKind::Function ||
paramContext == Parser::ParameterContextKind::Operator ||
paramContext == Parser::ParameterContextKind::Initializer ||
paramContext == Parser::ParameterContextKind::EnumElement ||
paramContext == Parser::ParameterContextKind::Subscript) &&
"Default arguments are only permitted on the first param clause");
if (param.DefaultArg) {
DefaultArgumentKind kind = getDefaultArgKind(param.DefaultArg);
result->setDefaultArgumentKind(kind);
result->setDefaultValue(param.DefaultArg);
} else if (param.hasInheritedDefaultArg) {
result->setDefaultArgumentKind(DefaultArgumentKind::Inherited);
}
elements.push_back(result);
if (argNames)
argNames->push_back(argName);
}
return ParameterList::create(ctx, leftParenLoc, elements, rightParenLoc);
}
/// Parse a single parameter-clause.
ParserResult<ParameterList>
Parser::parseSingleParameterClause(ParameterContextKind paramContext,
SmallVectorImpl<Identifier> *namePieces,
DefaultArgumentInfo *defaultArgs) {
if (!Tok.is(tok::l_paren)) {
// If we don't have the leading '(', complain.
Diag<> diagID;
switch (paramContext) {
case ParameterContextKind::Function:
case ParameterContextKind::Operator:
diagID = diag::func_decl_without_paren;
break;
case ParameterContextKind::Subscript:
diagID = diag::expected_lparen_subscript;
break;
case ParameterContextKind::Initializer:
diagID = diag::expected_lparen_initializer;
break;
case ParameterContextKind::EnumElement:
case ParameterContextKind::Closure:
case ParameterContextKind::Curried:
llvm_unreachable("should never be here");
}
{
auto diag = diagnose(Tok, diagID);
if (Tok.isAny(tok::l_brace, tok::arrow, tok::kw_throws, tok::kw_rethrows))
diag.fixItInsertAfter(PreviousLoc, "()");
}
// Create an empty parameter list to recover.
return makeParserErrorResult(
ParameterList::createEmpty(Context, PreviousLoc, PreviousLoc));
}
ParserStatus status;
SmallVector<ParsedParameter, 4> params;
SourceLoc leftParenLoc, rightParenLoc;
// Parse the parameter clause.
status |= parseParameterClause(leftParenLoc, params, rightParenLoc,
defaultArgs, paramContext);
// Turn the parameter clause into argument and body patterns.
auto paramList = mapParsedParameters(*this, leftParenLoc, params,
rightParenLoc, namePieces, paramContext);
return makeParserResult(status, paramList);
}
/// Parse function arguments.
///
/// Emits a special diagnostic if there are multiple parameter lists,
/// but otherwise is identical to parseSingleParameterClause().
ParserStatus
Parser::parseFunctionArguments(SmallVectorImpl<Identifier> &NamePieces,
ParameterList *&BodyParams,
ParameterContextKind paramContext,
DefaultArgumentInfo &DefaultArgs) {
// Parse parameter-clauses.
ParserStatus status;
auto FirstParameterClause
= parseSingleParameterClause(paramContext, &NamePieces, &DefaultArgs);
status |= FirstParameterClause;
BodyParams = FirstParameterClause.get();
bool MultipleParameterLists = false;
while (Tok.is(tok::l_paren)) {
MultipleParameterLists = true;
auto CurriedParameterClause
= parseSingleParameterClause(ParameterContextKind::Curried);
status |= CurriedParameterClause;
}
// If the decl uses currying syntax, complain that that syntax has gone away.
if (MultipleParameterLists) {
diagnose(BodyParams->getStartLoc(),
diag::parameter_curry_syntax_removed);
}
return status;
}
/// Parse a function definition signature.
/// func-signature:
/// func-arguments func-throws? func-signature-result?
/// func-signature-result:
/// '->' type
///
/// Note that this leaves retType as null if unspecified.
ParserStatus
Parser::parseFunctionSignature(Identifier SimpleName,
DeclName &FullName,
ParameterList *&bodyParams,
DefaultArgumentInfo &defaultArgs,
SourceLoc &throwsLoc,
bool &rethrows,
TypeRepr *&retType) {
SyntaxParsingContext SigContext(SyntaxContext, SyntaxKind::FunctionSignature);
SmallVector<Identifier, 4> NamePieces;
ParserStatus Status;
ParameterContextKind paramContext = SimpleName.isOperator() ?
ParameterContextKind::Operator : ParameterContextKind::Function;
Status |= parseFunctionArguments(NamePieces, bodyParams, paramContext,
defaultArgs);
FullName = DeclName(Context, SimpleName, NamePieces);
// Check for the 'throws' keyword.
rethrows = false;
if (Tok.is(tok::kw_throws)) {
throwsLoc = consumeToken();
} else if (Tok.is(tok::kw_rethrows)) {
throwsLoc = consumeToken();
rethrows = true;
} else if (Tok.is(tok::kw_throw)) {
throwsLoc = consumeToken();
diagnose(throwsLoc, diag::throw_in_function_type)
.fixItReplace(throwsLoc, "throws");
}
SourceLoc arrowLoc;
auto diagnoseInvalidThrows = [&]() -> Optional<InFlightDiagnostic> {
if (throwsLoc.isValid())
return None;
if (Tok.is(tok::kw_throws)) {
throwsLoc = consumeToken();
} else if (Tok.is(tok::kw_rethrows)) {
throwsLoc = consumeToken();
rethrows = true;
}
if (!throwsLoc.isValid())
return None;
auto diag = rethrows ? diag::rethrows_in_wrong_position
: diag::throws_in_wrong_position;
return diagnose(Tok, diag);
};
// If there's a trailing arrow, parse the rest as the result type.
if (Tok.isAny(tok::arrow, tok::colon)) {
SyntaxParsingContext ReturnCtx(SyntaxContext, SyntaxKind::ReturnClause);
if (!consumeIf(tok::arrow, arrowLoc)) {
// FixIt ':' to '->'.
diagnose(Tok, diag::func_decl_expected_arrow)
.fixItReplace(Tok.getLoc(), " -> ");
arrowLoc = consumeToken(tok::colon);
}
// Check for 'throws' and 'rethrows' after the arrow, but
// before the type, and correct it.
if (auto diagOpt = diagnoseInvalidThrows()) {
assert(arrowLoc.isValid());
assert(throwsLoc.isValid());
(*diagOpt).fixItExchange(SourceRange(arrowLoc),
SourceRange(throwsLoc));
}
ParserResult<TypeRepr> ResultType =
parseType(diag::expected_type_function_result);
if (ResultType.hasCodeCompletion())
return ResultType;
retType = ResultType.getPtrOrNull();
if (!retType) {
Status.setIsParseError();
return Status;
}
} else {
// Otherwise, we leave retType null.
retType = nullptr;
}
// Check for 'throws' and 'rethrows' after the type and correct it.
if (auto diagOpt = diagnoseInvalidThrows()) {
assert(arrowLoc.isValid());
assert(retType);
SourceLoc typeEndLoc = Lexer::getLocForEndOfToken(SourceMgr,
retType->getEndLoc());
SourceLoc throwsEndLoc = Lexer::getLocForEndOfToken(SourceMgr, throwsLoc);
(*diagOpt).fixItInsert(arrowLoc, rethrows ? "rethrows " : "throws ")
.fixItRemoveChars(typeEndLoc, throwsEndLoc);
}
return Status;
}
/// Parse a pattern with an optional type annotation.
///
/// typed-pattern ::= pattern (':' type)?
///
ParserResult<Pattern> Parser::parseTypedPattern() {
auto result = parsePattern();
// Now parse an optional type annotation.
if (Tok.is(tok::colon)) {
SyntaxParsingContext TypeAnnoCtx(SyntaxContext, SyntaxKind::TypeAnnotation);
SourceLoc colonLoc = consumeToken(tok::colon);
if (result.isNull()) // Recover by creating AnyPattern.
result = makeParserErrorResult(new (Context) AnyPattern(colonLoc));
ParserResult<TypeRepr> Ty = parseType();
if (Ty.hasCodeCompletion())
return makeParserCodeCompletionResult<Pattern>();
if (!Ty.isNull()) {
// Attempt to diagnose initializer calls incorrectly written
// as typed patterns, such as "var x: [Int]()".
// Disable this tentative parse when in code-completion mode, otherwise
// code-completion may enter the delayed-decl state twice.
if (Tok.isFollowingLParen() && !L->isCodeCompletion()) {
BacktrackingScope backtrack(*this);
// Create a local context if needed so we can parse trailing closures.
LocalContext dummyContext;
Optional<ContextChange> contextChange;
if (!CurLocalContext) {
contextChange.emplace(*this, CurDeclContext, &dummyContext);
}
SourceLoc lParenLoc, rParenLoc;
SmallVector<Expr *, 2> args;
SmallVector<Identifier, 2> argLabels;
SmallVector<SourceLoc, 2> argLabelLocs;
Expr *trailingClosure;
ParserStatus status = parseExprList(tok::l_paren, tok::r_paren,
/*isPostfix=*/true,
/*isExprBasic=*/false,
lParenLoc, args, argLabels,
argLabelLocs, rParenLoc,
trailingClosure,
SyntaxKind::Unknown);
if (status.isSuccess()) {
backtrack.cancelBacktrack();
// Suggest replacing ':' with '='
diagnose(lParenLoc, diag::initializer_as_typed_pattern)
.highlight({Ty.get()->getStartLoc(), rParenLoc})
.fixItReplace(colonLoc, " = ");
result.setIsParseError();
}
}
} else {
Ty = makeParserResult(new (Context) ErrorTypeRepr(PreviousLoc));
}
result = makeParserResult(result,
new (Context) TypedPattern(result.get(), Ty.get()));
}
return result;
}
/// Parse a pattern.
/// pattern ::= identifier
/// pattern ::= '_'
/// pattern ::= pattern-tuple
/// pattern ::= 'var' pattern
/// pattern ::= 'let' pattern
///
ParserResult<Pattern> Parser::parsePattern() {
SyntaxParsingContext PatternCtx(SyntaxContext, SyntaxContextKind::Pattern);
bool isLet = (InVarOrLetPattern != IVOLP_InVar);
auto specifier = isLet
? VarDecl::Specifier::Let
: VarDecl::Specifier::Var;
switch (Tok.getKind()) {
case tok::l_paren:
return parsePatternTuple();
case tok::kw__:
// Normally, '_' is invalid in type context for patterns, but they show up
// in interface files as the name for type members that are non-public.
// Treat them as an implicitly synthesized NamedPattern with a nameless
// VarDecl inside.
if (CurDeclContext->isTypeContext() &&
SF.Kind == SourceFileKind::Interface) {
PatternCtx.setCreateSyntax(SyntaxKind::IdentifierPattern);
auto VD = new (Context) VarDecl(
/*IsStatic*/false, specifier, /*IsCaptureList*/false,
consumeToken(tok::kw__), Identifier(), CurDeclContext);
return makeParserResult(new (Context) NamedPattern(VD, /*implicit*/true));
}
PatternCtx.setCreateSyntax(SyntaxKind::WildcardPattern);
return makeParserResult(new (Context) AnyPattern(consumeToken(tok::kw__)));
case tok::identifier: {
PatternCtx.setCreateSyntax(SyntaxKind::IdentifierPattern);
Identifier name;
SourceLoc loc = consumeIdentifier(&name);
if (Tok.isIdentifierOrUnderscore() && !Tok.isContextualDeclKeyword())
diagnoseConsecutiveIDs(name.str(), loc, isLet ? "constant" : "variable");
return makeParserResult(createBindingFromPattern(loc, name, specifier));
}
case tok::code_complete:
if (!CurDeclContext->isTypeContext()) {
// This cannot be an overridden property, so just eat the token. We cannot
// code complete anything here -- we expect an identifier.
consumeToken(tok::code_complete);
}
return nullptr;
case tok::kw_var:
case tok::kw_let: {
PatternCtx.setCreateSyntax(SyntaxKind::ValueBindingPattern);
bool isLet = Tok.is(tok::kw_let);
SourceLoc varLoc = consumeToken();
// 'var' and 'let' patterns shouldn't nest.
if (InVarOrLetPattern == IVOLP_InLet ||
InVarOrLetPattern == IVOLP_InVar)
diagnose(varLoc, diag::var_pattern_in_var, unsigned(isLet));
// 'let' isn't valid inside an implicitly immutable context, but var is.
if (isLet && InVarOrLetPattern == IVOLP_ImplicitlyImmutable)
diagnose(varLoc, diag::let_pattern_in_immutable_context);
// In our recursive parse, remember that we're in a var/let pattern.
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
T(InVarOrLetPattern, isLet ? IVOLP_InLet : IVOLP_InVar);
ParserResult<Pattern> subPattern = parsePattern();
if (subPattern.hasCodeCompletion())
return makeParserCodeCompletionResult<Pattern>();
if (subPattern.isNull())
return nullptr;
return makeParserResult(new (Context) VarPattern(varLoc, isLet,
subPattern.get()));
}
default:
if (Tok.isKeyword() &&
(peekToken().is(tok::colon) || peekToken().is(tok::equal))) {
diagnose(Tok, diag::keyword_cant_be_identifier, Tok.getText());
diagnose(Tok, diag::backticks_to_escape)
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
SourceLoc Loc = Tok.getLoc();
consumeToken();
return makeParserErrorResult(new (Context) AnyPattern(Loc));
}
diagnose(Tok, diag::expected_pattern);
return nullptr;
}
}
Pattern *Parser::createBindingFromPattern(SourceLoc loc, Identifier name,
VarDecl::Specifier specifier) {
auto var = new (Context) VarDecl(/*IsStatic*/false, specifier,
/*IsCaptureList*/false, loc, name,
CurDeclContext);
return new (Context) NamedPattern(var);
}
/// Parse an element of a tuple pattern.
///
/// pattern-tuple-element:
/// (identifier ':')? pattern
std::pair<ParserStatus, Optional<TuplePatternElt>>
Parser::parsePatternTupleElement() {
// If this element has a label, parse it.
Identifier Label;
SourceLoc LabelLoc;
// If the tuple element has a label, parse it.
if (Tok.is(tok::identifier) && peekToken().is(tok::colon)) {
LabelLoc = consumeIdentifier(&Label);
consumeToken(tok::colon);
}
// Parse the pattern.
ParserResult<Pattern> pattern = parsePattern();
if (pattern.hasCodeCompletion())
return std::make_pair(makeParserCodeCompletionStatus(), None);
if (pattern.isNull())
return std::make_pair(makeParserError(), None);
auto Elt = TuplePatternElt(Label, LabelLoc, pattern.get());
return std::make_pair(makeParserSuccess(), Elt);
}
/// Parse a tuple pattern.
///
/// pattern-tuple:
/// '(' pattern-tuple-body? ')'
/// pattern-tuple-body:
/// pattern-tuple-element (',' pattern-tuple-body)*
ParserResult<Pattern> Parser::parsePatternTuple() {
SyntaxParsingContext TuplePatternCtxt(SyntaxContext,
SyntaxKind::TuplePattern);
StructureMarkerRAII ParsingPatternTuple(*this, Tok);
if (ParsingPatternTuple.isFailed()) {
return makeParserError();
}
SourceLoc LPLoc = consumeToken(tok::l_paren);
SourceLoc RPLoc;
// Parse all the elements.
SmallVector<TuplePatternElt, 8> elts;
ParserStatus ListStatus =
parseList(tok::r_paren, LPLoc, RPLoc,
/*AllowSepAfterLast=*/false,
diag::expected_rparen_tuple_pattern_list,
SyntaxKind::TuplePatternElementList,
[&] () -> ParserStatus {
// Parse the pattern tuple element.
ParserStatus EltStatus;
Optional<TuplePatternElt> elt;
std::tie(EltStatus, elt) = parsePatternTupleElement();
if (EltStatus.hasCodeCompletion())
return makeParserCodeCompletionStatus();
if (!elt)
return makeParserError();
// Add this element to the list.
elts.push_back(*elt);
return makeParserSuccess();
});
return makeParserResult(
ListStatus,
TuplePattern::createSimple(Context, LPLoc, elts, RPLoc));
}
/// Parse an optional type annotation on a pattern.
///
/// pattern-type-annotation ::= (':' type)?
///
ParserResult<Pattern> Parser::
parseOptionalPatternTypeAnnotation(ParserResult<Pattern> result,
bool isOptional) {
if (!Tok.is(tok::colon))
return result;
// Parse an optional type annotation.
SyntaxParsingContext TypeAnnotationCtxt(SyntaxContext,
SyntaxKind::TypeAnnotation);
consumeToken(tok::colon);
if (result.isNull())
return result;
Pattern *P = result.get();
ParserStatus status;
if (result.hasCodeCompletion())
status.setHasCodeCompletion();
ParserResult<TypeRepr> Ty = parseType();
if (Ty.hasCodeCompletion()) {
result.setHasCodeCompletion();
return result;
}
TypeRepr *repr = Ty.getPtrOrNull();
if (!repr)
repr = new (Context) ErrorTypeRepr(PreviousLoc);
// In an if-let, the actual type of the expression is Optional of whatever
// was written.
if (isOptional)
repr = new (Context) OptionalTypeRepr(repr, SourceLoc());
return makeParserResult(status, new (Context) TypedPattern(P, repr));
}
/// matching-pattern ::= 'is' type
/// matching-pattern ::= matching-pattern-var
/// matching-pattern ::= expr
///
ParserResult<Pattern> Parser::parseMatchingPattern(bool isExprBasic) {
// TODO: Since we expect a pattern in this position, we should optimistically
// parse pattern nodes for productions shared by pattern and expression
// grammar. For short-term ease of initial implementation, we always go
// through the expr parser for ambiguous productions.
SyntaxParsingContext PatternCtx(SyntaxContext, SyntaxContextKind::Pattern);
// Parse productions that can only be patterns.
if (Tok.isAny(tok::kw_var, tok::kw_let)) {
PatternCtx.setCreateSyntax(SyntaxKind::ValueBindingPattern);
assert(Tok.isAny(tok::kw_let, tok::kw_var) && "expects var or let");
bool isLet = Tok.is(tok::kw_let);
SourceLoc varLoc = consumeToken();
return parseMatchingPatternAsLetOrVar(isLet, varLoc, isExprBasic);
}
// matching-pattern ::= 'is' type
if (Tok.is(tok::kw_is)) {
PatternCtx.setCreateSyntax(SyntaxKind::IsTypePattern);
SourceLoc isLoc = consumeToken(tok::kw_is);
ParserResult<TypeRepr> castType = parseType();
if (castType.isNull() || castType.hasCodeCompletion())
return nullptr;
return makeParserResult(new (Context) IsPattern(isLoc, castType.get(),
nullptr,
CheckedCastKind::Unresolved));
}
// matching-pattern ::= expr
// Fall back to expression parsing for ambiguous forms. Name lookup will
// disambiguate.
ParserResult<Expr> subExpr =
parseExprImpl(diag::expected_pattern, isExprBasic);
ParserStatus status = subExpr;
if (subExpr.isNull())
return status;
if (SyntaxContext->isEnabled()) {
if (auto UPES = PatternCtx.popIf<ParsedUnresolvedPatternExprSyntax>()) {
PatternCtx.addSyntax(UPES->getDeferredPattern());
} else {
PatternCtx.setCreateSyntax(SyntaxKind::ExpressionPattern);
}
}
// The most common case here is to parse something that was a lexically
// obvious pattern, which will come back wrapped in an immediate
// UnresolvedPatternExpr. Transform this now to simplify later code.
if (auto *UPE = dyn_cast<UnresolvedPatternExpr>(subExpr.get()))
return makeParserResult(status, UPE->getSubPattern());
return makeParserResult(status, new (Context) ExprPattern(subExpr.get()));
}
ParserResult<Pattern> Parser::parseMatchingPatternAsLetOrVar(bool isLet,
SourceLoc varLoc,
bool isExprBasic) {
// 'var' and 'let' patterns shouldn't nest.
if (InVarOrLetPattern == IVOLP_InLet ||
InVarOrLetPattern == IVOLP_InVar)
diagnose(varLoc, diag::var_pattern_in_var, unsigned(isLet));
// 'let' isn't valid inside an implicitly immutable context, but var is.
if (isLet && InVarOrLetPattern == IVOLP_ImplicitlyImmutable)
diagnose(varLoc, diag::let_pattern_in_immutable_context);
// In our recursive parse, remember that we're in a var/let pattern.
llvm::SaveAndRestore<decltype(InVarOrLetPattern)>
T(InVarOrLetPattern, isLet ? IVOLP_InLet : IVOLP_InVar);
ParserResult<Pattern> subPattern = parseMatchingPattern(isExprBasic);
if (subPattern.isNull())
return nullptr;
auto *varP = new (Context) VarPattern(varLoc, isLet, subPattern.get());
return makeParserResult(ParserStatus(subPattern), varP);
}
bool Parser::isOnlyStartOfMatchingPattern() {
return Tok.isAny(tok::kw_var, tok::kw_let, tok::kw_is);
}
static bool canParsePatternTuple(Parser &P);
/// pattern ::= identifier
/// pattern ::= '_'
/// pattern ::= pattern-tuple
/// pattern ::= 'var' pattern
/// pattern ::= 'let' pattern
static bool canParsePattern(Parser &P) {
switch (P.Tok.getKind()) {
case tok::identifier:
case tok::kw__:
P.consumeToken();
return true;
case tok::kw_let:
case tok::kw_var:
P.consumeToken();
return canParsePattern(P);
case tok::l_paren:
return canParsePatternTuple(P);
default:
return false;
}
}
static bool canParsePatternTuple(Parser &P) {
if (!P.consumeIf(tok::l_paren)) return false;
if (P.Tok.isNot(tok::r_paren)) {
do {
if (!canParsePattern(P)) return false;
} while (P.consumeIf(tok::comma));
}
return P.consumeIf(tok::r_paren);
}
/// typed-pattern ::= pattern (':' type)?
///
bool Parser::canParseTypedPattern() {
if (!canParsePattern(*this)) return false;
if (consumeIf(tok::colon))
return canParseType();
return true;
}