# 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