Skip to content

Commit 9676d66

Browse files
committed
[Typechecker] Emit a specialized diagnostic for redeclaration errors when the declaration is synthesized
1 parent 12a5243 commit 9676d66

File tree

4 files changed

+85
-1
lines changed

4 files changed

+85
-1
lines changed

include/swift/AST/Decl.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5217,6 +5217,12 @@ class VarDecl : public AbstractStorageDecl {
52175217
Optional<PropertyWrapperMutability>
52185218
getPropertyWrapperMutability() const;
52195219

5220+
/// Returns whether this property is the backing storage property or a storage
5221+
/// wrapper for wrapper instance's projectedValue. If this property is
5222+
/// neither, then it returns `None`.
5223+
Optional<PropertyWrapperSynthesizedPropertyKind>
5224+
getPropertyWrapperSynthesizedPropertyKind() const;
5225+
52205226
/// Retrieve the backing storage property for a property that has an
52215227
/// attached property wrapper.
52225228
///

include/swift/AST/DiagnosticsSema.def

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -755,12 +755,17 @@ ERROR(invalid_redecl,none,"invalid redeclaration of %0", (DeclName))
755755
ERROR(invalid_redecl_init,none,
756756
"invalid redeclaration of synthesized %select{|memberwise }1%0",
757757
(DeclName, bool))
758+
ERROR(invalid_redecl_implicit,none,
759+
"invalid redeclaration of synthesized %select{%0|witness for protocol requirement}1 %2",
760+
(DescriptiveDeclKind, bool, DeclName))
758761
WARNING(invalid_redecl_swift5_warning,none,
759762
"redeclaration of %0 is deprecated and will be an error in Swift 5",
760763
(DeclName))
761764

762765
NOTE(invalid_redecl_prev,none,
763766
"%0 previously declared here", (DeclName))
767+
NOTE(invalid_redecl_implicit_wrapper,none,
768+
"%0 synthesized for property wrapper %select{projected value|backing storage}1", (DeclName, bool))
764769

765770
ERROR(ambiguous_type_base,none,
766771
"%0 is ambiguous for type lookup in this context", (DeclNameRef))

lib/AST/Decl.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5950,6 +5950,17 @@ VarDecl::getPropertyWrapperMutability() const {
59505950
None);
59515951
}
59525952

5953+
Optional<PropertyWrapperSynthesizedPropertyKind>
5954+
VarDecl::getPropertyWrapperSynthesizedPropertyKind() const {
5955+
if (getOriginalWrappedProperty(
5956+
PropertyWrapperSynthesizedPropertyKind::Backing))
5957+
return PropertyWrapperSynthesizedPropertyKind::Backing;
5958+
if (getOriginalWrappedProperty(
5959+
PropertyWrapperSynthesizedPropertyKind::StorageWrapper))
5960+
return PropertyWrapperSynthesizedPropertyKind::StorageWrapper;
5961+
return None;
5962+
}
5963+
59535964
VarDecl *VarDecl::getPropertyWrapperBackingProperty() const {
59545965
return getPropertyWrapperBackingPropertyInfo().backingVar;
59555966
}

lib/Sema/TypeCheckDeclPrimary.cpp

Lines changed: 63 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -711,7 +711,69 @@ CheckRedeclarationRequest::evaluate(Evaluator &eval, ValueDecl *current) const {
711711
current->diagnose(diag::invalid_redecl_init,
712712
current->getName(),
713713
otherInit->isMemberwiseInitializer());
714-
} else if (!current->isImplicit() && !other->isImplicit()) {
714+
} else if (current->isImplicit() || other->isImplicit()) {
715+
// If both declarations are implicit, we diagnose the nearest
716+
// non-implicit DC because it is likely that we do not have a
717+
// valid source location for the declaration and we want to
718+
// avoid emitting it at an unknown location.
719+
//
720+
// Otherwise, if 'current' is implicit, then we diagnose 'other'
721+
// since 'other' is a redeclaration of 'current'. Similarly, if
722+
// 'other' is implicit, we diagnose 'current'.
723+
const auto *declToDiagnose = currentDC->getAsDecl();
724+
if (current->isImplicit() && other->isImplicit()) {
725+
// Get the nearest non-implicit decl context
726+
while (declToDiagnose && declToDiagnose->isImplicit() &&
727+
declToDiagnose->getDeclContext()) {
728+
declToDiagnose = declToDiagnose->getDeclContext()->getAsDecl();
729+
}
730+
} else {
731+
declToDiagnose = current->isImplicit() ? other : current;
732+
}
733+
734+
// Figure out if the the declaration we've redeclared is a synthesized
735+
// witness for a protocol requirement.
736+
bool isProtocolRequirement = false;
737+
if (auto VD = dyn_cast<ValueDecl>(current->isImplicit() ? current
738+
: other)) {
739+
isProtocolRequirement = llvm::any_of(
740+
VD->getSatisfiedProtocolRequirements(), [&](ValueDecl *req) {
741+
return req->getName() == VD->getName();
742+
});
743+
}
744+
declToDiagnose->diagnose(diag::invalid_redecl_implicit,
745+
current->getDescriptiveKind(),
746+
isProtocolRequirement, other->getName());
747+
748+
// Emit a specialized note if the one of the declarations is
749+
// the backing storage property ('_foo') or projected value
750+
// property ('$foo') for a wrapped property. The backing or
751+
// storage var has the same source location as the wrapped
752+
// property we diagnosed above, so we don't need to extract
753+
// the original property.
754+
const VarDecl *varToDiagnose = nullptr;
755+
auto kind = PropertyWrapperSynthesizedPropertyKind::Backing;
756+
if (auto currentVD = dyn_cast<VarDecl>(current)) {
757+
if (auto currentKind =
758+
currentVD->getPropertyWrapperSynthesizedPropertyKind()) {
759+
varToDiagnose = currentVD;
760+
kind = *currentKind;
761+
}
762+
}
763+
if (auto otherVD = dyn_cast<VarDecl>(other)) {
764+
if (auto otherKind =
765+
otherVD->getPropertyWrapperSynthesizedPropertyKind()) {
766+
varToDiagnose = otherVD;
767+
kind = *otherKind;
768+
}
769+
}
770+
771+
if (varToDiagnose) {
772+
varToDiagnose->diagnose(
773+
diag::invalid_redecl_implicit_wrapper, varToDiagnose->getName(),
774+
kind == PropertyWrapperSynthesizedPropertyKind::Backing);
775+
}
776+
} else {
715777
ctx.Diags.diagnoseWithNotes(
716778
current->diagnose(diag::invalid_redecl,
717779
current->getName()), [&]() {

0 commit comments

Comments
 (0)