Skip to content

Commit ebcbaca

Browse files
authored
[Diagnostics] Add a diagnostic for inserting a $ to remove an extraneous property wrapper unwrap (#25507)
1 parent 38995f5 commit ebcbaca

File tree

8 files changed

+164
-5
lines changed

8 files changed

+164
-5
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -997,6 +997,10 @@ ERROR(missing_address_of,none,
997997
ERROR(missing_address_of_yield,none,
998998
"yielding mutable value of type %0 requires explicit '&'",
999999
(Type))
1000+
ERROR(extraneous_property_wrapper_unwrap,none,
1001+
"property %0 will be unwrapped to value of type %1, use '$' to refer to "
1002+
"wrapper type %2",
1003+
(DeclName, Type, Type))
10001004
ERROR(extraneous_address_of,none,
10011005
"use of extraneous '&'",
10021006
())

lib/Sema/CSDiagnostics.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1975,6 +1975,14 @@ bool MissingCallFailure::diagnoseAsError() {
19751975
return true;
19761976
}
19771977

1978+
bool MissingPropertyWrapperUnwrapFailure::diagnoseAsError() {
1979+
emitDiagnostic(getAnchor()->getLoc(),
1980+
diag::extraneous_property_wrapper_unwrap, getPropertyName(),
1981+
getFromType(), getToType())
1982+
.fixItInsert(getAnchor()->getLoc(), "$");
1983+
return true;
1984+
}
1985+
19781986
bool SubscriptMisuseFailure::diagnoseAsError() {
19791987
auto &sourceMgr = getASTContext().SourceMgr;
19801988

lib/Sema/CSDiagnostics.h

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -799,6 +799,22 @@ class MissingCallFailure final : public FailureDiagnostic {
799799
bool diagnoseAsError() override;
800800
};
801801

802+
class MissingPropertyWrapperUnwrapFailure final : public ContextualFailure {
803+
DeclName PropertyName;
804+
805+
public:
806+
MissingPropertyWrapperUnwrapFailure(Expr *root, ConstraintSystem &cs,
807+
DeclName propertyName, Type base,
808+
Type wrapper, ConstraintLocator *locator)
809+
: ContextualFailure(root, cs, base, wrapper, locator),
810+
PropertyName(propertyName) {}
811+
812+
bool diagnoseAsError() override;
813+
814+
private:
815+
DeclName getPropertyName() const { return PropertyName; }
816+
};
817+
802818
class SubscriptMisuseFailure final : public FailureDiagnostic {
803819
public:
804820
SubscriptMisuseFailure(Expr *root, ConstraintSystem &cs,

lib/Sema/CSFix.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,21 @@ InsertExplicitCall *InsertExplicitCall::create(ConstraintSystem &cs,
278278
return new (cs.getAllocator()) InsertExplicitCall(cs, locator);
279279
}
280280

281+
bool InsertPropertyWrapperUnwrap::diagnose(Expr *root, bool asNote) const {
282+
auto failure = MissingPropertyWrapperUnwrapFailure(
283+
root, getConstraintSystem(), getPropertyName(), getBase(), getWrapper(),
284+
getLocator());
285+
return failure.diagnose(asNote);
286+
}
287+
288+
InsertPropertyWrapperUnwrap *
289+
InsertPropertyWrapperUnwrap::create(ConstraintSystem &cs, DeclName propertyName,
290+
Type base, Type wrapper,
291+
ConstraintLocator *locator) {
292+
return new (cs.getAllocator())
293+
InsertPropertyWrapperUnwrap(cs, propertyName, base, wrapper, locator);
294+
}
295+
281296
bool UseSubscriptOperator::diagnose(Expr *root, bool asNote) const {
282297
auto failure = SubscriptMisuseFailure(root, getConstraintSystem(), getLocator());
283298
return failure.diagnose(asNote);

lib/Sema/CSFix.h

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,9 @@ enum class FixKind : uint8_t {
103103
/// Add explicit `()` at the end of function or member to call it.
104104
InsertCall,
105105

106+
/// Add one or more property unwrap operators ('$')
107+
InsertPropertyWrapperUnwrap,
108+
106109
/// Instead of spelling out `subscript` directly, use subscript operator.
107110
UseSubscriptOperator,
108111

@@ -629,6 +632,34 @@ class InsertExplicitCall final : public ConstraintFix {
629632
ConstraintLocator *locator);
630633
};
631634

635+
class InsertPropertyWrapperUnwrap final : public ConstraintFix {
636+
DeclName PropertyName;
637+
Type Base;
638+
Type Wrapper;
639+
640+
InsertPropertyWrapperUnwrap(ConstraintSystem &cs, DeclName propertyName,
641+
Type base, Type wrapper,
642+
ConstraintLocator *locator)
643+
: ConstraintFix(cs, FixKind::InsertPropertyWrapperUnwrap, locator),
644+
PropertyName(propertyName), Base(base), Wrapper(wrapper) {}
645+
646+
public:
647+
std::string getName() const override {
648+
return "insert a $ to unwrap the property wrapper";
649+
}
650+
651+
DeclName getPropertyName() const { return PropertyName; }
652+
Type getBase() const { return Base; }
653+
Type getWrapper() const { return Wrapper; }
654+
655+
bool diagnose(Expr *root, bool asNote = false) const override;
656+
657+
static InsertPropertyWrapperUnwrap *create(ConstraintSystem &cs,
658+
DeclName propertyName, Type base,
659+
Type wrapper,
660+
ConstraintLocator *locator);
661+
};
662+
632663
class UseSubscriptOperator final : public ConstraintFix {
633664
UseSubscriptOperator(ConstraintSystem &cs, ConstraintLocator *locator)
634665
: ConstraintFix(cs, FixKind::UseSubscriptOperator, locator) {}

lib/Sema/CSSimplify.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include "swift/AST/GenericEnvironment.h"
2222
#include "swift/AST/GenericSignature.h"
2323
#include "swift/AST/ParameterList.h"
24+
#include "swift/AST/PropertyWrappers.h"
2425
#include "swift/AST/ProtocolConformance.h"
2526
#include "swift/Basic/StringExtras.h"
2627
#include "swift/ClangImporter/ClangModule.h"
@@ -4810,6 +4811,25 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyMemberConstraint(
48104811
flags, locatorB);
48114812
};
48124813

4814+
// Check if any property wrappers on the base of the member lookup have
4815+
// mactching members that we can fall back to.
4816+
if (auto dotExpr =
4817+
dyn_cast_or_null<UnresolvedDotExpr>(locator->getAnchor())) {
4818+
auto baseExpr = dotExpr->getBase();
4819+
auto resolvedOverload = findSelectedOverloadFor(baseExpr);
4820+
if (auto wrappedProperty =
4821+
getPropertyWrapperInformation(resolvedOverload)) {
4822+
auto wrapperTy = wrappedProperty->second;
4823+
auto result = solveWithNewBaseOrName(wrapperTy, member);
4824+
if (result == SolutionKind::Solved) {
4825+
auto *fix = InsertPropertyWrapperUnwrap::create(
4826+
*this, wrappedProperty->first->getFullName(), baseTy, wrapperTy,
4827+
locator);
4828+
return recordFix(fix) ? SolutionKind::Error : SolutionKind::Solved;
4829+
}
4830+
}
4831+
}
4832+
48134833
if (auto *funcType = baseTy->getAs<FunctionType>()) {
48144834
// We can't really suggest anything useful unless
48154835
// function takes no arguments, otherwise it
@@ -6780,6 +6800,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
67806800
case FixKind::TreatKeyPathSubscriptIndexAsHashable:
67816801
case FixKind::AllowInvalidRefInKeyPath:
67826802
case FixKind::ExplicitlySpecifyGenericArguments:
6803+
case FixKind::InsertPropertyWrapperUnwrap:
67836804
case FixKind::GenericArgumentsMismatch:
67846805
llvm_unreachable("handled elsewhere");
67856806
}

lib/Sema/ConstraintSystem.h

Lines changed: 34 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -25,19 +25,20 @@
2525
#include "ConstraintLocator.h"
2626
#include "OverloadChoice.h"
2727
#include "TypeChecker.h"
28-
#include "swift/Basic/LLVM.h"
29-
#include "swift/Basic/OptionSet.h"
3028
#include "swift/AST/ASTVisitor.h"
3129
#include "swift/AST/ASTWalker.h"
3230
#include "swift/AST/NameLookup.h"
33-
#include "swift/AST/Types.h"
31+
#include "swift/AST/PropertyWrappers.h"
3432
#include "swift/AST/TypeCheckerDebugConsumer.h"
35-
#include "llvm/ADT/ilist.h"
33+
#include "swift/AST/Types.h"
34+
#include "swift/Basic/LLVM.h"
35+
#include "swift/Basic/OptionSet.h"
3636
#include "llvm/ADT/PointerUnion.h"
37+
#include "llvm/ADT/STLExtras.h"
3738
#include "llvm/ADT/SetOperations.h"
3839
#include "llvm/ADT/SetVector.h"
3940
#include "llvm/ADT/SmallPtrSet.h"
40-
#include "llvm/ADT/STLExtras.h"
41+
#include "llvm/ADT/ilist.h"
4142
#include "llvm/Support/ErrorHandling.h"
4243
#include "llvm/Support/Timer.h"
4344
#include "llvm/Support/raw_ostream.h"
@@ -1561,6 +1562,16 @@ class ConstraintSystem {
15611562
return resolvedOverloadSets;
15621563
}
15631564

1565+
ResolvedOverloadSetListItem *findSelectedOverloadFor(Expr *expr) const {
1566+
auto resolvedOverload = getResolvedOverloadSets();
1567+
while (resolvedOverload) {
1568+
if (resolvedOverload->Locator->getAnchor() == expr)
1569+
return resolvedOverload;
1570+
resolvedOverload = resolvedOverload->Previous;
1571+
}
1572+
return nullptr;
1573+
}
1574+
15641575
private:
15651576
unsigned assignTypeVariableID() {
15661577
return TypeCounter++;
@@ -2244,6 +2255,24 @@ class ConstraintSystem {
22442255
return typeVar->getImpl().getRepresentative(getSavedBindings());
22452256
}
22462257

2258+
/// Gets the a VarDecl, and the type of its attached property wrapper if the
2259+
/// resolved overload has an attached property wrapper.
2260+
///
2261+
/// \return A pair of the VarDecl and the attached property wrapper type if
2262+
/// the given resolved overload has an attached property wrapper.
2263+
Optional<std::pair<VarDecl *, Type>>
2264+
getPropertyWrapperInformation(ResolvedOverloadSetListItem *resolvedOverload) {
2265+
if (resolvedOverload && resolvedOverload->Choice.isDecl()) {
2266+
if (auto *decl = dyn_cast<VarDecl>(resolvedOverload->Choice.getDecl())) {
2267+
if (decl->hasAttachedPropertyWrapper()) {
2268+
auto wrapperTy = decl->getPropertyWrapperBackingPropertyType();
2269+
return std::make_pair(decl, wrapperTy);
2270+
}
2271+
}
2272+
}
2273+
return None;
2274+
}
2275+
22472276
/// Merge the equivalence sets of the two type variables.
22482277
///
22492278
/// Note that both \c typeVar1 and \c typeVar2 must be the

test/decl/var/property_wrappers.swift

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -877,3 +877,38 @@ struct TestComposition {
877877
$p3 = d // expected-error{{cannot assign value of type 'Double' to type 'WrapperD<WrapperE<Int?>, Int, String>'}}
878878
}
879879
}
880+
881+
// ---------------------------------------------------------------------------
882+
// Missing Property Wrapper Unwrap Diagnostics
883+
// ---------------------------------------------------------------------------
884+
@propertyWrapper
885+
struct Foo<T> {
886+
var wrappedValue: T
887+
888+
func foo() {}
889+
}
890+
891+
@propertyWrapper
892+
struct Bar<T, V> {
893+
var wrappedValue: T
894+
895+
func bar() {}
896+
}
897+
898+
extension Bar where V == String { // expected-note {{where 'V' = 'Bool'}}
899+
func barWhereVIsString() {}
900+
}
901+
902+
struct MissingPropertyWrapperUnwrap {
903+
@Foo var x: Int
904+
@Bar<Int, Bool> var y: Int
905+
@Bar<Int, String> var z: Int
906+
907+
func baz() {
908+
self.x.foo() // expected-error {{property 'x' will be unwrapped to value of type 'Int', use '$' to refer to wrapper type 'Foo<Int>'}}{{10-10=$}}
909+
self.y.bar() // expected-error {{property 'y' will be unwrapped to value of type 'Int', use '$' to refer to wrapper type 'Bar<Int, Bool>'}}{{10-10=$}}
910+
self.y.barWhereVIsString() // expected-error {{property 'y' will be unwrapped to value of type 'Int', use '$' to refer to wrapper type 'Bar<Int, Bool>'}}{{10-10=$}}
911+
// expected-error@-1 {{referencing instance method 'barWhereVIsString()' on 'Bar' requires the types 'Bool' and 'String' be equivalent}}
912+
self.z.barWhereVIsString() // expected-error {{property 'z' will be unwrapped to value of type 'Int', use '$' to refer to wrapper type 'Bar<Int, String>'}}{{10-10=$}}
913+
}
914+
}

0 commit comments

Comments
 (0)