Skip to content

class Crystal::MainVisitor
inherits Crystal::SemanticVisitor

This is the main visitor of the program, ran after types have been declared and their type declarations (like @x : Int32) have been processed.

This visits the "main" code of the program and resolves calls, instantiates methods and visits them, recursively, with other MainVisitors.

The visitor keeps track of a method's variables (or the main program, split into several files, in case of top-level code). It keeps track both of the type of a variable at a single point (stored in @vars) and the combined type of all assignments to it (in @meta_vars).

Call resolution logic is in Call#recalculate, where method lookup is done.

Constants

ValidClassVarAnnotations = ["ThreadLocal"] of ::String

ValidGlobalAnnotations = ["ThreadLocal"] of ::String

Class methods

.check_automatic_cast(program, value, var_type, assign = nil)

View source

.check_type_allowed_as_proc_argument(node, type)

View source

.new(program, vars = MetaVars.new, typed_def = nil, meta_vars = nil)

View source

Methods

#assign_to_meta_var(name, context = current_context)

View source

#bind_block_var(node, target, meta_vars, before_block_vars)

View source

#bind_initialize_instance_vars(owner)

View source

#bind_meta_var(var : InstanceVar)

View source

#bind_meta_var(var : Var)

View source

#bind_meta_var(var)

View source

#bind_vars(from_vars, to_vars, ignored = nil)

View source

#block : Block?

View source

#block=(block)

View source

#block_nest : Int32

View source

#block_nest=(block_nest)

View source

#call : Call?

View source

#call=(call : Call?)

View source

#call_needs_splat_expansion?(node)

View source

#check_automatic_cast(value, var_type, assign = nil)

See if we can automatically cast the value if the types don't exactly match

View source

#check_call_in_initialize(node)

Checks if it's a call to self. In that case, all instance variables not mentioned so far will be considered nil.

View source

#check_closured(var, mark_as_mutably_closured : Bool = false)

View source

#check_exception_handler_vars(var_name, node)

View source

#check_initialize_instance_vars_types(owner)

View source

#check_lib_call(node, obj_type)

Fill function literal argument types for C functions

View source

#check_lib_call_arg

View source

#check_mutably_closured(meta_var, var)

If the meta_var is closured but not readonly, then bind var to it (it gets all types assigned to meta_var). Otherwise, add it to the local vars so that they could be bond later on, if the meta_var stops being readonly.

View source

#check_not_a_constant(node)

View source

#check_self_closured

View source

#check_special_new_call(node, obj_type)

Checks if it's ProcType#new

View source

#check_super_or_previous_def_in_initialize(node)

If it's a super or previous_def call inside an initialize we treat set instance vars from superclasses to not-nil.

View source

#check_type_in_type_args(type)

View source

#closure_context

View source

#conditional_no_return(node, var)

View source

#convert_struct_or_union_numeric_argument(node, unaliased_type, expected_type, actual_type)

View source

#current_context

View source

#current_non_block_context

View source

#define_special_var(name, value)

View source

#end_visit(node : Splat)

View source

#end_visit(node : DoubleSplat)

View source

#end_visit(node : RespondsTo)

View source

#end_visit(node : Break)

View source

#end_visit(node : Next)

View source

#end_visit(node : TupleLiteral)

View source

#end_visit(node : NamedTupleLiteral)

View source

#end_visit(node : Expressions)

View source

#exception_handler_vars : MetaVars?

View source

#exception_handler_vars=(exception_handler_vars : MetaVars?)

View source

#expand(node)

View source

#expand

View source

#expand_named(node)

View source

#filter_vars(filters)

If we have:

if a ... end

then inside the if 'a' must not be nil.

This is what we do here: we create a meta-variable for it and filter it accordingly. This also applied to .is_a? and .responds_to?.

This also applies to 'while' conditions and also to the else part of an if, but with filters inverted.

View source

#filter_vars

View source

#first_time_accessing_meta_type_var?(var)

View source

#free_vars

Returns free variables

View source

#fun_literal_context : Def | Program | Nil

View source

#fun_literal_context=(fun_literal_context : Def | Program | Nil)

View source

#gather_instance_vars_read(node)

View source

#get_expression_var(exp)

Get the variable of an expression. If it's a variable, it's that variable. If it's an assignment to a variable, it's that variable.

View source

#get_while_cond_assign_target(node)

View source

#ignoring_type_filters

View source

#inside_block?

View source

#inside_constant=(inside_constant)

View source

#inside_constant? : Bool

View source

#inside_ensure=(inside_ensure : Bool)

View source

#inside_ensure? : Bool

View source

#inside_while?

View source

#is_initialize : Bool

View source

#is_initialize=(is_initialize : Bool)

View source

#last_block_kind : Symbol?

It means the last block kind, that is one of block, while and ensure. It is used to detect break or next from ensure.

begin
  # `last_block_kind == nil`
ensure
  # `last_block_kind == :ensure`
  while true
    # `last_block_kind == :while`
  end
  loop do
    # `last_block_kind == :block`
  end
  # `last_block_kind == :ensure`
end
View source

#last_block_kind=(last_block_kind : Symbol?)

It means the last block kind, that is one of block, while and ensure. It is used to detect break or next from ensure.

begin
  # `last_block_kind == nil`
ensure
  # `last_block_kind == :ensure`
  while true
    # `last_block_kind == :while`
  end
  loop do
    # `last_block_kind == :block`
  end
  # `last_block_kind == :ensure`
end
View source

#lookup_class_var(node)

View source

#lookup_global_variable(node)

View source

#lookup_instance_var(node, scope)

View source

#lookup_instance_var(node)

View source

#lookup_similar_class_variable_name(node, owner)

View source

#lookup_similar_global_variable_name(node)

View source

#lookup_similar_instance_variable_name(node, owner)

View source

#lookup_similar_var_name(name)

View source

#lookup_var_or_instance_var(var : InstanceVar)

View source

#lookup_var_or_instance_var(var : Var)

View source

#lookup_var_or_instance_var(var)

View source

#mark_as_closured(var, var_context, mark_as_mutably_closured : Bool)

View source

#match_context : MatchContext?

View source

#match_context=(match_context : MatchContext?)

View source

#merge_if_vars(node, cond_vars, then_vars, else_vars, before_then_vars, before_else_vars, then_unreachable, else_unreachable)

Here we merge the variables from both branches of an if. We basically: - Create a variable whose type is the merged types of the last type of each branch. - Make the variable nilable if the variable wasn't declared before the 'if' and it doesn't appear in one of the branches. - Don't use the type of a branch that is unreachable (ends with return, break or with a call that is NoReturn)

View source

#merge_rescue_vars(body_vars, all_rescue_vars)

View source

#merge_while_vars(cond, endless, before_cond_vars_copy, before_cond_vars, after_cond_vars, while_vars, all_break_vars)

Here we assign the types of variables after a while.

View source

#meta_vars : MetaVars

Here we store the cumulative types of variables as we traverse the nodes.

View source

#needs_type_filters?

View source

#new_meta_var(name, context = current_context)

View source

#node_exp_or_nil_literal(node)

Returns node.exp if it's not nil. Otherwise, creates a NilLiteral node that has the same location as node, and returns that. We use this NilLiteral when the user writes return, next or break without arguments, so that in the error trace we can show it right (those expressions have a NoReturn type so we can't directly bind to them).

View source

#parent=(parent : MainVisitor?)

View source

#path_lookup : Crystal::Type?

View source

#path_lookup=(path_lookup)

View source

#pointerof_var(node)

View source

#prepare_call(node)

View source

#replace_call_splats(node)

View source

#request_type_filters

View source

#special_c_struct_or_union_new_with_named_args(node, type, named_args)

Rewrite:

LibFoo::Struct.new arg0: value0, argN: value0

To:

temp = LibFoo::Struct.new temp.arg0 = value0 temp.argN = valueN temp

View source

#the_self(node)

View source

#type_assign(target : InstanceVar, value, node)

View source

#type_assign(target : Path, value, node)

View source

#type_assign(target : Global, value, node)

View source

#type_assign(target : ClassVar, value, node)

View source

#type_assign(target : Underscore, value, node)

View source

#type_assign(target, value, node)

View source

#type_assign(target : Var, value, node, restriction = nil)

View source

#typed_def

View source

#typed_def? : Crystal::Def?

View source

#undefined_class_variable(node, owner)

View source

#undefined_global_variable(node)

View source

#undefined_instance_variable(owner, node)

View source

#untyped_def : Def

#untyped_def=(untyped_def : Def)

View source

#untyped_def=(untyped_def)

View source

#untyped_def? : Def?

#vars : Hash(String, Crystal::MetaVar)

In vars we store the types of variables as we traverse the nodes. These type are not cumulative: if you do x = 1, 'x' will have type Int32. Then if you do x = false, 'x' will have type Bool.

View source

#visit(node : Assign)

View source

#visit(node : Yield)

View source

#visit(node : Block)

View source

#visit(node : ProcLiteral)

View source

#visit(node : ProcPointer)

View source

#visit(node : Call)

View source

#visit(node : Return)

View source

#visit(node : Underscore)

View source

#visit(node : IsA)

View source

#visit(node : Cast | NilableCast)

View source

#visit(node : FunDef)

View source

#visit(node : Unreachable)

View source

#visit(node : If)

View source

#visit(node : ClassVar)

View source

#visit(node : ReadInstanceVar)

View source

#visit(node : InstanceVar)

View source

#visit(node : Global)

View source

#visit(node : Out)

View source

#visit(node : TypeDeclaration)

View source

#visit(node : Var)

View source

#visit(node : Self)

View source

#visit(node : Metaclass)

View source

#visit(node : Union)

View source

#visit(node : ProcNotation)

View source

#visit(node : Generic)

View source

#visit(node : Path)

View source

#visit(node : FileNode)

View source

#visit(node : While)

View source

#visit(node : TupleIndexer)

View source

#visit(node : SymbolLiteral)

View source

#visit(node : StringLiteral)

View source

#visit(node : RegexLiteral)

View source

#visit(node : ArrayLiteral)

View source

#visit(node : HashLiteral)

View source

#visit(node : And)

View source

#visit(node : Or)

View source

#visit(node : RangeLiteral)

View source

#visit(node : Case)

View source

#visit(node : Select)

View source

#visit(node : MultiAssign)

View source

#visit(node : Not)

View source

#visit(node : Arg)

View source

#visit(node : ImplicitObj)

View source

#visit(node : CharLiteral)

View source

#visit(node : NumberLiteral)

View source

#visit(node : Primitive)

View source

#visit(node : PointerOf)

View source

#visit(node : TypeOf)

View source

#visit(node : SizeOf)

View source

#visit(node : InstanceSizeOf)

View source

#visit(node : OffsetOf)

View source

#visit(node : Rescue)

View source

#visit(node : Asm)

View source

#visit(node : BoolLiteral)

View source

#visit(node : NilLiteral)

View source

#visit(node : Nop)

View source

#visit_allocate(node)

View source

#visit_any(node)

View source

#visit_class_var(node)

View source

#visit_global(node)

View source

#visit_pointer_malloc(node)

View source

#visit_pointer_new(node)

View source

#visit_pointer_set(node)

View source

#visit_read_instance_var(node)

View source

#visit_struct_or_union_set(node)

View source

#visit_va_arg(node)

View source

#with_block_kind

View source

#with_scope : Type?

View source

#with_scope=(with_scope : Type?)

View source

#yield_vars : Array(Var)?

These are the variables and types that come from a block specification like &block : Int32 -> Int32. When doing yield 1 we need to verify that the yielded expression has the type that the block specification said.

View source

#yield_vars=(yield_vars : Array(Var)?)

These are the variables and types that come from a block specification like &block : Int32 -> Int32. When doing yield 1 we need to verify that the yielded expression has the type that the block specification said.

View source