# frozen_string_literal: truemoduleRBSmoduleTestclassTesterattr_reader:envattr_reader:targetsattr_reader:instance_testersattr_reader:singleton_testersdefinitialize(env:)@env=env@targets=[]@instance_testers={}@singleton_testers={}enddeffactory@factory||=Factory.newenddefbuilder@builder||=DefinitionBuilder.new(env: env)enddefskip_method?(type_name,method)ifmethod.implemented_in==type_nameifmethod.annotations.any?{|a|a.string=="rbs:test:skip"}:skipelsefalseendelseifmethod.annotations.any?{|a|a.string=="rbs:test:target"}falseelse:implemented_inendendenddefinstall!(klass,sample_size:,unchecked_classes:)RBS.logger.info{"Installing runtime type checker in #{klass}..."}type_name=factory.type_name(klass.name).absolute!builder.build_instance(type_name).tapdo|definition|instance_key=new_key(type_name,"InstanceChecker")tester,set=instance_testers[klass]||=[MethodCallTester.new(klass,builder,definition,kind: :instance,sample_size: sample_size,unchecked_classes: unchecked_classes),Set[]]Observer.register(instance_key,tester)definition.methods.eachdo|name,method|ifreason=skip_method?(type_name,method)unlessreason==:implemented_inRBS.logger.info{"Skipping ##{name} because of `#{reason}`..."}endelseif!set.include?(name)&&(name==:initialize||klass.instance_methods(false).include?(name)||klass.private_instance_methods(false).include?(name))RBS.logger.info{"Setting up method hook in ##{name}..."}Hook.hook_instance_methodklass,name,key: instance_keyset<<nameendendendendbuilder.build_singleton(type_name).tapdo|definition|singleton_key=new_key(type_name,"SingletonChecker")tester,set=singleton_testers[klass]||=[MethodCallTester.new(klass.singleton_class,builder,definition,kind: :singleton,sample_size: sample_size,unchecked_classes: unchecked_classes),Set[]]Observer.register(singleton_key,tester)definition.methods.eachdo|name,method|ifreason=skip_method?(type_name,method)unlessreason==:implemented_inRBS.logger.info{"Skipping .#{name} because of `#{reason}`..."}endelseifklass.methods(false).include?(name)&&!set.include?(name)RBS.logger.info{"Setting up method hook in .#{name}..."}Hook.hook_singleton_methodklass,name,key: singleton_keyset<<nameendendendendtargets<<klassenddefnew_key(type_name,prefix)"#{prefix}__#{type_name}__#{SecureRandom.hex(10)}"endclassTypeError<Exceptionattr_reader:errorsdefinitialize(errors)@errors=errorssuper"TypeError: #{errors.map{|e|Errors.to_string(e)}.join(",")}"endendclassMethodCallTesterattr_reader:self_classattr_reader:definitionattr_reader:builderattr_reader:kindattr_reader:sample_sizeattr_reader:unchecked_classesdefinitialize(self_class,builder,definition,kind:,sample_size:,unchecked_classes:)@self_class=self_class@definition=definition@builder=builder@kind=kind@sample_size=sample_size@unchecked_classes=unchecked_classesenddefenvbuilder.envenddefcheck@check||=TypeCheck.new(self_class: self_class,builder: builder,sample_size: sample_size,unchecked_classes: unchecked_classes)enddefformat_method_name(name)casekindwhen:instance"##{name}"when:singleton".#{name}"endenddefcall(receiver,trace)method_name=trace.method_namemethod=definition.methods[method_name]ifmethodRBS.logger.debug{"Type checking `#{self_class}#{format_method_name(method_name)}`..."}errors=check.overloaded_call(method,format_method_name(method_name),trace,errors: [])iferrors.empty?RBS.logger.debug{"No type error detected 👏"}elseRBS.logger.debug{"Detected type error 🚨"}raiseTypeError.new(errors)endelseRBS.logger.error{"Type checking `#{self_class}#{method_name}` call but no method found in definition"}endendendendendend