Skip to content

Commit 9a31dc1

Browse files
author
Harlan Haskins
committed
[ModuleInterface] Escape Type and Protocol when they're type members
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 faa6693 commit 9a31dc1

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);
@@ -1029,6 +1033,14 @@ static bool mustPrintPropertyName(VarDecl *decl, PrintOptions opts) {
10291033
return false;
10301034
}
10311035

1036+
/// Gets the print name context of a given decl, choosing between TypeMember
1037+
/// and Normal, depending if this decl lives in a nominal type decl.
1038+
static PrintNameContext getTypeMemberPrintNameContext(const Decl *d) {
1039+
return d->getDeclContext()->isTypeContext() ?
1040+
PrintNameContext::TypeMember :
1041+
PrintNameContext::Normal;
1042+
}
1043+
10321044
void PrintAST::printPattern(const Pattern *pattern) {
10331045
switch (pattern->getKind()) {
10341046
case PatternKind::Any:
@@ -1042,7 +1054,8 @@ void PrintAST::printPattern(const Pattern *pattern) {
10421054
// FIXME: This always returns true now, because of the FIXMEs listed in
10431055
// mustPrintPropertyName.
10441056
if (mustPrintPropertyName(decl, Options))
1045-
Printer.printName(named->getBoundName());
1057+
Printer.printName(named->getBoundName(),
1058+
getTypeMemberPrintNameContext(decl));
10461059
else
10471060
Printer << "_";
10481061
});
@@ -2255,7 +2268,7 @@ void PrintAST::visitTypeAliasDecl(TypeAliasDecl *decl) {
22552268
printContextIfNeeded(decl);
22562269
recordDeclLoc(decl,
22572270
[&]{
2258-
Printer.printName(decl->getName());
2271+
Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl));
22592272
}, [&]{ // Signature
22602273
printGenericDeclGenericParams(decl);
22612274
});
@@ -2298,7 +2311,7 @@ void PrintAST::visitAssociatedTypeDecl(AssociatedTypeDecl *decl) {
22982311
Printer << tok::kw_associatedtype << " ";
22992312
recordDeclLoc(decl,
23002313
[&]{
2301-
Printer.printName(decl->getName());
2314+
Printer.printName(decl->getName(), PrintNameContext::TypeMember);
23022315
});
23032316

23042317
auto proto = decl->getProtocol();
@@ -2339,7 +2352,7 @@ void PrintAST::visitEnumDecl(EnumDecl *decl) {
23392352
printContextIfNeeded(decl);
23402353
recordDeclLoc(decl,
23412354
[&]{
2342-
Printer.printName(decl->getName());
2355+
Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl));
23432356
}, [&]{ // Signature
23442357
printGenericDeclGenericParams(decl);
23452358
});
@@ -2367,7 +2380,7 @@ void PrintAST::visitStructDecl(StructDecl *decl) {
23672380
printContextIfNeeded(decl);
23682381
recordDeclLoc(decl,
23692382
[&]{
2370-
Printer.printName(decl->getName());
2383+
Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl));
23712384
}, [&]{ // Signature
23722385
printGenericDeclGenericParams(decl);
23732386
});
@@ -2395,7 +2408,7 @@ void PrintAST::visitClassDecl(ClassDecl *decl) {
23952408
printContextIfNeeded(decl);
23962409
recordDeclLoc(decl,
23972410
[&]{
2398-
Printer.printName(decl->getName());
2411+
Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl));
23992412
}, [&]{ // Signature
24002413
printGenericDeclGenericParams(decl);
24012414
});
@@ -2528,7 +2541,7 @@ void PrintAST::visitVarDecl(VarDecl *decl) {
25282541
printContextIfNeeded(decl);
25292542
recordDeclLoc(decl,
25302543
[&]{
2531-
Printer.printName(decl->getName());
2544+
Printer.printName(decl->getName(), getTypeMemberPrintNameContext(decl));
25322545
});
25332546
if (decl->hasInterfaceType()) {
25342547
Printer << ": ";
@@ -2785,7 +2798,8 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
27852798
if (!decl->hasName()) {
27862799
Printer << "<anonymous>";
27872800
} else {
2788-
Printer.printName(decl->getName());
2801+
Printer.printName(decl->getName(),
2802+
getTypeMemberPrintNameContext(decl));
27892803
if (decl->isOperator())
27902804
Printer << " ";
27912805
}
@@ -2831,7 +2845,7 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
28312845
void PrintAST::printEnumElement(EnumElementDecl *elt) {
28322846
recordDeclLoc(elt,
28332847
[&]{
2834-
Printer.printName(elt->getName());
2848+
Printer.printName(elt->getName(), getTypeMemberPrintNameContext(elt));
28352849
});
28362850

28372851
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)