# File typeprof-0.21.2/lib/typeprof/config.rb, line 79
def self.analyze(config, cancel_token = nil)
# Deploy the config to the TypeProf::Config (Note: This is thread local)
Config.set_current(config)
if Config.current.options[:stackprof]
require "stackprof"
out = "typeprof-stackprof-#{ Config.current.options[:stackprof] }.dump"
StackProf.start(mode: Config.current.options[:stackprof], out: out, raw: true)
end
scratch = Scratch.new
Builtin.setup_initial_global_env(scratch)
Config.current.gem_rbs_features.each do |feature|
Import.import_library(scratch, feature)
end
rbs_files = []
rbs_codes = []
Config.current.rbs_files.each do |rbs|
if rbs.is_a?(Array) # [String name, String content]
rbs_codes << rbs
else
rbs_files << rbs
end
end
Import.import_rbs_files(scratch, rbs_files)
rbs_codes.each do |name, content|
Import.import_rbs_code(scratch, name, content)
end
def_code_range_table = nil
caller_code_range_table = nil
Config.current.rb_files.each do |rb|
if rb.is_a?(Array) # [String name, String content]
iseq, def_tbl, caller_tbl = ISeq.compile_str(*rb.reverse)
def_code_range_table ||= def_tbl
caller_code_range_table ||= caller_tbl
else
iseq = rb
end
scratch.add_entrypoint(iseq)
end
result = scratch.type_profile(cancel_token)
if Config.current.options[:lsp]
return scratch.report_lsp, def_code_range_table, caller_code_range_table
end
if Config.current.output.respond_to?(:write)
scratch.report(result, Config.current.output)
else
open(Config.current.output, "w") do |output|
scratch.report(result, output)
end
end
rescue TypeProfError => exc
exc.report(Config.current.output)
return nil
ensure
if Config.current.options[:stackprof] && defined?(StackProf)
StackProf.stop
StackProf.results
end
end
# File typeprof-0.21.2/lib/typeprof/type.rb, line 424
def self.any
Thread.current[:any] ||= Any.new
end
# File typeprof-0.21.2/lib/typeprof/type.rb, line 432
def self.bool
Thread.current[:bool] ||= Union.new(Utils::Set[
Instance.new(Type::Builtin[:true]),
Instance.new(Type::Builtin[:false])
], nil)
end
# File typeprof-0.21.2/lib/typeprof/type.rb, line 428
def self.bot
Thread.current[:bot] ||= Union.new(Utils::Set[], nil)
end
# File typeprof-0.21.2/lib/typeprof/type.rb, line 806
def self.builtin_global_variable_type(var)
case var
when :$_, :$/, :$\, :$,, :$;
Type.optional(Type::Instance.new(Type::Builtin[:str]))
when :$0, :$PROGRAM_NAME
Type::Instance.new(Type::Builtin[:str])
when :$~
Type.optional(Type::Instance.new(Type::Builtin[:matchdata]))
when :$., :$$
Type::Instance.new(Type::Builtin[:int])
when :$?
Type.optional(Type::Instance.new(Type::Builtin[:int]))
when :$!
Type.optional(Type::Instance.new(Type::Builtin[:exc]))
when :$@
str = Type::Instance.new(Type::Builtin[:str])
base_ty = Type::Instance.new(Type::Builtin[:ary])
Type.optional(Type::Array.new(Type::Array::Elements.new([], str), base_ty))
when :$*, :$:, :$LOAD_PATH, :$", :$LOADED_FEATURES
str = Type::Instance.new(Type::Builtin[:str])
base_ty = Type::Instance.new(Type::Builtin[:ary])
Type::Array.new(Type::Array::Elements.new([], str), base_ty)
when :$<
:ARGF
when :$>
:STDOUT
when :$DEBUG
Type.bool
when :$FILENAME
Type::Instance.new(Type::Builtin[:str])
when :$stdin
:STDIN
when :$stdout
:STDOUT
when :$stderr
:STDERR
when :$VERBOSE
Type.bool.union(Type.nil)
else
nil
end
end
# File typeprof-0.21.2/lib/typeprof/type.rb, line 747
def self.gen_hash(base_ty = Type::Instance.new(Type::Builtin[:hash]))
hg = HashGenerator.new
yield hg
Type::Hash.new(Type::Hash::Elements.new(hg.map_tys), base_ty)
end
# File typeprof-0.21.2/lib/typeprof/type.rb, line 753
def self.guess_literal_type(obj)
case obj
when ::Symbol
Type::Symbol.new(obj, Type::Instance.new(Type::Builtin[:sym]))
when ::Integer
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:int]))
when ::Rational
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:rational]))
when ::Complex
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:complex]))
when ::Float
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:float]))
when ::Class
return Type.any if obj < Exception
case obj
when ::Object
Type::Builtin[:obj]
when ::Array
Type::Builtin[:ary]
else
raise "unknown class: #{ obj.inspect }"
end
when ::TrueClass
Type::Instance.new(Type::Builtin[:true])
when ::FalseClass
Type::Instance.new(Type::Builtin[:false])
when ::Array
base_ty = Type::Instance.new(Type::Builtin[:ary])
lead_tys = obj.map {|arg| guess_literal_type(arg) }
Type::Array.new(Type::Array::Elements.new(lead_tys), base_ty)
when ::Hash
Type.gen_hash do |h|
obj.each do |k, v|
k_ty = guess_literal_type(k).globalize(nil, {}, Config.current.options[:type_depth_limit])
v_ty = guess_literal_type(v)
h[k_ty] = v_ty
end
end
when ::String
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:str]))
when ::Regexp
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:regexp]))
when ::NilClass
Type.nil
when ::Range
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:range]))
when ::Encoding
Type::Literal.new(obj, Type::Instance.new(Type::Builtin[:encoding]))
else
raise "unknown object: #{ obj.inspect }"
end
end
# File typeprof-0.21.2/lib/typeprof/type.rb, line 439
def self.nil
Thread.current[:nil] ||= Instance.new(Type::Builtin[:nil])
end
# File typeprof-0.21.2/lib/typeprof/type.rb, line 443
def self.optional(ty)
ty.union(Type.nil)
end
# File typeprof-0.21.2/lib/typeprof/lsp.rb, line 6
def self.start_lsp_server(config)
if config.lsp_options[:stdio]
reader = LSP::Reader.new($stdin)
writer = LSP::Writer.new($stdout)
# pipe all builtin print output to stderr to avoid conflicting with lsp
$stdout = $stderr
TypeProf::LSP::Server.new(config, reader, writer).run
else
Socket.tcp_server_sockets("localhost", config.lsp_options[:port]) do |servs|
serv = servs[0].local_address
$stdout << JSON.generate({
host: serv.ip_address,
port: serv.ip_port,
pid: $$,
})
$stdout.flush
$stdout = $stderr
Socket.accept_loop(servs) do |sock|
sock.set_encoding("UTF-8")
begin
reader = LSP::Reader.new(sock)
writer = LSP::Writer.new(sock)
TypeProf::LSP::Server.new(config, reader, writer).run
ensure
sock.close
end
exit
end
end
end
end
# File typeprof-0.21.2/lib/typeprof/config.rb, line 148
def self.starting_state(iseq)
cref = CRef.new(:bottom, Type::Builtin[:obj], false) # object
recv = Type::Instance.new(Type::Builtin[:obj])
ctx = Context.new(iseq, cref, nil)
ep = ExecutionPoint.new(ctx, 0, nil)
locals = [Type.nil] * iseq.locals.size
env = Env.new(StaticEnv.new(recv, Type.nil, false, false), locals, [], Utils::HashWrapper.new({}))
return ep, env
end
# File typeprof-0.21.2/lib/typeprof/lsp.rb, line 275
def analyze(uri, text, cancel_token: nil, signature_help_loc: nil)
config = @server.typeprof_config.dup
path = URI(uri).path
config.rb_files = [[path, text]]
config.rbs_files = ["typeprof.rbs"] # XXX
config.verbose = 0
config.max_sec = 1
config.options[:show_errors] = true
config.options[:show_indicator] = false
config.options[:lsp] = true
config.options[:signature_help_loc] = [path, signature_help_loc] if signature_help_loc
TypeProf.analyze(config, cancel_token)
rescue SyntaxError
end
# File typeprof-0.21.2/lib/typeprof/iseq.rb, line 71
def code_range_from_node(node)
CodeRange.new(
CodeLocation.new(node.first_lineno, node.first_column),
CodeLocation.new(node.last_lineno, node.last_column),
)
end
# File typeprof-0.21.2/lib/typeprof/code-range.rb, line 82
def contain?(other)
@first <= other.first && other.last <= @last
end
# File typeprof-0.21.2/lib/typeprof/code-range.rb, line 78
def contain_loc?(loc)
@first <= loc && loc < @last
end
# File typeprof-0.21.2/lib/typeprof/iseq.rb, line 78
def find_node_by_id(node, id)
node = RubyVM::AbstractSyntaxTree.parse(node) if node.is_a?(String)
return node if id == node.node_id
node.children.each do |child|
if child.is_a?(RubyVM::AbstractSyntaxTree::Node)
ret = find_node_by_id(child, id)
return ret if ret
end
end
nil
end
# File typeprof-0.21.2/lib/typeprof/type.rb, line 161
def generate_substitution
{}
end
# File typeprof-0.21.2/lib/typeprof/type.rb, line 174
def include_untyped?(_scratch)
false
end
# File typeprof-0.21.2/lib/typeprof/lsp.rb, line 295
def on_text_changed
cancel_token = AnalysisToken.new
@last_analysis_cancel_token&.cancel
@last_analysis_cancel_token = cancel_token
uri = @uri
text = @text
self.push_analysis_queue do
if cancel_token.cancelled?
next
end
res, def_table, caller_table = self.analyze(uri, text, cancel_token: cancel_token)
unless cancel_token.cancelled?
on_text_changed_analysis(res, def_table, caller_table)
end
end
end
# File typeprof-0.21.2/lib/typeprof/lsp.rb, line 313
def on_text_changed_analysis(res, definition_table, caller_table)
@definition_table = definition_table
@caller_table = caller_table
return unless res
@sigs = []
res[:sigs].each do |file, lineno, sig_str, rbs_code_range, class_kind, class_name|
uri0 = "file://" + file
if @uri == uri0
command = { title: sig_str }
if rbs_code_range
command[:command] = "typeprof.jumpToRBS"
command[:arguments] = [uri0, { line: lineno - 1, character: 0 }, @server.root_uri + "/" + rbs_code_range[0], rbs_code_range[1].to_lsp]
else
command[:command] = "typeprof.createPrototypeRBS"
command[:arguments] = [class_kind, class_name, sig_str]
end
@sigs << {
range: {
start: { line: lineno - 1, character: 0 },
end: { line: lineno - 1, character: 1 },
},
command: command,
}
end
end
# File typeprof-0.21.2/lib/typeprof/code-range.rb, line 86
def overlap?(other)
if @first <= other.first
return @last > other.first
else
return @first < other.last
end
end
# File typeprof-0.21.2/lib/typeprof/lsp.rb, line 291
def push_analysis_queue(&work)
@analysis_queue.push(work)
end
# File typeprof-0.21.2/lib/typeprof/type.rb, line 170
def remove_type_vars
substitute(DummySubstitution, Config.current.options[:type_depth_limit])
end
# File typeprof-0.21.2/lib/typeprof/lsp.rb, line 248
def signature_help(loc, trigger_kind)
loc = CodeLocation.from_lsp(loc)
res, = analyze(@uri, @text, signature_help_loc: loc)
if res
res[:signature_help].filter_map do |sig_str, sig_help, node_id|
node = ISeq.find_node_by_id(@text, node_id)
if node && ISeq.code_range_from_node(node).contain_loc?(loc)
idx = locate_arg_index_in_signature_help(node, loc, sig_help)
{
label: sig_str,
parameters: sig_help.values.map do |r|
{
label: [r.begin, r.end],
}
end,
activeParameter: idx,
}
end
end
else
nil
end
end