Browse Source

Don't add explicit returns in `do` blocks (#70)

pull/71/head
Fredrik Ekre 1 year ago committed by GitHub
parent
commit
65f4cc340b
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
  1. 15
      README.md
  2. 2
      src/runestone.jl
  3. 20
      test/runtests.jl

15
README.md

@ -411,9 +411,8 @@ x = a + b *
### Explicit `return` ### Explicit `return`
Explicit `return` statements are ensured in function/macro definitions as well as in Explicit `return` statements are ensured in function and macro definitions by adding
`do`-blocks by adding `return` in front of the last expression, with some exceptions listed `return` in front of the last expression, with some exceptions listed below.
below.
- If the last expression is a `for` or `while` loop (which both always evaluate to - If the last expression is a `for` or `while` loop (which both always evaluate to
`nothing`) `return` is added *after* the loop. `nothing`) `return` is added *after* the loop.
@ -423,6 +422,8 @@ below.
there is no `return` inside the block. there is no `return` inside the block.
- If the last expression is a macro call, the `return` is only added in case there is no - If the last expression is a macro call, the `return` is only added in case there is no
`return` inside the macro. `return` inside the macro.
- No `return` is added in short form functions (`f(...) = ...`), short form anonymous
functions (`(...) -> ...`), and `do`-blocks (`f(...) do ...; ...; end`).
- If the last expression is a function call, and the function name is (or contains) `throw` - If the last expression is a function call, and the function name is (or contains) `throw`
or `error`, no `return` is added. This is because it is already obvious that these calls or `error`, no `return` is added. This is because it is already obvious that these calls
terminate the function and don't return any value. terminate the function and don't return any value.
@ -449,14 +450,6 @@ Examples:
- :(generate_expr(args...)) - :(generate_expr(args...))
+ return :(generate_expr(args...)) + return :(generate_expr(args...))
end end
function g()
- open("/dev/random", "r") do f
- read(f, 8)
+ return open("/dev/random", "r") do f
+ return read(f, 8)
end
end
``` ```
#### Potential changes #### Potential changes

2
src/runestone.jl

@ -3584,7 +3584,7 @@ function explicit_return_block(ctx, node)
end end
function explicit_return(ctx::Context, node::Node) function explicit_return(ctx::Context, node::Node)
if !(!is_leaf(node) && kind(node) in KSet"function macro do") if !(!is_leaf(node) && kind(node) in KSet"function macro")
return nothing return nothing
end end
if !safe_to_insert_return(ctx, node) if !safe_to_insert_return(ctx, node)

20
test/runtests.jl

@ -487,9 +487,9 @@ end
@test format_string("try\nerror()\ncatch$(sp)e\nend") == "try\n error()\ncatch e\nend" @test format_string("try\nerror()\ncatch$(sp)e\nend") == "try\n error()\ncatch e\nend"
@test format_string("A$(sp)where$(sp){T}") == "A where {T}" @test format_string("A$(sp)where$(sp){T}") == "A where {T}"
@test format_string("A$(sp)where$(sp){T}$(sp)where$(sp){S}") == "A where {T} where {S}" @test format_string("A$(sp)where$(sp){T}$(sp)where$(sp){S}") == "A where {T} where {S}"
@test format_string("f()$(sp)do$(sp)x\ny\nend") == "f() do x\n return y\nend" @test format_string("f()$(sp)do$(sp)x\ny\nend") == "f() do x\n y\nend"
@test format_string("f()$(sp)do\ny\nend") == "f() do\n return y\nend" @test format_string("f()$(sp)do\ny\nend") == "f() do\n y\nend"
@test format_string("f()$(sp)do; y end") == "f() do;\n return y\nend" @test format_string("f()$(sp)do; y end") == "f() do;\n y\nend"
# After `where` (anywhere else?) a newline can be used instead of a space # After `where` (anywhere else?) a newline can be used instead of a space
@test format_string("A$(sp)where$(sp)\n{A}") == "A where\n{A}" @test format_string("A$(sp)where$(sp)\n{A}") == "A where\n{A}"
end end
@ -647,8 +647,8 @@ end
"try\n$(sp)x\n$(sp)catch err\n$(sp)y\n$(sp)else\n$(sp)z\n$(sp)finally\n$(sp)z\n$(sp)end" "try\n$(sp)x\n$(sp)catch err\n$(sp)y\n$(sp)else\n$(sp)z\n$(sp)finally\n$(sp)z\n$(sp)end"
) == "try\n x\ncatch err\n y\nelse\n z\nfinally\n z\nend" ) == "try\n x\ncatch err\n y\nelse\n z\nfinally\n z\nend"
# do-end # do-end
@test format_string("open() do\n$(sp)a\n$(sp)end") == "open() do\n return a\nend" @test format_string("open() do\n$(sp)a\n$(sp)end") == "open() do\n a\nend"
@test format_string("open() do io\n$(sp)a\n$(sp)end") == "open() do io\n return a\nend" @test format_string("open() do io\n$(sp)a\n$(sp)end") == "open() do io\n a\nend"
# module-end, baremodule-end # module-end, baremodule-end
for b in ("", "bare") for b in ("", "bare")
# Just a module # Just a module
@ -1082,11 +1082,11 @@ end
@test format_string("try$(d)x$(d)catch err$(d)y$(d)else$(d)z$(d)finally$(d)z$(d)end") == @test format_string("try$(d)x$(d)catch err$(d)y$(d)else$(d)z$(d)finally$(d)z$(d)end") ==
"try\n x\ncatch err\n y\nelse\n z\nfinally\n z\nend" "try\n x\ncatch err\n y\nelse\n z\nfinally\n z\nend"
# do-end # do-end
@test format_string("open() do\na$(d)end") == "open() do\n return a\nend" @test format_string("open() do\na$(d)end") == "open() do\n a\nend"
@test format_string("open() do\nend") == "open() do\nend" @test format_string("open() do\nend") == "open() do\nend"
@test_broken format_string("open() do;a$(d)end") == "open() do\n a\nend" @test_broken format_string("open() do;a$(d)end") == "open() do\n a\nend"
@test_broken format_string("open() do ;a$(d)end") == "open() do\n a\nend" @test_broken format_string("open() do ;a$(d)end") == "open() do\n a\nend"
@test format_string("open() do io$(d)a end") == "open() do io\n return a\nend" @test format_string("open() do io$(d)a end") == "open() do io\n a\nend"
# let-end # let-end
@test format_string("let a = 1\nx$(d)end") == "let a = 1\n x\nend" @test format_string("let a = 1\nx$(d)end") == "let a = 1\n x\nend"
@test format_string("let\nx$(d)end") == "let\n x\nend" @test format_string("let\nx$(d)end") == "let\n x\nend"
@ -1216,7 +1216,7 @@ end
"begin", "quote", "for i in I", "let", "let x = 1", "while cond", "begin", "quote", "for i in I", "let", "let x = 1", "while cond",
"if cond", "macro f()", "function f()", "f() do", "f() do x", "if cond", "macro f()", "function f()", "f() do", "f() do x",
) )
rx = prefix in ("function f()", "macro f()", "f() do", "f() do x") ? " return x\n" : "" rx = prefix in ("function f()", "macro f()") ? " return x\n" : ""
@test format_string("$(prefix)\n$(body)$(rx)\nend") == "$prefix\n$(bodyfmt)$(rx)\nend" @test format_string("$(prefix)\n$(body)$(rx)\nend") == "$prefix\n$(bodyfmt)$(rx)\nend"
end end
@test format_string( @test format_string(
@ -1255,7 +1255,7 @@ end
end end
@testset "explicit return" begin @testset "explicit return" begin
for f in ("function f()", "function ()", "f() do", "macro m()") for f in ("function f()", "function ()", "macro m()")
# Simple cases just prepend `return` # Simple cases just prepend `return`
for r in ( for r in (
"x", "*", "x, y", "(x, y)", "f()", "[1, 2]", "Int[1, 2]", "[1 2]", "Int[1 2]", "x", "*", "x, y", "(x, y)", "f()", "[1, 2]", "Int[1, 2]", "[1 2]", "Int[1 2]",
@ -1265,7 +1265,7 @@ end
"a.b", "a.b.c", "x -> x^2", "[x for x in X]", "Int[x for x in X]", "a.b", "a.b.c", "x -> x^2", "[x for x in X]", "Int[x for x in X]",
"A{T} where {T}", "(@m a, b)", "A{T}", "A{T} where {T}", "(@m a, b)", "A{T}",
"r\"foo\"", "r\"foo\"m", "`foo`", "```foo```", "r`foo`", "r\"foo\"", "r\"foo\"m", "`foo`", "```foo```", "r`foo`",
"f() do\n return x\n end", "f() do x\n return x\n end", "f() do\n x\n end", "f() do x\n x\n end",
"function f()\n return x\n end", "function f()\n return x\n end",
"function ()\n return x\n end", "function ()\n return x\n end",
"quote\n x\n end", "begin\n x\n end", "quote\n x\n end", "begin\n x\n end",

Loading…
Cancel
Save