Skip to content

Commit 4beaaf3

Browse files
committed
[CS] Improve placeholder diagnostics slightly
Make sure `CouldNotInferPlaceholderType` can produce a diagnostic for a `PlaceholderType` locator element, and avoid emitting an extra diagnostic for a placeholder type in an invalid position.
1 parent e2f8006 commit 4beaaf3

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> {

0 commit comments

Comments
 (0)