Skip to content

Commit 61606a6

Browse files
committed
[Parse] Provide better diagnostics for func 1() {}.
It is apparently a common mistake for beginners to start the names of functions etc. with numbers, and before this patch the diagnostic wasn't specific about the problem. It seems likely that most instances of `func 1(...` will be mistakes in this vein, so this patch specifically diagnoses that case, and also parses the number as the identifier to avoid follow on errors about top-level closures (from the {}) and other invalid syntax. Fixes rdar://problem/32316666 .
1 parent 77de3dc commit 61606a6

File tree

3 files changed

+100
-1
lines changed

3 files changed

+100
-1
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,9 @@ ERROR(expected_decl,none,
182182
"expected declaration", ())
183183
ERROR(expected_identifier_in_decl,none,
184184
"expected identifier in %0 declaration", (StringRef))
185+
ERROR(number_cant_start_decl_name,none,
186+
"%0 name can only start with a letter or underscore, not a number",
187+
(StringRef))
185188
ERROR(expected_identifier_after_case_comma,none,
186189
"expected identifier after comma in enum 'case' declaration", ())
187190
ERROR(decl_redefinition,none,

lib/Parse/ParseDecl.cpp

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2766,6 +2766,23 @@ parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc,
27662766

27672767
P.checkForInputIncomplete();
27682768

2769+
if (P.Tok.is(tok::integer_literal) || P.Tok.is(tok::floating_literal) ||
2770+
(P.Tok.is(tok::unknown) && isdigit(P.Tok.getText()[0]))) {
2771+
// Per rdar://problem/32316666, using numbers for identifiers is a common
2772+
// error for beginners, so it's worth handling this in a special way.
2773+
P.diagnose(P.Tok, diag::number_cant_start_decl_name, DeclKindName);
2774+
2775+
// Pretend this works as an identifier, which shouldn't be observable since
2776+
// actual uses of it will hit random other errors, e.g. `1()` won't be
2777+
// callable.
2778+
Result = P.Context.getIdentifier(P.Tok.getText());
2779+
Loc = P.Tok.getLoc();
2780+
P.consumeToken();
2781+
2782+
// We recovered, so this is a success.
2783+
return makeParserSuccess();
2784+
}
2785+
27692786
if (P.Tok.isKeyword()) {
27702787
P.diagnose(P.Tok, diag::keyword_cant_be_identifier, P.Tok.getText());
27712788
P.diagnose(P.Tok, diag::backticks_to_escape)
@@ -4708,7 +4725,12 @@ Parser::parseDeclFunc(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling,
47084725
Token NameTok = Tok;
47094726
SourceLoc NameLoc;
47104727

4711-
if (Tok.is(tok::identifier) || Tok.isKeyword()) {
4728+
if (Tok.isAny(tok::identifier, tok::integer_literal, tok::floating_literal,
4729+
tok::unknown) ||
4730+
Tok.isKeyword()) {
4731+
// This non-operator path is quite accepting of what tokens might be a name,
4732+
// because we're aggressive about recovering/providing good diagnostics for
4733+
// beginners.
47124734
ParserStatus NameStatus =
47134735
parseIdentifierDeclName(*this, SimpleName, NameLoc, "function",
47144736
tok::l_paren, tok::arrow, tok::l_brace,
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// Per rdar://problem/32316666 , it is a common mistake for beginners
4+
// to start a function name with a number, so it's worth
5+
// special-casing the diagnostic to make it clearer.
6+
7+
func 1() {}
8+
// expected-error@-1 {{function name can only start with a letter or underscore, not a number}}
9+
func 2.0() {}
10+
// expected-error@-1 {{function name can only start with a letter or underscore, not a number}}
11+
func 3func() {}
12+
// expected-error@-1 {{function name can only start with a letter or underscore, not a number}}
13+
// expected-error@-2 {{expected a digit after integer literal prefix}}
14+
15+
protocol 4 {
16+
// expected-error@-1 {{protocol name can only start with a letter or underscore, not a number}}
17+
associatedtype 5
18+
// expected-error@-1 {{associatedtype name can only start with a letter or underscore, not a number}}
19+
}
20+
protocol 6.0 {
21+
// expected-error@-1 {{protocol name can only start with a letter or underscore, not a number}}
22+
associatedtype 7.0
23+
// expected-error@-1 {{associatedtype name can only start with a letter or underscore, not a number}}
24+
}
25+
protocol 8protocol {
26+
// expected-error@-1 {{protocol name can only start with a letter or underscore, not a number}}
27+
// expected-error@-2 {{expected a digit after integer literal prefix}}
28+
associatedtype 9associatedtype
29+
// expected-error@-1 {{associatedtype name can only start with a letter or underscore, not a number}}
30+
// expected-error@-2 {{expected a digit after integer literal prefix}}
31+
}
32+
33+
typealias 10 = Int
34+
// expected-error@-1 {{typealias name can only start with a letter or underscore, not a number}}
35+
typealias 11.0 = Int
36+
// expected-error@-1 {{typealias name can only start with a letter or underscore, not a number}}
37+
typealias 12typealias = Int
38+
// expected-error@-1 {{typealias name can only start with a letter or underscore, not a number}}
39+
// expected-error@-2 {{expected a digit after integer literal prefix}}
40+
41+
struct 13 {}
42+
// expected-error@-1 {{struct name can only start with a letter or underscore, not a number}}
43+
struct 14.0 {}
44+
// expected-error@-1 {{struct name can only start with a letter or underscore, not a number}}
45+
struct 15struct {}
46+
// expected-error@-1 {{struct name can only start with a letter or underscore, not a number}}
47+
// expected-error@-2 {{expected a digit after integer literal prefix}}
48+
49+
enum 16 {}
50+
// expected-error@-1 {{enum name can only start with a letter or underscore, not a number}}
51+
enum 17.0 {}
52+
// expected-error@-1 {{enum name can only start with a letter or underscore, not a number}}
53+
enum 18enum {}
54+
// expected-error@-1 {{enum name can only start with a letter or underscore, not a number}}
55+
// expected-error@-2 {{expected a digit in floating point exponent}}
56+
57+
class 19 {
58+
// expected-error@-1 {{class name can only start with a letter or underscore, not a number}}
59+
func 20() {}
60+
// expected-error@-1 {{function name can only start with a letter or underscore, not a number}}
61+
}
62+
class 21.0 {
63+
// expected-error@-1 {{class name can only start with a letter or underscore, not a number}}
64+
func 22.0() {}
65+
// expected-error@-1 {{function name can only start with a letter or underscore, not a number}}
66+
}
67+
68+
class 23class {
69+
// expected-error@-1 {{class name can only start with a letter or underscore, not a number}}
70+
// expected-error@-2 {{expected a digit after integer literal prefix}}
71+
func 24method() {}
72+
// expected-error@-1 {{function name can only start with a letter or underscore, not a number}}
73+
// expected-error@-2 {{expected a digit after integer literal prefix}}
74+
}

0 commit comments

Comments
 (0)