Skip to content

Commit 77fb537

Browse files
committed
Parse module selectors in permitted locations
1 parent a68f1ca commit 77fb537

File tree

8 files changed

+995
-64
lines changed

8 files changed

+995
-64
lines changed

include/swift/AST/DiagnosticsParse.def

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,15 @@ ERROR(forbidden_interpolated_string,none,
9393
ERROR(forbidden_extended_escaping_string,none,
9494
"%0 cannot be an extended escaping string literal", (StringRef))
9595

96+
ERROR(expected_identifier_in_module_selector,none,
97+
"expected module name in module selector", ())
98+
ERROR(expected_identifier_after_module_selector,none,
99+
"expected identifier after module selector", ())
100+
NOTE(extra_whitespace_module_selector,none,
101+
"remove extraneous whitespace after '::'", ())
102+
ERROR(module_selector_submodule_not_allowed,none,
103+
"module selector cannot specify a submodule", ())
104+
96105
//------------------------------------------------------------------------------
97106
// MARK: Lexer diagnostics
98107
//------------------------------------------------------------------------------

include/swift/Parse/Parser.h

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1820,6 +1820,23 @@ class Parser {
18201820
void parseOptionalArgumentLabel(Identifier &name, SourceLoc &loc,
18211821
bool isAttr = false);
18221822

1823+
/// If a \c module-selector is present, returns a true value (specifically,
1824+
/// 1 or 2 depending on how many tokens should be consumed to skip it).
1825+
unsigned isAtModuleSelector();
1826+
1827+
/// Attempts to parse a \c module-selector if one is present.
1828+
///
1829+
/// \verbatim
1830+
/// module-selector: identifier '::'
1831+
/// \endverbatim
1832+
///
1833+
/// \return \c None if no selector is present or a selector is present but
1834+
/// is not allowed; an instance with an empty \c Identifier if a
1835+
/// selector is present but has no valid identifier; an instance with
1836+
/// a valid \c Identifier if a selector is present and includes a
1837+
/// module name.
1838+
std::optional<Located<Identifier>> parseModuleSelector();
1839+
18231840
enum class DeclNameFlag : uint8_t {
18241841
/// If passed, operator basenames are allowed.
18251842
AllowOperators = 1 << 0,
@@ -1832,6 +1849,9 @@ class Parser {
18321849
/// not ordinary identifiers.
18331850
AllowKeywordsUsingSpecialNames = AllowKeywords | 1 << 2,
18341851

1852+
/// If passed, module selectors are not permitted on this declaration name.
1853+
ModuleSelectorUnsupported = 1 << 3,
1854+
18351855
/// If passed, compound names with argument lists are allowed, unless they
18361856
/// have empty argument lists.
18371857
AllowCompoundNames = 1 << 4,
@@ -1876,7 +1896,8 @@ class Parser {
18761896
SourceLoc &rightAngleLoc, ArgumentList *&argList, bool isExprBasic,
18771897
DiagRef diag);
18781898

1879-
ParserResult<Expr> parseExprIdentifier(bool allowKeyword);
1899+
ParserResult<Expr> parseExprIdentifier(bool allowKeyword,
1900+
bool allowModuleSelector = true);
18801901
Expr *parseExprEditorPlaceholder(Token PlaceholderTok,
18811902
Identifier PlaceholderId);
18821903

lib/Parse/Lexer.cpp

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -819,9 +819,12 @@ static bool isLeftBound(const char *tokBegin, const char *bufferBegin) {
819819
static bool isRightBound(const char *tokEnd, bool isLeftBound,
820820
const char *codeCompletionPtr) {
821821
switch (*tokEnd) {
822+
case ':': // ':' is an expression separator; '::' is not
823+
return tokEnd[1] == ':';
824+
822825
case ' ': case '\r': case '\n': case '\t': // whitespace
823826
case ')': case ']': case '}': // closing delimiters
824-
case ',': case ';': case ':': // expression separators
827+
case ',': case ';': // expression separators
825828
return false;
826829

827830
case '\0':
@@ -2765,7 +2768,7 @@ void Lexer::lexImpl() {
27652768
case '\\': return formToken(tok::backslash, TokStart);
27662769

27672770
case ':':
2768-
if (CurPtr[0] == ':') {
2771+
if (CurPtr[0] == ':' && LangOpts.hasFeature(Feature::ModuleSelector)) {
27692772
CurPtr++;
27702773
return formToken(tok::colon_colon, TokStart);
27712774
}
@@ -2879,6 +2882,7 @@ Token Lexer::getTokenAtLocation(const SourceManager &SM, SourceLoc Loc,
28792882
// Use fake language options; language options only affect validity
28802883
// and the exact token produced.
28812884
LangOptions FakeLangOpts;
2885+
FakeLangOpts.enableFeature(Feature::ModuleSelector);
28822886

28832887
// Here we return comments as tokens because either the caller skipped
28842888
// comments and normally we won't be at the beginning of a comment token

lib/Parse/ParseDecl.cpp

Lines changed: 24 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -682,7 +682,8 @@ bool Parser::parseSpecializeAttributeArguments(
682682
DeclNameFlag::AllowZeroArgCompoundNames |
683683
DeclNameFlag::AllowKeywordsUsingSpecialNames |
684684
DeclNameFlag::AllowOperators |
685-
DeclNameFlag::AllowLowercaseAndUppercaseSelf);
685+
DeclNameFlag::AllowLowercaseAndUppercaseSelf |
686+
DeclNameFlag::ModuleSelectorUnsupported);
686687
}
687688
}
688689
if (ParamLabel == "spiModule") {
@@ -1136,7 +1137,8 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) {
11361137
MemberNameLoc, diag::attr_implements_expected_member_name,
11371138
DeclNameFlag::AllowZeroArgCompoundNames |
11381139
DeclNameFlag::AllowOperators |
1139-
DeclNameFlag::AllowLowercaseAndUppercaseSelf);
1140+
DeclNameFlag::AllowLowercaseAndUppercaseSelf |
1141+
DeclNameFlag::ModuleSelectorUnsupported);
11401142
if (!MemberName) {
11411143
Status.setIsParseError();
11421144
}
@@ -1159,7 +1161,7 @@ Parser::parseImplementsAttribute(SourceLoc AtLoc, SourceLoc Loc) {
11591161
return Status;
11601162
}
11611163

1162-
// FIXME(ModQual): Reject module qualification on MemberName.
1164+
assert(MemberName.getModuleSelector().empty());
11631165
return ParserResult<ImplementsAttr>(
11641166
ImplementsAttr::create(Context, AtLoc, SourceRange(Loc, rParenLoc),
11651167
ProtocolType.get(), MemberName.getFullName(),
@@ -2440,7 +2442,8 @@ Parser::parseMacroRoleAttribute(
24402442
(DeclNameFlag::AllowOperators | DeclNameFlag::AllowKeywords |
24412443
DeclNameFlag::AllowKeywordsUsingSpecialNames |
24422444
DeclNameFlag::AllowCompoundNames |
2443-
DeclNameFlag::AllowZeroArgCompoundNames));
2445+
DeclNameFlag::AllowZeroArgCompoundNames |
2446+
DeclNameFlag::ModuleSelectorUnsupported));
24442447
if (!name) {
24452448
status.setIsParseError();
24462449
return status;
@@ -4256,11 +4259,15 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes,
42564259
.warnUntilSwiftVersion(6);
42574260
}
42584261

4262+
bool hasModuleSelector = Context.LangOpts.hasFeature(Feature::ModuleSelector)
4263+
&& peekToken().is(tok::colon_colon);
4264+
42594265
// If this not an identifier, the attribute is malformed.
42604266
if (Tok.isNot(tok::identifier) &&
42614267
Tok.isNot(tok::kw_in) &&
42624268
Tok.isNot(tok::kw_inout) &&
4263-
Tok.isNot(tok::kw_rethrows)) {
4269+
Tok.isNot(tok::kw_rethrows) &&
4270+
!hasModuleSelector) {
42644271

42654272
if (Tok.is(tok::code_complete)) {
42664273
if (CodeCompletionCallbacks) {
@@ -4281,7 +4288,7 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes,
42814288

42824289
// If the attribute follows the new representation, switch
42834290
// over to the alternate parsing path.
4284-
std::optional<DeclAttrKind> DK =
4291+
std::optional<DeclAttrKind> DK = hasModuleSelector ? std::nullopt :
42854292
DeclAttribute::getAttrKindFromString(Tok.getText());
42864293
if (DK == DeclAttrKind::Rethrows) {
42874294
DK = DeclAttrKind::AtRethrows;
@@ -4293,7 +4300,7 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes,
42934300
auto checkInvalidAttrName =
42944301
[&](StringRef invalidName, StringRef correctName, DeclAttrKind kind,
42954302
std::optional<Diag<StringRef, StringRef>> diag = std::nullopt) {
4296-
if (!DK && Tok.getText() == invalidName) {
4303+
if (!DK && !hasModuleSelector && Tok.getText() == invalidName) {
42974304
DK = kind;
42984305

42994306
if (diag) {
@@ -4357,7 +4364,7 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes,
43574364
DeclAttrKind::Preconcurrency,
43584365
diag::attr_renamed_warning);
43594366

4360-
if (!DK && Tok.getText() == "warn_unused_result") {
4367+
if (!DK && !hasModuleSelector && Tok.getText() == "warn_unused_result") {
43614368
// The behavior created by @warn_unused_result is now the default. Emit a
43624369
// Fix-It to remove.
43634370
SourceLoc attrLoc = consumeToken();
@@ -4432,9 +4439,10 @@ ParserStatus Parser::parseDeclAttribute(DeclAttributes &Attributes,
44324439
return parseNewDeclAttribute(Attributes, AtLoc, *DK, isFromClangAttribute);
44334440
}
44344441

4435-
if (TypeAttribute::getAttrKindFromString(Tok.getText()).has_value())
4442+
if (!hasModuleSelector &&
4443+
TypeAttribute::getAttrKindFromString(Tok.getText()).has_value())
44364444
diagnose(Tok, diag::type_attribute_applied_to_decl);
4437-
else if (Tok.isContextualKeyword("unknown")) {
4445+
else if (!hasModuleSelector && Tok.isContextualKeyword("unknown")) {
44384446
diagnose(Tok, diag::unknown_attr_name, "unknown");
44394447
} else {
44404448
// Change the context to create a custom attribute syntax.
@@ -5761,6 +5769,8 @@ static void skipAttribute(Parser &P) {
57615769
// Parse the attribute name, which can be qualified, have
57625770
// generic arguments, and so on.
57635771
do {
5772+
P.parseModuleSelector();
5773+
57645774
if (!(P.consumeIf(tok::identifier) || P.consumeIf(tok::kw_rethrows)) &&
57655775
!P.consumeIf(tok::code_complete))
57665776
return;
@@ -10343,9 +10353,11 @@ Parser::parseDeclOperatorImpl(SourceLoc OperatorLoc, Identifier Name,
1034310353
return makeParserCodeCompletionResult<OperatorDecl>();
1034410354
}
1034510355

10356+
// TODO: We could support module selectors for precedence groups if we
10357+
// implemented the lookup for it.
1034610358
groupName = parseDeclNameRef(groupLoc,
1034710359
diag::operator_decl_expected_precedencegroup,
10348-
{});
10360+
DeclNameFlag::ModuleSelectorUnsupported);
1034910361

1035010362
if (Context.TypeCheckerOpts.EnableOperatorDesignatedTypes) {
1035110363
// Designated types have been removed; consume the list (mainly for source
@@ -10643,7 +10655,7 @@ Parser::parseDeclPrecedenceGroup(ParseDeclOptions flags,
1064310655
auto name = parseDeclNameRef(nameLoc,
1064410656
{ diag::expected_precedencegroup_relation,
1064510657
attrName },
10646-
{});
10658+
DeclNameFlag::ModuleSelectorUnsupported);
1064710659
if (!name) {
1064810660
return abortBody();
1064910661
}

0 commit comments

Comments
 (0)