Skip to content

Commit ba5564a

Browse files
author
Harlan Haskins
committed
[Interface] Print private/internal properties
All properties which contribute to the storage of a type should be printed, and their names should be hidden from interfaces. Print them with '_' as their name, and teach the parser to recognize these special patterns when parsing interface files. Partially resolves rdar://43810647
1 parent 9b62f0d commit ba5564a

File tree

5 files changed

+123
-8
lines changed

5 files changed

+123
-8
lines changed

include/swift/AST/PrintOptions.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,9 @@ struct PrintOptions {
322322
/// for optionals that are nested within other optionals.
323323
bool PrintOptionalAsImplicitlyUnwrapped = false;
324324

325+
/// Replaces the name of private and internal properties of types with '_'.
326+
bool OmitNameOfInaccessibleProperties = false;
327+
325328
/// \brief Print dependent types as references into this generic environment.
326329
GenericEnvironment *GenericEnv = nullptr;
327330

lib/AST/ASTPrinter.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,19 @@ void PrintOptions::clearSynthesizedExtension() {
6464
TransformContext.reset();
6565
}
6666

67+
static bool contributesToParentTypeStorage(const AbstractStorageDecl *ASD) {
68+
return ASD->getDeclContext()->isTypeContext() && ASD->hasStorage() &&
69+
!ASD->isStatic();
70+
}
71+
6772
PrintOptions PrintOptions::printTextualInterfaceFile() {
6873
PrintOptions result;
6974
result.PrintLongAttrsOnSeparateLines = true;
7075
result.TypeDefinitions = true;
7176
result.PrintIfConfig = false;
7277
result.FullyQualifiedTypes = true;
7378
result.SkipImports = true;
79+
result.OmitNameOfInaccessibleProperties = true;
7480

7581
class ShouldPrintForTextualInterface : public ShouldPrintChecker {
7682
bool shouldPrint(const Decl *D, const PrintOptions &options) override {
@@ -79,8 +85,14 @@ PrintOptions PrintOptions::printTextualInterfaceFile() {
7985
AccessScope accessScope =
8086
VD->getFormalAccessScope(/*useDC*/nullptr,
8187
/*treatUsableFromInlineAsPublic*/true);
82-
if (!accessScope.isPublic())
88+
if (!accessScope.isPublic()) {
89+
// We do want to print private stored properties, without their
90+
// original names present.
91+
if (auto *ASD = dyn_cast<AbstractStorageDecl>(VD))
92+
if (contributesToParentTypeStorage(ASD))
93+
return true;
8394
return false;
95+
}
8496
}
8597

8698
// Skip typealiases that just redeclare generic parameters.
@@ -821,7 +833,13 @@ void PrintAST::printPattern(const Pattern *pattern) {
821833

822834
case PatternKind::Named: {
823835
auto named = cast<NamedPattern>(pattern);
824-
recordDeclLoc(named->getDecl(), [&]{
836+
auto decl = named->getDecl();
837+
recordDeclLoc(decl, [&]{
838+
if (Options.OmitNameOfInaccessibleProperties &&
839+
contributesToParentTypeStorage(decl) &&
840+
decl->getFormalAccess() < AccessLevel::Public)
841+
Printer << "_";
842+
else
825843
Printer.printName(named->getBoundName());
826844
});
827845
break;
@@ -1362,6 +1380,12 @@ bool ShouldPrintChecker::shouldPrint(const Decl *D,
13621380
return !EED->getSourceRange().isValid();
13631381
}
13641382

1383+
if (auto *ASD = dyn_cast<AbstractStorageDecl>(D)) {
1384+
if (Options.OmitNameOfInaccessibleProperties &&
1385+
contributesToParentTypeStorage(ASD))
1386+
return true;
1387+
}
1388+
13651389
// Skip declarations that are not accessible.
13661390
if (auto *VD = dyn_cast<ValueDecl>(D)) {
13671391
if (Options.AccessFilter > AccessLevel::Private &&

lib/Parse/ParsePattern.cpp

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
#include "swift/AST/ASTWalker.h"
2020
#include "swift/AST/Initializer.h"
21+
#include "swift/AST/Module.h"
2122
#include "swift/Basic/StringExtras.h"
2223
#include "swift/Parse/CodeCompletionCallbacks.h"
2324
#include "swift/Parse/SyntaxParsingContext.h"
@@ -907,22 +908,34 @@ ParserResult<Pattern> Parser::parseTypedPattern() {
907908
///
908909
ParserResult<Pattern> Parser::parsePattern() {
909910
SyntaxParsingContext PatternCtx(SyntaxContext, SyntaxContextKind::Pattern);
911+
bool isLet = (InVarOrLetPattern != IVOLP_InVar);
912+
auto specifier = isLet
913+
? VarDecl::Specifier::Let
914+
: VarDecl::Specifier::Var;
910915
switch (Tok.getKind()) {
911916
case tok::l_paren:
912917
return parsePatternTuple();
913918

914919
case tok::kw__:
920+
// Normally, '_' is invalid in type context for patterns, but they show up
921+
// in interface files as the name for type members that are non-public.
922+
// Treat them as an implicitly synthesized NamedPattern with a nameless
923+
// VarDecl inside.
924+
if (CurDeclContext->isTypeContext() &&
925+
SF.Kind == SourceFileKind::Interface) {
926+
PatternCtx.setCreateSyntax(SyntaxKind::IdentifierPattern);
927+
auto VD = new (Context) VarDecl(
928+
/*IsStatic*/false, specifier, /*IsCaptureList*/false,
929+
consumeToken(tok::kw__), Identifier(), CurDeclContext);
930+
return makeParserResult(new (Context) NamedPattern(VD, /*implicit*/true));
931+
}
915932
PatternCtx.setCreateSyntax(SyntaxKind::WildcardPattern);
916933
return makeParserResult(new (Context) AnyPattern(consumeToken(tok::kw__)));
917934

918935
case tok::identifier: {
919936
PatternCtx.setCreateSyntax(SyntaxKind::IdentifierPattern);
920937
Identifier name;
921938
SourceLoc loc = consumeIdentifier(&name);
922-
bool isLet = (InVarOrLetPattern != IVOLP_InVar);
923-
auto specifier = isLet
924-
? VarDecl::Specifier::Let
925-
: VarDecl::Specifier::Var;
926939
if (Tok.isIdentifierOrUnderscore() && !Tok.isContextualDeclKeyword())
927940
diagnoseConsecutiveIDs(name.str(), loc, isLet ? "constant" : "variable");
928941

lib/Sema/TypeCheckDecl.cpp

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -946,9 +946,15 @@ static void checkRedeclaration(TypeChecker &tc, ValueDecl *current) {
946946
/// Does the context allow pattern bindings that don't bind any variables?
947947
static bool contextAllowsPatternBindingWithoutVariables(DeclContext *dc) {
948948

949-
// Property decls in type context must bind variables.
950-
if (dc->isTypeContext())
949+
// Property decls in type context must bind variables unless in a module
950+
// interface, where private members don't bind variables but contribute
951+
// to storage.
952+
if (dc->isTypeContext()) {
953+
if (auto SF = dc->getParentSourceFile())
954+
if (SF->Kind == SourceFileKind::Interface)
955+
return true;
951956
return false;
957+
}
952958

953959
// Global variable decls must bind variables, except in scripts.
954960
if (dc->isModuleScopeContext()) {
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// RUN: %empty-directory(%t)
2+
// R UN: %target-swift-frontend -emit-module -o %t/Test~partial.swiftmodule -module-name Test -primary-file %s
3+
// R UN: %target-swift-frontend -merge-modules -emit-module -o %t/Test.swiftmodule %t/Test~partial.swiftmodule
4+
// R UN: %target-swift-ide-test -print-module -module-to-print=Test -source-filename=x -I %t | %FileCheck %s
5+
6+
// RUN: %target-swift-frontend -emit-interface-path %t.swiftinterface -enable-resilience -emit-module -o /dev/null %s
7+
// RUN: %FileCheck %s < %t.swiftinterface
8+
9+
// CHECK: struct MyStruct {{{$}}
10+
public struct MyStruct {
11+
// CHECK-NEXT: var publicVar: Int{{$}}
12+
public var publicVar: Int
13+
14+
// CHECK-NEXT: let publicLet: Bool{{$}}
15+
public let publicLet: Bool
16+
17+
// CHECK-NEXT: internal var _: String{{$}}
18+
var internalVar: String
19+
20+
// CHECK-NEXT: internal let _: Bool{{$}}
21+
let internalLet: Bool
22+
23+
// CHECK-NEXT: private var _: String{{$}}
24+
private var privateVar: String
25+
26+
// CHECK-NEXT: private let _: Bool{{$}}
27+
private let privateLet: Bool
28+
29+
// CHECK-NOT: private var
30+
private var computedPrivateVar: String {
31+
return "computedPrivateVar"
32+
}
33+
34+
// CHECK-NOT: private static var
35+
private static var staticPrivateVar: String = ""
36+
37+
// CHECK: }{{$}}
38+
}
39+
40+
// CHECK: class MyClass {{{$}}
41+
public class MyClass {
42+
// CHECK-NEXT: public var publicVar: Int{{$}}
43+
public var publicVar: Int = 0
44+
45+
// CHECK-NEXT: public let publicLet: Bool{{$}}
46+
public let publicLet: Bool = true
47+
48+
// CHECK-NEXT: internal var _: String{{$}}
49+
var internalVar: String = ""
50+
51+
// CHECK-NEXT: internal let _: Bool{{$}}
52+
let internalLet: Bool = true
53+
54+
// CHECK-NEXT: private var _: String{{$}}
55+
private var privateVar: String = ""
56+
57+
// CHECK-NEXT: private let _: Bool{{$}}
58+
private let privateLet: Bool = true
59+
60+
// CHECK-NOT: private var
61+
private var computedPrivateVar: String {
62+
return "computedPrivateVar"
63+
}
64+
65+
// CHECK-NOT: private static var
66+
private static var staticPrivateVar: String = ""
67+
68+
// CHECK: }{{$}}
69+
}

0 commit comments

Comments
 (0)