Skip to content

Commit b4731f8

Browse files
author
ematejska
authored
Merge pull request #10458 from huonw/number-function
[Parse] Provide better diagnostics for `func 1() {}`.
2 parents 5289bf3 + be7fddc commit b4731f8

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
@@ -2739,6 +2739,23 @@ parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc,
27392739

27402740
P.checkForInputIncomplete();
27412741

2742+
if (P.Tok.is(tok::integer_literal) || P.Tok.is(tok::floating_literal) ||
2743+
(P.Tok.is(tok::unknown) && isdigit(P.Tok.getText()[0]))) {
2744+
// Per rdar://problem/32316666, using numbers for identifiers is a common
2745+
// error for beginners, so it's worth handling this in a special way.
2746+
P.diagnose(P.Tok, diag::number_cant_start_decl_name, DeclKindName);
2747+
2748+
// Pretend this works as an identifier, which shouldn't be observable since
2749+
// actual uses of it will hit random other errors, e.g. `1()` won't be
2750+
// callable.
2751+
Result = P.Context.getIdentifier(P.Tok.getText());
2752+
Loc = P.Tok.getLoc();
2753+
P.consumeToken();
2754+
2755+
// We recovered, so this is a success.
2756+
return makeParserSuccess();
2757+
}
2758+
27422759
if (P.Tok.isKeyword()) {
27432760
P.diagnose(P.Tok, diag::keyword_cant_be_identifier, P.Tok.getText());
27442761
P.diagnose(P.Tok, diag::backticks_to_escape)
@@ -4682,7 +4699,12 @@ Parser::parseDeclFunc(SourceLoc StaticLoc, StaticSpellingKind StaticSpelling,
46824699
Token NameTok = Tok;
46834700
SourceLoc NameLoc;
46844701

4685-
if (Tok.is(tok::identifier) || Tok.isKeyword()) {
4702+
if (Tok.isAny(tok::identifier, tok::integer_literal, tok::floating_literal,
4703+
tok::unknown) ||
4704+
Tok.isKeyword()) {
4705+
// This non-operator path is quite accepting of what tokens might be a name,
4706+
// because we're aggressive about recovering/providing good diagnostics for
4707+
// beginners.
46864708
ParserStatus NameStatus =
46874709
parseIdentifierDeclName(*this, SimpleName, NameLoc, "function",
46884710
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)