Steepコードリーディング(7日目)
7日目とか書いておきながら本日は15日です。まーじでコード読む時間が確保できない...。
今回は型検査してそうなservice.typecheck_source
を追っていく。
Steep::Services::TypeCheckService
Steep::Services::TypeCheckService#typecheck_source
signature_services
を見にいく必要がありそうだが、最終的にfile.diagnostics
で型検査をしていそうな雰囲気。
def typecheck_source(path:, target:)
return unless target
Steep.logger.tagged "#typecheck_source(path=#{path})" do
Steep.measure "typecheck" do
signature_service = signature_services[target.name]
subtyping = signature_service.current_subtyping
if subtyping
text = source_files[path].content
file = type_check_file(target: target, subtyping: subtyping, path: path, text: text) { signature_service.latest_constant_resolver }
source_files[path] = file
file.diagnostics
end
end
end
end
signature_services
はinitialize
で作られている。
Steep::Services::TypeCheckService#initialize
target
毎にSignatureService.load_from(loader)
を保持したHashが入っている。loaderってのが型ファイルを取得するのに必要な情報を持っていそうだが、なんだろう?
def initialize(project:)
@project = project
@source_files = {}
@signature_services = project.targets.each.with_object({}) do |target, hash| #$ Hash[Symbol, SignatureService]
loader = Project::Target.construct_env_loader(options: target.options, project: project)
hash[target.name] = SignatureService.load_from(loader)
end
@signature_validation_diagnostics = project.targets.each.with_object({}) do |target, hash| #$ Hash[Symbol, Hash[Pathname, Array[Diagnostic::Signature::Base]]]
hash[target.name] = {}
end
end
Steep::Project::Target
Steep::Project::Target#.construct_env_loader
RBS::EnvironmentLoader
を返している。RBS::EnvironmentLoader
ってなんだろう...。ruby/rbsのコードを見に行ってみたけどよくわからず、なんとなく利用しているgemとそのバージョンを管理しているオブジェクトかなーと予想。
def self.construct_env_loader(options:, project:)
repo = RBS::Repository.new(no_stdlib: options.paths.customized_stdlib?)
if options.paths.stdlib_root
repo.add(project.absolute_path(options.paths.stdlib_root))
end
options.paths.repo_paths.each do |path|
repo.add(project.absolute_path(path))
end
core_root_path =
if options.paths.customized_core?
if options.paths.core_root
project.absolute_path(options.paths.core_root)
end
else
RBS::EnvironmentLoader::DEFAULT_CORE_ROOT
end
loader = RBS::EnvironmentLoader.new(core_root: core_root_path, repository: repo)
options.libraries.each do |lib|
name, version = lib.split(/:/, 2)
name or raise
loader.add(library: name, version: version)
end
loader.add_collection(options.collection_lock) if options.collection_lock
loader
end
Steep::SignatureService
Steep::SignatureService#.load_from
うーん、これはRBSのコードリーディングも必要そうだなぁ。
サッと読んでみたけど、これも利用しているgemの型にまつわる何かのようで、ただ具体的に何みたいなのはちょっとよくわからなかった。最終的には自身のインスタンスを返している。
def self.load_from(loader)
env = RBS::Environment.from_loader(loader).resolve_type_names
new(env: env)
end
Steep::SignatureService#current_subtyping
status
がLoadedStatus
オブジェクトであればstatus.subtyping
を返し、そうでなければnil
を返している。
このあたりで完全に置いてけぼりになっている感覚を感じる。
status
はinitialize
でLoadedStatus
オブジェクトが代入されており、それ以外ではSyntaxErrorStatus
が再代入されている箇所がある。つまり、SyntaxErrorが起きているようなものでない限りstatus.subtyping
を返すというものみたい。
def current_subtyping
if status.is_a?(LoadedStatus)
status.subtyping
end
end
Steep::SignatureService::LoadedStatus#subtyping
builder
とはRBS::DefinitionBuilder.new(env: env)
←これ。
抽象構文木が出てきたのでいよいよ型検査という雰囲気が出てきた。
def subtyping
@subtyping ||= begin
factory = AST::Types::Factory.new(builder: builder)
interface_builder = Interface::Builder.new(factory)
Subtyping::Check.new(builder: interface_builder)
end
end
大部分でよくわからないまま進んでおり、かなり置いてけぼり感を感じているがちゃんと目的は果たせるのだろうか...。