Skip to content

Commit e630f66

Browse files
authored
Merge pull request #75046 from hamishknight/out-of-placeholder
[CS] Improve placeholder diagnostics slightly
2 parents 9aa7463 + 4beaaf3 commit e630f66

File tree

12 files changed

+125
-48
lines changed

12 files changed

+125
-48
lines changed

include/swift/Sema/CSFix.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,9 @@ enum class FixKind : uint8_t {
361361
/// property wrapper.
362362
AllowWrappedValueMismatch,
363363

364+
/// Ignore an out-of-place placeholder type.
365+
IgnoreInvalidPlaceholder,
366+
364367
/// Specify a type for an explicitly written placeholder that could not be
365368
/// resolved.
366369
SpecifyTypeForPlaceholder,
@@ -3133,6 +3136,29 @@ class SpecifyContextualTypeForNil final : public ConstraintFix {
31333136
}
31343137
};
31353138

3139+
class IgnoreInvalidPlaceholder final : public ConstraintFix {
3140+
IgnoreInvalidPlaceholder(ConstraintSystem &cs, ConstraintLocator *locator)
3141+
: ConstraintFix(cs, FixKind::IgnoreInvalidPlaceholder, locator) {}
3142+
3143+
public:
3144+
std::string getName() const override {
3145+
return "ignore out-of-place placeholder type";
3146+
}
3147+
3148+
bool diagnose(const Solution &solution, bool asNote = false) const override;
3149+
3150+
bool diagnoseForAmbiguity(CommonFixesArray commonFixes) const override {
3151+
return diagnose(*commonFixes.front().first);
3152+
}
3153+
3154+
static IgnoreInvalidPlaceholder *create(ConstraintSystem &cs,
3155+
ConstraintLocator *locator);
3156+
3157+
static bool classof(const ConstraintFix *fix) {
3158+
return fix->getKind() == FixKind::IgnoreInvalidPlaceholder;
3159+
}
3160+
};
3161+
31363162
class SpecifyTypeForPlaceholder final : public ConstraintFix {
31373163
SpecifyTypeForPlaceholder(ConstraintSystem &cs, ConstraintLocator *locator)
31383164
: ConstraintFix(cs, FixKind::SpecifyTypeForPlaceholder, locator) {}

lib/Sema/CSBindings.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2529,6 +2529,11 @@ TypeVariableBinding::fixForHole(ConstraintSystem &cs) const {
25292529
return std::make_pair(fix, /*impact=*/(unsigned)10);
25302530
}
25312531

2532+
// If the placeholder is in an invalid position, we'll have already
2533+
// recorded a fix, and can skip recording another.
2534+
if (cs.hasFixFor(dstLocator, FixKind::IgnoreInvalidPlaceholder))
2535+
return std::nullopt;
2536+
25322537
ConstraintFix *fix = SpecifyTypeForPlaceholder::create(cs, srcLocator);
25332538
return std::make_pair(fix, defaultImpact);
25342539
}

lib/Sema/CSDiagnostics.cpp

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8668,7 +8668,21 @@ bool MissingContextualTypeForNil::diagnoseAsError() {
86688668
return true;
86698669
}
86708670

8671+
bool InvalidPlaceholderFailure::diagnoseAsError() {
8672+
auto elt = getLocator()->castLastElementTo<LocatorPathElt::PlaceholderType>();
8673+
emitDiagnosticAt(elt.getPlaceholderRepr()->getLoc(),
8674+
diag::placeholder_type_not_allowed);
8675+
return true;
8676+
}
8677+
86718678
bool CouldNotInferPlaceholderType::diagnoseAsError() {
8679+
// When placeholder type appears in an editor placeholder i.e.
8680+
// `<#T##() -> _#>` we rely on the parser to produce a diagnostic
8681+
// about editor placeholder and glance over all placeholder type
8682+
// inference issues.
8683+
if (isExpr<EditorPlaceholderExpr>(getAnchor()))
8684+
return true;
8685+
86728686
// If this placeholder was explicitly written out by the user, they can maybe
86738687
// fix things by specifying an actual type.
86748688
if (auto *typeExpr = getAsExpr<TypeExpr>(getAnchor())) {
@@ -8678,12 +8692,13 @@ bool CouldNotInferPlaceholderType::diagnoseAsError() {
86788692
}
86798693
}
86808694

8681-
// When placeholder type appears in an editor placeholder i.e.
8682-
// `<#T##() -> _#>` we rely on the parser to produce a diagnostic
8683-
// about editor placeholder and glance over all placeholder type
8684-
// inference issues.
8685-
if (isExpr<EditorPlaceholderExpr>(getAnchor()))
8695+
// Also check for a placeholder path element.
8696+
auto *loc = getLocator();
8697+
if (auto elt = loc->getLastElementAs<LocatorPathElt::PlaceholderType>()) {
8698+
emitDiagnosticAt(elt->getPlaceholderRepr()->getLoc(),
8699+
diag::could_not_infer_placeholder);
86868700
return true;
8701+
}
86878702

86888703
return false;
86898704
}

lib/Sema/CSDiagnostics.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2621,6 +2621,20 @@ class MissingContextualTypeForNil final : public FailureDiagnostic {
26212621
bool diagnoseAsError() override;
26222622
};
26232623

2624+
/// Diagnose a placeholder type in an invalid place, e.g:
2625+
///
2626+
/// \code
2627+
/// y as? _
2628+
/// \endcode
2629+
class InvalidPlaceholderFailure final : public FailureDiagnostic {
2630+
public:
2631+
InvalidPlaceholderFailure(const Solution &solution,
2632+
ConstraintLocator *locator)
2633+
: FailureDiagnostic(solution, locator) {}
2634+
2635+
bool diagnoseAsError() override;
2636+
};
2637+
26242638
/// Diagnose situations where there is no context to determine the type of a
26252639
/// placeholder, e.g.,
26262640
///

lib/Sema/CSFix.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2123,6 +2123,18 @@ SpecifyContextualTypeForNil::create(ConstraintSystem &cs,
21232123
return new (cs.getAllocator()) SpecifyContextualTypeForNil(cs, locator);
21242124
}
21252125

2126+
bool IgnoreInvalidPlaceholder::diagnose(const Solution &solution,
2127+
bool asNote) const {
2128+
InvalidPlaceholderFailure failure(solution, getLocator());
2129+
return failure.diagnose(asNote);
2130+
}
2131+
2132+
IgnoreInvalidPlaceholder *
2133+
IgnoreInvalidPlaceholder::create(ConstraintSystem &cs,
2134+
ConstraintLocator *locator) {
2135+
return new (cs.getAllocator()) IgnoreInvalidPlaceholder(cs, locator);
2136+
}
2137+
21262138
bool SpecifyTypeForPlaceholder::diagnose(const Solution &solution,
21272139
bool asNote) const {
21282140
CouldNotInferPlaceholderType failure(solution, getLocator());

lib/Sema/CSGen.cpp

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1676,9 +1676,9 @@ namespace {
16761676
TVO_CanBindToHole);
16771677
}
16781678
// Diagnose top-level usages of placeholder types.
1679-
if (isa<PlaceholderTypeRepr>(repr->getWithoutParens())) {
1680-
CS.getASTContext().Diags.diagnose(repr->getLoc(),
1681-
diag::placeholder_type_not_allowed);
1679+
if (auto *ty = dyn_cast<PlaceholderTypeRepr>(repr->getWithoutParens())) {
1680+
auto *loc = CS.getConstraintLocator(locator, {LocatorPathElt::PlaceholderType(ty)});
1681+
CS.recordFix(IgnoreInvalidPlaceholder::create(CS, loc));
16821682
}
16831683
return result;
16841684
}

lib/Sema/CSSimplify.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15446,6 +15446,7 @@ ConstraintSystem::SolutionKind ConstraintSystem::simplifyFixConstraint(
1544615446
case FixKind::DestructureTupleToMatchPackExpansionParameter:
1544715447
case FixKind::AllowValueExpansionWithoutPackReferences:
1544815448
case FixKind::IgnoreInvalidPatternInExpr:
15449+
case FixKind::IgnoreInvalidPlaceholder:
1544915450
case FixKind::IgnoreOutOfPlaceThenStmt:
1545015451
case FixKind::IgnoreMissingEachKeyword:
1545115452
llvm_unreachable("handled elsewhere");

test/Constraints/diagnostics.swift

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,8 +1152,7 @@ func badTypes() {
11521152
// rdar://34357545
11531153
func unresolvedTypeExistential() -> Bool {
11541154
return (Int.self==_{})
1155-
// expected-error@-1 {{could not infer type for placeholder}}
1156-
// expected-error@-2 {{type placeholder not allowed here}}
1155+
// expected-error@-1 {{type placeholder not allowed here}}
11571156
}
11581157

11591158
do {
@@ -1552,19 +1551,19 @@ func testNilCoalescingOperatorRemoveFix() {
15521551
let _ = "" /* This is a comment */ ?? "" // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{13-43=}}
15531552

15541553
let _ = "" // This is a comment
1555-
?? "" // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{1554:13-1555:10=}}
1554+
?? "" // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{-1:13-+0:10=}}
15561555

15571556
let _ = "" // This is a comment
15581557
/*
15591558
* The blank line below is part of the test case, do not delete it
15601559
*/
1561-
?? "" // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{1557:13-1561:10=}}
1560+
?? "" // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{-4:13-+0:10=}}
15621561

1563-
if ("" ?? // This is a comment // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{9-1564:9=}}
1562+
if ("" ?? // This is a comment // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{9-+1:9=}}
15641563
"").isEmpty {}
15651564

15661565
if ("" // This is a comment
1567-
?? "").isEmpty {} // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{1566:9-1567:12=}}
1566+
?? "").isEmpty {} // expected-warning {{left side of nil coalescing operator '??' has non-optional type 'String', so the right side is never used}} {{-1:9-+0:12=}}
15681567
}
15691568

15701569
// https://github.com/apple/swift/issues/74617

test/Constraints/if_expr.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -89,8 +89,7 @@ func testNil3(_ x: Bool) {
8989
let _: _? = if x { 42 } else { nil }
9090
}
9191
func testNil4(_ x: Bool) {
92-
// FIXME: Bad diagnostic (#63130)
93-
let _: _? = if x { nil } else { 42 } // expected-error {{type of expression is ambiguous without a type annotation}}
92+
let _: _? = if x { nil } else { 42 } // expected-error {{could not infer type for placeholder}}
9493
}
9594

9695
enum F<T> {

test/Constraints/switch_expr.swift

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,7 @@ func testNil3(_ x: Bool) {
207207
let _: _? = switch x { case true: 42 case false: nil }
208208
}
209209
func testNil4(_ x: Bool) {
210-
// FIXME: Bad diagnostic (#63130)
211-
let _: _? = switch x { case true: nil case false: 42 } // expected-error {{type of expression is ambiguous without a type annotation}}
210+
let _: _? = switch x { case true: nil case false: 42 } // expected-error {{could not infer type for placeholder}}
212211
}
213212

214213
enum G<T> {

test/Sema/placeholder_type.swift

Lines changed: 36 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ let arr = [_](repeating: "hi", count: 3)
1313
func foo(_ arr: [_] = [0]) {} // expected-error {{type placeholder may not appear in top-level parameter}}
1414
// expected-note@-1 {{replace the placeholder with the inferred type 'Int'}}
1515

16-
let foo = _.foo // expected-error {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
16+
let foo = _.foo // expected-error {{type placeholder not allowed here}}
1717
let zero: _ = .zero // expected-error {{cannot infer contextual base in reference to member 'zero'}}
1818

1919
struct S<T> {
@@ -110,15 +110,15 @@ extension Bar {
110110
}
111111

112112
// FIXME: We should probably have better diagnostics for these situations--the user probably meant to use implicit member syntax
113-
let _: Int = _() // expected-error {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
114-
let _: () -> Int = { _() } // expected-error 2 {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
115-
let _: Int = _.init() // expected-error {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
116-
let _: () -> Int = { _.init() } // expected-error 2 {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
113+
let _: Int = _() // expected-error {{type placeholder not allowed here}}
114+
let _: () -> Int = { _() } // expected-error {{type placeholder not allowed here}}
115+
let _: Int = _.init() // expected-error {{type placeholder not allowed here}}
116+
let _: () -> Int = { _.init() } // expected-error {{type placeholder not allowed here}}
117117

118-
func returnsInt() -> Int { _() } // expected-error {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
119-
func returnsIntClosure() -> () -> Int { { _() } } // expected-error 2 {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
120-
func returnsInt2() -> Int { _.init() } // expected-error {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
121-
func returnsIntClosure2() -> () -> Int { { _.init() } } // expected-error 2 {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
118+
func returnsInt() -> Int { _() } // expected-error {{type placeholder not allowed here}}
119+
func returnsIntClosure() -> () -> Int { { _() } } // expected-error {{type placeholder not allowed here}}
120+
func returnsInt2() -> Int { _.init() } // expected-error {{type placeholder not allowed here}}
121+
func returnsIntClosure2() -> () -> Int { { _.init() } } // expected-error {{type placeholder not allowed here}}
122122

123123
let _: Int.Type = _ // expected-error {{'_' can only appear in a pattern or on the left side of an assignment}}
124124
let _: Int.Type = _.self // expected-error {{type placeholder not allowed here}}
@@ -145,17 +145,17 @@ let _ = [_].otherStaticMember.member
145145
let _ = [_].otherStaticMember.method()
146146

147147
func f(x: Any, arr: [Int]) {
148-
// FIXME: Better diagnostics here. Maybe we should suggest replacing placeholders with 'Any'?
149-
150-
if x is _ {} // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without a type annotation}}
151-
if x is [_] {} // expected-error {{type of expression is ambiguous without a type annotation}}
152-
if x is () -> _ {} // expected-error {{type of expression is ambiguous without a type annotation}}
153-
if let y = x as? _ {} // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without a type annotation}}
154-
if let y = x as? [_] {} // expected-error {{type of expression is ambiguous without a type annotation}}
155-
if let y = x as? () -> _ {} // expected-error {{type of expression is ambiguous without a type annotation}}
156-
let y1 = x as! _ // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without a type annotation}}
157-
let y2 = x as! [_] // expected-error {{type of expression is ambiguous without a type annotation}}
158-
let y3 = x as! () -> _ // expected-error {{type of expression is ambiguous without a type annotation}}
148+
// TODO: Maybe we should suggest replacing placeholders with 'Any'?
149+
150+
if x is _ {} // expected-error {{type placeholder not allowed here}}
151+
if x is [_] {} // expected-error {{could not infer type for placeholder}}
152+
if x is () -> _ {} // expected-error {{could not infer type for placeholder}}
153+
if let y = x as? _ {} // expected-error {{type placeholder not allowed here}}
154+
if let y = x as? [_] {} // expected-error {{could not infer type for placeholder}}
155+
if let y = x as? () -> _ {} // expected-error {{could not infer type for placeholder}}
156+
let y1 = x as! _ // expected-error {{type placeholder not allowed here}}
157+
let y2 = x as! [_] // expected-error {{could not infer type for placeholder}}
158+
let y3 = x as! () -> _ // expected-error {{could not infer type for placeholder}}
159159

160160
switch x {
161161
case is _: break // expected-error {{type placeholder not allowed here}}
@@ -166,15 +166,22 @@ func f(x: Any, arr: [Int]) {
166166
case let y as () -> _: break // expected-error {{type placeholder not allowed here}}
167167
}
168168

169-
if arr is _ {} // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without a type annotation}}
170-
if arr is [_] {} // expected-error {{type of expression is ambiguous without a type annotation}}
171-
if arr is () -> _ {} // expected-error {{type of expression is ambiguous without a type annotation}}
172-
if let y = arr as? _ {} // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without a type annotation}}
173-
if let y = arr as? [_] {} // expected-error {{type of expression is ambiguous without a type annotation}}
174-
if let y = arr as? () -> _ {} // expected-error {{type of expression is ambiguous without a type annotation}}
175-
let y1 = arr as! _ // expected-error {{type placeholder not allowed here}} expected-error {{type of expression is ambiguous without a type annotation}}
176-
let y2 = arr as! [_] // expected-error {{type of expression is ambiguous without a type annotation}}
177-
let y3 = arr as! () -> _ // expected-error {{type of expression is ambiguous without a type annotation}}
169+
if case is _ = x {} // expected-error {{type placeholder not allowed here}}
170+
if case is [_] = x {} // expected-error {{could not infer type for placeholder}}
171+
if case is () -> _ = x {} // expected-error {{could not infer type for placeholder}}
172+
if case let y as _ = x {} // expected-error {{type placeholder not allowed here}}
173+
if case let y as [_] = x {} // expected-error {{could not infer type for placeholder}}
174+
if case let y as () -> _ = x {} // expected-error {{could not infer type for placeholder}}
175+
176+
if arr is _ {} // expected-error {{type placeholder not allowed here}}
177+
if arr is [_] {} // expected-error {{could not infer type for placeholder}}
178+
if arr is () -> _ {} // expected-error {{could not infer type for placeholder}}
179+
if let y = arr as? _ {} // expected-error {{type placeholder not allowed here}}
180+
if let y = arr as? [_] {} // expected-error {{could not infer type for placeholder}}
181+
if let y = arr as? () -> _ {} // expected-error {{could not infer type for placeholder}}
182+
let y1 = arr as! _ // expected-error {{type placeholder not allowed here}}
183+
let y2 = arr as! [_] // expected-error {{could not infer type for placeholder}}
184+
let y3 = arr as! () -> _ // expected-error {{could not infer type for placeholder}}
178185

179186
switch arr {
180187
case is _: break // expected-error {{type placeholder not allowed here}}

test/expr/expressions.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,7 @@ func r20802757(_ z: inout Int = &g20802757) { // expected-error {{cannot provide
868868
print(z)
869869
}
870870

871-
_ = _.foo // expected-error {{type placeholder not allowed here}} expected-error {{could not infer type for placeholder}}
871+
_ = _.foo // expected-error {{type placeholder not allowed here}}
872872

873873
// <rdar://problem/22211854> wrong arg list crashing sourcekit
874874
func r22211854() {

0 commit comments

Comments
 (0)