From 6accc5989ef9851395700ef49e5000957f53a41a Mon Sep 17 00:00:00 2001 From: Robert Widmann Date: Thu, 13 Oct 2016 11:47:05 -0400 Subject: [PATCH] Disable the ability to use '$' as an identifier harder When in Swift 3 Compatibility Mode we now acceptable a standalone '$' as an identifier. In all other cases this is now disallowed and must be surrounded by backticks. --- include/swift/AST/DiagnosticsParse.def | 4 +- lib/Basic/StringExtras.cpp | 3 +- lib/Parse/Lexer.cpp | 13 +++-- test/Compatibility/dollar_identifier.swift | 23 +++++++++ test/Parse/dollar_identifier.swift | 59 ++++++++++++++++------ 5 files changed, 80 insertions(+), 22 deletions(-) create mode 100644 test/Compatibility/dollar_identifier.swift diff --git a/include/swift/AST/DiagnosticsParse.def b/include/swift/AST/DiagnosticsParse.def index ebe7200ae322a..e1a3bb9833717 100644 --- a/include/swift/AST/DiagnosticsParse.def +++ b/include/swift/AST/DiagnosticsParse.def @@ -994,7 +994,9 @@ ERROR(expected_dollar_numeric,none, ERROR(dollar_numeric_too_large,none, "numeric value following '$' is too large", ()) ERROR(numeric_literal_numeric_member,none, - "expected named member of numeric literal", ()) + "expected named member of numeric literal", ()) +ERROR(standalone_dollar_identifier,none, + "'$' is not an identifier; use backticks to escape it", ()) ERROR(anon_closure_arg_not_in_closure,none, "anonymous closure argument not contained in a closure", ()) diff --git a/lib/Basic/StringExtras.cpp b/lib/Basic/StringExtras.cpp index 8c2993230dc30..486cb2e16b817 100644 --- a/lib/Basic/StringExtras.cpp +++ b/lib/Basic/StringExtras.cpp @@ -28,7 +28,8 @@ using namespace swift; using namespace camel_case; bool swift::canBeArgumentLabel(StringRef identifier) { - if (identifier == "var" || identifier == "let" || identifier == "inout") + if (identifier == "var" || identifier == "let" || identifier == "inout" || + identifier == "$") return false; return true; diff --git a/lib/Parse/Lexer.cpp b/lib/Parse/Lexer.cpp index 07986369b3025..0d5d6a2bf0b46 100644 --- a/lib/Parse/Lexer.cpp +++ b/lib/Parse/Lexer.cpp @@ -474,7 +474,7 @@ static bool isValidIdentifierContinuationCodePoint(uint32_t c) { static bool isValidIdentifierStartCodePoint(uint32_t c) { if (!isValidIdentifierContinuationCodePoint(c)) return false; - if (c < 0x80 && (isDigit(c) || c == '$')) + if (c < 0x80 && isDigit(c)) return false; // N1518: Recommendations for extended identifier characters for C and C++ @@ -819,10 +819,15 @@ void Lexer::lexDollarIdent() { } } - // It's always an error to see a standalone $ if (CurPtr == tokStart + 1) { - diagnose(tokStart, diag::expected_dollar_numeric); - return formToken(tok::unknown, tokStart); + // It is always an error to see a standalone '$' when not in Swift 3 + // compatibility mode. + if (!LangOpts.isSwiftVersion3()) { + // Offer to replace '$' with '`$`'. + diagnose(tokStart, diag::standalone_dollar_identifier) + .fixItReplaceChars(getSourceLoc(tokStart), getSourceLoc(CurPtr), "`$`"); + } + return formToken(tok::identifier, tokStart); } // We reserve $nonNumeric for persistent bindings in the debugger. diff --git a/test/Compatibility/dollar_identifier.swift b/test/Compatibility/dollar_identifier.swift new file mode 100644 index 0000000000000..bcb686639bde0 --- /dev/null +++ b/test/Compatibility/dollar_identifier.swift @@ -0,0 +1,23 @@ +// RUN: %target-parse-verify-swift -swift-version 3 + +// Dollar is allowed as an identifier head in Swift 3. + +func dollarVar() { + var $ : Int = 42 // No error + $ += 1 + print($) +} +func dollarLet() { + let $ = 42 // No error + print($) +} +func dollarClass() { + class $ {} // No error +} +func dollarEnum() { + enum $ {} // No error +} +func dollarStruct() { + struct $ {} // No error +} + diff --git a/test/Parse/dollar_identifier.swift b/test/Parse/dollar_identifier.swift index bea5eaf92ad15..986668551d313 100644 --- a/test/Parse/dollar_identifier.swift +++ b/test/Parse/dollar_identifier.swift @@ -1,29 +1,56 @@ -// RUN: %target-parse-verify-swift +// RUN: %target-parse-verify-swift -swift-version 4 -// SR-1661: Dollar was accidentally allowed as an identifier and identifier head. +// SR-1661: Dollar was accidentally allowed as an identifier in Swift 3. +// SE-0144: Reject this behavior in the future. func dollarVar() { - var $ : Int = 42 // expected-error {{expected numeric value following '$'}} expected-error {{expected pattern}} + var $ : Int = 42 // expected-error {{'$' is not an identifier; use backticks to escape it}} {{7-8=`$`}} + $ += 1 // expected-error {{'$' is not an identifier; use backticks to escape it}} {{3-4=`$`}} + print($) // expected-error {{'$' is not an identifier; use backticks to escape it}} {{9-10=`$`}} } func dollarLet() { - let $ = 42 // expected-error {{expected numeric value following '$'}} expected-error {{expected pattern}} + let $ = 42 // expected-error {{'$' is not an identifier; use backticks to escape it}} {{7-8=`$`}} + print($) // expected-error {{'$' is not an identifier; use backticks to escape it}} {{9-10=`$`}} } func dollarClass() { - class $ {} // expected-error {{expected numeric value following '$'}} - // expected-error@-1 {{expression resolves to an unused function}} - // expected-error@-2 {{expected identifier in class declaration}} - // expected-error@-3 {{braced block of statements is an unused closure}} + class $ {} // expected-error {{'$' is not an identifier; use backticks to escape it}} {{9-10=`$`}} } func dollarEnum() { - enum $ {} // expected-error {{expected numeric value following '$'}} - // expected-error@-1 {{expected identifier in enum declaration}} - // expected-error@-2 {{expression resolves to an unused function}} - // expected-error@-3 {{braced block of statements is an unused closure}} + enum $ {} // expected-error {{'$' is not an identifier; use backticks to escape it}} {{8-9=`$`}} } func dollarStruct() { - struct $ {} // expected-error {{expected numeric value following '$'}} - // expected-error@-1 {{expected identifier in struct declaration}} - // expected-error@-2 {{braced block of statements is an unused closure}} - // expected-error@-3 {{expression resolves to an unused function}} + struct $ {} // expected-error {{'$' is not an identifier; use backticks to escape it}} {{10-11=`$`}} } +func dollarFunc() { + func $($ dollarParam: Int) {} + // expected-error@-1 {{'$' is not an identifier; use backticks to escape it}} {{8-9=`$`}} + // expected-error@-2 {{'$' is not an identifier; use backticks to escape it}} {{10-11=`$`}} + $($: 24) + // expected-error@-1 {{'$' is not an identifier; use backticks to escape it}} {{3-4=`$`}} + // expected-error@-2 {{'$' is not an identifier; use backticks to escape it}} {{5-6=`$`}} +} + +func escapedDollarVar() { + var `$` : Int = 42 // no error + `$` += 1 + print(`$`) +} +func escapedDollarLet() { + let `$` = 42 // no error + print(`$`) +} +func escapedDollarClass() { + class `$` {} // no error +} +func escapedDollarEnum() { + enum `$` {} // no error +} +func escapedDollarStruct() { + struct `$` {} // no error +} + +func escapedDollarFunc() { + func `$`(`$`: Int) {} // no error + `$`(`$`: 25) // no error +}