rbs 0.6.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
 - data/CHANGELOG.md +32 -0
 - data/lib/rbs/ast/comment.rb +6 -0
 - data/lib/rbs/cli.rb +21 -3
 - data/lib/rbs/constant_table.rb +1 -1
 - data/lib/rbs/definition.rb +4 -2
 - data/lib/rbs/definition_builder.rb +264 -96
 - data/lib/rbs/environment.rb +1 -1
 - data/lib/rbs/errors.rb +25 -0
 - data/lib/rbs/location.rb +15 -0
 - data/lib/rbs/parser.y +29 -17
 - data/lib/rbs/prototype/rb.rb +1 -1
 - data/lib/rbs/substitution.rb +6 -2
 - data/lib/rbs/test.rb +1 -0
 - data/lib/rbs/test/errors.rb +5 -1
 - data/lib/rbs/test/hook.rb +1 -0
 - data/lib/rbs/test/setup.rb +33 -15
 - data/lib/rbs/test/setup_helper.rb +29 -0
 - data/lib/rbs/test/tester.rb +59 -15
 - data/lib/rbs/test/type_check.rb +32 -16
 - data/lib/rbs/validator.rb +4 -0
 - data/lib/rbs/version.rb +1 -1
 - data/lib/rbs/writer.rb +7 -2
 - data/stdlib/builtin/enumerable.rbs +2 -2
 - data/stdlib/json/json.rbs +6 -0
 - data/stdlib/pty/pty.rbs +159 -0
 - data/stdlib/zlib/zlib.rbs +392 -0
 - metadata +5 -2
 
    
        data/lib/rbs/environment.rb
    CHANGED
    
    | 
         @@ -126,7 +126,7 @@ module RBS 
     | 
|
| 
       126 
126 
     | 
    
         | 
| 
       127 
127 
     | 
    
         
             
                def cache_name(cache, name:, decl:, outer:)
         
     | 
| 
       128 
128 
     | 
    
         
             
                  if cache.key?(name)
         
     | 
| 
       129 
     | 
    
         
            -
                    raise DuplicatedDeclarationError.new(name, decl, cache[name])
         
     | 
| 
      
 129 
     | 
    
         
            +
                    raise DuplicatedDeclarationError.new(name, decl, cache[name].decl)
         
     | 
| 
       130 
130 
     | 
    
         
             
                  end
         
     | 
| 
       131 
131 
     | 
    
         | 
| 
       132 
132 
     | 
    
         
             
                  cache[name] = SingleEntry.new(name: name, decl: decl, outer: outer)
         
     | 
    
        data/lib/rbs/errors.rb
    CHANGED
    
    | 
         @@ -141,6 +141,31 @@ module RBS 
     | 
|
| 
       141 
141 
     | 
    
         
             
                end
         
     | 
| 
       142 
142 
     | 
    
         
             
              end
         
     | 
| 
       143 
143 
     | 
    
         | 
| 
      
 144 
     | 
    
         
            +
              class NoSelfTypeFoundError < StandardError
         
     | 
| 
      
 145 
     | 
    
         
            +
                attr_reader :type_name
         
     | 
| 
      
 146 
     | 
    
         
            +
                attr_reader :location
         
     | 
| 
      
 147 
     | 
    
         
            +
             
     | 
| 
      
 148 
     | 
    
         
            +
                def initialize(type_name:, location:)
         
     | 
| 
      
 149 
     | 
    
         
            +
                  @type_name = type_name
         
     | 
| 
      
 150 
     | 
    
         
            +
                  @location = location
         
     | 
| 
      
 151 
     | 
    
         
            +
             
     | 
| 
      
 152 
     | 
    
         
            +
                  super "#{Location.to_string location}: Could not find self type: #{type_name}"
         
     | 
| 
      
 153 
     | 
    
         
            +
                end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                def self.check!(self_type, env:)
         
     | 
| 
      
 156 
     | 
    
         
            +
                  type_name = self_type.name
         
     | 
| 
      
 157 
     | 
    
         
            +
             
     | 
| 
      
 158 
     | 
    
         
            +
                  dic = case
         
     | 
| 
      
 159 
     | 
    
         
            +
                        when type_name.class?
         
     | 
| 
      
 160 
     | 
    
         
            +
                          env.class_decls
         
     | 
| 
      
 161 
     | 
    
         
            +
                        when type_name.interface?
         
     | 
| 
      
 162 
     | 
    
         
            +
                          env.interface_decls
         
     | 
| 
      
 163 
     | 
    
         
            +
                        end
         
     | 
| 
      
 164 
     | 
    
         
            +
             
     | 
| 
      
 165 
     | 
    
         
            +
                  dic.key?(type_name) or raise new(type_name: type_name, location: self_type.location)
         
     | 
| 
      
 166 
     | 
    
         
            +
                end
         
     | 
| 
      
 167 
     | 
    
         
            +
              end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
       144 
169 
     | 
    
         
             
              class NoMixinFoundError < StandardError
         
     | 
| 
       145 
170 
     | 
    
         
             
                attr_reader :type_name
         
     | 
| 
       146 
171 
     | 
    
         
             
                attr_reader :member
         
     | 
    
        data/lib/rbs/location.rb
    CHANGED
    
    | 
         @@ -77,6 +77,21 @@ module RBS 
     | 
|
| 
       77 
77 
     | 
    
         
             
                  locations.inject {|l1, l2| l1 + l2 }
         
     | 
| 
       78 
78 
     | 
    
         
             
                end
         
     | 
| 
       79 
79 
     | 
    
         | 
| 
      
 80 
     | 
    
         
            +
                def concat(*others)
         
     | 
| 
      
 81 
     | 
    
         
            +
                  others.each { |other| self << other }
         
     | 
| 
      
 82 
     | 
    
         
            +
                  self
         
     | 
| 
      
 83 
     | 
    
         
            +
                end
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                def <<(other)
         
     | 
| 
      
 86 
     | 
    
         
            +
                  if other
         
     | 
| 
      
 87 
     | 
    
         
            +
                    raise "Invalid concat: buffer=#{buffer.name}, other.buffer=#{other.buffer.name}" unless other.buffer == buffer
         
     | 
| 
      
 88 
     | 
    
         
            +
                    @end_pos = other.end_pos
         
     | 
| 
      
 89 
     | 
    
         
            +
                    @source = nil
         
     | 
| 
      
 90 
     | 
    
         
            +
                    @end_loc = nil
         
     | 
| 
      
 91 
     | 
    
         
            +
                  end
         
     | 
| 
      
 92 
     | 
    
         
            +
                  self
         
     | 
| 
      
 93 
     | 
    
         
            +
                end
         
     | 
| 
      
 94 
     | 
    
         
            +
             
     | 
| 
       80 
95 
     | 
    
         
             
                def pred?(loc)
         
     | 
| 
       81 
96 
     | 
    
         
             
                  loc.is_a?(Location) &&
         
     | 
| 
       82 
97 
     | 
    
         
             
                    loc.name == name &&
         
     | 
    
        data/lib/rbs/parser.y
    CHANGED
    
    | 
         @@ -4,9 +4,9 @@ class RBS::Parser 
     | 
|
| 
       4 
4 
     | 
    
         
             
                    tANNOTATION
         
     | 
| 
       5 
5 
     | 
    
         
             
                    tSTRING tSYMBOL tINTEGER tWRITE_ATTR
         
     | 
| 
       6 
6 
     | 
    
         
             
                    kLPAREN kRPAREN kLBRACKET kRBRACKET kLBRACE kRBRACE
         
     | 
| 
       7 
     | 
    
         
            -
                    kVOID kNIL kTRUE kFALSE kANY kUNTYPED kTOP kBOT kSELF kSELFQ kINSTANCE kCLASS kBOOL kSINGLETON kTYPE kDEF kMODULE 
     | 
| 
      
 7 
     | 
    
         
            +
                    kVOID kNIL kTRUE kFALSE kANY kUNTYPED kTOP kBOT kSELF kSELFQ kINSTANCE kCLASS kBOOL kSINGLETON kTYPE kDEF kMODULE
         
     | 
| 
       8 
8 
     | 
    
         
             
                    kPRIVATE kPUBLIC kALIAS
         
     | 
| 
       9 
     | 
    
         
            -
                    kCOLON kCOLON2 kCOMMA kBAR kAMP kHAT kARROW kQUESTION kEXCLAMATION kSTAR kSTAR2 kFATARROW kEQ kDOT kLT
         
     | 
| 
      
 9 
     | 
    
         
            +
                    kCOLON kCOLON2 kCOMMA kBAR kAMP kHAT kARROW kQUESTION kEXCLAMATION kSTAR kSTAR2 kFATARROW kEQ kDOT kDOT3 kLT
         
     | 
| 
       10 
10 
     | 
    
         
             
                    kINTERFACE kEND kINCLUDE kEXTEND kATTRREADER kATTRWRITER kATTRACCESSOR tOPERATOR tQUOTEDMETHOD tQUOTEDIDENT
         
     | 
| 
       11 
11 
     | 
    
         
             
                    kPREPEND kEXTENSION kINCOMPATIBLE
         
     | 
| 
       12 
12 
     | 
    
         
             
                    type_TYPE type_SIGNATURE type_METHODTYPE tEOF
         
     | 
| 
         @@ -425,7 +425,12 @@ rule 
     | 
|
| 
       425 
425 
     | 
    
         
             
                                                  comment: leading_comment(val[0].first&.location || location))
         
     | 
| 
       426 
426 
     | 
    
         
             
                  }
         
     | 
| 
       427 
427 
     | 
    
         | 
| 
       428 
     | 
    
         
            -
              overload: 
     | 
| 
      
 428 
     | 
    
         
            +
              overload:
         
     | 
| 
      
 429 
     | 
    
         
            +
                    { result = nil }
         
     | 
| 
      
 430 
     | 
    
         
            +
                | kOVERLOAD {
         
     | 
| 
      
 431 
     | 
    
         
            +
                    RBS.logger.warn "`overload def` syntax is deprecated. Use `...` syntax instead."
         
     | 
| 
      
 432 
     | 
    
         
            +
                    result = val[0]
         
     | 
| 
      
 433 
     | 
    
         
            +
                  }
         
     | 
| 
       429 
434 
     | 
    
         | 
| 
       430 
435 
     | 
    
         
             
              method_member:
         
     | 
| 
       431 
436 
     | 
    
         
             
                  annotations attributes overload kDEF method_kind def_name method_types {
         
     | 
| 
         @@ -438,15 +443,24 @@ rule 
     | 
|
| 
       438 
443 
     | 
    
         
             
                        type
         
     | 
| 
       439 
444 
     | 
    
         
             
                      end
         
     | 
| 
       440 
445 
     | 
    
         
             
                    end
         
     | 
| 
      
 446 
     | 
    
         
            +
             
     | 
| 
      
 447 
     | 
    
         
            +
                    last_type = val[6].last
         
     | 
| 
      
 448 
     | 
    
         
            +
                    if last_type.is_a?(LocatedValue) && last_type.value == :dot3
         
     | 
| 
      
 449 
     | 
    
         
            +
                      overload = true
         
     | 
| 
      
 450 
     | 
    
         
            +
                      val[6].pop
         
     | 
| 
      
 451 
     | 
    
         
            +
                    else
         
     | 
| 
      
 452 
     | 
    
         
            +
                      overload = false
         
     | 
| 
      
 453 
     | 
    
         
            +
                    end
         
     | 
| 
      
 454 
     | 
    
         
            +
             
     | 
| 
       441 
455 
     | 
    
         
             
                    result = Members::MethodDefinition.new(
         
     | 
| 
       442 
456 
     | 
    
         
             
                      name: val[5].value,
         
     | 
| 
       443 
457 
     | 
    
         
             
                      kind: val[4],
         
     | 
| 
       444 
     | 
    
         
            -
                      types:  
     | 
| 
      
 458 
     | 
    
         
            +
                      types: val[6],
         
     | 
| 
       445 
459 
     | 
    
         
             
                      annotations: val[0],
         
     | 
| 
       446 
460 
     | 
    
         
             
                      location: location,
         
     | 
| 
       447 
461 
     | 
    
         
             
                      comment: leading_comment(val[0].first&.location || val[1].first&.location || val[2]&.location || val[3].location),
         
     | 
| 
       448 
462 
     | 
    
         
             
                      attributes: val[1].map(&:value),
         
     | 
| 
       449 
     | 
    
         
            -
                      overload: !!val[2]
         
     | 
| 
      
 463 
     | 
    
         
            +
                      overload: overload || !!val[2]
         
     | 
| 
       450 
464 
     | 
    
         
             
                    )
         
     | 
| 
       451 
465 
     | 
    
         
             
                  }
         
     | 
| 
       452 
466 
     | 
    
         | 
| 
         @@ -463,7 +477,7 @@ rule 
     | 
|
| 
       463 
477 
     | 
    
         | 
| 
       464 
478 
     | 
    
         
             
              method_types:
         
     | 
| 
       465 
479 
     | 
    
         
             
                  method_type { result = [val[0]] }
         
     | 
| 
       466 
     | 
    
         
            -
                |  
     | 
| 
      
 480 
     | 
    
         
            +
                | kDOT3 { result = [LocatedValue.new(value: :dot3, location: val[0].location)] }
         
     | 
| 
       467 
481 
     | 
    
         
             
                | method_type kBAR method_types {
         
     | 
| 
       468 
482 
     | 
    
         
             
                    result = val[2].unshift(val[0])
         
     | 
| 
       469 
483 
     | 
    
         
             
                  }
         
     | 
| 
         @@ -520,7 +534,7 @@ rule 
     | 
|
| 
       520 
534 
     | 
    
         | 
| 
       521 
535 
     | 
    
         
             
              method_name:
         
     | 
| 
       522 
536 
     | 
    
         
             
                  tOPERATOR
         
     | 
| 
       523 
     | 
    
         
            -
                | kAMP | kHAT | kSTAR | kLT | kEXCLAMATION | kSTAR2 | kBAR 
     | 
| 
      
 537 
     | 
    
         
            +
                | kAMP | kHAT | kSTAR | kLT | kEXCLAMATION | kSTAR2 | kBAR
         
     | 
| 
       524 
538 
     | 
    
         
             
                | method_name0
         
     | 
| 
       525 
539 
     | 
    
         
             
                | method_name0 kQUESTION {
         
     | 
| 
       526 
540 
     | 
    
         
             
                    unless val[0].location.pred?(val[1].location)
         
     | 
| 
         @@ -548,7 +562,7 @@ rule 
     | 
|
| 
       548 
562 
     | 
    
         
             
                  kCLASS | kVOID | kNIL | kTRUE | kFALSE | kANY | kUNTYPED | kTOP | kBOT | kINSTANCE | kBOOL | kSINGLETON
         
     | 
| 
       549 
563 
     | 
    
         
             
                | kTYPE | kMODULE | kPRIVATE | kPUBLIC | kEND | kINCLUDE | kEXTEND | kPREPEND
         
     | 
| 
       550 
564 
     | 
    
         
             
                | kATTRREADER | kATTRACCESSOR | kATTRWRITER | kDEF | kEXTENSION | kSELF | kINCOMPATIBLE
         
     | 
| 
       551 
     | 
    
         
            -
                | kUNCHECKED | kINTERFACE |  
     | 
| 
      
 565 
     | 
    
         
            +
                | kUNCHECKED | kINTERFACE | kALIAS | kOUT | kIN | kOVERLOAD
         
     | 
| 
       552 
566 
     | 
    
         | 
| 
       553 
567 
     | 
    
         
             
              module_type_params:
         
     | 
| 
       554 
568 
     | 
    
         
             
                  { result = nil }
         
     | 
| 
         @@ -1150,15 +1164,13 @@ def leading_comment(location) 
     | 
|
| 
       1150 
1164 
     | 
    
         
             
            end
         
     | 
| 
       1151 
1165 
     | 
    
         | 
| 
       1152 
1166 
     | 
    
         
             
            def push_comment(string, location)
         
     | 
| 
       1153 
     | 
    
         
            -
               
     | 
| 
       1154 
     | 
    
         
            -
             
     | 
| 
       1155 
     | 
    
         
            -
             
     | 
| 
       1156 
     | 
    
         
            -
             
     | 
| 
       1157 
     | 
    
         
            -
                new_comment = AST::Comment.new(string:  
     | 
| 
       1158 
     | 
    
         
            -
             
     | 
| 
      
 1167 
     | 
    
         
            +
              if (comment = leading_comment(location)) && comment.location.start_column == location.start_column
         
     | 
| 
      
 1168 
     | 
    
         
            +
                comment.concat(string: "#{string}\n", location: location)
         
     | 
| 
      
 1169 
     | 
    
         
            +
                @comments[comment.location.end_line] = comment
         
     | 
| 
      
 1170 
     | 
    
         
            +
              else
         
     | 
| 
      
 1171 
     | 
    
         
            +
                new_comment = AST::Comment.new(string: "#{string}\n", location: location)
         
     | 
| 
      
 1172 
     | 
    
         
            +
                @comments[new_comment.location.end_line] = new_comment
         
     | 
| 
       1159 
1173 
     | 
    
         
             
              end
         
     | 
| 
       1160 
     | 
    
         
            -
             
     | 
| 
       1161 
     | 
    
         
            -
              @comments[new_comment.location.end_line] = new_comment
         
     | 
| 
       1162 
1174 
     | 
    
         
             
            end
         
     | 
| 
       1163 
1175 
     | 
    
         | 
| 
       1164 
1176 
     | 
    
         
             
            def new_token(type, value = input.matched)
         
     | 
| 
         @@ -1218,7 +1230,6 @@ KEYWORDS = { 
     | 
|
| 
       1218 
1230 
     | 
    
         
             
              "attr_reader" => :kATTRREADER,
         
     | 
| 
       1219 
1231 
     | 
    
         
             
              "attr_writer" => :kATTRWRITER,
         
     | 
| 
       1220 
1232 
     | 
    
         
             
              "attr_accessor" => :kATTRACCESSOR,
         
     | 
| 
       1221 
     | 
    
         
            -
              "super" => :kSUPER,
         
     | 
| 
       1222 
1233 
     | 
    
         
             
              "public" => :kPUBLIC,
         
     | 
| 
       1223 
1234 
     | 
    
         
             
              "private" => :kPRIVATE,
         
     | 
| 
       1224 
1235 
     | 
    
         
             
              "alias" => :kALIAS,
         
     | 
| 
         @@ -1267,6 +1278,7 @@ PUNCTS = { 
     | 
|
| 
       1267 
1278 
     | 
    
         
             
              "!" => :kEXCLAMATION,
         
     | 
| 
       1268 
1279 
     | 
    
         
             
              "**" => :kSTAR2,
         
     | 
| 
       1269 
1280 
     | 
    
         
             
              "*" => :kSTAR,
         
     | 
| 
      
 1281 
     | 
    
         
            +
              "..." => :kDOT3,
         
     | 
| 
       1270 
1282 
     | 
    
         
             
              "." => :kDOT,
         
     | 
| 
       1271 
1283 
     | 
    
         
             
              "<" => :kLT,
         
     | 
| 
       1272 
1284 
     | 
    
         
             
              "-@" => :tOPERATOR,
         
     | 
    
        data/lib/rbs/prototype/rb.rb
    CHANGED
    
    
    
        data/lib/rbs/substitution.rb
    CHANGED
    
    | 
         @@ -11,7 +11,7 @@ module RBS 
     | 
|
| 
       11 
11 
     | 
    
         
             
                  mapping[from] = to
         
     | 
| 
       12 
12 
     | 
    
         
             
                end
         
     | 
| 
       13 
13 
     | 
    
         | 
| 
       14 
     | 
    
         
            -
                def self.build(variables, types, instance_type:  
     | 
| 
      
 14 
     | 
    
         
            +
                def self.build(variables, types, instance_type: nil, &block)
         
     | 
| 
       15 
15 
     | 
    
         
             
                  unless variables.size == types.size
         
     | 
| 
       16 
16 
     | 
    
         
             
                    raise "Broken substitution: variables=#{variables}, types=#{types}"
         
     | 
| 
       17 
17 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -33,7 +33,11 @@ module RBS 
     | 
|
| 
       33 
33 
     | 
    
         
             
                  when Types::Variable
         
     | 
| 
       34 
34 
     | 
    
         
             
                    mapping[ty.name] || ty
         
     | 
| 
       35 
35 
     | 
    
         
             
                  when Types::Bases::Instance
         
     | 
| 
       36 
     | 
    
         
            -
                    instance_type
         
     | 
| 
      
 36 
     | 
    
         
            +
                    if instance_type
         
     | 
| 
      
 37 
     | 
    
         
            +
                      instance_type
         
     | 
| 
      
 38 
     | 
    
         
            +
                    else
         
     | 
| 
      
 39 
     | 
    
         
            +
                      ty
         
     | 
| 
      
 40 
     | 
    
         
            +
                    end
         
     | 
| 
       37 
41 
     | 
    
         
             
                  else
         
     | 
| 
       38 
42 
     | 
    
         
             
                    ty
         
     | 
| 
       39 
43 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/rbs/test.rb
    CHANGED
    
    
    
        data/lib/rbs/test/errors.rb
    CHANGED
    
    | 
         @@ -28,7 +28,11 @@ module RBS 
     | 
|
| 
       28 
28 
     | 
    
         
             
                  end
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                  def self.inspect_(obj)
         
     | 
| 
       31 
     | 
    
         
            -
                     
     | 
| 
      
 31 
     | 
    
         
            +
                    if obj.respond_to?(:inspect)
         
     | 
| 
      
 32 
     | 
    
         
            +
                      obj.inspect
         
     | 
| 
      
 33 
     | 
    
         
            +
                    else
         
     | 
| 
      
 34 
     | 
    
         
            +
                      Test::INSPECT.bind(obj).call     # For the case inspect is not defined (like BasicObject)
         
     | 
| 
      
 35 
     | 
    
         
            +
                    end
         
     | 
| 
       32 
36 
     | 
    
         
             
                  end
         
     | 
| 
       33 
37 
     | 
    
         | 
| 
       34 
38 
     | 
    
         
             
                  def self.to_string(error)
         
     | 
    
        data/lib/rbs/test/hook.rb
    CHANGED
    
    
    
        data/lib/rbs/test/setup.rb
    CHANGED
    
    | 
         @@ -1,24 +1,30 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            require "rbs"
         
     | 
| 
       2 
2 
     | 
    
         
             
            require "rbs/test"
         
     | 
| 
       3 
     | 
    
         
            -
             
     | 
| 
       4 
3 
     | 
    
         
             
            require "optparse"
         
     | 
| 
       5 
4 
     | 
    
         
             
            require "shellwords"
         
     | 
| 
       6 
5 
     | 
    
         | 
| 
      
 6 
     | 
    
         
            +
            include RBS::Test::SetupHelper
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
       7 
8 
     | 
    
         
             
            logger = Logger.new(STDERR)
         
     | 
| 
       8 
9 
     | 
    
         | 
| 
       9 
10 
     | 
    
         
             
            begin
         
     | 
| 
       10 
11 
     | 
    
         
             
              opts = Shellwords.shellsplit(ENV["RBS_TEST_OPT"] || "-I sig")
         
     | 
| 
       11 
     | 
    
         
            -
              filter = ENV.fetch( 
     | 
| 
       12 
     | 
    
         
            -
              skips = (ENV[ 
     | 
| 
       13 
     | 
    
         
            -
              sampling = !ENV.key?("RBS_TEST_NO_SAMPLE")
         
     | 
| 
      
 12 
     | 
    
         
            +
              filter = ENV.fetch('RBS_TEST_TARGET', "").split(',').map! { |e| e.strip }
         
     | 
| 
      
 13 
     | 
    
         
            +
              skips = (ENV['RBS_TEST_SKIP'] || '').split(',').map! { |e| e.strip }
         
     | 
| 
       14 
14 
     | 
    
         
             
              RBS.logger_level = (ENV["RBS_TEST_LOGLEVEL"] || "info")
         
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
      
 15 
     | 
    
         
            +
              sample_size = get_sample_size(ENV['RBS_TEST_SAMPLE_SIZE'] || '')
         
     | 
| 
      
 16 
     | 
    
         
            +
            rescue InvalidSampleSizeError => exception
         
     | 
| 
      
 17 
     | 
    
         
            +
              RBS.logger.error exception.message
         
     | 
| 
      
 18 
     | 
    
         
            +
              exit 1
         
     | 
| 
      
 19 
     | 
    
         
            +
            end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
            if filter.empty?
         
     | 
| 
       16 
22 
     | 
    
         
             
              STDERR.puts "rbs/test/setup handles the following environment variables:"
         
     | 
| 
       17 
23 
     | 
    
         
             
              STDERR.puts "  [REQUIRED] RBS_TEST_TARGET: test target class name, `Foo::Bar,Foo::Baz` for each class or `Foo::*` for all classes under `Foo`"
         
     | 
| 
       18 
24 
     | 
    
         
             
              STDERR.puts "  [OPTIONAL] RBS_TEST_SKIP: skip testing classes"
         
     | 
| 
       19 
25 
     | 
    
         
             
              STDERR.puts "  [OPTIONAL] RBS_TEST_OPT: options for signatures (`-r` for libraries or `-I` for signatures)"
         
     | 
| 
       20 
26 
     | 
    
         
             
              STDERR.puts "  [OPTIONAL] RBS_TEST_LOGLEVEL: one of debug|info|warn|error|fatal (defaults to info)"
         
     | 
| 
       21 
     | 
    
         
            -
              STDERR.puts "  [OPTIONAL]  
     | 
| 
      
 27 
     | 
    
         
            +
              STDERR.puts "  [OPTIONAL] RBS_TEST_SAMPLE_SIZE: sets the amount of values in a collection to be type-checked (Set to `ALL` to type check all the values)"
         
     | 
| 
       22 
28 
     | 
    
         
             
              exit 1
         
     | 
| 
       23 
29 
     | 
    
         
             
            end
         
     | 
| 
       24 
30 
     | 
    
         | 
| 
         @@ -32,26 +38,38 @@ env = RBS::Environment.from_loader(loader).resolve_type_names 
     | 
|
| 
       32 
38 
     | 
    
         | 
| 
       33 
39 
     | 
    
         
             
            def match(filter, name)
         
     | 
| 
       34 
40 
     | 
    
         
             
              if filter.end_with?("*")
         
     | 
| 
       35 
     | 
    
         
            -
                 
     | 
| 
      
 41 
     | 
    
         
            +
                size = filter.size
         
     | 
| 
      
 42 
     | 
    
         
            +
                name.start_with?(filter[0, size - 1]) || name == filter[0, size-3]
         
     | 
| 
       36 
43 
     | 
    
         
             
              else
         
     | 
| 
       37 
44 
     | 
    
         
             
                filter == name
         
     | 
| 
       38 
45 
     | 
    
         
             
              end
         
     | 
| 
       39 
46 
     | 
    
         
             
            end
         
     | 
| 
       40 
47 
     | 
    
         | 
| 
       41 
     | 
    
         
            -
             
     | 
| 
      
 48 
     | 
    
         
            +
            def to_absolute_typename(type_name)
         
     | 
| 
      
 49 
     | 
    
         
            +
              RBS::Factory.new().type_name(type_name).absolute!
         
     | 
| 
      
 50 
     | 
    
         
            +
            end
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
       42 
52 
     | 
    
         
             
            tester = RBS::Test::Tester.new(env: env)
         
     | 
| 
       43 
53 
     | 
    
         | 
| 
      
 54 
     | 
    
         
            +
            module_name = Module.instance_method(:name)
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
       44 
56 
     | 
    
         
             
            TracePoint.trace :end do |tp|
         
     | 
| 
       45 
     | 
    
         
            -
              class_name = tp.self. 
     | 
| 
      
 57 
     | 
    
         
            +
              class_name = module_name.bind(tp.self).call&.yield_self {|name| to_absolute_typename name }
         
     | 
| 
       46 
58 
     | 
    
         | 
| 
       47 
59 
     | 
    
         
             
              if class_name
         
     | 
| 
       48 
     | 
    
         
            -
                if filter.any? {|f| match(f, class_name.to_s) } && skips.none? {|f| match(f, class_name.to_s) }
         
     | 
| 
       49 
     | 
    
         
            -
                  if  
     | 
| 
       50 
     | 
    
         
            -
                     
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
                      tester.install!(tp.self, sampling: sampling)
         
     | 
| 
       53 
     | 
    
         
            -
                    end
         
     | 
| 
      
 60 
     | 
    
         
            +
                if filter.any? {|f| match(to_absolute_typename(f).to_s, class_name.to_s) } && skips.none? {|f| match(f, class_name.to_s) }
         
     | 
| 
      
 61 
     | 
    
         
            +
                  if env.class_decls.key?(class_name)
         
     | 
| 
      
 62 
     | 
    
         
            +
                    logger.info "Setting up hooks for #{class_name}"
         
     | 
| 
      
 63 
     | 
    
         
            +
                    tester.install!(tp.self, sample_size: sample_size)
         
     | 
| 
       54 
64 
     | 
    
         
             
                  end
         
     | 
| 
       55 
65 
     | 
    
         
             
                end
         
     | 
| 
       56 
66 
     | 
    
         
             
              end
         
     | 
| 
       57 
67 
     | 
    
         
             
            end
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
            at_exit do
         
     | 
| 
      
 70 
     | 
    
         
            +
              if $!.nil? || $!.is_a?(SystemExit) && $!.success?
         
     | 
| 
      
 71 
     | 
    
         
            +
                if tester.targets.empty?
         
     | 
| 
      
 72 
     | 
    
         
            +
                  logger.debug { "No type checker was installed!" }
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            module RBS
         
     | 
| 
      
 2 
     | 
    
         
            +
              module Test
         
     | 
| 
      
 3 
     | 
    
         
            +
                module SetupHelper
         
     | 
| 
      
 4 
     | 
    
         
            +
                  class InvalidSampleSizeError < StandardError
         
     | 
| 
      
 5 
     | 
    
         
            +
                    attr_reader :string
         
     | 
| 
      
 6 
     | 
    
         
            +
                    
         
     | 
| 
      
 7 
     | 
    
         
            +
                    def initialize(string)
         
     | 
| 
      
 8 
     | 
    
         
            +
                      @string = string
         
     | 
| 
      
 9 
     | 
    
         
            +
                      super("Sample size should be a positive integer: `#{string}`")
         
     | 
| 
      
 10 
     | 
    
         
            +
                    end
         
     | 
| 
      
 11 
     | 
    
         
            +
                  end
         
     | 
| 
      
 12 
     | 
    
         
            +
                  
         
     | 
| 
      
 13 
     | 
    
         
            +
                  DEFAULT_SAMPLE_SIZE = 100
         
     | 
| 
      
 14 
     | 
    
         
            +
                  
         
     | 
| 
      
 15 
     | 
    
         
            +
                  def get_sample_size(string)
         
     | 
| 
      
 16 
     | 
    
         
            +
                    case string
         
     | 
| 
      
 17 
     | 
    
         
            +
                    when ""
         
     | 
| 
      
 18 
     | 
    
         
            +
                      DEFAULT_SAMPLE_SIZE
         
     | 
| 
      
 19 
     | 
    
         
            +
                    when 'ALL'
         
     | 
| 
      
 20 
     | 
    
         
            +
                      nil
         
     | 
| 
      
 21 
     | 
    
         
            +
                    else
         
     | 
| 
      
 22 
     | 
    
         
            +
                      int_size = string.to_i
         
     | 
| 
      
 23 
     | 
    
         
            +
                      raise InvalidSampleSizeError.new(string) unless int_size.positive?
         
     | 
| 
      
 24 
     | 
    
         
            +
                      int_size
         
     | 
| 
      
 25 
     | 
    
         
            +
                    end
         
     | 
| 
      
 26 
     | 
    
         
            +
                  end
         
     | 
| 
      
 27 
     | 
    
         
            +
                end
         
     | 
| 
      
 28 
     | 
    
         
            +
              end
         
     | 
| 
      
 29 
     | 
    
         
            +
            end
         
     | 
    
        data/lib/rbs/test/tester.rb
    CHANGED
    
    | 
         @@ -2,11 +2,15 @@ module RBS 
     | 
|
| 
       2 
2 
     | 
    
         
             
              module Test
         
     | 
| 
       3 
3 
     | 
    
         
             
                class Tester
         
     | 
| 
       4 
4 
     | 
    
         
             
                  attr_reader :env
         
     | 
| 
       5 
     | 
    
         
            -
                  attr_reader : 
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_reader :targets
         
     | 
| 
      
 6 
     | 
    
         
            +
                  attr_reader :instance_testers
         
     | 
| 
      
 7 
     | 
    
         
            +
                  attr_reader :singleton_testers
         
     | 
| 
       6 
8 
     | 
    
         | 
| 
       7 
9 
     | 
    
         
             
                  def initialize(env:)
         
     | 
| 
       8 
10 
     | 
    
         
             
                    @env = env
         
     | 
| 
       9 
     | 
    
         
            -
                    @ 
     | 
| 
      
 11 
     | 
    
         
            +
                    @targets = []
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @instance_testers = {}
         
     | 
| 
      
 13 
     | 
    
         
            +
                    @singleton_testers = {}
         
     | 
| 
       10 
14 
     | 
    
         
             
                  end
         
     | 
| 
       11 
15 
     | 
    
         | 
| 
       12 
16 
     | 
    
         
             
                  def factory
         
     | 
| 
         @@ -17,34 +21,74 @@ module RBS 
     | 
|
| 
       17 
21 
     | 
    
         
             
                    @builder ||= DefinitionBuilder.new(env: env)
         
     | 
| 
       18 
22 
     | 
    
         
             
                  end
         
     | 
| 
       19 
23 
     | 
    
         | 
| 
       20 
     | 
    
         
            -
                  def  
     | 
| 
      
 24 
     | 
    
         
            +
                  def skip_method?(type_name, method)
         
     | 
| 
      
 25 
     | 
    
         
            +
                    if method.implemented_in == type_name
         
     | 
| 
      
 26 
     | 
    
         
            +
                      if method.annotations.any? {|a| a.string == "rbs:test:skip" }
         
     | 
| 
      
 27 
     | 
    
         
            +
                        :skip
         
     | 
| 
      
 28 
     | 
    
         
            +
                      else
         
     | 
| 
      
 29 
     | 
    
         
            +
                        false
         
     | 
| 
      
 30 
     | 
    
         
            +
                      end
         
     | 
| 
      
 31 
     | 
    
         
            +
                    else
         
     | 
| 
      
 32 
     | 
    
         
            +
                      if method.annotations.any? {|a| a.string == "rbs:test:target" }
         
     | 
| 
      
 33 
     | 
    
         
            +
                        false
         
     | 
| 
      
 34 
     | 
    
         
            +
                      else
         
     | 
| 
      
 35 
     | 
    
         
            +
                        :implemented_in
         
     | 
| 
      
 36 
     | 
    
         
            +
                      end
         
     | 
| 
      
 37 
     | 
    
         
            +
                    end
         
     | 
| 
      
 38 
     | 
    
         
            +
                  end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                  def install!(klass, sample_size:)
         
     | 
| 
       21 
41 
     | 
    
         
             
                    RBS.logger.info { "Installing runtime type checker in #{klass}..." }
         
     | 
| 
       22 
42 
     | 
    
         | 
| 
       23 
43 
     | 
    
         
             
                    type_name = factory.type_name(klass.name).absolute!
         
     | 
| 
       24 
44 
     | 
    
         | 
| 
       25 
45 
     | 
    
         
             
                    builder.build_instance(type_name).tap do |definition|
         
     | 
| 
       26 
46 
     | 
    
         
             
                      instance_key = new_key(type_name, "InstanceChecker")
         
     | 
| 
       27 
     | 
    
         
            -
                       
     | 
| 
      
 47 
     | 
    
         
            +
                      tester, set = instance_testers[klass] ||= [
         
     | 
| 
      
 48 
     | 
    
         
            +
                        MethodCallTester.new(klass, builder, definition, kind: :instance, sample_size: sample_size),
         
     | 
| 
      
 49 
     | 
    
         
            +
                        Set[]
         
     | 
| 
      
 50 
     | 
    
         
            +
                      ]
         
     | 
| 
      
 51 
     | 
    
         
            +
                      Observer.register(instance_key, tester)
         
     | 
| 
       28 
52 
     | 
    
         | 
| 
       29 
53 
     | 
    
         
             
                      definition.methods.each do |name, method|
         
     | 
| 
       30 
     | 
    
         
            -
                        if  
     | 
| 
       31 
     | 
    
         
            -
                           
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
      
 54 
     | 
    
         
            +
                        if reason = skip_method?(type_name, method)
         
     | 
| 
      
 55 
     | 
    
         
            +
                          unless reason == :implemented_in
         
     | 
| 
      
 56 
     | 
    
         
            +
                            RBS.logger.info { "Skipping ##{name} because of `#{reason}`..." }
         
     | 
| 
      
 57 
     | 
    
         
            +
                          end
         
     | 
| 
      
 58 
     | 
    
         
            +
                        else
         
     | 
| 
      
 59 
     | 
    
         
            +
                          if klass.instance_methods(false).include?(name) && !set.include?(name)
         
     | 
| 
      
 60 
     | 
    
         
            +
                            RBS.logger.info { "Setting up method hook in ##{name}..." }
         
     | 
| 
      
 61 
     | 
    
         
            +
                            Hook.hook_instance_method klass, name, key: instance_key
         
     | 
| 
      
 62 
     | 
    
         
            +
                            set << name
         
     | 
| 
      
 63 
     | 
    
         
            +
                          end
         
     | 
| 
       33 
64 
     | 
    
         
             
                        end
         
     | 
| 
       34 
65 
     | 
    
         
             
                      end
         
     | 
| 
       35 
66 
     | 
    
         
             
                    end
         
     | 
| 
       36 
67 
     | 
    
         | 
| 
       37 
68 
     | 
    
         
             
                    builder.build_singleton(type_name).tap do |definition|
         
     | 
| 
       38 
69 
     | 
    
         
             
                      singleton_key = new_key(type_name, "SingletonChecker")
         
     | 
| 
       39 
     | 
    
         
            -
                       
     | 
| 
      
 70 
     | 
    
         
            +
                      tester, set = singleton_testers[klass] ||= [
         
     | 
| 
      
 71 
     | 
    
         
            +
                        MethodCallTester.new(klass.singleton_class, builder, definition, kind: :singleton, sample_size: sample_size),
         
     | 
| 
      
 72 
     | 
    
         
            +
                        Set[]
         
     | 
| 
      
 73 
     | 
    
         
            +
                      ]
         
     | 
| 
      
 74 
     | 
    
         
            +
                      Observer.register(singleton_key, tester)
         
     | 
| 
       40 
75 
     | 
    
         | 
| 
       41 
76 
     | 
    
         
             
                      definition.methods.each do |name, method|
         
     | 
| 
       42 
     | 
    
         
            -
                        if  
     | 
| 
       43 
     | 
    
         
            -
                           
     | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
      
 77 
     | 
    
         
            +
                        if reason = skip_method?(type_name, method)
         
     | 
| 
      
 78 
     | 
    
         
            +
                          unless reason == :implemented_in
         
     | 
| 
      
 79 
     | 
    
         
            +
                            RBS.logger.info { "Skipping .#{name} because of `#{reason}`..." }
         
     | 
| 
      
 80 
     | 
    
         
            +
                          end
         
     | 
| 
      
 81 
     | 
    
         
            +
                        else
         
     | 
| 
      
 82 
     | 
    
         
            +
                          if klass.methods(false).include?(name) && !set.include?(name)
         
     | 
| 
      
 83 
     | 
    
         
            +
                            RBS.logger.info { "Setting up method hook in .#{name}..." }
         
     | 
| 
      
 84 
     | 
    
         
            +
                            Hook.hook_singleton_method klass, name, key: singleton_key
         
     | 
| 
      
 85 
     | 
    
         
            +
                            set << name
         
     | 
| 
      
 86 
     | 
    
         
            +
                          end
         
     | 
| 
       45 
87 
     | 
    
         
             
                        end
         
     | 
| 
       46 
88 
     | 
    
         
             
                      end
         
     | 
| 
       47 
89 
     | 
    
         
             
                    end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                    targets << klass
         
     | 
| 
       48 
92 
     | 
    
         
             
                  end
         
     | 
| 
       49 
93 
     | 
    
         | 
| 
       50 
94 
     | 
    
         
             
                  def new_key(type_name, prefix)
         
     | 
| 
         @@ -66,14 +110,14 @@ module RBS 
     | 
|
| 
       66 
110 
     | 
    
         
             
                    attr_reader :definition
         
     | 
| 
       67 
111 
     | 
    
         
             
                    attr_reader :builder
         
     | 
| 
       68 
112 
     | 
    
         
             
                    attr_reader :kind
         
     | 
| 
       69 
     | 
    
         
            -
                    attr_reader : 
     | 
| 
      
 113 
     | 
    
         
            +
                    attr_reader :sample_size
         
     | 
| 
       70 
114 
     | 
    
         | 
| 
       71 
     | 
    
         
            -
                    def initialize(self_class, builder, definition, kind:,  
     | 
| 
      
 115 
     | 
    
         
            +
                    def initialize(self_class, builder, definition, kind:, sample_size:)
         
     | 
| 
       72 
116 
     | 
    
         
             
                      @self_class = self_class
         
     | 
| 
       73 
117 
     | 
    
         
             
                      @definition = definition
         
     | 
| 
       74 
118 
     | 
    
         
             
                      @builder = builder
         
     | 
| 
       75 
119 
     | 
    
         
             
                      @kind = kind
         
     | 
| 
       76 
     | 
    
         
            -
                      @ 
     | 
| 
      
 120 
     | 
    
         
            +
                      @sample_size = sample_size
         
     | 
| 
       77 
121 
     | 
    
         
             
                    end
         
     | 
| 
       78 
122 
     | 
    
         | 
| 
       79 
123 
     | 
    
         
             
                    def env
         
     | 
| 
         @@ -81,7 +125,7 @@ module RBS 
     | 
|
| 
       81 
125 
     | 
    
         
             
                    end
         
     | 
| 
       82 
126 
     | 
    
         | 
| 
       83 
127 
     | 
    
         
             
                    def check
         
     | 
| 
       84 
     | 
    
         
            -
                      @check ||= TypeCheck.new(self_class: self_class, builder: builder,  
     | 
| 
      
 128 
     | 
    
         
            +
                      @check ||= TypeCheck.new(self_class: self_class, builder: builder, sample_size: sample_size)
         
     | 
| 
       85 
129 
     | 
    
         
             
                    end
         
     | 
| 
       86 
130 
     | 
    
         | 
| 
       87 
131 
     | 
    
         
             
                    def format_method_name(name)
         
     |