Skip to content

Commit

Permalink
Add explanation and docs to T.absurd error (sorbet#5314)
Browse files Browse the repository at this point in the history
* Document error 6004

* addErrorLine to T.absurd error

* Update website/docs/error-reference.md

Co-authored-by: Trevor Elliott <[email protected]>

Co-authored-by: Trevor Elliott <[email protected]>
  • Loading branch information
jez and elliottt authored Feb 16, 2022
1 parent 8dbdc0e commit 94fbf56
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 4 deletions.
12 changes: 8 additions & 4 deletions cfg/builder/builder_walk.cc
Original file line number Diff line number Diff line change
Expand Up @@ -359,24 +359,28 @@ BasicBlock *CFGBuilder::walk(CFGContext cctx, ast::ExpressionPtr &what, BasicBlo
return;
}

if (!ast::isa_tree<ast::Local>(s.getPosArg(0)) &&
!ast::isa_tree<ast::UnresolvedIdent>(s.getPosArg(0))) {
auto &posArg0 = s.getPosArg(0);
if (!ast::isa_tree<ast::Local>(posArg0) && !ast::isa_tree<ast::UnresolvedIdent>(posArg0)) {
if (auto e = cctx.ctx.beginError(s.loc, core::errors::CFG::MalformedTAbsurd)) {
// Providing a send is the most common way T.absurd is misused, so we provide a
// little extra hint in the error message in that case.
if (ast::isa_tree<ast::Send>(s.getPosArg(0))) {
if (ast::isa_tree<ast::Send>(posArg0)) {
e.setHeader("`{}` expects to be called on a variable, not a method call",
"T.absurd");
} else {
e.setHeader("`{}` expects to be called on a variable", "T.absurd");
}
e.addErrorLine(core::Loc(cctx.ctx.file, posArg0.loc()),
"Assign this expression to a variable, and use it in both the "
"conditional and the `{}` call",
"T.absurd");
}
ret = current;
return;
}

auto temp = cctx.newTemporary(core::Names::statTemp());
current = walk(cctx.withTarget(temp), s.getPosArg(0), current);
current = walk(cctx.withTarget(temp), posArg0, current);
current->exprs.emplace_back(cctx.target, s.loc, make_insn<TAbsurd>(temp));
ret = current;
return;
Expand Down
41 changes: 41 additions & 0 deletions website/docs/error-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -964,6 +964,47 @@ For how to fix, see [Type Annotations](type-annotations.md).

See also: [5028](#5028), [7017](#7017).

# 6004

In order to statically check [exhaustiveness](exhaustiveness), Sorbet provides
`T.absurd`, which lets people opt into exhaustiveness checks.

`T.absurd` must be given a variable. If not, like in this example, it reports an
error:

```ruby
# -- bad example --
sig {returns(T.any(Integer, String))}
def returns_int_or_string; 0; end
case returns_int_or_string
when Integer then puts 'got int'
when String then puts 'got string'
# error! `returns_int_or_string` is not a variable!
else T.absurd(returns_int_or_string)
end
```

While it looks like `returns_int_or_string` is the name of a variable, it's
actually a method call (Ruby allows method calls to omit parentheses). To fix
this error, store the result of calling `returns_int_or_string` in a variable,
and use that variable with the `case` and `T.absurd`:
```ruby
sig {returns(T.any(Integer, String))}
def returns_int_or_string; 0; end
# calls returns_int_or_string, stores result in x
x = returns_int_or_string
case x
when Integer then puts 'got int'
when String then puts 'got string'
else T.absurd(x)
end
```
## 7001
Sorbet does not allow reassigning a variable to a different type within a loop
Expand Down

0 comments on commit 94fbf56

Please sign in to comment.