Skip to content

Commit 22a18de

Browse files
[Sema] Improving integer literal as boolean diagnostic
1 parent 4696610 commit 22a18de

File tree

12 files changed

+80
-25
lines changed

12 files changed

+80
-25
lines changed

include/swift/AST/DiagnosticsSema.def

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3893,6 +3893,9 @@ ERROR(optional_used_as_boolean,none,
38933893
ERROR(integer_used_as_boolean,none,
38943894
"type %0 cannot be used as a boolean; "
38953895
"test for '%select{!|=}1= 0' instead", (Type, bool))
3896+
ERROR(integer_used_as_boolean_literal,none,
3897+
"integer literal value '%0' cannot be used as a boolean; "
3898+
"did you mean '%select{false|true}1'?", (StringRef, bool))
38963899

38973900
ERROR(interpolation_missing_proto,none,
38983901
"string interpolation requires the protocol 'ExpressibleByStringInterpolation' to be defined",

lib/Sema/CSDiagnostics.cpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2930,6 +2930,21 @@ bool ContextualFailure::diagnoseConversionToBool() const {
29302930
// comparison against nil was probably expected.
29312931
auto fromType = getFromType();
29322932
if (fromType->getOptionalObjectType()) {
2933+
if (auto *OE = getAsExpr<OptionalEvaluationExpr>(
2934+
anchor->getValueProvidingExpr())) {
2935+
auto *subExpr = OE->getSubExpr();
2936+
while (auto *BOE = getAsExpr<BindOptionalExpr>(subExpr)) {
2937+
subExpr = BOE->getSubExpr();
2938+
}
2939+
// The contextual mismatch is anchored in an optional evaluation
2940+
// expression wrapping a literal expression e.g. `0?` suggesting to use
2941+
// `!= nil` will not be accurate in this case, so let's fallback to
2942+
// default mismatch diagnostic.
2943+
if (isa<LiteralExpr>(subExpr)) {
2944+
return false;
2945+
}
2946+
}
2947+
29332948
StringRef prefix = "((";
29342949
StringRef suffix;
29352950
if (notOperatorLoc.isValid())
@@ -2960,6 +2975,22 @@ bool ContextualFailure::diagnoseConversionToBool() const {
29602975
if (conformsToKnownProtocol(fromType, KnownProtocolKind::BinaryInteger) &&
29612976
conformsToKnownProtocol(fromType,
29622977
KnownProtocolKind::ExpressibleByIntegerLiteral)) {
2978+
if (auto *IL =
2979+
getAsExpr<IntegerLiteralExpr>(anchor->getValueProvidingExpr())) {
2980+
// If integer literal value is either zero or one, let's suggest replacing
2981+
// with boolean literal `true` or `false`. Otherwise fallback to generic
2982+
// type mismatch diagnostic.
2983+
const auto value = IL->getRawValue();
2984+
if (value.isOne() || value.isZero()) {
2985+
StringRef boolLiteral = value.isZero() ? "false" : "true";
2986+
emitDiagnostic(diag::integer_used_as_boolean_literal,
2987+
IL->getDigitsText(), value.isOne())
2988+
.fixItReplace(IL->getSourceRange(), boolLiteral);
2989+
return true;
2990+
}
2991+
return false;
2992+
}
2993+
29632994
StringRef prefix = "((";
29642995
StringRef suffix;
29652996
if (notOperatorLoc.isValid())

test/Constraints/argument_matching.swift

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -531,13 +531,13 @@ struct PositionsAroundDefaultsAndVariadics {
531531
// expected-error@-1 {{cannot convert value of type '[Int]' to expected argument type 'Bool'}}
532532

533533
f1(b: "2", 1) // expected-error {{incorrect argument labels in call (have 'b:_:', expected '_:_:c:_:')}}
534-
// expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
534+
// expected-error@-1 {{integer literal value '1' cannot be used as a boolean; did you mean 'true'?}}
535535

536536
f1(b: "2", [3], 1) // expected-error {{incorrect argument labels in call (have 'b:_:_:', expected '_:_:c:_:')}}
537537
// expected-error@-1 {{cannot convert value of type '[Int]' to expected argument type 'Bool'}}
538538

539539
f1(b: "2", 1, [3]) // expected-error {{incorrect argument labels in call (have 'b:_:_:', expected '_:_:c:_:')}}
540-
// expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
540+
// expected-error@-1 {{integer literal value '1' cannot be used as a boolean; did you mean 'true'?}}
541541
// expected-error@-2 {{cannot convert value of type '[Int]' to expected argument type 'Int'}}
542542
}
543543

@@ -574,11 +574,11 @@ struct PositionsAroundDefaultsAndVariadics {
574574
f2(true, 21, 22, [4]) // expected-error {{cannot pass array of type '[Int]' as variadic arguments of type 'Int'}}
575575
// expected-note@-1 {{remove brackets to pass array elements directly}}
576576

577-
f2(21, 22, 23, c: "3", [4]) // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
577+
f2(21, 22, 23, c: "3", [4]) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Bool'}}
578578

579-
f2(21, 22, c: "3", [4]) // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
579+
f2(21, 22, c: "3", [4]) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Bool'}}
580580

581-
f2(21, c: "3", [4]) // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
581+
f2(21, c: "3", [4]) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Bool'}}
582582

583583
f2(c: "3", [4])
584584
f2(c: "3")
@@ -591,15 +591,15 @@ struct PositionsAroundDefaultsAndVariadics {
591591
// expected-error@-2 {{cannot convert value of type '[Int]' to expected argument type 'Bool'}}
592592

593593
f2(c: "3", [4], 21) // expected-error {{incorrect argument labels in call (have 'c:_:_:', expected '_:_:c:_:')}}
594-
// expected-error@-1 {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
594+
// expected-error@-1 {{cannot convert value of type 'Int' to expected argument type 'Bool'}}
595595

596596
f2([4]) // expected-error {{cannot convert value of type '[Int]' to expected argument type 'Bool'}}
597597

598-
f2(21, [4]) // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
598+
f2(21, [4]) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Bool'}}
599599
// expected-error@-1 {{cannot pass array of type '[Int]' as variadic arguments of type 'Int'}}
600600
// expected-note@-2 {{remove brackets to pass array elements directly}}
601601

602-
f2(21, 22, [4]) // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
602+
f2(21, 22, [4]) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Bool'}}
603603
// expected-error@-1 {{cannot pass array of type '[Int]' as variadic arguments of type 'Int'}}
604604
// expected-note@-2 {{remove brackets to pass array elements directly}}
605605
}
@@ -690,7 +690,7 @@ struct PositionsAroundDefaultsAndVariadics {
690690
f4(b: "2", 31, d: [4])
691691
f4(b: "2", d: [4])
692692

693-
f4(31, b: "2", d: [4]) // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
693+
f4(31, b: "2", d: [4]) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Bool'}}
694694

695695
f4(b: "2", d: [4], 31) // expected-error {{unnamed argument #3 must precede argument 'b'}}
696696

@@ -700,13 +700,13 @@ struct PositionsAroundDefaultsAndVariadics {
700700

701701
f4()
702702

703-
f4(31) // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
703+
f4(31) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Bool'}}
704704

705-
f4(31, d: [4]) // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
705+
f4(31, d: [4]) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Bool'}}
706706

707-
f4(31, 32) // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
707+
f4(31, 32) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Bool'}}
708708

709-
f4(31, 32, d: [4]) // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
709+
f4(31, 32, d: [4]) // expected-error {{cannot convert value of type 'Int' to expected argument type 'Bool'}}
710710
}
711711

712712
// labeled variadics after labeled parameter

test/Constraints/diagnostics.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1535,13 +1535,13 @@ func testUnwrapFixIts(x: Int?) throws {
15351535
func issue63746() {
15361536
let fn1 = {
15371537
switch 0 {
1538-
case 1 where 0: // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
1538+
case 1 where 0: // expected-error {{integer literal value '0' cannot be used as a boolean; did you mean 'false'?}}
15391539
()
15401540
}
15411541
}
15421542
let fn2 = {
15431543
switch 0 {
1544-
case 1 where 0: // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
1544+
case 1 where 0: // expected-error {{integer literal value '0' cannot be used as a boolean; did you mean 'false'?}}
15451545
break
15461546
}
15471547
}

test/Constraints/ternary_expr.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ useD1(i) // expected-error{{cannot convert value of type 'B' to expected argumen
5151
useD2(i) // expected-error{{cannot convert value of type 'B' to expected argument type 'D2'}}
5252

5353
var x = true ? 1 : 0
54-
var y = 22 ? 1 : 0 // expected-error{{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
54+
var y = 22 ? 1 : 0 // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}}
5555

5656
_ = x ? x : x // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
5757
_ = true ? x : 1.2 // expected-error {{result values in '? :' expression have mismatching types 'Int' and 'Double'}}

test/ImportResolution/import-resolution-overload.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ ambiguousWithVar(true) // no-warning
3232

3333
var localVar : Bool
3434
localVar = false
35-
localVar = 42 // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
35+
localVar = 42 // expected-error {{cannot assign value of type 'Int' to type 'Bool'}}
3636
localVar(42) // expected-error {{cannot call value of non-function type 'Bool'}}
3737
var _ : localVar // should still work
3838

test/Misc/misc_diagnostics.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,8 @@ let total = 15.0
2626
let count = 7
2727
let median = total / count // expected-error {{binary operator '/' cannot be applied to operands of type 'Double' and 'Int'}} expected-note {{overloads for '/' exist with these partially matching parameter lists:}}
2828

29-
if (1) {} // expected-error{{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
30-
if 1 {} // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
29+
if (1) {} // expected-error{{integer literal value '1' cannot be used as a boolean; did you mean 'true'?}} {{5-6=true}}
30+
if 1 {} // expected-error {{integer literal value '1' cannot be used as a boolean; did you mean 'true'?}} {{4-5=true}}
3131

3232
var a: [String] = [1] // expected-error{{cannot convert value of type 'Int' to expected element type 'String'}}
3333
var b: Int = [1, 2, 3] // expected-error{{cannot convert value of type '[Int]' to specified type 'Int'}}

test/Parse/switch.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ case _ where x % 2 == 0,
6464
x = 1
6565
case var y where y % 2 == 0:
6666
x = y + 1
67-
case _ where 0: // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
67+
case _ where 0: // expected-error {{integer literal value '0' cannot be used as a boolean; did you mean 'false'?}}
6868
x = 0
6969
default:
7070
x = 1

test/Sema/diag_integer_literals.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
// RUN: %target-typecheck-verify-swift
2+
3+
// https://github.com/apple/swift/issues/63753
4+
5+
if 0 {} // expected-error{{integer literal value '0' cannot be used as a boolean; did you mean 'false'?}} {{4-5=false}}
6+
if (0) {} // expected-error{{integer literal value '0' cannot be used as a boolean; did you mean 'false'?}} {{5-6=false}}
7+
if 1 {} // expected-error{{integer literal value '1' cannot be used as a boolean; did you mean 'true'?}} {{4-5=true}}
8+
if (1) {} // expected-error{{integer literal value '1' cannot be used as a boolean; did you mean 'true'?}} {{5-6=true}}
9+
if 12 {} // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}}
10+
if (12) {} // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}}
11+
12+
if 0? {} // expected-error {{cannot use optional chaining on non-optional value of type 'Int'}}
13+
// expected-error@-1{{cannot convert value of type 'Int?' to expected condition type 'Bool'}}
14+
if (0?) {} // expected-error {{cannot use optional chaining on non-optional value of type 'Int'}}
15+
// expected-error@-1{{cannot convert value of type 'Int?' to expected condition type 'Bool'}}
16+
if 0?? {} // expected-error 2{{cannot use optional chaining on non-optional value of type 'Int'}}
17+
// expected-error@-1{{cannot convert value of type 'Int?' to expected condition type 'Bool'}}
18+
19+
let _ = 0? as Int? // expected-error {{cannot use optional chaining on non-optional value of type 'Int'}}
20+
let _ = nil? as Int? // expected-error {{'?' must be followed by a call, member lookup, or subscript}}
21+
let _ = ""? as String? // expected-error {{cannot use optional chaining on non-optional value of type 'String'}}

test/Sema/pound_assert.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,6 @@
88

99
#assert(false, "error message")
1010

11-
#assert(123) // expected-error{{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
11+
#assert(123) // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}}
1212

13-
#assert(123, "error message") // expected-error{{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
13+
#assert(123, "error message") // expected-error{{cannot convert value of type 'Int' to expected condition type 'Bool'}}

test/expr/expressions.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func basictest() {
3636

3737
var x4 : Bool = true
3838
var x5 : Bool =
39-
4 // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
39+
4 // expected-error {{cannot convert value of type 'Int' to specified type 'Bool'}}
4040

4141
//var x6 : Float = 4+5
4242

@@ -297,7 +297,7 @@ func fib(_ n: Int) -> Int {
297297

298298
// FIXME: Should warn about integer constants being too large <rdar://problem/14070127>
299299
var
300-
il_a: Bool = 4 // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
300+
il_a: Bool = 4 // expected-error {{cannot convert value of type 'Int' to specified type 'Bool'}}
301301
var il_b: Int8
302302
= 123123
303303
var il_c: Int8 = 4 // ok

test/stmt/statements.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -525,7 +525,7 @@ func fn(x: Int) {
525525
}
526526

527527
func bad_if() {
528-
if 1 {} // expected-error {{type 'Int' cannot be used as a boolean; test for '!= 0' instead}}
528+
if 1 {} // expected-error {{integer literal value '1' cannot be used as a boolean; did you mean 'true'?}} {{6-7=true}}
529529
if (x: false) {} // expected-error {{cannot convert value of type '(x: Bool)' to expected condition type 'Bool'}}
530530
if (x: 1) {} // expected-error {{cannot convert value of type '(x: Int)' to expected condition type 'Bool'}}
531531
if nil {} // expected-error {{'nil' is not compatible with expected condition type 'Bool'}}

0 commit comments

Comments
 (0)