Ungate accepted parts of SE439

This commit is contained in:
Mateus Rodrigues
2024-07-22 09:55:16 -03:00
parent e1f537107f
commit e0d416cdab
6 changed files with 525 additions and 522 deletions

View File

@@ -398,7 +398,7 @@ EXPERIMENTAL_FEATURE(ReinitializeConsumeInMultiBlockDefer, false)
EXPERIMENTAL_FEATURE(SE427NoInferenceOnExtension, false) EXPERIMENTAL_FEATURE(SE427NoInferenceOnExtension, false)
// Enable trailing comma for comma-separated lists. // Enable trailing comma for comma-separated lists.
EXPERIMENTAL_FEATURE(TrailingComma, true) EXPERIMENTAL_FEATURE(TrailingComma, false)
#undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE #undef EXPERIMENTAL_FEATURE_EXCLUDED_FROM_MODULE_INTERFACE
#undef EXPERIMENTAL_FEATURE #undef EXPERIMENTAL_FEATURE

View File

@@ -2549,195 +2549,190 @@ Parser::parseMacroRoleAttribute(
bool sawNames = false; bool sawNames = false;
SmallVector<MacroIntroducedDeclName, 2> names; SmallVector<MacroIntroducedDeclName, 2> names;
SmallVector<TypeExpr *, 2> conformances; SmallVector<TypeExpr *, 2> conformances;
auto argumentsStatus = parseList(tok::r_paren, lParenLoc, rParenLoc, auto argumentsStatus = parseList(
/*AllowSepAfterLast=*/Context.LangOpts.hasFeature(Feature::TrailingComma), tok::r_paren, lParenLoc, rParenLoc,
diag::expected_rparen_expr_list, /*AllowSepAfterLast=*/true, diag::expected_rparen_expr_list, [&] {
[&] { ParserStatus status;
ParserStatus status;
if (consumeIf(tok::code_complete)) { if (consumeIf(tok::code_complete)) {
status.setHasCodeCompletionAndIsError(); status.setHasCodeCompletionAndIsError();
if (!sawRole) { if (!sawRole) {
sawRole = true; sawRole = true;
if (this->CodeCompletionCallbacks) { if (this->CodeCompletionCallbacks) {
this->CodeCompletionCallbacks->completeDeclAttrParam( this->CodeCompletionCallbacks->completeDeclAttrParam(
getCustomSyntaxAttributeKind(isAttached), 0, /*HasLabel=*/false); getCustomSyntaxAttributeKind(isAttached), 0,
/*HasLabel=*/false);
}
} else if (!sawNames) {
if (this->CodeCompletionCallbacks) {
this->CodeCompletionCallbacks->completeDeclAttrParam(
getCustomSyntaxAttributeKind(isAttached), 1,
/*HasLabel=*/false);
}
}
} }
} else if (!sawNames) {
if (this->CodeCompletionCallbacks) { // Parse the argment label, if there is one.
this->CodeCompletionCallbacks->completeDeclAttrParam( Identifier fieldName;
getCustomSyntaxAttributeKind(isAttached), 1, /*HasLabel=*/false); SourceLoc fieldNameLoc;
parseOptionalArgumentLabel(fieldName, fieldNameLoc);
// If there is a field name, it better be 'names'.
if (!(fieldName.empty() || fieldName.is("names") ||
fieldName.is("conformances"))) {
diagnose(fieldNameLoc, diag::macro_attribute_unknown_label,
isAttached, fieldName);
status.setIsParseError();
return status;
} }
}
}
// Parse the argment label, if there is one. // If there is no field name and we haven't seen either names or the
Identifier fieldName; // role, this is the role.
SourceLoc fieldNameLoc; if (fieldName.empty() && !sawConformances && !sawNames && !sawRole) {
parseOptionalArgumentLabel(fieldName, fieldNameLoc); // Whether we saw anything we tried to treat as a role.
sawRole = true;
// If there is a field name, it better be 'names'. auto diagKind =
if (!(fieldName.empty() || fieldName.is("names") || isAttached ? diag::macro_role_attr_expected_attached_kind
fieldName.is("conformances"))) { : diag::macro_role_attr_expected_freestanding_kind;
diagnose( Identifier roleName;
fieldNameLoc, diag::macro_attribute_unknown_label, isAttached, SourceLoc roleNameLoc;
fieldName); if (Tok.is(tok::kw_extension)) {
status.setIsParseError(); roleNameLoc = consumeToken();
return status; role = MacroRole::Extension;
} } else if (parseIdentifier(roleName, roleNameLoc, diagKind,
/*diagnoseDollarPrefix=*/true)) {
status.setIsParseError();
return status;
}
// If there is no field name and we haven't seen either names or the role, if (!role)
// this is the role. role = getMacroRole(roleName.str());
if (fieldName.empty() && !sawConformances && !sawNames && !sawRole) {
// Whether we saw anything we tried to treat as a role. if (!role) {
sawRole = true; diagnose(roleNameLoc, diag::macro_role_attr_expected_kind,
isAttached);
status.setIsParseError();
return status;
}
if (!isMacroSupported(*role, Context)) {
diagnose(roleNameLoc, diag::macro_experimental, roleName.str(), "");
status.setIsParseError();
return status;
}
// Check that the role makes sense.
if (isAttached == !isAttachedMacro(*role)) {
diagnose(roleNameLoc, diag::macro_role_syntax_mismatch, isAttached,
roleName);
status.setIsParseError();
return status;
}
return status;
}
if (fieldName.is("conformances") ||
(fieldName.empty() && sawConformances && !sawNames)) {
if (fieldName.is("conformances") && sawConformances) {
diagnose(fieldNameLoc.isValid() ? fieldNameLoc : Tok.getLoc(),
diag::macro_attribute_duplicate_label, isAttached,
"conformances");
}
sawConformances = true;
// Parse the introduced conformances
auto type = parseType();
auto *typeExpr = new (Context) TypeExpr(type.get());
conformances.push_back(typeExpr);
return status;
}
// If the field name is empty and we haved seen "names", or the field
// name is "names" but we've already seen the argument label, complain.
if (fieldName.empty() != sawNames) {
diagnose(fieldNameLoc.isValid() ? fieldNameLoc : Tok.getLoc(),
sawNames ? diag::macro_attribute_duplicate_label
: diag::macro_attribute_missing_label,
isAttached, "names");
}
sawNames = true;
// Parse the introduced name kind.
Identifier introducedNameKind;
SourceLoc introducedNameKindLoc;
if (consumeIf(tok::code_complete)) {
status.setHasCodeCompletionAndIsError();
if (this->CodeCompletionCallbacks) {
this->CodeCompletionCallbacks->completeDeclAttrParam(
getCustomSyntaxAttributeKind(isAttached), 1, /*HasLabel=*/true);
}
} else if (parseIdentifier(introducedNameKind, introducedNameKindLoc,
diag::macro_attribute_unknown_argument_form,
/*diagnoseDollarPrefix=*/true)) {
status.setIsParseError();
return status;
}
auto introducedKind =
getMacroIntroducedDeclNameKind(introducedNameKind);
if (!introducedKind) {
diagnose(introducedNameKindLoc,
diag::macro_attribute_unknown_name_kind, introducedNameKind);
status.setIsParseError();
return status;
}
// If we don't need an argument, we're done.
if (!macroIntroducedNameRequiresArgument(*introducedKind)) {
// If there is an argument, complain about it.
if (Tok.is(tok::l_paren)) {
diagnose(Tok,
diag::macro_attribute_introduced_name_requires_no_argument,
introducedNameKind);
skipSingle();
}
names.push_back(MacroIntroducedDeclName(*introducedKind));
return status;
}
if (!Tok.is(tok::l_paren)) {
diagnose(Tok, diag::macro_attribute_introduced_name_requires_argument,
introducedNameKind);
status.setIsParseError();
return status;
}
// Parse the name.
(void)consumeToken(tok::l_paren);
DeclNameLoc nameLoc;
DeclNameRef name = parseDeclNameRef(
nameLoc, diag::macro_attribute_unknown_argument_form,
(DeclNameFlag::AllowOperators | DeclNameFlag::AllowKeywords |
DeclNameFlag::AllowKeywordsUsingSpecialNames |
DeclNameFlag::AllowCompoundNames |
DeclNameFlag::AllowZeroArgCompoundNames));
if (!name) {
status.setIsParseError();
return status;
}
SourceLoc rParenLoc;
if (!consumeIf(tok::r_paren, rParenLoc)) {
diagnose(Tok, diag::attr_expected_rparen, attrName, false);
rParenLoc = Tok.getLoc();
}
// Add the name we introduced.
names.push_back(
MacroIntroducedDeclName(*introducedKind, name.getFullName()));
auto diagKind = isAttached
? diag::macro_role_attr_expected_attached_kind
: diag::macro_role_attr_expected_freestanding_kind;
Identifier roleName;
SourceLoc roleNameLoc;
if (Tok.is(tok::kw_extension)) {
roleNameLoc = consumeToken();
role = MacroRole::Extension;
} else if (parseIdentifier(roleName, roleNameLoc, diagKind,
/*diagnoseDollarPrefix=*/true)) {
status.setIsParseError();
return status; return status;
} });
if (!role)
role = getMacroRole(roleName.str());
if (!role) {
diagnose(roleNameLoc, diag::macro_role_attr_expected_kind, isAttached);
status.setIsParseError();
return status;
}
if (!isMacroSupported(*role, Context)) {
diagnose(roleNameLoc, diag::macro_experimental, roleName.str(), "");
status.setIsParseError();
return status;
}
// Check that the role makes sense.
if (isAttached == !isAttachedMacro(*role)) {
diagnose(
roleNameLoc, diag::macro_role_syntax_mismatch, isAttached, roleName
);
status.setIsParseError();
return status;
}
return status;
}
if (fieldName.is("conformances") ||
(fieldName.empty() && sawConformances && !sawNames)) {
if (fieldName.is("conformances") && sawConformances) {
diagnose(fieldNameLoc.isValid() ? fieldNameLoc : Tok.getLoc(),
diag::macro_attribute_duplicate_label,
isAttached,
"conformances");
}
sawConformances = true;
// Parse the introduced conformances
auto type = parseType();
auto *typeExpr = new (Context) TypeExpr(type.get());
conformances.push_back(typeExpr);
return status;
}
// If the field name is empty and we haved seen "names", or the field name
// is "names" but we've already seen the argument label, complain.
if (fieldName.empty() != sawNames) {
diagnose(fieldNameLoc.isValid() ? fieldNameLoc : Tok.getLoc(),
sawNames ? diag::macro_attribute_duplicate_label
: diag::macro_attribute_missing_label,
isAttached,
"names");
}
sawNames = true;
// Parse the introduced name kind.
Identifier introducedNameKind;
SourceLoc introducedNameKindLoc;
if (consumeIf(tok::code_complete)) {
status.setHasCodeCompletionAndIsError();
if (this->CodeCompletionCallbacks) {
this->CodeCompletionCallbacks->completeDeclAttrParam(
getCustomSyntaxAttributeKind(isAttached), 1, /*HasLabel=*/true);
}
} else if (parseIdentifier(introducedNameKind, introducedNameKindLoc,
diag::macro_attribute_unknown_argument_form,
/*diagnoseDollarPrefix=*/true)) {
status.setIsParseError();
return status;
}
auto introducedKind = getMacroIntroducedDeclNameKind(introducedNameKind);
if (!introducedKind) {
diagnose(
introducedNameKindLoc, diag::macro_attribute_unknown_name_kind,
introducedNameKind
);
status.setIsParseError();
return status;
}
// If we don't need an argument, we're done.
if (!macroIntroducedNameRequiresArgument(*introducedKind)) {
// If there is an argument, complain about it.
if (Tok.is(tok::l_paren)) {
diagnose(
Tok, diag::macro_attribute_introduced_name_requires_no_argument,
introducedNameKind);
skipSingle();
}
names.push_back(MacroIntroducedDeclName(*introducedKind));
return status;
}
if (!Tok.is(tok::l_paren)) {
diagnose(
Tok, diag::macro_attribute_introduced_name_requires_argument,
introducedNameKind);
status.setIsParseError();
return status;
}
// Parse the name.
(void)consumeToken(tok::l_paren);
DeclNameLoc nameLoc;
DeclNameRef name = parseDeclNameRef(
nameLoc, diag::macro_attribute_unknown_argument_form,
(DeclNameFlag::AllowOperators |
DeclNameFlag::AllowKeywords |
DeclNameFlag::AllowKeywordsUsingSpecialNames |
DeclNameFlag::AllowCompoundNames |
DeclNameFlag::AllowZeroArgCompoundNames));
if (!name) {
status.setIsParseError();
return status;
}
SourceLoc rParenLoc;
if (!consumeIf(tok::r_paren, rParenLoc)) {
diagnose(Tok, diag::attr_expected_rparen, attrName, false);
rParenLoc = Tok.getLoc();
}
// Add the name we introduced.
names.push_back(
MacroIntroducedDeclName(*introducedKind, name.getFullName()));
return status;
});
if (argumentsStatus.isErrorOrHasCompletion()) if (argumentsStatus.isErrorOrHasCompletion())
return argumentsStatus; return argumentsStatus;
@@ -6804,10 +6799,13 @@ ParserStatus Parser::parseInheritance(
ParserStatus Status; ParserStatus Status;
SourceLoc prevComma; SourceLoc prevComma;
bool HasNextType; bool HasNextType;
bool IsEndOfList;
do { do {
SWIFT_DEFER { SWIFT_DEFER {
// Check for a ',', which indicates that there are more protocols coming. // Check for a ',', which indicates that there are more protocols coming.
HasNextType = consumeIf(tok::comma, prevComma); HasNextType = consumeIf(tok::comma, prevComma);
IsEndOfList = (Context.LangOpts.hasFeature(Feature::TrailingComma) &&
(Tok.is(tok::l_brace) || Tok.is(tok::kw_where)));
}; };
// Parse the 'class' keyword for a class requirement. // Parse the 'class' keyword for a class requirement.
if (Tok.is(tok::kw_class)) { if (Tok.is(tok::kw_class)) {
@@ -6859,7 +6857,7 @@ ParserStatus Parser::parseInheritance(
// Record the type if its a single type. // Record the type if its a single type.
if (ParsedTypeResult.isNonNull()) if (ParsedTypeResult.isNonNull())
Inherited.push_back(InheritedEntry(ParsedTypeResult.get())); Inherited.push_back(InheritedEntry(ParsedTypeResult.get()));
} while (HasNextType); } while (HasNextType && !IsEndOfList);
return Status; return Status;
} }

View File

@@ -1926,8 +1926,7 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {
default: default:
UnknownCharacter: UnknownCharacter:
checkForInputIncomplete(); checkForInputIncomplete();
if (Context.LangOpts.hasFeature(Feature::TrailingComma) && if (Tok.is(tok::eof) && Tok.getText() == ")") {
Tok.is(tok::eof) && Tok.getText() == ")") {
return nullptr; return nullptr;
} }
// FIXME: offer a fixit: 'Self' -> 'self' // FIXME: offer a fixit: 'Self' -> 'self'
@@ -2774,8 +2773,7 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
VD->setIsSelfParamCapture(); VD->setIsSelfParamCapture();
captureList.push_back(CLE); captureList.push_back(CLE);
} while (HasNext && !(Context.LangOpts.hasFeature(Feature::TrailingComma) && } while (HasNext && !Tok.is(tok::r_square));
Tok.is(tok::r_square)));
// The capture list needs to be closed off with a ']'. // The capture list needs to be closed off with a ']'.
SourceLoc rBracketLoc = Tok.getLoc(); SourceLoc rBracketLoc = Tok.getLoc();
@@ -3295,12 +3293,13 @@ ParserStatus Parser::parseExprList(tok leftTok, tok rightTok,
StructureMarkerRAII ParsingExprList(*this, Tok); StructureMarkerRAII ParsingExprList(*this, Tok);
leftLoc = consumeToken(leftTok); leftLoc = consumeToken(leftTok);
return parseList(rightTok, leftLoc, rightLoc, /*AllowSepAfterLast=*/Context.LangOpts.hasFeature(Feature::TrailingComma), return parseList(rightTok, leftLoc, rightLoc, /*AllowSepAfterLast=*/true,
rightTok == tok::r_paren ? diag::expected_rparen_expr_list rightTok == tok::r_paren ? diag::expected_rparen_expr_list
: diag::expected_rsquare_expr_list, : diag::expected_rsquare_expr_list,
[&] () -> ParserStatus { [&]() -> ParserStatus {
return parseExprListElement(rightTok, isArgumentList, leftLoc, elts); return parseExprListElement(rightTok, isArgumentList,
}); leftLoc, elts);
});
} }
static bool isStartOfLabelledTrailingClosure(Parser &P) { static bool isStartOfLabelledTrailingClosure(Parser &P) {

View File

@@ -134,8 +134,7 @@ Parser::parseGenericParametersBeforeWhere(SourceLoc LAngleLoc,
// Parse the comma, if the list continues. // Parse the comma, if the list continues.
HasNextParam = consumeIf(tok::comma); HasNextParam = consumeIf(tok::comma);
IsEndOfList = (Context.LangOpts.hasFeature(Feature::TrailingComma) && IsEndOfList = startsWithGreater(Tok);
startsWithGreater(Tok));
} while (HasNextParam && !IsEndOfList); } while (HasNextParam && !IsEndOfList);
return Result; return Result;

View File

@@ -213,347 +213,356 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
// Parse the parameter list. // Parse the parameter list.
bool isClosure = paramContext == ParameterContextKind::Closure; bool isClosure = paramContext == ParameterContextKind::Closure;
return parseList(tok::r_paren, leftParenLoc, rightParenLoc, return parseList(
/*AllowSepAfterLast=*/Context.LangOpts.hasFeature(Feature::TrailingComma), tok::r_paren, leftParenLoc, rightParenLoc,
diag::expected_rparen_parameter, /*AllowSepAfterLast=*/true, diag::expected_rparen_parameter,
[&]() -> ParserStatus { [&]() -> ParserStatus {
ParsedParameter param; ParsedParameter param;
ParserStatus status; ParserStatus status;
SourceLoc StartLoc = Tok.getLoc(); SourceLoc StartLoc = Tok.getLoc();
unsigned defaultArgIndex = defaultArgs ? defaultArgs->NextIndex++ : 0; unsigned defaultArgIndex = defaultArgs ? defaultArgs->NextIndex++ : 0;
// Attributes. // Attributes.
if (paramContext != ParameterContextKind::EnumElement) { if (paramContext != ParameterContextKind::EnumElement) {
auto AttrStatus = parseDeclAttributeList(param.Attrs); auto AttrStatus = parseDeclAttributeList(param.Attrs);
if (AttrStatus.hasCodeCompletion()) { if (AttrStatus.hasCodeCompletion()) {
if (this->CodeCompletionCallbacks) if (this->CodeCompletionCallbacks)
this->CodeCompletionCallbacks->setAttrTargetDeclKind(DeclKind::Param); this->CodeCompletionCallbacks->setAttrTargetDeclKind(
status.setHasCodeCompletionAndIsError(); DeclKind::Param);
} status.setHasCodeCompletionAndIsError();
}
{
// ('inout' | '__shared' | '__owned' | isolated)?
bool hasSpecifier = false;
while (isParameterSpecifier()) {
// Placing 'inout' in front of the parameter specifiers was allowed in
// the Swift 2-ish era and got moved to the return type in Swift 3
// (SE-0031).
// But new parameters that don't store there location in
// `SpecifierLoc` were added afterwards and didn't get diagnosed.
// We thus need to parameter specifiers that don't store their location
// in `SpecifierLoc` here. `SpecifierLoc` parameters get diagnosed in
// `validateParameterWithOwnership`
// is this token the identifier of an argument label? `inout` is a
// reserved keyword but the other modifiers are not.
if (!Tok.is(tok::kw_inout)) {
bool partOfArgumentLabel = lookahead<bool>(1, [&](CancellableBacktrackingScope &) {
if (Tok.is(tok::colon))
return true; // isolated :
return Tok.canBeArgumentLabel() && peekToken().is(tok::colon);
});
if (partOfArgumentLabel)
break;
}
if (Tok.isContextualKeyword("isolated")) {
diagnose(Tok, diag::parameter_specifier_as_attr_disallowed, Tok.getText())
.warnUntilSwiftVersion(6);
// did we already find an 'isolated' type modifier?
if (param.IsolatedLoc.isValid()) {
diagnose(Tok, diag::parameter_specifier_repeated)
.fixItRemove(Tok.getLoc());
consumeToken();
continue;
} }
// consume 'isolated' as type modifier
param.IsolatedLoc = consumeToken();
continue;
} }
if (Tok.isContextualKeyword("_const")) { {
diagnose(Tok, diag::parameter_specifier_as_attr_disallowed, Tok.getText()) // ('inout' | '__shared' | '__owned' | isolated)?
.warnUntilSwiftVersion(6); bool hasSpecifier = false;
param.CompileConstLoc = consumeToken(); while (isParameterSpecifier()) {
continue; // Placing 'inout' in front of the parameter specifiers was allowed
} // in the Swift 2-ish era and got moved to the return type in Swift
// 3 (SE-0031). But new parameters that don't store there location
// in `SpecifierLoc` were added afterwards and didn't get diagnosed.
// We thus need to parameter specifiers that don't store their
// location in `SpecifierLoc` here. `SpecifierLoc` parameters get
// diagnosed in `validateParameterWithOwnership`
if (Context.LangOpts.hasFeature(Feature::SendingArgsAndResults) && // is this token the identifier of an argument label? `inout` is a
Tok.isContextualKeyword("transferring")) { // reserved keyword but the other modifiers are not.
diagnose(Tok, diag::parameter_specifier_as_attr_disallowed, Tok.getText()) if (!Tok.is(tok::kw_inout)) {
.warnUntilSwiftVersion(6); bool partOfArgumentLabel =
if (param.TransferringLoc.isValid()) { lookahead<bool>(1, [&](CancellableBacktrackingScope &) {
diagnose(Tok, diag::parameter_specifier_repeated) if (Tok.is(tok::colon))
.fixItRemove(Tok.getLoc()); return true; // isolated :
consumeToken();
continue;
}
diagnose(Tok, diag::transferring_is_now_sendable) return Tok.canBeArgumentLabel() &&
.fixItReplace(Tok.getLoc(), "sending"); peekToken().is(tok::colon);
});
if (param.SendingLoc.isValid()) { if (partOfArgumentLabel)
diagnose(Tok, diag::sending_and_transferring_used_together) break;
.fixItRemove(Tok.getLoc()); }
consumeToken();
continue;
}
param.TransferringLoc = consumeToken(); if (Tok.isContextualKeyword("isolated")) {
continue; diagnose(Tok, diag::parameter_specifier_as_attr_disallowed,
} Tok.getText())
.warnUntilSwiftVersion(6);
// did we already find an 'isolated' type modifier?
if (param.IsolatedLoc.isValid()) {
diagnose(Tok, diag::parameter_specifier_repeated)
.fixItRemove(Tok.getLoc());
consumeToken();
continue;
}
if (Context.LangOpts.hasFeature(Feature::SendingArgsAndResults) && // consume 'isolated' as type modifier
Tok.isContextualKeyword("sending")) { param.IsolatedLoc = consumeToken();
diagnose(Tok, diag::parameter_specifier_as_attr_disallowed, continue;
Tok.getText()) }
.warnUntilSwiftVersion(6);
if (param.SendingLoc.isValid()) {
diagnose(Tok, diag::parameter_specifier_repeated)
.fixItRemove(Tok.getLoc());
consumeToken();
continue;
}
if (param.TransferringLoc.isValid()) { if (Tok.isContextualKeyword("_const")) {
diagnose(Tok, diag::sending_and_transferring_used_together) diagnose(Tok, diag::parameter_specifier_as_attr_disallowed,
.fixItRemove(param.TransferringLoc); Tok.getText())
consumeToken(); .warnUntilSwiftVersion(6);
continue; param.CompileConstLoc = consumeToken();
} continue;
}
param.SendingLoc = consumeToken(); if (Context.LangOpts.hasFeature(Feature::SendingArgsAndResults) &&
continue; Tok.isContextualKeyword("transferring")) {
} diagnose(Tok, diag::parameter_specifier_as_attr_disallowed,
Tok.getText())
.warnUntilSwiftVersion(6);
if (param.TransferringLoc.isValid()) {
diagnose(Tok, diag::parameter_specifier_repeated)
.fixItRemove(Tok.getLoc());
consumeToken();
continue;
}
if (!hasSpecifier) { diagnose(Tok, diag::transferring_is_now_sendable)
// These cases are handled later when mapping to ParamDecls for .fixItReplace(Tok.getLoc(), "sending");
// better fixits.
if (Tok.is(tok::kw_inout)) {
param.SpecifierKind = ParamDecl::Specifier::InOut;
param.SpecifierLoc = consumeToken();
} else if (Tok.isContextualKeyword("borrowing")) {
param.SpecifierKind = ParamDecl::Specifier::Borrowing;
param.SpecifierLoc = consumeToken();
} else if (Tok.isContextualKeyword("consuming")) {
param.SpecifierKind = ParamDecl::Specifier::Consuming;
param.SpecifierLoc = consumeToken();
} else if (Tok.isContextualKeyword("__shared")) {
param.SpecifierKind = ParamDecl::Specifier::LegacyShared;
param.SpecifierLoc = consumeToken();
} else if (Tok.isContextualKeyword("__owned")) {
param.SpecifierKind = ParamDecl::Specifier::LegacyOwned;
param.SpecifierLoc = consumeToken();
}
if (param.SendingLoc.isValid()) { if (param.SendingLoc.isValid()) {
diagnose(Tok, diag::sending_before_parameter_specifier, diagnose(Tok, diag::sending_and_transferring_used_together)
getNameForParamSpecifier(param.SpecifierKind)); .fixItRemove(Tok.getLoc());
} consumeToken();
continue;
}
hasSpecifier = true; param.TransferringLoc = consumeToken();
} else { continue;
// 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) ||
(Context.LangOpts.hasFeature(Feature::ReferenceBindings) &&
Tok.isAny(tok::kw_inout)))) {
diagnose(Tok, diag::parameter_let_var_as_attr, Tok.getText())
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
}
auto parseParamType = [&]() -> ParserResult<TypeRepr> { if (Context.LangOpts.hasFeature(Feature::SendingArgsAndResults) &&
// Currently none of the parameter type completions are relevant for Tok.isContextualKeyword("sending")) {
// enum cases, so don't include them. We'll complete for a regular type diagnose(Tok, diag::parameter_specifier_as_attr_disallowed,
// beginning instead. Tok.getText())
if (paramContext != ParameterContextKind::EnumElement) { .warnUntilSwiftVersion(6);
if (auto CCLoc = tryCompleteFunctionParamTypeBeginning()) { if (param.SendingLoc.isValid()) {
auto *ET = ErrorTypeRepr::create(Context, CCLoc); diagnose(Tok, diag::parameter_specifier_repeated)
return makeParserCodeCompletionResult<TypeRepr>(ET); .fixItRemove(Tok.getLoc());
} consumeToken();
} continue;
return parseType(diag::expected_parameter_type); }
};
if (startsParameterName(isClosure)) { if (param.TransferringLoc.isValid()) {
// identifier-or-none for the first name diagnose(Tok, diag::sending_and_transferring_used_together)
param.FirstNameLoc = consumeArgumentLabel(param.FirstName, .fixItRemove(param.TransferringLoc);
/*diagnoseDollarPrefix=*/!isClosure); consumeToken();
continue;
}
// identifier-or-none? for the second name param.SendingLoc = consumeToken();
if (Tok.canBeArgumentLabel()) continue;
param.SecondNameLoc = consumeArgumentLabel(param.SecondName, }
/*diagnoseDollarPrefix=*/true);
// Operators, closures, and enum elements cannot have API names. if (!hasSpecifier) {
if ((paramContext == ParameterContextKind::Operator || // These cases are handled later when mapping to ParamDecls for
paramContext == ParameterContextKind::Closure || // better fixits.
paramContext == ParameterContextKind::EnumElement) && if (Tok.is(tok::kw_inout)) {
!param.FirstName.empty() && param.SpecifierKind = ParamDecl::Specifier::InOut;
param.SecondNameLoc.isValid()) { param.SpecifierLoc = consumeToken();
enum KeywordArgumentDiagnosticContextKind { } else if (Tok.isContextualKeyword("borrowing")) {
Operator = 0, param.SpecifierKind = ParamDecl::Specifier::Borrowing;
Closure = 1, param.SpecifierLoc = consumeToken();
EnumElement = 2, } else if (Tok.isContextualKeyword("consuming")) {
} diagContextKind; param.SpecifierKind = ParamDecl::Specifier::Consuming;
param.SpecifierLoc = consumeToken();
} else if (Tok.isContextualKeyword("__shared")) {
param.SpecifierKind = ParamDecl::Specifier::LegacyShared;
param.SpecifierLoc = consumeToken();
} else if (Tok.isContextualKeyword("__owned")) {
param.SpecifierKind = ParamDecl::Specifier::LegacyOwned;
param.SpecifierLoc = consumeToken();
}
switch (paramContext) { if (param.SendingLoc.isValid()) {
case ParameterContextKind::Operator: diagnose(Tok, diag::sending_before_parameter_specifier,
diagContextKind = Operator; getNameForParamSpecifier(param.SpecifierKind));
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)? hasSpecifier = true;
if (consumeIf(tok::colon)) {
auto type = parseParamType();
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.isNull() && !type.hasCodeCompletion())
param.isInvalid = true;
} else if (paramContext != Parser::ParameterContextKind::Closure) {
diagnose(Tok, diag::expected_parameter_colon);
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 = parseParamType();
status |= type;
param.Type = type.getPtrOrNull();
param.FirstName = Identifier();
param.FirstNameLoc = SourceLoc();
param.SecondName = Identifier();
param.SecondNameLoc = SourceLoc();
} else if (isBareType && !Tok.is(tok::code_complete)) {
// Otherwise, if this is a bare type, then the user forgot to name the
// parameter, e.g. "func foo(Int) {}"
// Don't enter this case if the element could only be parsed as a bare
// type because a code completion token is positioned here. In this case
// the user is about to type the parameter label and we shouldn't
// suggest types.
SourceLoc typeStartLoc = Tok.getLoc();
auto type = parseParamType();
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 type as invalid so it is possible
// to diagnose it as destructuring of the closure parameter list.
param.isPotentiallyDestructured = true;
if (!isClosure) {
// Unnamed parameters must be written as "_: Type".
diagnose(typeStartLoc, diag::parameter_unnamed)
.fixItInsert(typeStartLoc, "_: ");
} else {
// Unnamed parameters were accidentally possibly accepted after
// SE-110 depending on the kind of declaration. We now need to
// warn about the misuse of this syntax and offer to
// fix it.
// An exception to this rule is when the type is declared with type sugar
// Reference: https://github.com/apple/swift/issues/54133
if (isa<OptionalTypeRepr>(param.Type)
|| isa<ImplicitlyUnwrappedOptionalTypeRepr>(param.Type)) {
diagnose(typeStartLoc, diag::parameter_unnamed)
.fixItInsert(typeStartLoc, "_: ");
} else { } else {
diagnose(typeStartLoc, diag::parameter_unnamed) // Redundant specifiers are fairly common, recognize, reject, and
.warnUntilSwiftVersion(6) // recover from this gracefully.
.fixItInsert(typeStartLoc, "_: "); diagnose(Tok, diag::parameter_specifier_repeated)
.fixItRemove(Tok.getLoc());
consumeToken();
} }
} }
} }
} 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 this parameter had an ellipsis, check it has a TypeRepr. // If let or var is being used as an argument label, allow it but
if (Tok.isEllipsis()) { // generate a warning.
if (param.Type == nullptr && !param.isInvalid) { if (!isClosure &&
diagnose(Tok, diag::untyped_pattern_ellipsis); (Tok.isAny(tok::kw_let, tok::kw_var) ||
consumeToken(); (Context.LangOpts.hasFeature(Feature::ReferenceBindings) &&
} Tok.isAny(tok::kw_inout)))) {
} diagnose(Tok, diag::parameter_let_var_as_attr, Tok.getText())
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
}
// ('=' expr) or ('==' expr)? auto parseParamType = [&]() -> ParserResult<TypeRepr> {
bool isEqualBinaryOperator = // Currently none of the parameter type completions are relevant for
Tok.isBinaryOperator() && Tok.getText() == "=="; // enum cases, so don't include them. We'll complete for a regular
if (Tok.is(tok::equal) || isEqualBinaryOperator) { // type beginning instead.
SourceLoc EqualLoc = Tok.getLoc(); if (paramContext != ParameterContextKind::EnumElement) {
if (auto CCLoc = tryCompleteFunctionParamTypeBeginning()) {
auto *ET = ErrorTypeRepr::create(Context, CCLoc);
return makeParserCodeCompletionResult<TypeRepr>(ET);
}
}
return parseType(diag::expected_parameter_type);
};
if (isEqualBinaryOperator) { if (startsParameterName(isClosure)) {
diagnose(Tok, diag::expected_assignment_instead_of_comparison_operator) // identifier-or-none for the first name
.fixItReplace(EqualLoc, "="); param.FirstNameLoc =
} consumeArgumentLabel(param.FirstName,
/*diagnoseDollarPrefix=*/!isClosure);
status |= parseDefaultArgument(*this, defaultArgs, defaultArgIndex, // identifier-or-none? for the second name
param.DefaultArg, paramContext); if (Tok.canBeArgumentLabel())
} param.SecondNameLoc =
consumeArgumentLabel(param.SecondName,
/*diagnoseDollarPrefix=*/true);
// If we haven't made progress, don't add the parameter. // Operators, closures, and enum elements cannot have API names.
if (Tok.getLoc() == StartLoc) { if ((paramContext == ParameterContextKind::Operator ||
// If we took a default argument index for this parameter, but didn't add paramContext == ParameterContextKind::Closure ||
// one, then give it back. paramContext == ParameterContextKind::EnumElement) &&
if (defaultArgs) --defaultArgs->NextIndex; !param.FirstName.empty() && param.SecondNameLoc.isValid()) {
return status; enum KeywordArgumentDiagnosticContextKind {
} Operator = 0,
Closure = 1,
EnumElement = 2,
} diagContextKind;
params.push_back(param); switch (paramContext) {
return status; 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 = parseParamType();
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.isNull() && !type.hasCodeCompletion())
param.isInvalid = true;
} else if (paramContext != Parser::ParameterContextKind::Closure) {
diagnose(Tok, diag::expected_parameter_colon);
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 = parseParamType();
status |= type;
param.Type = type.getPtrOrNull();
param.FirstName = Identifier();
param.FirstNameLoc = SourceLoc();
param.SecondName = Identifier();
param.SecondNameLoc = SourceLoc();
} else if (isBareType && !Tok.is(tok::code_complete)) {
// Otherwise, if this is a bare type, then the user forgot to name
// the parameter, e.g. "func foo(Int) {}" Don't enter this case if
// the element could only be parsed as a bare type because a code
// completion token is positioned here. In this case the user is
// about to type the parameter label and we shouldn't suggest types.
SourceLoc typeStartLoc = Tok.getLoc();
auto type = parseParamType();
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 type as invalid so it is possible
// to diagnose it as destructuring of the closure parameter list.
param.isPotentiallyDestructured = true;
if (!isClosure) {
// Unnamed parameters must be written as "_: Type".
diagnose(typeStartLoc, diag::parameter_unnamed)
.fixItInsert(typeStartLoc, "_: ");
} else {
// Unnamed parameters were accidentally possibly accepted after
// SE-110 depending on the kind of declaration. We now need to
// warn about the misuse of this syntax and offer to
// fix it.
// An exception to this rule is when the type is declared with
// type sugar Reference:
// https://github.com/apple/swift/issues/54133
if (isa<OptionalTypeRepr>(param.Type) ||
isa<ImplicitlyUnwrappedOptionalTypeRepr>(param.Type)) {
diagnose(typeStartLoc, diag::parameter_unnamed)
.fixItInsert(typeStartLoc, "_: ");
} else {
diagnose(typeStartLoc, diag::parameter_unnamed)
.warnUntilSwiftVersion(6)
.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 this parameter had an ellipsis, check it has a TypeRepr.
if (Tok.isEllipsis()) {
if (param.Type == nullptr && !param.isInvalid) {
diagnose(Tok, diag::untyped_pattern_ellipsis);
consumeToken();
}
}
// ('=' expr) or ('==' expr)?
bool isEqualBinaryOperator =
Tok.isBinaryOperator() && Tok.getText() == "==";
if (Tok.is(tok::equal) || isEqualBinaryOperator) {
SourceLoc EqualLoc = Tok.getLoc();
if (isEqualBinaryOperator) {
diagnose(Tok,
diag::expected_assignment_instead_of_comparison_operator)
.fixItReplace(EqualLoc, "=");
}
status |= parseDefaultArgument(*this, defaultArgs, defaultArgIndex,
param.DefaultArg, paramContext);
}
// 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;
});
} }
static TypeRepr * static TypeRepr *
@@ -1312,24 +1321,23 @@ ParserResult<Pattern> Parser::parsePatternTuple() {
// Parse all the elements. // Parse all the elements.
SmallVector<TuplePatternElt, 8> elts; SmallVector<TuplePatternElt, 8> elts;
ParserStatus ListStatus = ParserStatus ListStatus = parseList(
parseList(tok::r_paren, LPLoc, RPLoc, tok::r_paren, LPLoc, RPLoc,
/*AllowSepAfterLast=*/Context.LangOpts.hasFeature(Feature::TrailingComma), /*AllowSepAfterLast=*/true, diag::expected_rparen_tuple_pattern_list,
diag::expected_rparen_tuple_pattern_list, [&]() -> ParserStatus {
[&] () -> ParserStatus { // Parse the pattern tuple element.
// Parse the pattern tuple element. ParserStatus EltStatus;
ParserStatus EltStatus; std::optional<TuplePatternElt> elt;
std::optional<TuplePatternElt> elt; std::tie(EltStatus, elt) = parsePatternTupleElement();
std::tie(EltStatus, elt) = parsePatternTupleElement(); if (EltStatus.hasCodeCompletion())
if (EltStatus.hasCodeCompletion()) return makeParserCodeCompletionStatus();
return makeParserCodeCompletionStatus(); if (!elt)
if (!elt) return makeParserError();
return makeParserError();
// Add this element to the list. // Add this element to the list.
elts.push_back(*elt); elts.push_back(*elt);
return makeParserSuccess(); return makeParserSuccess();
}); });
return makeParserResult( return makeParserResult(
ListStatus, ListStatus,

View File

@@ -1523,8 +1523,7 @@ Parser::parseAvailabilitySpecList(SmallVectorImpl<AvailabilitySpec *> &Specs,
Status.setIsParseError(); Status.setIsParseError();
} else if (consumeIf(tok::comma)) { } else if (consumeIf(tok::comma)) {
// End of list with a trailing comma. // End of list with a trailing comma.
if (Context.LangOpts.hasFeature(Feature::TrailingComma) && if (Source != AvailabilitySpecSource::Available && Tok.is(tok::r_paren)) {
Tok.is(tok::r_paren)) {
break; break;
} }
// There is more to parse in this list. // There is more to parse in this list.