Skip to content

Commit

Permalink
Raise UnsupportedSyntax if a proc object is given as block-pass-argument
Browse files Browse the repository at this point in the history
At present, users will see BlockTypeMismatch diagnostic when a proc
object is passed as block-pass-argument.  We'll usually see the diagnostic
with `&method` idiom.

```ruby
[1, 2, 3].map(&method(:puts))
```

But there is nothing to do from the user side because such a call is
not invalid.  It seems like a false positive.

This changes the diagnostic type for the case to UnsupportedSyntax.  It
notifies users that the source code is valid and that change is
unnecessary.

refs: #149
  • Loading branch information
tk0miya committed Oct 2, 2024
1 parent 8ebbb39 commit 44ace21
Show file tree
Hide file tree
Showing 3 changed files with 153 additions and 122 deletions.
10 changes: 10 additions & 0 deletions lib/steep/type_construction.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4190,6 +4190,16 @@ def try_method_type(node, receiver_type:, method_name:, method_overload:, argume
method_type: method_type
)
else
if node_type.is_a?(AST::Types::Name::Instance) && node_type.name.to_s == '::Proc'
errors << Diagnostic::Ruby::UnsupportedSyntax.new(
node: arg.node,
message: "Unsupported block-pass-argument given `#{node_type}`"
)

type = Interface::Function.new(params: nil, return_type: AST::Builtin.any_type, location: nil)
node_type = AST::Types::Proc.new(type: type, self_type: nil, block: nil)
end

# non-nil value is given
constr.check_relation(sub_type: node_type, super_type: arg.node_type, constraints: constraints).else do |result|
errors << Diagnostic::Ruby::BlockTypeMismatch.new(
Expand Down
242 changes: 120 additions & 122 deletions smoke/block/test_expectations.yml
Original file line number Diff line number Diff line change
@@ -1,133 +1,131 @@
---
- file: a.rb
diagnostics:
- range:
start:
line: 8
character: 0
end:
line: 8
character: 9
severity: ERROR
message: |-
Cannot assign a value of type `::Integer` to a variable of type `::String`
::Integer <: ::String
::Numeric <: ::String
::Object <: ::String
::BasicObject <: ::String
code: Ruby::IncompatibleAssignment
- range:
start:
line: 10
character: 0
end:
line: 10
character: 17
severity: ERROR
message: |-
Cannot assign a value of type `::String` to a variable of type `::Integer`
::String <: ::Integer
::Object <: ::Integer
::BasicObject <: ::Integer
code: Ruby::IncompatibleAssignment
- file: b.rb
diagnostics:
- range:
start:
line: 5
character: 2
end:
line: 5
character: 9
severity: ERROR
message: |-
Cannot break with a value of type `::Integer` because type `::Symbol` is assumed
::Integer <: ::Symbol
::Numeric <: ::Symbol
::Object <: ::Symbol
::BasicObject <: ::Symbol
code: Ruby::BreakTypeMismatch
- range:
start:
line: 10
character: 0
end:
line: 13
character: 3
severity: ERROR
message: |-
Cannot assign a value of type `(::Integer | ::Symbol)` to a variable of type `::String`
(::Integer | ::Symbol) <: ::String
- range:
start:
line: 8
character: 0
end:
line: 8
character: 9
severity: ERROR
message: |-
Cannot assign a value of type `::Integer` to a variable of type `::String`
::Integer <: ::String
::Numeric <: ::String
::Object <: ::String
::BasicObject <: ::String
code: Ruby::IncompatibleAssignment
code: Ruby::IncompatibleAssignment
- range:
start:
line: 10
character: 0
end:
line: 10
character: 17
severity: ERROR
message: |-
Cannot assign a value of type `::String` to a variable of type `::Integer`
::String <: ::Integer
::Object <: ::Integer
::BasicObject <: ::Integer
code: Ruby::IncompatibleAssignment
- file: b.rb
diagnostics:
- range:
start:
line: 5
character: 2
end:
line: 5
character: 9
severity: ERROR
message: |-
Cannot break with a value of type `::Integer` because type `::Symbol` is assumed
::Integer <: ::Symbol
::Numeric <: ::Symbol
::Object <: ::Symbol
::BasicObject <: ::Symbol
code: Ruby::BreakTypeMismatch
- range:
start:
line: 10
character: 0
end:
line: 13
character: 3
severity: ERROR
message: |-
Cannot assign a value of type `(::Integer | ::Symbol)` to a variable of type `::String`
(::Integer | ::Symbol) <: ::String
::Integer <: ::String
::Numeric <: ::String
::Object <: ::String
::BasicObject <: ::String
code: Ruby::IncompatibleAssignment
- file: d.rb
diagnostics:
- range:
start:
line: 6
character: 0
end:
line: 6
character: 19
severity: ERROR
message: |-
Cannot assign a value of type `::Array[::String]` to a variable of type `::Array[::Float]`
::Array[::String] <: ::Array[::Float]
::String <: ::Float
::Object <: ::Float
::BasicObject <: ::Float
code: Ruby::IncompatibleAssignment
- range:
start:
line: 8
character: 0
end:
line: 8
character: 23
severity: ERROR
message: |-
Cannot assign a value of type `::Array[::String]` to a variable of type `::Array[::Float]`
::Array[::String] <: ::Array[::Float]
::String <: ::Float
::Object <: ::Float
::BasicObject <: ::Float
code: Ruby::IncompatibleAssignment
- range:
start:
line: 10
character: 12
end:
line: 10
character: 28
severity: ERROR
message: |-
Cannot pass a value of type `::Proc` as a block-pass-argument of type `^(::Integer) -> U(3)`
::Proc <: ^(::Integer) -> U(3)
code: Ruby::BlockTypeMismatch
- range:
start:
line: 11
character: 12
end:
line: 11
character: 20
severity: ERROR
message: |-
Cannot pass a value of type `::Proc` as a block-pass-argument of type `^(::Integer) -> U(4)`
::Proc <: ^(::Integer) -> U(4)
code: Ruby::BlockTypeMismatch
- range:
start:
line: 6
character: 0
end:
line: 6
character: 19
severity: ERROR
message: |-
Cannot assign a value of type `::Array[::String]` to a variable of type `::Array[::Float]`
::Array[::String] <: ::Array[::Float]
::String <: ::Float
::Object <: ::Float
::BasicObject <: ::Float
code: Ruby::IncompatibleAssignment
- range:
start:
line: 8
character: 0
end:
line: 8
character: 23
severity: ERROR
message: |-
Cannot assign a value of type `::Array[::String]` to a variable of type `::Array[::Float]`
::Array[::String] <: ::Array[::Float]
::String <: ::Float
::Object <: ::Float
::BasicObject <: ::Float
code: Ruby::IncompatibleAssignment
- range:
start:
line: 10
character: 12
end:
line: 10
character: 28
severity: ERROR
message: |-
Unsupported block-pass-argument given `::Proc`
code: Ruby::UnsupportedSyntax
- range:
start:
line: 11
character: 12
end:
line: 11
character: 20
severity: ERROR
message: |-
Unsupported block-pass-argument given `::Proc`
code: Ruby::UnsupportedSyntax
- file: e.rb
diagnostics:
- range:
start:
line: 11
character: 2
end:
line: 11
character: 5
severity: ERROR
message: Type `(::Integer | ::String)` does not have method `foo`
code: Ruby::NoMethod
- range:
start:
line: 11
character: 2
end:
line: 11
character: 5
severity: ERROR
message: Type `(::Integer | ::String)` does not have method `foo`
code: Ruby::NoMethod
23 changes: 23 additions & 0 deletions test/type_construction_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7529,6 +7529,29 @@ def test_proc_with_block_annotation
end
end

def test_proc_for_block_pass_argument
with_checker() do |checker|
source = parse_ruby(<<-'RUBY')
# @type var f: Proc
f = _ = nil
r = [1, 2, 3].map(&f)
RUBY

with_standard_construction(checker, source) do |construction, typing|
type, _, context = construction.synthesize(source.node)

assert_typing_error(typing, size: 1) do |errors|
assert_any!(errors) do |error|
assert_instance_of Diagnostic::Ruby::UnsupportedSyntax, error
assert_equal "Unsupported block-pass-argument given `::Proc`", error.message
end
end
assert_equal parse_type("::Array[untyped]"), context.type_env[:r]
end
end
end


def test_next_with_next_type
with_checker(<<RBS) do |checker|
class NextTest
Expand Down

0 comments on commit 44ace21

Please sign in to comment.