Skip to content

Commit

Permalink
Implement exhaustiveness check
Browse files Browse the repository at this point in the history
  • Loading branch information
makenowjust committed Aug 16, 2017
1 parent e5b6efe commit 02e2e88
Show file tree
Hide file tree
Showing 3 changed files with 455 additions and 3 deletions.
4 changes: 2 additions & 2 deletions spec/compiler/codegen/case_spec.cr
Expand Up @@ -51,7 +51,7 @@ describe "Code gen: case" do

it "codegens case with class" do
run("
struct Nil; def to_i; 0; end; end
require \"prelude\"
struct Int32
def foo
Expand All @@ -65,7 +65,7 @@ describe "Code gen: case" do
a.foo
when Char
a.ord
end.to_i
end
").to_i.should eq(-1)
end

Expand Down
187 changes: 187 additions & 0 deletions spec/compiler/semantic/case_spec.cr
@@ -0,0 +1,187 @@
require "../../spec_helper"

describe "Semantic: case" do
it "check exhaustiveness for union types (Path)" do
assert_error %(
foo = true ? 42 : "foo"
case foo
when String
end
), "found non-exhaustive pattern: Int32"
end

it "check exhaustiveness for union types (IsA)" do
assert_error %(
foo = true ? 42 : "foo"
case foo
when .is_a?(String)
end
), "found non-exhaustive pattern: Int32"
end

it "checks exhaustiveness for enum types (Path)" do
assert_error %(
enum FooBar
Foo
Bar
end
foo = FooBar::Foo
case foo
when FooBar::Foo
end
), "found non-exhaustive pattern: Bar"
end

it "checks exhaustiveness for enum types (Call)" do
assert_error %(
enum FooBar
Foo
Bar
end
foo = FooBar::Foo
case foo
when .foo?
end
), "found non-exhaustive pattern: Bar"
end

it "checks exhaustiveness for bool type (BoolLiteral)" do
assert_error %(
foo = true
case foo
when true
end
), "found non-exhaustive pattern: false"
end

it "checks exhaustiveness for nilable types (NilLiteral)" do
assert_error %(
foo = true ? "foo" : nil
case foo
when nil
end
), "found non-exhaustive pattern: String"
end

it "checks exhaustiveness for complex type" do
assert_error %(
enum FooBar
Foo
Bar
end
foo = nil.as(Nil | Bool | FooBar)
case foo
when .foo?
when true
end
), "found non-exhaustive patterns: nil, false, FooBar::Bar"
end

it "passes exhaustivness check if 'case' has 'else' block" do
assert_type %(
foo = true ? 42 : "foo"
case foo
when String
else
end
) { nil_type }
end

it "passes exhaustivness check if 'case' has 'when _' block" do
assert_type %(
require "prelude"
foo = true ? 42 : "foo"
case foo
when String
when _
end
) { nil_type }
end

it "passes exhaustiveness check if all values are exhausted" do
assert_type %(
require "prelude"
foo = true ? 42 : "foo"
case foo
when String
:string
when Int32
:int32
end
) { symbol }
end

it "checks tuple exhaustiveness" do
assert_error %(
require "prelude"
foo = true ? 42 : "foo"
bar = true ? 3.14 : :bar
case {foo, bar}
when {String, _}
when {_, Float64}
end
), "found non-exhaustive pattern: {Int32, Symbol}"
end

it "checks tuple exhaustiveness (multiple non-exhaustive patterns)" do
assert_error %(
require "prelude"
foo = true ? 42 : "foo"
bar = true ? 3.14 : :bar
case {foo, bar}
when {String, _}
end
), "found non-exhaustive patterns: {Int32, Float64}, {Int32, Symbol}"
end

it "passes tuple exhaustiveness check if all values are exhausted" do
assert_type %(
require "prelude"
foo = true ? 42 : "foo"
bar = true ? 3.14 : :bar
case {foo, bar}
when {String, _}
:left_string
when {_, Float64}
:right_float64
when {Int32, Symbol}
:int32_string
end
) { symbol }
end

it "passes tuple exhaustivness check if 'case' has 'when _' block" do

This comment has been minimized.

Copy link
@ysbaddaden

ysbaddaden Aug 17, 2017

Contributor

I believe this case spec is duplicated.

Im not sure we should support this particular case. Using underscore in tuples is great, but the proper thing to do, here, is use an else.

assert_type %(
require "prelude"
foo = true ? 42 : "foo"
bar = true ? 3.14 : :bar
case foo
when {String, Float64}
when _
end
) { nil_type }
end
end

0 comments on commit 02e2e88

Please sign in to comment.