Skip to content

Commit dfe00c3

Browse files
authored
Merge pull request #24277 from AnthonyLatsis/mutability-proto-stubs
ASTPrinter: Mutability fixes for protocol stubs
2 parents 42f72cb + 6f119b0 commit dfe00c3

File tree

4 files changed

+57
-10
lines changed

4 files changed

+57
-10
lines changed

lib/AST/ASTPrinter.cpp

+12-9
Original file line numberDiff line numberDiff line change
@@ -950,6 +950,9 @@ void PrintAST::printAttributes(const Decl *D) {
950950
#define CONTEXTUAL_SIMPLE_DECL_ATTR(X, Class, Y, Z) EXCLUDE_ATTR(Class)
951951
#define CONTEXTUAL_DECL_ATTR_ALIAS(X, Class) EXCLUDE_ATTR(Class)
952952
#include "swift/AST/Attr.def"
953+
} else if (isa<FuncDecl>(D)) {
954+
Options.ExcludeAttrList.push_back(DAK_Mutating);
955+
Options.ExcludeAttrList.push_back(DAK_NonMutating);
953956
}
954957

955958
// If the declaration is implicitly @objc, print the attribute now.
@@ -978,12 +981,6 @@ void PrintAST::printAttributes(const Decl *D) {
978981
}
979982
}
980983

981-
// Explicitly print 'mutating' and 'nonmutating' before getters and setters
982-
// for which that is true.
983-
if (auto accessor = dyn_cast<AccessorDecl>(D)) {
984-
printMutatingModifiersIfNeeded(accessor);
985-
}
986-
987984
Options.ExcludeAttrList.resize(originalExcludeAttrCount);
988985
}
989986

@@ -1706,9 +1703,11 @@ void PrintAST::printBodyIfNecessary(const AbstractFunctionDecl *decl) {
17061703
}
17071704

17081705
void PrintAST::printMutatingModifiersIfNeeded(const AccessorDecl *accessor) {
1709-
if (accessor->isAssumedNonMutating() && accessor->isMutating()) {
1706+
if (accessor->isAssumedNonMutating() && accessor->isMutating() &&
1707+
!Options.excludeAttrKind(DAK_Mutating)) {
17101708
Printer.printKeyword("mutating", Options, " ");
1711-
} else if (accessor->isExplicitNonMutating()) {
1709+
} else if (accessor->isExplicitNonMutating() &&
1710+
!Options.excludeAttrKind(DAK_NonMutating)) {
17121711
Printer.printKeyword("nonmutating", Options, " ");
17131712
}
17141713
}
@@ -2717,6 +2716,10 @@ bool PrintAST::printASTNodes(const ArrayRef<ASTNode> &Elements,
27172716
void PrintAST::visitAccessorDecl(AccessorDecl *decl) {
27182717
printDocumentationComment(decl);
27192718
printAttributes(decl);
2719+
2720+
// Explicitly print 'mutating' and 'nonmutating' before getters and setters
2721+
// for which that is true.
2722+
printMutatingModifiersIfNeeded(decl);
27202723
switch (auto kind = decl->getAccessorKind()) {
27212724
case AccessorKind::Get:
27222725
case AccessorKind::Address:
@@ -2773,7 +2776,7 @@ void PrintAST::visitFuncDecl(FuncDecl *decl) {
27732776
if (!Options.SkipIntroducerKeywords) {
27742777
if (decl->isStatic() && Options.PrintStaticKeyword)
27752778
printStaticKeyword(decl->getCorrectStaticSpelling());
2776-
if (decl->isMutating() && !decl->getAttrs().hasAttribute<MutatingAttr>()) {
2779+
if (decl->isMutating() && !Options.excludeAttrKind(DAK_Mutating)) {
27772780
Printer.printKeyword("mutating", Options, " ");
27782781
} else if (decl->isConsuming() && !decl->getAttrs().hasAttribute<ConsumingAttr>()) {
27792782
Printer.printKeyword("__consuming", Options, " ");

lib/Sema/TypeCheckProtocol.cpp

+9
Original file line numberDiff line numberDiff line change
@@ -2627,6 +2627,15 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
26272627
Options.FunctionDefinitions = true;
26282628
Options.PrintAccessorBodiesInProtocols = true;
26292629

2630+
// Skip 'mutating' only inside classes: mutating methods usually
2631+
// don't have a sensible non-mutating implementation.
2632+
bool isClass = Adopter->getSelfClassDecl() != nullptr;
2633+
if (isClass)
2634+
Options.ExcludeAttrList.push_back(DAK_Mutating);
2635+
// 'nonmutating' is only meaningful on value type member accessors.
2636+
if (isClass || !isa<AbstractStorageDecl>(Requirement))
2637+
Options.ExcludeAttrList.push_back(DAK_NonMutating);
2638+
26302639
// FIXME: Once we support move-only types, remove this if the
26312640
// conforming type is move-only. Until then, don't suggest printing
26322641
// __consuming on a protocol requirement.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
public protocol ExternalMutabilityProto {
2+
mutating func foo()
3+
subscript() -> Int { mutating get nonmutating set }
4+
}

test/decl/protocol/conforms/fixit_stub_editor.swift

+32-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,7 @@
1-
// RUN: %target-typecheck-verify-swift -diagnostics-editor-mode
1+
// RUN: %empty-directory(%t)
2+
// RUN: %target-swift-frontend %S/Inputs/fixit_stub_mutability_proto_module.swift -emit-module -parse-as-library -o %t
3+
4+
// RUN: %target-swift-frontend -typecheck %s -I %t -verify -diagnostics-editor-mode
25

36
protocol P1 {
47
@available(*, deprecated)
@@ -26,3 +29,31 @@ protocol P4 : P3 {
2629
}
2730

2831
class C2 : P4 {} // expected-error{{type 'C2' does not conform to protocol 'P4'}} expected-error{{type 'C2' does not conform to protocol 'P3'}} expected-note{{do you want to add protocol stubs?}}{{16-16=\n typealias T1 = <#type#>\n\n typealias T2 = <#type#>\n\n typealias T3 = <#type#>\n}}
32+
33+
34+
// =============================================================================
35+
// Test how we print stubs for mutating and non-mutating requirements.
36+
//
37+
// - Test that we don't print 'mutating' in classes.
38+
// - Test that we print 'non-mutating' for non-mutating setters
39+
// in structs.
40+
// =============================================================================
41+
42+
protocol LocalMutabilityProto {
43+
mutating func foo()
44+
subscript() -> Int { get nonmutating set }
45+
}
46+
47+
class Class1: LocalMutabilityProto { // expected-error{{type 'Class1' does not conform to protocol 'LocalMutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{37-37=\n func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n get {\n <#code#>\n \}\n set {\n <#code#>\n \}\n \}\n}}
48+
}
49+
50+
struct Struct1: LocalMutabilityProto { // expected-error{{type 'Struct1' does not conform to protocol 'LocalMutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{39-39=\n mutating func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n get {\n <#code#>\n \}\n nonmutating set {\n <#code#>\n \}\n \}\n}}
51+
}
52+
53+
import fixit_stub_mutability_proto_module
54+
55+
class Class2: ExternalMutabilityProto { // expected-error{{type 'Class2' does not conform to protocol 'ExternalMutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{40-40=\n func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n get {\n <#code#>\n \}\n set(newValue) {\n <#code#>\n \}\n \}\n}}
56+
}
57+
58+
struct Struct2: ExternalMutabilityProto { // expected-error{{type 'Struct2' does not conform to protocol 'ExternalMutabilityProto'}} expected-note{{do you want to add protocol stubs?}} {{42-42=\n mutating func foo() {\n <#code#>\n \}\n\n subscript() -> Int {\n mutating get {\n <#code#>\n \}\n nonmutating set(newValue) {\n <#code#>\n \}\n \}\n}}
59+
}

0 commit comments

Comments
 (0)