Skip to content

Commit a1f4a8f

Browse files
author
Harlan Haskins
authored
[ModuleInterface] Escape Type and Protocol when they're type members (#24322)
Previously, we wouldn't escape `Type` and `Protocol` at all in the ASTPrinter, which lead to unfortunate build failures while compiling an interface. Instead, make sure we escape them whenever we print a name that's a type member. Except for methods, which are erroneously allowed to be called `Type` and `Protocol`. rdar://49858651
1 parent 997f0a0 commit a1f4a8f

File tree

3 files changed

+109
-20
lines changed

3 files changed

+109
-20
lines changed

include/swift/AST/ASTPrinter.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,8 @@ enum class PrintNameContext {
4444
Normal,
4545
/// Keyword context, where no keywords are escaped.
4646
Keyword,
47+
/// Type member context, e.g. properties or enum cases.
48+
TypeMember,
4749
/// Generic parameter context, where 'Self' is not escaped.
4850
GenericParameter,
4951
/// Class method return type, where 'Self' is not escaped.

lib/AST/ASTPrinter.cpp

Lines changed: 34 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -379,16 +379,26 @@ ASTPrinter &operator<<(ASTPrinter &printer, tok keyword) {
379379

380380
/// Determine whether to escape the given keyword in the given context.
381381
static bool escapeKeywordInContext(StringRef keyword, PrintNameContext context){
382+
383+
bool isKeyword = llvm::StringSwitch<bool>(keyword)
384+
#define KEYWORD(KW) \
385+
.Case(#KW, true)
386+
#include "swift/Syntax/TokenKinds.def"
387+
.Default(false);
388+
382389
switch (context) {
383390
case PrintNameContext::Normal:
384391
case PrintNameContext::Attribute:
385-
return true;
392+
return isKeyword;
386393
case PrintNameContext::Keyword:
387394
return false;
388395

389396
case PrintNameContext::ClassDynamicSelf:
390397
case PrintNameContext::GenericParameter:
391-
return keyword != "Self";
398+
return isKeyword && keyword != "Self";
399+
400+
case PrintNameContext::TypeMember:
401+
return isKeyword || !canBeMemberName(keyword);
392402

393403
case PrintNameContext::FunctionParameterExternal:
394404
case PrintNameContext::FunctionParameterLocal:
@@ -407,19 +417,13 @@ void ASTPrinter::printName(Identifier Name, PrintNameContext Context) {
407417
printNamePost(Context);
408418
return;
409419
}
410-
bool IsKeyword = llvm::StringSwitch<bool>(Name.str())
411-
#define KEYWORD(KW) \
412-
.Case(#KW, true)
413-
#include "swift/Syntax/TokenKinds.def"
414-
.Default(false);
415420

416-
if (IsKeyword)
417-
IsKeyword = escapeKeywordInContext(Name.str(), Context);
421+
bool shouldEscapeKeyword = escapeKeywordInContext(Name.str(), Context);
418422

419-
if (IsKeyword)
423+
if (shouldEscapeKeyword)
420424
*this << "`";
421425
*this << Name.str();
422-
if (IsKeyword)
426+
if (shouldEscapeKeyword)
423427
*this << "`";
424428

425429
printNamePost(Context);
@@ -1015,6 +1019,14 @@ static bool mustPrintPropertyName(VarDecl *decl, PrintOptions opts) {
10151019
return false;
10161020
}
10171021

1022+
/// Gets the print name context of a given decl, choosing between TypeMember
1023+
/// and Normal, depending if this decl lives in a nominal type decl.
1024+
static PrintNameContext getTypeMemberPrintNameContext(const Decl *d) {
1025+
return d->getDeclContext()->isTypeContext() ?
1026+
PrintNameContext::TypeMember :
1027+
PrintNameContext::Normal;
1028+
}
1029+
10181030
void PrintAST::printPattern(const Pattern *pattern) {
10191031
switch (pattern->getKind()) {
10201032
case PatternKind::Any:
@@ -1028,7 +1040,8 @@ void PrintAST::printPattern(const Pattern *pattern) {
10281040
// FIXME: This always returns true now, because of the FIXMEs listed in
10291041
// mustPrintPropertyName.
10301042
if (mustPrintPropertyName(decl, Options))
1031-
Printer.printName(named->getBoundName());
1043+
Printer.printName(named->getBoundName(),
1044+
getTypeMemberPrintNameContext(decl));
10321045
else
10331046
Printer << "_";
10341047
});
@@ -2241,7 +2254,7 @@ void PrintAST::visitTypeAliasDecl(TypeAliasDecl *decl) {
22412254
printContextIfNeeded(decl);
22422255
recordDeclLoc(decl,
22432256
[&]{
2244-
Printer.printName(decl->getName());
2257+
Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl));
22452258
}, [&]{ // Signature
22462259
printGenericDeclGenericParams(decl);
22472260
});
@@ -2284,7 +2297,7 @@ void PrintAST::visitAssociatedTypeDecl(AssociatedTypeDecl *decl) {
22842297
Printer << tok::kw_associatedtype << " ";
22852298
recordDeclLoc(decl,
22862299
[&]{
2287-
Printer.printName(decl->getName());
2300+
Printer.printName(decl->getName(), PrintNameContext::TypeMember);
22882301
});
22892302

22902303
auto proto = decl->getProtocol();
@@ -2325,7 +2338,7 @@ void PrintAST::visitEnumDecl(EnumDecl *decl) {
23252338
printContextIfNeeded(decl);
23262339
recordDeclLoc(decl,
23272340
[&]{
2328-
Printer.printName(decl->getName());
2341+
Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl));
23292342
}, [&]{ // Signature
23302343
printGenericDeclGenericParams(decl);
23312344
});
@@ -2353,7 +2366,7 @@ void PrintAST::visitStructDecl(StructDecl *decl) {
23532366
printContextIfNeeded(decl);
23542367
recordDeclLoc(decl,
23552368
[&]{
2356-
Printer.printName(decl->getName());
2369+
Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl));
23572370
}, [&]{ // Signature
23582371
printGenericDeclGenericParams(decl);
23592372
});
@@ -2381,7 +2394,7 @@ void PrintAST::visitClassDecl(ClassDecl *decl) {
23812394
printContextIfNeeded(decl);
23822395
recordDeclLoc(decl,
23832396
[&]{
2384-
Printer.printName(decl->getName());
2397+
Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl));
23852398
}, [&]{ // Signature
23862399
printGenericDeclGenericParams(decl);
23872400
});
@@ -2514,7 +2527,7 @@ void PrintAST::visitVarDecl(VarDecl *decl) {
25142527
printContextIfNeeded(decl);
25152528
recordDeclLoc(decl,
25162529
[&]{
2517-
Printer.printName(decl->getName());
2530+
Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl));
25182531
});
25192532
if (decl->hasInterfaceType()) {
25202533
Printer << ": ";
@@ -2771,7 +2784,8 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
27712784
if (!decl->hasName()) {
27722785
Printer << "<anonymous>";
27732786
} else {
2774-
Printer.printName(decl->getName());
2787+
Printer.printName(decl->getName(),
2788+
getTypeMemberPrintNameContext(decl));
27752789
if (decl->isOperator())
27762790
Printer << " ";
27772791
}
@@ -2817,7 +2831,7 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
28172831
void PrintAST::printEnumElement(EnumElementDecl *elt) {
28182832
recordDeclLoc(elt,
28192833
[&]{
2820-
Printer.printName(elt->getName());
2834+
Printer.printName(elt->getName(), getTypeMemberPrintNameContext(elt));
28212835
});
28222836

28232837
if (auto *PL = elt->getParameterList()) {
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// RUN: %target-swift-frontend -typecheck -emit-module-interface-path - %s | %FileCheck %s
2+
3+
// CHECK: public let Type: Swift.Int
4+
public let Type = 0
5+
6+
// CHECK: public struct A {
7+
public struct A {
8+
// CHECK-NEXT: public struct `Type` {
9+
// CHECK-NEXT: }
10+
public struct `Type` {}
11+
// CHECK-NEXT: }
12+
}
13+
14+
// CHECK: public class B {
15+
public class B {
16+
// CHECK-NEXT: public class `Type` {
17+
// CHECK: }
18+
public class `Type` {}
19+
20+
// CHECK-NEXT: @_hasInitialValue public var `Type`: Swift.Int
21+
public var `Type` = 0
22+
// CHECK: }
23+
}
24+
25+
// CHECK: public struct C {
26+
public struct C {
27+
// CHECK: public enum `Type` {
28+
public enum `Type` {
29+
// CHECK: }
30+
}
31+
// CHECK-NEXT: }
32+
}
33+
34+
// CHECK: public struct D {
35+
public struct D {
36+
// CHECK: public typealias `Type` = Int
37+
public typealias `Type` = Int
38+
// CHECK-NEXT: }
39+
}
40+
41+
// CHECK: public protocol BestProtocol {
42+
public protocol BestProtocol {
43+
// CHECK-NEXT: associatedtype `Type`
44+
associatedtype `Type`
45+
// CHECK-NEXT: }
46+
}
47+
48+
// CHECK: public enum CoolEnum {
49+
public enum CoolEnum {
50+
// CHECK-NEXT: case `Type`
51+
case `Type`
52+
// CHECK-NEXT: case `Protocol`
53+
case `Protocol`
54+
// CHECK-NEXT: case `init`
55+
case `init`
56+
// CHECK-NEXT: case `self`
57+
case `self`
58+
59+
// We allow Type and Protocol as method names, but we should still print them
60+
// escaped in case we tighten this restriction.
61+
// CHECK-NEXT: public func `Type`()
62+
public func Type() {}
63+
// CHECK-NEXT: public func `Protocol`()
64+
public func Protocol() {}
65+
// CHECK: }
66+
}
67+
68+
// CHECK: public enum UncoolEnum {
69+
public enum UncoolEnum {
70+
// CHECK-NEXT: case `Type`, `Protocol`
71+
case `Type`, `Protocol`
72+
// CHECK: }
73+
}

0 commit comments

Comments
 (0)