Skip to content

[6.0][Sema] Diagnose deprecated default implementations in the witness checker. #74276

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions include/swift/AST/Attr.h
Original file line number Diff line number Diff line change
Expand Up @@ -2693,6 +2693,10 @@ class DeclAttributes {
return getUnavailable(ctx) != nullptr;
}

bool isDeprecated(const ASTContext &ctx) const {
return getDeprecated(ctx) != nullptr;
}

/// Determine whether there is a swiftVersionSpecific attribute that's
/// unavailable relative to the provided language version.
bool
Expand Down
5 changes: 5 additions & 0 deletions include/swift/AST/DiagnosticsSema.def
Original file line number Diff line number Diff line change
Expand Up @@ -3119,6 +3119,11 @@ ERROR(witness_unavailable,none,
"unavailable %kind0 was used to satisfy a requirement of protocol %1%select{|: %2}2",
(const ValueDecl *, Identifier, StringRef))

WARNING(witness_deprecated,none,
"deprecated default implementation is used to satisfy %kind0 required by "
"protocol %1%select{|: %2}2",
(const ValueDecl *, Identifier, StringRef))

ERROR(redundant_conformance,none,
"redundant conformance of %0 to protocol %1", (Type, Identifier))
ERROR(redundant_conformance_conditional,none,
Expand Down
6 changes: 5 additions & 1 deletion include/swift/AST/RequirementMatch.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,10 @@ enum class CheckKind : unsigned {

/// The witness itself is inaccessible.
WitnessUnavailable,

/// The witness is a deprecated default implementation provided by the
/// protocol.
DefaultWitnessDeprecated,
};
/// Describes the suitability of the chosen witness for
/// the requirement.
Expand Down Expand Up @@ -464,4 +468,4 @@ struct RequirementMatch {

}

#endif // SWIFT_AST_REQUIREMENTMATCH_H
#endif // SWIFT_AST_REQUIREMENTMATCH_H
2 changes: 1 addition & 1 deletion lib/APIDigester/ModuleAnalyzerNodes.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1474,7 +1474,7 @@ SDKNodeInitInfo::SDKNodeInitInfo(SDKContext &Ctx, Decl *D):
ObjCName(Ctx.getObjcName(D)),
InitKind(Ctx.getInitKind(D)),
IsImplicit(D->isImplicit()),
IsDeprecated(D->getAttrs().getDeprecated(D->getASTContext())),
IsDeprecated(D->getAttrs().isDeprecated(D->getASTContext())),
IsABIPlaceholder(isABIPlaceholderRecursive(D)),
IsFromExtension(isDeclaredInExtension(D)),
DeclAttrs(collectDeclAttributes(D)) {
Expand Down
2 changes: 1 addition & 1 deletion lib/IDE/CodeCompletionResultBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ void CodeCompletionResultBuilder::setAssociatedDecl(const Decl *D) {
CurrentModule = MD;
}

if (D->getAttrs().getDeprecated(D->getASTContext()))
if (D->getAttrs().isDeprecated(D->getASTContext()))
setContextFreeNotRecommended(ContextFreeNotRecommendedReason::Deprecated);
else if (D->getAttrs().getSoftDeprecated(D->getASTContext()))
setContextFreeNotRecommended(
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/CSDiagnostics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4230,7 +4230,7 @@ findImportedCaseWithMatchingSuffix(Type instanceTy, DeclNameRef name) {

// Is one more available than the other?
WORSE(->getAttrs().isUnavailable(ctx));
WORSE(->getAttrs().getDeprecated(ctx));
WORSE(->getAttrs().isDeprecated(ctx));

// Does one have a shorter name (so the non-matching prefix is shorter)?
WORSE(->getName().getBaseName().userFacingName().size());
Expand Down
2 changes: 1 addition & 1 deletion lib/Sema/TypeCheckAvailability.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -198,7 +198,7 @@ computeExportContextBits(ASTContext &Ctx, Decl *D, bool *spi, bool *implicit,
if (D->isImplicit() && !isDeferBody)
*implicit = true;

if (D->getAttrs().getDeprecated(Ctx))
if (D->getAttrs().isDeprecated(Ctx))
*deprecated = true;

if (auto *A = D->getAttrs().getUnavailable(Ctx)) {
Expand Down
43 changes: 39 additions & 4 deletions lib/Sema/TypeCheckProtocol.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1740,6 +1740,8 @@ checkWitnessAvailability(ValueDecl *requirement,

RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
const RequirementMatch &match) {
auto &ctx = getASTContext();

if (!match.OptionalAdjustments.empty())
return CheckKind::OptionalityConflict;

Expand Down Expand Up @@ -1769,7 +1771,7 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
return RequirementCheck(CheckKind::Availability, requiredAvailability);
}

if (requirement->getAttrs().isUnavailable(getASTContext()) &&
if (requirement->getAttrs().isUnavailable(ctx) &&
match.Witness->getDeclContext() == DC) {
return RequirementCheck(CheckKind::Unavailable);
}
Expand All @@ -1792,11 +1794,11 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
}
}

if (match.Witness->getAttrs().isUnavailable(getASTContext()) &&
!requirement->getAttrs().isUnavailable(getASTContext())) {
if (match.Witness->getAttrs().isUnavailable(ctx) &&
!requirement->getAttrs().isUnavailable(ctx)) {
auto nominalOrExtensionIsUnavailable = [&]() {
if (auto extension = dyn_cast<ExtensionDecl>(DC)) {
if (extension->getAttrs().isUnavailable(getASTContext()))
if (extension->getAttrs().isUnavailable(ctx))
return true;
}

Expand All @@ -1813,6 +1815,20 @@ RequirementCheck WitnessChecker::checkWitness(ValueDecl *requirement,
return CheckKind::WitnessUnavailable;
}

// Warn about deprecated default implementations if the requirement is
// not deprecated, and the conformance is not deprecated.
bool isDefaultWitness = false;
if (auto *nominal = match.Witness->getDeclContext()->getSelfNominalTypeDecl())
isDefaultWitness = isa<ProtocolDecl>(nominal);
if (isDefaultWitness &&
match.Witness->getAttrs().isDeprecated(ctx) &&
!requirement->getAttrs().isDeprecated(ctx)) {
auto conformanceContext = ExportContext::forConformance(DC, Proto);
if (!conformanceContext.isDeprecated()) {
return RequirementCheck(CheckKind::DefaultWitnessDeprecated);
}
}

return CheckKind::Success;
}

Expand Down Expand Up @@ -4374,6 +4390,25 @@ ConformanceChecker::resolveWitnessViaLookup(ValueDecl *requirement) {
requirement->getName());
});
break;

case CheckKind::DefaultWitnessDeprecated:
getASTContext().addDelayedConformanceDiag(
Conformance, /*isError=*/false,
[witness, requirement](NormalProtocolConformance *conformance) {
auto &ctx = witness->getASTContext();
auto &diags = ctx.Diags;
SourceLoc diagLoc = getLocForDiagnosingWitness(conformance, witness);
auto *attr = witness->getAttrs().getDeprecated(ctx);
EncodedDiagnosticMessage EncodedMessage(attr->Message);
diags.diagnose(diagLoc, diag::witness_deprecated,
witness, conformance->getProtocol()->getName(),
EncodedMessage.Message);
emitDeclaredHereIfNeeded(diags, diagLoc, witness);
diags.diagnose(requirement, diag::kind_declname_declared_here,
DescriptiveDeclKind::Requirement,
requirement->getName());
});
break;
}

if (auto *classDecl = DC->getSelfClassDecl()) {
Expand Down
60 changes: 60 additions & 0 deletions test/decl/protocol/req/deprecated.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// RUN: %target-typecheck-verify-swift

protocol DeprecatedRequirement {
@available(*, deprecated)
func f()
}

extension DeprecatedRequirement {
@available(*, deprecated)
func f() {}
}

// No warning if both the requirement and the default implementation are deprecated
struct S1: DeprecatedRequirement {}

protocol DeprecatedDefault {
func f() // expected-note {{requirement 'f()' declared here}}
}

extension DeprecatedDefault {
@available(*, deprecated)
func f() {} // expected-note {{'f()' declared here}}
}

// expected-warning@+1 {{deprecated default implementation is used to satisfy instance method 'f()' required by protocol 'DeprecatedDefault'}}
struct S2: DeprecatedDefault {}

// No warning if the conformance itself is deprecated
@available(*, deprecated)
struct S3: DeprecatedDefault {
}

struct S4: DeprecatedDefault {
func f() {}
}

struct S5 {}

// No warning if the conformance itself is deprecated
@available(*, deprecated)
extension S5: DeprecatedDefault {}

@available(*, deprecated)
enum UnavailableEnum {
struct Nested: DeprecatedDefault {}
}

// Include message string from @available attribute if provided
protocol DeprecatedDefaultWithMessage {
func f() // expected-note {{requirement 'f()' declared here}}
}

extension DeprecatedDefaultWithMessage {
@available(*, deprecated, message: "write it yourself")
func f() {} // expected-note {{'f()' declared here}}
}


// expected-warning@+1 {{deprecated default implementation is used to satisfy instance method 'f()' required by protocol 'DeprecatedDefaultWithMessage': write it yourself}}
struct S6: DeprecatedDefaultWithMessage {}
2 changes: 1 addition & 1 deletion tools/SourceKit/lib/SwiftLang/SwiftDocSupport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -451,7 +451,7 @@ static bool initDocEntityInfo(const Decl *D,
}

Info.IsUnavailable = AvailableAttr::isUnavailable(D);
Info.IsDeprecated = D->getAttrs().getDeprecated(D->getASTContext()) != nullptr;
Info.IsDeprecated = D->getAttrs().isDeprecated(D->getASTContext());
Info.IsOptional = D->getAttrs().hasAttribute<OptionalAttr>();
if (auto *AFD = dyn_cast<AbstractFunctionDecl>(D)) {
Info.IsAsync = AFD->hasAsync();
Expand Down