Skip to content

Commit 4df138a

Browse files
committed
Diagnose incorrect uses of module selectors
# Conflicts: # lib/Parse/ParseDecl.cpp
1 parent 7a1cf71 commit 4df138a

File tree

7 files changed

+195
-14
lines changed

7 files changed

+195
-14
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,9 @@ ERROR(expected_identifier_in_module_selector,none,
9595
"expected identifier in module selector", ())
9696
ERROR(module_selector_not_allowed_here,none,
9797
"name cannot be qualified with module selector here", ())
98+
ERROR(module_selector_not_allowed_in_decl,none,
99+
"%select{%1|name of %1 declaration}0 cannot be qualified with module "
100+
"selector", (bool, StringRef))
98101

99102
//------------------------------------------------------------------------------
100103
// MARK: Lexer diagnostics

include/swift/Parse/Parser.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -645,6 +645,21 @@ class Parser {
645645
Context.getIdentifier(tok.getText()));
646646
}
647647

648+
/// If the next two tokens appear to form a module selector, consume them and
649+
/// diagnose an error. Call this method before consuming an identifier that
650+
/// should *not* have a module selector before it.
651+
///
652+
/// \param KindName A string describing the name or declaration being written.
653+
/// \param IsDef If \c true, the identifier defines a declaration's name, and
654+
/// \p KindName should be a string describing the declaration; if \c false,
655+
/// the identifier does something else, and \p KindName should be a more
656+
/// complete string.
657+
///
658+
/// \returns true if a module selector was consumed and an error was diagnosed;
659+
/// false otherwise.
660+
bool
661+
diagnoseAndConsumeIfModuleSelector(StringRef KindName, bool IsDef = true);
662+
648663
/// Retrieve the location just past the end of the previous
649664
/// source location.
650665
SourceLoc getEndOfPreviousLoc() const;

lib/Parse/ParseDecl.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1968,6 +1968,10 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
19681968
return false;
19691969
}
19701970

1971+
diagnoseAndConsumeIfModuleSelector(
1972+
"Objective-C class name in @_swift_native_objc_runtime_base",
1973+
/*IsDef=*/true);
1974+
19711975
if (Tok.isNot(tok::identifier)) {
19721976
diagnose(Loc, diag::swift_native_objc_runtime_base_must_be_identifier);
19731977
return false;
@@ -2588,6 +2592,8 @@ bool Parser::parseNewDeclAttribute(DeclAttributes &Attributes, SourceLoc AtLoc,
25882592
}
25892593

25902594
case DAK_ProjectedValueProperty: {
2595+
diagnoseAndConsumeIfModuleSelector("@_projectedValueProperty attribute");
2596+
25912597
if (!consumeIf(tok::l_paren)) {
25922598
diagnose(Loc, diag::attr_expected_lparen, AttrName,
25932599
DeclAttribute::isDeclModifier(DK));
@@ -4807,10 +4813,28 @@ ParserStatus Parser::parseInheritance(
48074813
return Status;
48084814
}
48094815

4816+
bool Parser::diagnoseAndConsumeIfModuleSelector(StringRef KindName,
4817+
bool IsDefinition) {
4818+
if (!Context.LangOpts.EnableExperimentalModuleSelector ||
4819+
peekToken().isNot(tok::colon_colon))
4820+
return false;
4821+
4822+
// Diagnose an error and consume the module selector so we can continue.
4823+
SourceLoc start = consumeToken();
4824+
SourceLoc end = consumeToken(tok::colon_colon);
4825+
diagnose(end, diag::module_selector_not_allowed_in_decl, IsDefinition,
4826+
KindName)
4827+
.fixItRemove({start, end});
4828+
4829+
return true;
4830+
}
4831+
48104832
static ParserStatus
48114833
parseIdentifierDeclName(Parser &P, Identifier &Result, SourceLoc &Loc,
48124834
StringRef DeclKindName,
48134835
llvm::function_ref<bool(const Token &)> canRecover) {
4836+
P.diagnoseAndConsumeIfModuleSelector(DeclKindName);
4837+
48144838
if (P.Tok.is(tok::identifier)) {
48154839
Loc = P.consumeIdentifier(Result, /*diagnoseDollarPrefix=*/true);
48164840

@@ -5725,6 +5749,9 @@ static ParameterList *parseOptionalAccessorArgument(SourceLoc SpecifierLoc,
57255749
if (SpecifierLoc.isValid() && P.Tok.is(tok::l_paren)) {
57265750
SyntaxParsingContext ParamCtx(P.SyntaxContext, SyntaxKind::AccessorParameter);
57275751
StartLoc = P.consumeToken(tok::l_paren);
5752+
5753+
P.diagnoseAndConsumeIfModuleSelector("accessor parameter");
5754+
57285755
if (P.Tok.isNot(tok::identifier)) {
57295756
P.diagnose(P.Tok, diag::expected_accessor_parameter_name,
57305757
Kind == AccessorKind::Set ? 0 :
@@ -7209,6 +7236,8 @@ Parser::parseDeclEnumCase(ParseDeclOptions Flags,
72097236
break;
72107237
}
72117238

7239+
diagnoseAndConsumeIfModuleSelector("enum 'case'");
7240+
72127241
if (Tok.is(tok::identifier)) {
72137242
Status |= parseIdentifierDeclName(
72147243
*this, Name, NameLoc, "enum 'case'", [](const Token &next) {
@@ -8124,6 +8153,10 @@ parsePrecedenceGroupNameList(Parser &P, Fn takeGroupName) {
81248153
return makeParserCodeCompletionStatus();
81258154
}
81268155

8156+
// TODO: We could support module selectors for precedence groups if we
8157+
// implemented the lookup for it.
8158+
P.diagnoseAndConsumeIfModuleSelector("precedence group specifier",
8159+
/*isDef=*/false);
81278160
DeclNameLoc nameLoc;
81288161
auto name = P.parseDeclNameRef(nameLoc,
81298162
diag::expected_group_name_in_precedencegroup_list, {});
@@ -8265,6 +8298,8 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags,
82658298
return nullptr;
82668299
}
82678300

8301+
diagnoseAndConsumeIfModuleSelector("precedence group");
8302+
82688303
Identifier name;
82698304
SourceLoc nameLoc;
82708305
if (parseIdentifier(name, nameLoc, /*diagnoseDollarPrefix=*/true,

lib/Parse/ParseExpr.cpp

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1642,6 +1642,10 @@ ParserResult<Expr> Parser::parseExprPrimary(Diag<> ID, bool isExprBasic) {
16421642
// call pattern.
16431643
peekToken().isNot(tok::period, tok::period_prefix, tok::l_paren)) {
16441644
DeferringContextRAII Deferring(*SyntaxContext);
1645+
1646+
diagnoseAndConsumeIfModuleSelector(InVarOrLetPattern != IVOLP_InVar
1647+
? "constant" : "variable");
1648+
16451649
Identifier name;
16461650
SourceLoc loc = consumeIdentifier(name, /*diagnoseDollarPrefix=*/false);
16471651
auto introducer = (InVarOrLetPattern != IVOLP_InVar
@@ -2576,9 +2580,23 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
25762580

25772581
// Okay, we have a closure signature.
25782582
} else if (Tok.isIdentifierOrUnderscore() || Tok.is(tok::code_complete)) {
2579-
// Parse identifier (',' identifier)*
2583+
// Parse module-selector? identifier (',' module-selector? identifier)*
2584+
// The module selectors aren't legal, but we want to diagnose them if
2585+
// they're present.
2586+
if (Context.LangOpts.EnableExperimentalModuleSelector &&
2587+
peekToken().is(tok::colon_colon)) {
2588+
consumeToken();
2589+
consumeToken(tok::colon_colon);
2590+
}
2591+
25802592
consumeToken();
25812593
while (consumeIf(tok::comma)) {
2594+
if (Context.LangOpts.EnableExperimentalModuleSelector &&
2595+
peekToken().is(tok::colon_colon)) {
2596+
consumeToken();
2597+
consumeToken(tok::colon_colon);
2598+
}
2599+
25822600
if (Tok.isIdentifierOrUnderscore() || Tok.is(tok::code_complete)) {
25832601
consumeToken();
25842602
continue;
@@ -2655,8 +2673,11 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
26552673
diagnose(Tok, diag::attr_unowned_expected_rparen);
26562674
}
26572675
} else if (Tok.isAny(tok::identifier, tok::kw_self, tok::code_complete) &&
2658-
peekToken().isAny(tok::equal, tok::comma, tok::r_square)) {
2676+
peekToken().isAny(tok::equal, tok::comma, tok::r_square,
2677+
tok::colon_colon)) {
26592678
// "x = 42", "x," and "x]" are all strong captures of x.
2679+
// "x::" is not permitted, but we want to diagnose it as though it were
2680+
// a strong capture.
26602681
} else {
26612682
diagnose(Tok, diag::expected_capture_specifier);
26622683
skipUntil(tok::comma, tok::r_square);
@@ -2674,10 +2695,17 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
26742695

26752696
// The thing being capture specified is an identifier, or as an identifier
26762697
// followed by an expression.
2698+
26772699
Expr *initializer;
26782700
Identifier name;
26792701
SourceLoc nameLoc = Tok.getLoc();
26802702
SourceLoc equalLoc;
2703+
2704+
// FIXME: It'd be nice to be able to capture with a module selector, but
2705+
// define a local name; however, that would complicate the equals check
2706+
// here.
2707+
diagnoseAndConsumeIfModuleSelector("captured variable");
2708+
26812709
if (peekToken().isNot(tok::equal)) {
26822710
// If this is the simple case, then the identifier is both the name and
26832711
// the expression to capture.
@@ -2782,6 +2810,9 @@ ParserStatus Parser::parseClosureSignatureIfPresent(
27822810
bool HasNext;
27832811
do {
27842812
SyntaxParsingContext ClParamCtx(SyntaxContext, SyntaxKind::ClosureParam);
2813+
2814+
diagnoseAndConsumeIfModuleSelector("parameter");
2815+
27852816
if (Tok.isNot(tok::identifier, tok::kw__, tok::code_complete)) {
27862817
diagnose(Tok, diag::expected_closure_parameter_name);
27872818
status.setIsParseError();

lib/Parse/ParseGeneric.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ Parser::parseGenericParametersBeforeWhere(SourceLoc LAngleLoc,
6868
attributes.add(new (Context) RawDocCommentAttr(Tok.getCommentRange()));
6969
parseDeclAttributeList(attributes);
7070

71+
diagnoseAndConsumeIfModuleSelector("generic parameter");
72+
7173
// Parse the name of the parameter.
7274
Identifier Name;
7375
SourceLoc NameLoc;

lib/Parse/ParsePattern.cpp

Lines changed: 29 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -204,6 +204,12 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
204204
rightParenLoc.isInvalid() && "Must start with empty state");
205205
SyntaxParsingContext ParamClauseCtx(SyntaxContext, SyntaxKind::ParameterClause);
206206

207+
// Operators and closures cannot have API names. Enum elements cannot have
208+
// internal names.
209+
bool canHaveLabels = !(paramContext == ParameterContextKind::Operator ||
210+
paramContext == ParameterContextKind::Closure);
211+
bool canHaveInternalName = paramContext != ParameterContextKind::EnumElement;
212+
207213
// Consume the starting '(';
208214
leftParenLoc = consumeToken(tok::l_paren);
209215

@@ -325,22 +331,25 @@ Parser::parseParameterClause(SourceLoc &leftParenLoc,
325331
.fixItReplace(Tok.getLoc(), "`" + Tok.getText().str() + "`");
326332
}
327333

334+
diagnoseAndConsumeIfModuleSelector(
335+
canHaveLabels ? "argument label" : "parameter",
336+
/*isDef=*/!canHaveLabels);
337+
328338
if (startsParameterName(isClosure)) {
329339
// identifier-or-none for the first name
330340
param.FirstNameLoc = consumeArgumentLabel(param.FirstName,
331341
/*diagnoseDollarPrefix=*/!isClosure);
332342

343+
diagnoseAndConsumeIfModuleSelector("parameter");
344+
333345
// identifier-or-none? for the second name
334346
if (Tok.canBeArgumentLabel())
335347
param.SecondNameLoc = consumeArgumentLabel(param.SecondName,
336348
/*diagnoseDollarPrefix=*/true);
337349

338-
// Operators, closures, and enum elements cannot have API names.
339-
if ((paramContext == ParameterContextKind::Operator ||
340-
paramContext == ParameterContextKind::Closure ||
341-
paramContext == ParameterContextKind::EnumElement) &&
342-
!param.FirstName.empty() &&
343-
param.SecondNameLoc.isValid()) {
350+
// Do we have two names in a parameter that can't have both?
351+
if (!(canHaveLabels && canHaveInternalName) &&
352+
!param.FirstName.empty() && param.SecondNameLoc.isValid()) {
344353
enum KeywordArgumentDiagnosticContextKind {
345354
Operator = 0,
346355
Closure = 1,
@@ -1101,6 +1110,12 @@ ParserResult<Pattern> Parser::parsePattern() {
11011110
auto introducer = (InVarOrLetPattern != IVOLP_InVar
11021111
? VarDecl::Introducer::Let
11031112
: VarDecl::Introducer::Var);
1113+
StringRef declNameKind = introducer == VarDecl::Introducer::Let
1114+
? "constant" : "variable";
1115+
1116+
// If there's a module selector here, consume and remove it.
1117+
diagnoseAndConsumeIfModuleSelector(declNameKind);
1118+
11041119
switch (Tok.getKind()) {
11051120
case tok::l_paren:
11061121
return parsePatternTuple();
@@ -1120,19 +1135,17 @@ ParserResult<Pattern> Parser::parsePattern() {
11201135
}
11211136
PatternCtx.setCreateSyntax(SyntaxKind::WildcardPattern);
11221137
return makeParserResult(new (Context) AnyPattern(consumeToken(tok::kw__)));
1123-
1138+
11241139
case tok::identifier: {
11251140
PatternCtx.setCreateSyntax(SyntaxKind::IdentifierPattern);
11261141
Identifier name;
11271142
SourceLoc loc = consumeIdentifier(name, /*diagnoseDollarPrefix=*/true);
11281143
if (Tok.isIdentifierOrUnderscore() && !Tok.isContextualDeclKeyword())
1129-
diagnoseConsecutiveIDs(name.str(), loc,
1130-
introducer == VarDecl::Introducer::Let
1131-
? "constant" : "variable");
1144+
diagnoseConsecutiveIDs(name.str(), loc, declNameKind);
11321145

11331146
return makeParserResult(createBindingFromPattern(loc, name, introducer));
11341147
}
1135-
1148+
11361149
case tok::code_complete:
11371150
if (!CurDeclContext->isTypeContext()) {
11381151
// This cannot be an overridden property, so just eat the token. We cannot
@@ -1168,7 +1181,7 @@ ParserResult<Pattern> Parser::parsePattern() {
11681181
return makeParserResult(
11691182
new (Context) BindingPattern(varLoc, isLet, subPattern.get()));
11701183
}
1171-
1184+
11721185
default:
11731186
if (Tok.isKeyword() &&
11741187
(peekToken().is(tok::colon) || peekToken().is(tok::equal))) {
@@ -1201,6 +1214,10 @@ Parser::parsePatternTupleElement() {
12011214
Identifier Label;
12021215
SourceLoc LabelLoc;
12031216

1217+
StringRef declNameKind = InVarOrLetPattern != IVOLP_InVar
1218+
? "constant" : "variable";
1219+
diagnoseAndConsumeIfModuleSelector(declNameKind);
1220+
12041221
// If the tuple element has a label, parse it.
12051222
if (Tok.is(tok::identifier) && peekToken().is(tok::colon)) {
12061223
LabelLoc = consumeIdentifier(Label, /*diagnoseDollarPrefix=*/true);

test/NameLookup/module_selector.swift

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,81 @@ func whitespace() {
125125
::
126126
print // expected-error{{expression resolves to an unused function}}
127127
}
128+
129+
// Error cases
130+
131+
func main::decl1( // expected-error {{name of function declaration cannot be qualified with module selector}}
132+
main::p1: main::A, // expected-error {{argument label cannot be qualified with module selector}}
133+
main::label p2: main::A, // expected-error {{argument label cannot be qualified with module selector}}
134+
label main::p3: main::A // expected-error {{name of parameter declaration cannot be qualified with module selector}}
135+
) {
136+
let main::decl1a = "a" // expected-error {{name of constant declaration cannot be qualified with module selector}} expected-warning {{never used}}
137+
var main::decl1b = "b" // expected-error {{name of variable declaration cannot be qualified with module selector}} expected-warning {{never used}}
138+
let (main::decl1c, main::decl1d) = ("c", "d") // expected-error {{name of constant declaration cannot be qualified with module selector}} expected-error {{name of constant declaration cannot be qualified with module selector}} expected-warning 2{{never used}}
139+
140+
if let (main::decl1e, main::decl1f) = Optional(("e", "f")) {} // expected-error {{name of constant declaration cannot be qualified with module selector}} expected-error {{name of constant declaration cannot be qualified with module selector}} expected-warning 2{{never used}}
141+
guard let (main::decl1g, main::decl1h) = Optional(("g", "h")) else { return } // expected-error {{name of constant declaration cannot be qualified with module selector}} expected-error {{name of constant declaration cannot be qualified with module selector}} expected-warning {{never used}}
142+
143+
switch Optional(main::decl1g) { // FIXME expecting an error later
144+
case Optional.some(let main::decl1i): // expected-error {{name of constant declaration cannot be qualified with module selector}} expected-warning {{never used}}
145+
break
146+
case .none:
147+
break
148+
}
149+
150+
switch Optional(main::decl1g) { // FIXME expecting an error later
151+
case let Optional.some(main::decl1j): // expected-error {{name of constant declaration cannot be qualified with module selector}} expected-warning {{never used}}
152+
break
153+
case .none:
154+
break
155+
}
156+
157+
switch Optional(main::decl1g) {
158+
case let main::decl1k?: // expected-error {{name of constant declaration cannot be qualified with module selector}} expected-warning {{never used}}
159+
break
160+
case .none:
161+
break
162+
}
163+
164+
for main::decl1l in "lll" {} // expected-error {{name of constant declaration cannot be qualified with module selector}} expected-warning {{never used}}
165+
166+
"lll".forEach { [main::magnitude] // expected-error {{name of captured variable declaration cannot be qualified with module selector}} expected-warning {{never used}}
167+
main::elem in print(elem) // expected-error {{name of parameter declaration cannot be qualified with module selector}}
168+
}
169+
"lll".forEach { (main::elem) in print(elem) } // expected-error {{name of parameter declaration cannot be qualified with module selector}}
170+
"lll".forEach { (main::elem) -> Void in print(elem) } // expected-error {{name of parameter declaration cannot be qualified with module selector}}
171+
"lll".forEach { (main::elem: Character) -> Void in print(elem) } // expected-error {{name of parameter declaration cannot be qualified with module selector}}
172+
}
173+
enum main::decl2 { // expected-error {{name of enum declaration cannot be qualified with module selector}}
174+
case main::decl2a // expected-error {{name of enum 'case' declaration cannot be qualified with module selector}}
175+
}
176+
struct main::decl3 {} // expected-error {{name of struct declaration cannot be qualified with module selector}}
177+
class main::decl4<main::T> {} // expected-error {{name of class declaration cannot be qualified with module selector}} expected-error {{name of generic parameter declaration cannot be qualified with module selector}}
178+
typealias main::decl5 = main::Bool // expected-error {{name of typealias declaration cannot be qualified with module selector}}
179+
protocol main::decl6 { // expected-error {{name of protocol declaration cannot be qualified with module selector}}
180+
associatedtype main::decl6a // expected-error {{name of associatedtype declaration cannot be qualified with module selector}}
181+
}
182+
let main::decl7 = 7 // expected-error {{name of constant declaration cannot be qualified with module selector}}
183+
var main::decl8 = 8 { // expected-error {{name of variable declaration cannot be qualified with module selector}}
184+
willSet(main::newValue) {} // expected-error {{name of accessor parameter declaration cannot be qualified with module selector}}
185+
didSet(main::oldValue) {} // expected-error {{name of accessor parameter declaration cannot be qualified with module selector}}
186+
}
187+
188+
struct Parent {
189+
func main::decl1() {} // expected-error {{name of function declaration cannot be qualified with module selector}}
190+
enum main::decl2 { // expected-error {{name of enum declaration cannot be qualified with module selector}}
191+
case main::decl2a // expected-error {{name of enum 'case' declaration cannot be qualified with module selector}}
192+
}
193+
struct main::decl3 {} // expected-error {{name of struct declaration cannot be qualified with module selector}}
194+
class main::decl4 {} // expected-error {{name of class declaration cannot be qualified with module selector}}
195+
typealias main::decl5 = main::Bool // expected-error {{name of typealias declaration cannot be qualified with module selector}}
196+
}
197+
198+
@_swift_native_objc_runtime_base(main::BaseClass) // expected-error {{Objective-C class name in @_swift_native_objc_runtime_base}}
199+
class C1 {}
200+
201+
infix operator <<<<< : Swift::AdditionPrecedence // expected-error {{precedence group specifier cannot be qualified with module selector}}
202+
203+
precedencegroup main::PG1 { // expected-error {{name of precedence group declaration cannot be qualified with module selector}}
204+
higherThan: Swift::AdditionPrecedence // expected-error {{precedence group specifier cannot be qualified with module selector}}
205+
}

0 commit comments

Comments
 (0)