Skip to content

Commit bd71e63

Browse files
authored
Merge pull request #5270 from CodaFi/going-toe-to-toe-on-bird-law
Disable the ability to use $ as an identifier head harder
2 parents bfd78f0 + 6accc59 commit bd71e63

File tree

5 files changed

+80
-22
lines changed

5 files changed

+80
-22
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -994,7 +994,9 @@ ERROR(expected_dollar_numeric,none,
994994
ERROR(dollar_numeric_too_large,none,
995995
"numeric value following '$' is too large", ())
996996
ERROR(numeric_literal_numeric_member,none,
997-
"expected named member of numeric literal", ())
997+
"expected named member of numeric literal", ())
998+
ERROR(standalone_dollar_identifier,none,
999+
"'$' is not an identifier; use backticks to escape it", ())
9981000

9991001
ERROR(anon_closure_arg_not_in_closure,none,
10001002
"anonymous closure argument not contained in a closure", ())

lib/Basic/StringExtras.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ using namespace swift;
2828
using namespace camel_case;
2929

3030
bool swift::canBeArgumentLabel(StringRef identifier) {
31-
if (identifier == "var" || identifier == "let" || identifier == "inout")
31+
if (identifier == "var" || identifier == "let" || identifier == "inout" ||
32+
identifier == "$")
3233
return false;
3334

3435
return true;

lib/Parse/Lexer.cpp

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -474,7 +474,7 @@ static bool isValidIdentifierContinuationCodePoint(uint32_t c) {
474474
static bool isValidIdentifierStartCodePoint(uint32_t c) {
475475
if (!isValidIdentifierContinuationCodePoint(c))
476476
return false;
477-
if (c < 0x80 && (isDigit(c) || c == '$'))
477+
if (c < 0x80 && isDigit(c))
478478
return false;
479479

480480
// N1518: Recommendations for extended identifier characters for C and C++
@@ -819,10 +819,15 @@ void Lexer::lexDollarIdent() {
819819
}
820820
}
821821

822-
// It's always an error to see a standalone $
823822
if (CurPtr == tokStart + 1) {
824-
diagnose(tokStart, diag::expected_dollar_numeric);
825-
return formToken(tok::unknown, tokStart);
823+
// It is always an error to see a standalone '$' when not in Swift 3
824+
// compatibility mode.
825+
if (!LangOpts.isSwiftVersion3()) {
826+
// Offer to replace '$' with '`$`'.
827+
diagnose(tokStart, diag::standalone_dollar_identifier)
828+
.fixItReplaceChars(getSourceLoc(tokStart), getSourceLoc(CurPtr), "`$`");
829+
}
830+
return formToken(tok::identifier, tokStart);
826831
}
827832

828833
// We reserve $nonNumeric for persistent bindings in the debugger.
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// RUN: %target-parse-verify-swift -swift-version 3
2+
3+
// Dollar is allowed as an identifier head in Swift 3.
4+
5+
func dollarVar() {
6+
var $ : Int = 42 // No error
7+
$ += 1
8+
print($)
9+
}
10+
func dollarLet() {
11+
let $ = 42 // No error
12+
print($)
13+
}
14+
func dollarClass() {
15+
class $ {} // No error
16+
}
17+
func dollarEnum() {
18+
enum $ {} // No error
19+
}
20+
func dollarStruct() {
21+
struct $ {} // No error
22+
}
23+

test/Parse/dollar_identifier.swift

Lines changed: 43 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,56 @@
1-
// RUN: %target-parse-verify-swift
1+
// RUN: %target-parse-verify-swift -swift-version 4
22

3-
// SR-1661: Dollar was accidentally allowed as an identifier and identifier head.
3+
// SR-1661: Dollar was accidentally allowed as an identifier in Swift 3.
4+
// SE-0144: Reject this behavior in the future.
45

56
func dollarVar() {
6-
var $ : Int = 42 // expected-error {{expected numeric value following '$'}} expected-error {{expected pattern}}
7+
var $ : Int = 42 // expected-error {{'$' is not an identifier; use backticks to escape it}} {{7-8=`$`}}
8+
$ += 1 // expected-error {{'$' is not an identifier; use backticks to escape it}} {{3-4=`$`}}
9+
print($) // expected-error {{'$' is not an identifier; use backticks to escape it}} {{9-10=`$`}}
710
}
811
func dollarLet() {
9-
let $ = 42 // expected-error {{expected numeric value following '$'}} expected-error {{expected pattern}}
12+
let $ = 42 // expected-error {{'$' is not an identifier; use backticks to escape it}} {{7-8=`$`}}
13+
print($) // expected-error {{'$' is not an identifier; use backticks to escape it}} {{9-10=`$`}}
1014
}
1115
func dollarClass() {
12-
class $ {} // expected-error {{expected numeric value following '$'}}
13-
// expected-error@-1 {{expression resolves to an unused function}}
14-
// expected-error@-2 {{expected identifier in class declaration}}
15-
// expected-error@-3 {{braced block of statements is an unused closure}}
16+
class $ {} // expected-error {{'$' is not an identifier; use backticks to escape it}} {{9-10=`$`}}
1617
}
1718
func dollarEnum() {
18-
enum $ {} // expected-error {{expected numeric value following '$'}}
19-
// expected-error@-1 {{expected identifier in enum declaration}}
20-
// expected-error@-2 {{expression resolves to an unused function}}
21-
// expected-error@-3 {{braced block of statements is an unused closure}}
19+
enum $ {} // expected-error {{'$' is not an identifier; use backticks to escape it}} {{8-9=`$`}}
2220
}
2321
func dollarStruct() {
24-
struct $ {} // expected-error {{expected numeric value following '$'}}
25-
// expected-error@-1 {{expected identifier in struct declaration}}
26-
// expected-error@-2 {{braced block of statements is an unused closure}}
27-
// expected-error@-3 {{expression resolves to an unused function}}
22+
struct $ {} // expected-error {{'$' is not an identifier; use backticks to escape it}} {{10-11=`$`}}
2823
}
2924

25+
func dollarFunc() {
26+
func $($ dollarParam: Int) {}
27+
// expected-error@-1 {{'$' is not an identifier; use backticks to escape it}} {{8-9=`$`}}
28+
// expected-error@-2 {{'$' is not an identifier; use backticks to escape it}} {{10-11=`$`}}
29+
$($: 24)
30+
// expected-error@-1 {{'$' is not an identifier; use backticks to escape it}} {{3-4=`$`}}
31+
// expected-error@-2 {{'$' is not an identifier; use backticks to escape it}} {{5-6=`$`}}
32+
}
33+
34+
func escapedDollarVar() {
35+
var `$` : Int = 42 // no error
36+
`$` += 1
37+
print(`$`)
38+
}
39+
func escapedDollarLet() {
40+
let `$` = 42 // no error
41+
print(`$`)
42+
}
43+
func escapedDollarClass() {
44+
class `$` {} // no error
45+
}
46+
func escapedDollarEnum() {
47+
enum `$` {} // no error
48+
}
49+
func escapedDollarStruct() {
50+
struct `$` {} // no error
51+
}
52+
53+
func escapedDollarFunc() {
54+
func `$`(`$`: Int) {} // no error
55+
`$`(`$`: 25) // no error
56+
}

0 commit comments

Comments
 (0)