Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
Comment thread
nobodywasishere marked this conversation as resolved.
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