Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 8 additions & 2 deletions include/swift/AST/DiagnosticsParse.def
Original file line number Diff line number Diff line change
Expand Up @@ -428,8 +428,14 @@ ERROR(operator_decl_inner_scope,none,
"'operator' may only be declared at file scope", ())
ERROR(expected_operator_name_after_operator,PointsToFirstBadToken,
"expected operator name in operator declaration", ())
ERROR(identifier_when_expecting_operator,PointsToFirstBadToken,
"%0 is considered to be an identifier, not an operator", (Identifier))
ERROR(identifier_within_operator_name,PointsToFirstBadToken,
"'%0' is considered an identifier and must not "
"appear within an operator name", (StringRef))
ERROR(operator_name_invalid_char,PointsToFirstBadToken,
"'%0' is not allowed in operator names", (StringRef))
ERROR(postfix_operator_name_cannot_start_with_unwrap,PointsToFirstBadToken,
"postfix operator names starting with '?' or '!' are disallowed "
"to avoid collisions with built-in unwrapping operators", ())

ERROR(deprecated_operator_body,PointsToFirstBadToken,
"operator should no longer be declared with body", ())
Expand Down
3 changes: 2 additions & 1 deletion lib/Parse/Lexer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -829,7 +829,8 @@ void Lexer::lexOperatorIdentifier() {
if (CurPtr-TokStart == 1) {
switch (TokStart[0]) {
case '=':
if (leftBound != rightBound) {
// Refrain from emitting this message in operator name position.
if (NextToken.isNot(tok::kw_operator) && leftBound != rightBound) {
auto d = diagnose(TokStart, diag::lex_unary_equal);
if (leftBound)
d.fixItInsert(getSourceLoc(TokStart), " ");
Expand Down
63 changes: 46 additions & 17 deletions lib/Parse/ParseDecl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7401,24 +7401,58 @@ Parser::parseDeclOperator(ParseDeclOptions Flags, DeclAttributes &Attributes) {
SourceLoc OperatorLoc = consumeToken(tok::kw_operator);
bool AllowTopLevel = Flags.contains(PD_AllowTopLevel);

if (!Tok.isAnyOperator() && !Tok.is(tok::exclaim_postfix)) {
// A common error is to try to define an operator with something in the
// unicode plane considered to be an operator, or to try to define an
// operator like "not". Diagnose this specifically.
if (Tok.is(tok::identifier))
diagnose(Tok, diag::identifier_when_expecting_operator,
Context.getIdentifier(Tok.getText()));
else
const auto maybeDiagnoseInvalidCharInOperatorName = [this](const Token &Tk) {
if (Tk.is(tok::identifier) &&
DeclAttribute::getAttrKindFromString(Tk.getText()) ==
DeclAttrKind::DAK_Count) {
diagnose(Tk, diag::identifier_within_operator_name, Tk.getText());
return true;
} else if (Tk.isNot(tok::colon, tok::l_brace, tok::semi) &&
Tk.isPunctuation()) {
diagnose(Tk, diag::operator_name_invalid_char,
Tk.getText().take_front());
return true;
}
return false;
};

// Postfix operators starting with ? or ! conflict with builtin
// unwrapping operators.
if (Attributes.hasAttribute<PostfixAttr>())
if (!Tok.getText().empty() && (Tok.getRawText().front() == '?' ||
Tok.getRawText().front() == '!'))
diagnose(Tok, diag::postfix_operator_name_cannot_start_with_unwrap);

// A common error is to try to define an operator with something in the
// unicode plane considered to be an operator, or to try to define an
// operator like "not". Analyze and diagnose this specifically.
if (Tok.isAnyOperator() || Tok.isAny(tok::exclaim_postfix,
tok::question_infix,
tok::question_postfix,
tok::equal, tok::arrow)) {
if (peekToken().getLoc() == Tok.getRange().getEnd() &&
maybeDiagnoseInvalidCharInOperatorName(peekToken())) {
consumeToken();

// If there's a deprecated body, skip it to improve recovery.
if (peekToken().is(tok::l_brace)) {
consumeToken();
skipSingle();
}
return nullptr;
}
} else {
if (maybeDiagnoseInvalidCharInOperatorName(Tok)) {
// We're done diagnosing.
} else {
diagnose(Tok, diag::expected_operator_name_after_operator);
}

// To improve recovery, check to see if we have a { right after this token.
// If so, swallow until the end } to avoid tripping over the body of the
// malformed operator decl.
// If there's a deprecated body, skip it to improve recovery.
if (peekToken().is(tok::l_brace)) {
consumeToken();
skipSingle();
}

return nullptr;
}

Expand All @@ -7427,11 +7461,6 @@ Parser::parseDeclOperator(ParseDeclOptions Flags, DeclAttributes &Attributes) {
Identifier Name = Context.getIdentifier(Tok.getText());
SourceLoc NameLoc = consumeToken();

if (Attributes.hasAttribute<PostfixAttr>()) {
if (!Name.empty() && (Name.get()[0] == '?' || Name.get()[0] == '!'))
diagnose(NameLoc, diag::expected_operator_name_after_operator);
}

auto Result = parseDeclOperatorImpl(OperatorLoc, Name, NameLoc, Attributes);

if (!DCC.movedToTopLevel() && !AllowTopLevel) {
Expand Down
10 changes: 8 additions & 2 deletions lib/Sema/TypeCheckAttr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1503,10 +1503,16 @@ void AttributeChecker::visitFinalAttr(FinalAttr *attr) {
static bool isBuiltinOperator(StringRef name, DeclAttribute *attr) {
return ((isa<PrefixAttr>(attr) && name == "&") || // lvalue to inout
(isa<PostfixAttr>(attr) && name == "!") || // optional unwrapping
// FIXME: Not actually a builtin operator, but should probably
// be allowed and accounted for in Sema?
(isa<PrefixAttr>(attr) && name == "?") ||
(isa<PostfixAttr>(attr) && name == "?") || // optional chaining
(isa<InfixAttr>(attr) && name == "?") || // ternary operator
(isa<InfixAttr>(attr) && name == "?") || // ternary operator
(isa<PostfixAttr>(attr) && name == ">") || // generic argument list
(isa<PrefixAttr>(attr) && name == "<")); // generic argument list
(isa<PrefixAttr>(attr) && name == "<") || // generic argument list
name == "=" || // Assignment
// FIXME: Should probably be allowed in expression position?
name == "->");
}

void AttributeChecker::checkOperatorAttribute(DeclAttribute *attr) {
Expand Down
21 changes: 19 additions & 2 deletions test/Parse/operator_decl.swift
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,26 @@ prefix operator // expected-error {{expected operator name in operator declarati
prefix operator %%+

prefix operator ??
postfix operator ?? // expected-error {{expected operator name in operator declaration}}
postfix operator ?? // expected-error {{postfix operator names starting with '?' or '!' are disallowed to avoid collisions with built-in unwrapping operators}}
prefix operator !!
postfix operator !! // expected-error {{expected operator name in operator declaration}}
postfix operator !! // expected-error {{postfix operator names starting with '?' or '!' are disallowed to avoid collisions with built-in unwrapping operators}}
postfix operator ?$$
// expected-error@-1 {{postfix operator names starting with '?' or '!' are disallowed}}
// expected-error@-2 {{'$$' is considered an identifier}}

infix operator --aa // expected-error {{'aa' is considered an identifier and must not appear within an operator name}}
infix operator aa--: A // expected-error {{'aa' is considered an identifier and must not appear within an operator name}}
infix operator <<$$@< // expected-error {{'$$' is considered an identifier and must not appear within an operator name}}
infix operator !!@aa // expected-error {{'@' is not allowed in operator names}}
infix operator #++= // expected-error {{'#' is not allowed in operator names}}
infix operator ++=# // expected-error {{'#' is not allowed in operator names}}
infix operator -># // expected-error {{'#' is not allowed in operator names}}

// FIXME: Ideally, we shouldn't emit the «consistent whitespace» diagnostic
// where = cannot possibly mean an assignment.
infix operator =#=
// expected-error@-1 {{'#' is not allowed in operator names}}
// expected-error@-2 {{'=' must have consistent whitespace on both sides}}

infix operator +++=
infix operator *** : A
Expand Down
5 changes: 4 additions & 1 deletion test/Parse/recovery.swift
Original file line number Diff line number Diff line change
Expand Up @@ -818,10 +818,13 @@ func test23719432() {
}

// <rdar://problem/19911096> QoI: terrible recovery when using '·' for an operator
infix operator · { // expected-error {{'·' is considered to be an identifier, not an operator}}
infix operator · { // expected-error {{'·' is considered an identifier and must not appear within an operator name}}
associativity none precedence 150
}

infix operator -@-class Recover1 {} // expected-error {{'@' is not allowed in operator names}}
prefix operator -фф--class Recover2 {} // expected-error {{'фф' is considered an identifier and must not appear within an operator name}}

// <rdar://problem/21712891> Swift Compiler bug: String subscripts with range should require closing bracket.
func r21712891(s : String) -> String {
let a = s.startIndex..<s.startIndex
Expand Down
8 changes: 6 additions & 2 deletions test/decl/func/operator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -172,13 +172,16 @@ infix prefix func +-+(x: Double, y: Double) {} // expected-error {{'infix' modif

// Don't allow one to define a postfix '!'; it's built into the
// language. Also illegal to have any postfix operator starting with '!'.
postfix operator ! // expected-error {{cannot declare a custom postfix '!' operator}} expected-error {{expected operator name in operator declaration}}
postfix operator ! // expected-error {{cannot declare a custom postfix '!' operator}} expected-error {{postfix operator names starting with '?' or '!' are disallowed}}
prefix operator & // expected-error {{cannot declare a custom prefix '&' operator}}

// <rdar://problem/14607026> Restrict use of '<' and '>' as prefix/postfix operator names
postfix operator > // expected-error {{cannot declare a custom postfix '>' operator}}
prefix operator < // expected-error {{cannot declare a custom prefix '<' operator}}

infix operator = // expected-error {{cannot declare a custom infix '=' operator}}
infix operator -> // expected-error {{cannot declare a custom infix '->' operator}}

postfix func !(x: Int) { } // expected-error{{cannot declare a custom postfix '!' operator}}
postfix func!(x: Int8) { } // expected-error{{cannot declare a custom postfix '!' operator}}
prefix func & (x: Int) {} // expected-error {{cannot declare a custom prefix '&' operator}}
Expand All @@ -188,7 +191,8 @@ func operator_in_func_bad () {
prefix func + (input: String) -> String { return "+" + input } // expected-error {{operator functions can only be declared at global or in type scope}}
}

infix operator ? // expected-error {{expected operator name in operator declaration}}
infix operator ? // expected-error {{cannot declare a custom infix '?' operator}}
prefix operator ? // expected-error {{cannot declare a custom prefix '?' operator}}

infix operator ??=

Expand Down