Skip to content

Commit a11cc4f

Browse files
committed
Handle more built-in operators and error intersections with the unwrap collision diagnostic
1 parent 1c0c397 commit a11cc4f

File tree

5 files changed

+35
-18
lines changed

5 files changed

+35
-18
lines changed

lib/Parse/Lexer.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -829,7 +829,8 @@ void Lexer::lexOperatorIdentifier() {
829829
if (CurPtr-TokStart == 1) {
830830
switch (TokStart[0]) {
831831
case '=':
832-
if (leftBound != rightBound) {
832+
// Refrain from emitting this message in operator name position.
833+
if (NextToken.isNot(tok::kw_operator) && leftBound != rightBound) {
833834
auto d = diagnose(TokStart, diag::lex_unary_equal);
834835
if (leftBound)
835836
d.fixItInsert(getSourceLoc(TokStart), " ");

lib/Parse/ParseDecl.cpp

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7416,10 +7416,20 @@ Parser::parseDeclOperator(ParseDeclOptions Flags, DeclAttributes &Attributes) {
74167416
return false;
74177417
};
74187418

7419+
// Postfix operators starting with ? or ! conflict with builtin
7420+
// unwrapping operators.
7421+
if (Attributes.hasAttribute<PostfixAttr>())
7422+
if (!Tok.getText().empty() && (Tok.getRawText().front() == '?' ||
7423+
Tok.getRawText().front() == '!'))
7424+
diagnose(Tok, diag::postfix_operator_name_cannot_start_with_unwrap);
7425+
74197426
// A common error is to try to define an operator with something in the
74207427
// unicode plane considered to be an operator, or to try to define an
74217428
// operator like "not". Analyze and diagnose this specifically.
7422-
if (Tok.isAnyOperator()) {
7429+
if (Tok.isAnyOperator() || Tok.isAny(tok::exclaim_postfix,
7430+
tok::question_infix,
7431+
tok::question_postfix,
7432+
tok::equal, tok::arrow)) {
74237433
if (peekToken().getLoc() == Tok.getRange().getEnd() &&
74247434
maybeDiagnoseInvalidCharInOperatorName(peekToken())) {
74257435
consumeToken();
@@ -7431,9 +7441,7 @@ Parser::parseDeclOperator(ParseDeclOptions Flags, DeclAttributes &Attributes) {
74317441
}
74327442
return nullptr;
74337443
}
7434-
// We will handle these either below or in Sema.
7435-
} else if (Tok.isNot(tok::exclaim_postfix, tok::question_infix,
7436-
tok::question_postfix)) {
7444+
} else {
74377445
if (maybeDiagnoseInvalidCharInOperatorName(Tok)) {
74387446
// We're done diagnosing.
74397447
} else {
@@ -7453,13 +7461,6 @@ Parser::parseDeclOperator(ParseDeclOptions Flags, DeclAttributes &Attributes) {
74537461
Identifier Name = Context.getIdentifier(Tok.getText());
74547462
SourceLoc NameLoc = consumeToken();
74557463

7456-
// Postfix operators starting with ? or ! conflict with builtin
7457-
// unwrapping operators.
7458-
if (Attributes.hasAttribute<PostfixAttr>()) {
7459-
if (!Name.empty() && (Name.get()[0] == '?' || Name.get()[0] == '!'))
7460-
diagnose(NameLoc, diag::postfix_operator_name_cannot_start_with_unwrap);
7461-
}
7462-
74637464
auto Result = parseDeclOperatorImpl(OperatorLoc, Name, NameLoc, Attributes);
74647465

74657466
if (!DCC.movedToTopLevel() && !AllowTopLevel) {

lib/Sema/TypeCheckAttr.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1505,11 +1505,14 @@ static bool isBuiltinOperator(StringRef name, DeclAttribute *attr) {
15051505
(isa<PostfixAttr>(attr) && name == "!") || // optional unwrapping
15061506
// FIXME: Not actually a builtin operator, but should probably
15071507
// be allowed and accounted for in Sema?
1508-
(isa<PrefixAttr>(attr) && name == "?") ||
1508+
(isa<PrefixAttr>(attr) && name == "?") ||
15091509
(isa<PostfixAttr>(attr) && name == "?") || // optional chaining
1510-
(isa<InfixAttr>(attr) && name == "?") || // ternary operator
1510+
(isa<InfixAttr>(attr) && name == "?") || // ternary operator
15111511
(isa<PostfixAttr>(attr) && name == ">") || // generic argument list
1512-
(isa<PrefixAttr>(attr) && name == "<")); // generic argument list
1512+
(isa<PrefixAttr>(attr) && name == "<") || // generic argument list
1513+
name == "=" || // Assignment
1514+
// FIXME: Should probably be allowed in expression position?
1515+
name == "->");
15131516
}
15141517

15151518
void AttributeChecker::checkOperatorAttribute(DeclAttribute *attr) {

test/Parse/operator_decl.swift

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,16 +40,26 @@ prefix operator // expected-error {{expected operator name in operator declarati
4040
prefix operator %%+
4141

4242
prefix operator ??
43-
postfix operator ?? // expected-error {{postfix operator names starting with '?' or '!' are disallowed}}
43+
postfix operator ?? // expected-error {{postfix operator names starting with '?' or '!' are disallowed to avoid collisions with built-in unwrapping operators}}
4444
prefix operator !!
45-
postfix operator !! // expected-error {{postfix operator names starting with '?' or '!' are disallowed}}
45+
postfix operator !! // expected-error {{postfix operator names starting with '?' or '!' are disallowed to avoid collisions with built-in unwrapping operators}}
46+
postfix operator ?$$
47+
// expected-error@-1 {{postfix operator names starting with '?' or '!' are disallowed}}
48+
// expected-error@-2 {{'$$' is considered an identifier}}
4649

4750
infix operator --aa // expected-error {{'aa' is considered an identifier and must not appear within an operator name}}
4851
infix operator aa--: A // expected-error {{'aa' is considered an identifier and must not appear within an operator name}}
4952
infix operator <<$$@< // expected-error {{'$$' is considered an identifier and must not appear within an operator name}}
5053
infix operator !!@aa // expected-error {{'@' is not allowed in operator names}}
5154
infix operator #++= // expected-error {{'#' is not allowed in operator names}}
5255
infix operator ++=# // expected-error {{'#' is not allowed in operator names}}
56+
infix operator -># // expected-error {{'#' is not allowed in operator names}}
57+
58+
// FIXME: Ideally, we shouldn't emit the «consistent whitespace» diagnostic
59+
// where = cannot possibly mean an assignment.
60+
infix operator =#=
61+
// expected-error@-1 {{'#' is not allowed in operator names}}
62+
// expected-error@-2 {{'=' must have consistent whitespace on both sides}}
5363

5464
infix operator +++=
5565
infix operator *** : A

test/decl/func/operator.swift

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,6 +179,9 @@ prefix operator & // expected-error {{cannot declare a custom prefix '&' operat
179179
postfix operator > // expected-error {{cannot declare a custom postfix '>' operator}}
180180
prefix operator < // expected-error {{cannot declare a custom prefix '<' operator}}
181181

182+
infix operator = // expected-error {{cannot declare a custom infix '=' operator}}
183+
infix operator -> // expected-error {{cannot declare a custom infix '->' operator}}
184+
182185
postfix func !(x: Int) { } // expected-error{{cannot declare a custom postfix '!' operator}}
183186
postfix func!(x: Int8) { } // expected-error{{cannot declare a custom postfix '!' operator}}
184187
prefix func & (x: Int) {} // expected-error {{cannot declare a custom prefix '&' operator}}
@@ -189,7 +192,6 @@ func operator_in_func_bad () {
189192
}
190193

191194
infix operator ? // expected-error {{cannot declare a custom infix '?' operator}}
192-
193195
prefix operator ? // expected-error {{cannot declare a custom prefix '?' operator}}
194196

195197
infix operator ??=

0 commit comments

Comments
 (0)