Skip to content

Commit

Permalink
Add documentation for NoReturn and Void (#14817)
Browse files Browse the repository at this point in the history
Co-authored-by: Johannes Müller <straightshoota@gmail.com>
Co-authored-by: Sijawusz Pur Rahnama <sija@sija.pl>
  • Loading branch information
3 people authored Aug 19, 2024
1 parent 7427990 commit ee894e0
Show file tree
Hide file tree
Showing 3 changed files with 49 additions and 2 deletions.
2 changes: 2 additions & 0 deletions src/compiler/crystal/tools/doc/generator.cr
Original file line number Diff line number Diff line change
Expand Up @@ -217,6 +217,8 @@ class Crystal::Doc::Generator

def crystal_builtin?(type)
return false unless project_info.crystal_stdlib?
# TODO: Enabling this allows links to `NoReturn` to work, but has two `NoReturn`s show up in the sidebar
# return true if type.is_a?(NamedType) && {"NoReturn", "Void"}.includes?(type.name)
return false unless type.is_a?(Const) || type.is_a?(NonGenericModuleType)

crystal_type = @program.types["Crystal"]
Expand Down
19 changes: 17 additions & 2 deletions src/compiler/crystal/tools/doc/type.cr
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,13 @@ require "./item"
class Crystal::Doc::Type
include Item

PSEUDO_CLASS_PREFIX = "CRYSTAL_PSEUDO__"
PSEUDO_CLASS_NOTE = <<-DOC
NOTE: This is a pseudo-class provided directly by the Crystal compiler.
It cannot be reopened nor overridden.
DOC

getter type : Crystal::Type

def initialize(@generator : Generator, type : Crystal::Type)
Expand Down Expand Up @@ -39,7 +46,11 @@ class Crystal::Doc::Type
when Program
"Top Level Namespace"
when NamedType
type.name
if @generator.project_info.crystal_stdlib?
type.name.lchop(PSEUDO_CLASS_PREFIX)
else
type.name
end
when NoReturnType
"NoReturn"
when VoidType
Expand Down Expand Up @@ -403,7 +414,11 @@ class Crystal::Doc::Type
end

def doc
@type.doc
if (t = type).is_a?(NamedType) && t.name.starts_with?(PSEUDO_CLASS_PREFIX)
"#{@type.doc}#{PSEUDO_CLASS_NOTE}"
else
@type.doc
end
end

def lookup_path(path_or_names : Path | Array(String))
Expand Down
30 changes: 30 additions & 0 deletions src/docs_pseudo_methods.cr
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,33 @@ class Object
def __crystal_pseudo_responds_to?(name : Symbol) : Bool
end
end

# Some expressions won't return to the current scope and therefore have no return type.
# This is expressed as the special return type `NoReturn`.
#
# Typical examples for non-returning methods and keywords are `return`, `exit`, `raise`, `next`, and `break`.
#
# This is for example useful for deconstructing union types:
#
# ```
# string = STDIN.gets
# typeof(string) # => String?
# typeof(raise "Empty input") # => NoReturn
# typeof(string || raise "Empty input") # => String
# ```
#
# The compiler recognizes that in case string is Nil, the right hand side of the expression `string || raise` will be evaluated.
# Since `typeof(raise "Empty input")` is `NoReturn` the execution would not return to the current scope in that case.
# That leaves only `String` as resulting type of the expression.
#
# Every expression whose code paths all result in `NoReturn` will be `NoReturn` as well.
# `NoReturn` does not show up in a union type because it would essentially be included in every expression's type.
# It is only used when an expression will never return to the current scope.
#
# `NoReturn` can be explicitly set as return type of a method or function definition but will usually be inferred by the compiler.
struct CRYSTAL_PSEUDO__NoReturn
end

# Similar in usage to `Nil`. `Void` is prefered for C lib bindings.
struct CRYSTAL_PSEUDO__Void
end

0 comments on commit ee894e0

Please sign in to comment.