Skip to content

Commit 1445b81

Browse files
authored
Merge pull request #80476 from beccadax/implement-this
Stub fix-its for missing objcImpl requirements
2 parents f4bb372 + 9a1f6ae commit 1445b81

17 files changed

+224
-86
lines changed

include/swift/AST/ASTPrinter.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -413,7 +413,8 @@ class ExtraIndentStreamPrinter : public StreamPrinter {
413413
void printContext(raw_ostream &os, DeclContext *dc);
414414

415415
bool printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
416-
Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS);
416+
Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS,
417+
bool withExplicitObjCAttr = false);
417418

418419
/// Print a keyword or punctuator directly by its kind.
419420
llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, tok keyword);

include/swift/AST/DiagnosticsSema.def

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1980,10 +1980,19 @@ ERROR(objc_implementation_wrong_swift_name,none,
19801980
"you mean %1?",
19811981
(ObjCSelector, const ValueDecl *))
19821982

1983-
ERROR(objc_implementation_missing_impl,none,
1984-
"extension for %select{main class interface|category %0}0 should "
1985-
"provide implementation for %kind1",
1986-
(Identifier, ValueDecl *))
1983+
ERROR(objc_implementation_missing_impls,none,
1984+
"extension for %select{main class interface|category %0}0 does not "
1985+
"provide all required implementations",
1986+
(Identifier))
1987+
NOTE(objc_implementation_missing_impls_fixit,none,
1988+
"add stub%s0 for missing '@implementation' requirement%s0",
1989+
(unsigned))
1990+
NOTE(objc_implementation_missing_impl,none,
1991+
"missing %kind0",
1992+
(ValueDecl *))
1993+
NOTE(objc_implementation_missing_impl_either,none,
1994+
"missing %kind0 or %1",
1995+
(ValueDecl *, ValueDecl *))
19871996

19881997
ERROR(objc_implementation_class_or_instance_mismatch,none,
19891998
"%kind0 does not match %kindonly1 declared in header",

include/swift/AST/PrintOptions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ struct PrintOptions {
143143
/// Whether to print *any* accessors on properties.
144144
bool PrintPropertyAccessors = true;
145145

146+
/// Use \c let for a read-only computed property.
147+
bool InferPropertyIntroducerFromAccessors = false;
148+
146149
/// Whether to print *any* accessors on subscript.
147150
bool PrintSubscriptAccessors = true;
148151

@@ -172,6 +175,10 @@ struct PrintOptions {
172175
/// Whether to print the bodies of accessors in protocol context.
173176
bool PrintAccessorBodiesInProtocols = false;
174177

178+
/// Whether to print the parameter list of accessors like \c set . (Even when
179+
/// \c true , parameters marked implicit still won't be printed.)
180+
bool PrintExplicitAccessorParameters = true;
181+
175182
/// Whether to print type definitions.
176183
bool TypeDefinitions = false;
177184

lib/AST/ASTPrinter.cpp

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3932,11 +3932,18 @@ void PrintAST::visitVarDecl(VarDecl *decl) {
39323932
if (decl->isStatic() && Options.PrintStaticKeyword)
39333933
printStaticKeyword(decl->getCorrectStaticSpelling());
39343934
if (decl->getKind() == DeclKind::Var || Options.PrintParameterSpecifiers) {
3935+
// If InferPropertyIntroducerFromAccessors is set, turn all read-only
3936+
// properties to `let`.
3937+
auto introducer = decl->getIntroducer();
3938+
if (Options.InferPropertyIntroducerFromAccessors &&
3939+
!cast<VarDecl>(decl)->isSettable(nullptr))
3940+
introducer = VarDecl::Introducer::Let;
3941+
39353942
// Map all non-let specifiers to 'var'. This is not correct, but
39363943
// SourceKit relies on this for info about parameter decls.
39373944

39383945
Printer.printIntroducerKeyword(
3939-
decl->getIntroducer() == VarDecl::Introducer::Let ? "let" : "var",
3946+
introducer == VarDecl::Introducer::Let ? "let" : "var",
39403947
Options, " ");
39413948
}
39423949
printContextIfNeeded(decl);
@@ -4221,7 +4228,8 @@ void PrintAST::visitAccessorDecl(AccessorDecl *decl) {
42214228
Printer << getAccessorLabel(decl->getAccessorKind());
42224229

42234230
auto params = decl->getParameters();
4224-
if (params->size() != 0 && !params->get(0)->isImplicit()) {
4231+
if (params->size() != 0 && !params->get(0)->isImplicit()
4232+
&& Options.PrintExplicitAccessorParameters) {
42254233
auto Name = params->get(0)->getName();
42264234
if (!Name.empty()) {
42274235
Printer << "(";

lib/Sema/TypeCheckDeclObjC.cpp

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "TypeCheckProtocol.h"
2020
#include "TypeChecker.h"
2121
#include "swift/AST/ASTContext.h"
22+
#include "swift/AST/ASTPrinter.h"
2223
#include "swift/AST/AvailabilityInference.h"
2324
#include "swift/AST/Decl.h"
2425
#include "swift/AST/ExistentialLayout.h"
@@ -4038,18 +4039,46 @@ class ObjCImplementationChecker {
40384039

40394040
public:
40404041
void diagnoseUnmatchedRequirements() {
4042+
auto ext = dyn_cast<ExtensionDecl>(decl);
4043+
if (!ext)
4044+
return;
4045+
4046+
llvm::SmallString<128> stubs;
4047+
llvm::raw_svector_ostream stubStream(stubs);
4048+
4049+
unsigned numEmitted = 0;
4050+
40414051
for (auto req : unmatchedRequirements) {
40424052
// Ignore `@optional` protocol requirements.
40434053
if (isOptionalObjCProtocolRequirement(req))
40444054
continue;
40454055

4046-
auto ext = cast<IterableDeclContext>(req->getDeclContext()->getAsDecl())
4047-
->getImplementationContext();
4056+
if (numEmitted == 0) {
4057+
// Emit overall diagnostic for all the notes to attach to.
4058+
diagnose(ext, diag::objc_implementation_missing_impls,
4059+
getCategoryName(req->getDeclContext()));
4060+
}
40484061

4049-
diagnose(ext->getDecl(), diag::objc_implementation_missing_impl,
4050-
getCategoryName(req->getDeclContext()), req);
4062+
numEmitted += 1;
4063+
4064+
// Emit different diagnostic if there's an async alternative.
4065+
if (auto asyncAlternative = getAsyncAlternative(req)) {
4066+
diagnose(ext, diag::objc_implementation_missing_impl_either,
4067+
asyncAlternative, req);
4068+
req = asyncAlternative;
4069+
} else {
4070+
diagnose(ext, diag::objc_implementation_missing_impl, req);
4071+
}
4072+
4073+
// Append stub for this requirement into eventual fix-it.
4074+
swift::printRequirementStub(req, ext, ext->getSelfInterfaceType(),
4075+
ext->getStartLoc(), stubStream,
4076+
/*objCAttr=*/true);
4077+
}
40514078

4052-
// FIXME: Should give fix-it to add stub implementation
4079+
if (!stubs.empty()) {
4080+
diagnose(ext, diag::objc_implementation_missing_impls_fixit, numEmitted)
4081+
.fixItInsertAfter(ext->getBraces().Start, stubs);
40534082
}
40544083
}
40554084

lib/Sema/TypeCheckProtocol.cpp

Lines changed: 42 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -3704,8 +3704,12 @@ static Type getTupleConformanceTypeWitness(DeclContext *dc,
37043704

37053705
bool swift::
37063706
printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
3707-
Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS) {
3708-
if (isa<ConstructorDecl>(Requirement)) {
3707+
Type AdopterTy, SourceLoc TypeLoc, raw_ostream &OS,
3708+
bool withExplicitObjCAttr) {
3709+
// We sometimes use this for @implementation extensions too.
3710+
bool forProtocol = isa<ProtocolDecl>(Requirement->getDeclContext());
3711+
3712+
if (isa<ConstructorDecl>(Requirement) && forProtocol) {
37093713
if (auto CD = Adopter->getSelfClassDecl()) {
37103714
if (!CD->isSemanticallyFinal() && isa<ExtensionDecl>(Adopter)) {
37113715
// In this case, user should mark class as 'final' or define
@@ -3731,15 +3735,35 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
37313735
ExtraIndentStreamPrinter Printer(OS, StubIndent);
37323736
Printer.printNewline();
37333737

3738+
PrintOptions Options = PrintOptions::printForDiagnostics(
3739+
AccessLevel::Private, Ctx.TypeCheckerOpts.PrintFullConvention);
3740+
Options.PrintDocumentationComments = false;
3741+
Options.PrintAccess = false;
3742+
Options.SkipAttributes = true;
3743+
Options.FunctionDefinitions = true;
3744+
Options.PrintAccessorBodiesInProtocols = true;
3745+
Options.PrintExplicitAccessorParameters = false;
3746+
Options.FullyQualifiedTypesIfAmbiguous = true;
3747+
3748+
if (withExplicitObjCAttr) {
3749+
if (auto runtimeName = Requirement->getObjCRuntimeName()) {
3750+
llvm::SmallString<32> scratch;
3751+
Printer.printAttrName("@objc");
3752+
Printer << "(" << runtimeName->getString(scratch) << ")";
3753+
Printer.printNewline();
3754+
Options.ExcludeAttrList.push_back(DeclAttrKind::ObjC);
3755+
}
3756+
}
3757+
37343758
AccessLevel Access =
37353759
std::min(
37363760
/* Access of the context */
37373761
Adopter->getSelfNominalTypeDecl()->getFormalAccess(),
37383762
/* Access of the protocol */
3739-
Requirement->getDeclContext()->getSelfProtocolDecl()->
3740-
getFormalAccess());
3741-
if (Access == AccessLevel::Public)
3742-
Printer << "public ";
3763+
Requirement->getDeclContext()->getSelfNominalTypeDecl()
3764+
->getFormalAccess());
3765+
if (Access > AccessLevel::Internal)
3766+
Printer.printKeyword(getAccessLevelSpelling(Access), Options, " ");
37433767

37443768
if (auto MissingTypeWitness = dyn_cast<AssociatedTypeDecl>(Requirement)) {
37453769
Printer << "typealias " << MissingTypeWitness->getName() << " = ";
@@ -3753,7 +3777,7 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
37533777

37543778
Printer << "\n";
37553779
} else {
3756-
if (isa<ConstructorDecl>(Requirement)) {
3780+
if (isa<ConstructorDecl>(Requirement) && forProtocol) {
37573781
if (auto CD = Adopter->getSelfClassDecl()) {
37583782
if (!CD->isFinal()) {
37593783
Printer << "required ";
@@ -3763,15 +3787,6 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
37633787
}
37643788
}
37653789

3766-
PrintOptions Options = PrintOptions::printForDiagnostics(
3767-
AccessLevel::Private, Ctx.TypeCheckerOpts.PrintFullConvention);
3768-
Options.PrintDocumentationComments = false;
3769-
Options.PrintAccess = false;
3770-
Options.SkipAttributes = true;
3771-
Options.FunctionDefinitions = true;
3772-
Options.PrintAccessorBodiesInProtocols = true;
3773-
Options.FullyQualifiedTypesIfAmbiguous = true;
3774-
37753790
bool AdopterIsClass = Adopter->getSelfClassDecl() != nullptr;
37763791
// Skip 'mutating' only inside classes: mutating methods usually
37773792
// don't have a sensible non-mutating implementation.
@@ -3797,9 +3812,12 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
37973812
};
37983813
Options.setBaseType(AdopterTy);
37993814
Options.CurrentModule = Adopter->getParentModule();
3800-
if (isa<NominalTypeDecl>(Adopter)) {
3801-
// Create a variable declaration instead of a computed property in
3802-
// nominal types...
3815+
3816+
// Can the conforming declaration declare a stored property?
3817+
auto ImplementedAdopter = Adopter->getImplementedObjCContext();
3818+
if (isa<NominalTypeDecl>(ImplementedAdopter) &&
3819+
(!isa<EnumDecl>(ImplementedAdopter) || Requirement->isStatic())) {
3820+
// Create a variable declaration instead of a computed property...
38033821
Options.PrintPropertyAccessors = false;
38043822

38053823
// ...but a non-mutating setter requirement will force us into a
@@ -3810,6 +3828,11 @@ printRequirementStub(ValueDecl *Requirement, DeclContext *Adopter,
38103828
if (const auto Set = VD->getOpaqueAccessor(AccessorKind::Set))
38113829
if (Set->getAttrs().hasAttribute<NonMutatingAttr>())
38123830
Options.PrintPropertyAccessors = true;
3831+
3832+
// If we're not printing the accessors, make them affect the introducer
3833+
// instead.
3834+
Options.InferPropertyIntroducerFromAccessors =
3835+
!Options.PrintPropertyAccessors;
38133836
}
38143837
Requirement->print(Printer, Options);
38153838
Printer << "\n";

test/ClangImporter/rdar123543707.swift

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import Module
77
import Module_Private.Sub4
88

99
@_objcImplementation extension Module {
10-
// expected-warning@-1 {{extension for main class interface should provide implementation for class method 'version()'}}
11-
// expected-warning@-2 {{extension for main class interface should provide implementation for class method 'alloc()'}}
10+
// expected-warning@-1 {{extension for main class interface does not provide all required implementations}}
11+
// expected-note@-2 {{missing class method 'version()'}}
12+
// expected-note@-3 {{missing class method 'alloc()'}}
13+
// expected-note@-4 {{add stubs for missing '@implementation' requirements}} {{40-40=\n @objc(version)\n open class func version() -> UnsafePointer<CChar>! {\n <#code#>\n \}\n\n @objc(alloc)\n open class func alloc() -> Self! {\n <#code#>\n \}\n}}
1214
}
1315

1416
extension Module: @retroactive ModuleProto {} // no-error

test/Sema/fixits-derived-conformances.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,5 +17,5 @@ extension GenericStruct: @retroactive Equatable { }
1717

1818
extension Enum: @retroactive CaseIterable { }
1919
// expected-error@-1 {{extension outside of file declaring enum 'Enum' prevents automatic synthesis of 'allCases' for protocol 'CaseIterable'}}
20-
// expected-note@-2 {{add stubs for conformance}}{{44-44=\n public static var allCases: [Enum]\n}}
20+
// expected-note@-2 {{add stubs for conformance}}{{44-44=\n public static let allCases: [Enum]\n}}
2121

test/decl/ext/Inputs/objc_implementation.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@
5353
@property (readonly) int readonlyPropertyFromHeader4;
5454
@property (readonly) int readonlyPropertyFromHeader5;
5555
@property (readonly) int readonlyPropertyFromHeader6;
56+
@property (readonly) int readonlyPropertyFromHeader7;
5657

5758
+ (void)classMethod1:(int)param;
5859
+ (void)classMethod2:(int)param;
@@ -84,6 +85,10 @@
8485
@property int categoryPropertyFromHeader2;
8586
@property int categoryPropertyFromHeader3;
8687
@property int categoryPropertyFromHeader4;
88+
@property int categoryPropertyFromHeader5;
89+
90+
@property (readonly) int categoryReadonlyPropertyFromHeader1;
91+
8792

8893
@end
8994

@@ -118,6 +123,7 @@
118123
- (void)doSomethingAsynchronousWithCompletionHandler:(void (^ _Nonnull)(id _Nullable result, NSError * _Nullable error))completionHandler;
119124
- (void)doSomethingElseAsynchronousWithCompletionHandler:(void (^ _Nullable)(id _Nonnull result))completionHandler;
120125
- (void)doSomethingFunAndAsynchronousWithCompletionHandler:(void (^ _Nonnull)(id _Nullable result, NSError * _Nullable error))completionHandler;
126+
- (void)doSomethingMissingAndAsynchronousWithCompletionHandler:(void (^ _Nonnull)(id _Nullable result, NSError * _Nullable error))completionHandler;
121127

122128
- (void)doSomethingOverloadedWithCompletionHandler:(void (^ _Nonnull)())completionHandler;
123129
- (void)doSomethingOverloaded __attribute__((__swift_attr__("@_unavailableFromAsync(message: \"Use async doSomethingOverloaded instead.\")")));

0 commit comments

Comments
 (0)