steep 0.31.1 → 0.36.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +27 -0
  3. data/lib/steep.rb +3 -2
  4. data/lib/steep/annotation_parser.rb +1 -1
  5. data/lib/steep/ast/types/factory.rb +66 -60
  6. data/lib/steep/ast/types/logic.rb +20 -1
  7. data/lib/steep/cli.rb +15 -2
  8. data/lib/steep/drivers/print_project.rb +11 -0
  9. data/lib/steep/drivers/stats.rb +85 -0
  10. data/lib/steep/drivers/vendor.rb +1 -20
  11. data/lib/steep/errors.rb +19 -15
  12. data/lib/steep/interface/method_type.rb +12 -23
  13. data/lib/steep/method_name.rb +28 -0
  14. data/lib/steep/project/completion_provider.rb +24 -15
  15. data/lib/steep/project/dsl.rb +13 -17
  16. data/lib/steep/project/options.rb +4 -4
  17. data/lib/steep/project/source_file.rb +2 -1
  18. data/lib/steep/project/target.rb +19 -10
  19. data/lib/steep/server/interaction_worker.rb +1 -1
  20. data/lib/steep/server/utils.rb +1 -1
  21. data/lib/steep/source.rb +2 -2
  22. data/lib/steep/subtyping/check.rb +51 -16
  23. data/lib/steep/subtyping/variable_occurrence.rb +2 -0
  24. data/lib/steep/type_construction.rb +635 -439
  25. data/lib/steep/type_inference/context.rb +7 -3
  26. data/lib/steep/type_inference/context_array.rb +1 -1
  27. data/lib/steep/type_inference/local_variable_type_env.rb +10 -1
  28. data/lib/steep/type_inference/logic_type_interpreter.rb +9 -0
  29. data/lib/steep/type_inference/method_call.rb +116 -0
  30. data/lib/steep/typing.rb +38 -8
  31. data/lib/steep/version.rb +1 -1
  32. data/smoke/regression/fun.rb +8 -0
  33. data/smoke/regression/fun.rbs +4 -0
  34. data/smoke/regression/range.rb +5 -0
  35. data/steep.gemspec +1 -1
  36. metadata +13 -9
  37. data/lib/steep/ast/buffer.rb +0 -51
  38. data/lib/steep/ast/location.rb +0 -86
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5fa95738ddb4d770cdba53deb56b05f2ad86523729852ac16823bfb433470a19
4
- data.tar.gz: 9c295a1c5f1d28c1b9ae76b0693c582911770185828e18ce114f062a05fd4d24
3
+ metadata.gz: 5203340eb3b3534da5f1071cc497bbe7a481553e909088277c83dbfe5e5c0bbc
4
+ data.tar.gz: 11863c0ea9b692de6fba552ec01645525240c025c598235f9b3bf88e35571ed5
5
5
  SHA512:
6
- metadata.gz: 709a0883a3c698f8ca2246763a9c18e8af04072aa6edf7ca5e339a5c6d6e079693e1405c543fe8690276d4eebdcc35e5b65077dcba4c3dca2e1f102e84906e9b
7
- data.tar.gz: 3eeef0c3d9cfd34a7678ab8b4f5f125e949d56706510aa1619f018c1346242e10452045b00a09b117bdbec9b5aa27f577ba6ec01c4f732335e9f69dfcf88e228
6
+ metadata.gz: 9897fecb7ca5b60da9eeb236e5f06af1a9b2fa2a2df2bd87d07f7a496f0f05b988b30db2e294797304c6d7f0e8fe478cdc1d41049cbaf2e56c45108444b2c721
7
+ data.tar.gz: fa2dca2cd37d74e39878ab3da7a5453a714f50e921fcd1c0e75054bf083a11003cb5799f4f92531a10bce6074dce3cce88c90daeb214b9fb45d7f5612e294fb3
@@ -2,6 +2,33 @@
2
2
 
3
3
  ## master
4
4
 
5
+ ## 0.36.0 (2020-11-16)
6
+
7
+ * Flow-sensitive typing improvements with `||` and `&&` ([#260](https://github.com/soutaro/steep/pull/260))
8
+ * Type-case improvement ([#259](https://github.com/soutaro/steep/pull/259))
9
+ * Subtyping between `bool` and logic types ([#258](https://github.com/soutaro/steep/pull/258))
10
+
11
+ ## 0.35.0 (2020-11-14)
12
+
13
+ * Support third party RBS repository ([#231](https://github.com/soutaro/steep/pull/231), [#254](https://github.com/soutaro/steep/pull/254), [#255](https://github.com/soutaro/steep/pull/255))
14
+ * Boolean type semantics update ([#252](https://github.com/soutaro/steep/pull/252))
15
+ * More flexible record typing ([#256](https://github.com/soutaro/steep/pull/256))
16
+
17
+ ## 0.34.0 (2020-10-27)
18
+
19
+ * Add `steep stats` command to show method call typing stats ([#246](https://github.com/soutaro/steep/pull/246))
20
+ * Fix attribute assignment typing ([#243](https://github.com/soutaro/steep/pull/243))
21
+ * Let `Range[T]` be covariant ([#242](https://github.com/soutaro/steep/pull/242))
22
+ * Fix constant typing ([#247](https://github.com/soutaro/steep/pull/247), [#248](https://github.com/soutaro/steep/pull/248))
23
+
24
+ ## 0.33.0 (2020-10-13)
25
+
26
+ * Make `!` typing flow sensitive ([#240](https://github.com/soutaro/steep/pull/240))
27
+
28
+ ## 0.32.0 (2020-10-09)
29
+
30
+ * Let type-case support interface types ([#237](https://github.com/soutaro/steep/pull/237))
31
+
5
32
  ## 0.31.1 (2020-10-07)
6
33
 
7
34
  * Fix `if-then-else` parsing ([#236](https://github.com/soutaro/steep/pull/236))
@@ -16,7 +16,7 @@ require 'uri'
16
16
 
17
17
  require "rbs"
18
18
 
19
- require "steep/ast/location"
19
+ require "steep/method_name"
20
20
  require "steep/ast/types/helper"
21
21
  require "steep/ast/types/any"
22
22
  require "steep/ast/types/instance"
@@ -39,7 +39,6 @@ require "steep/ast/types/logic"
39
39
  require "steep/ast/type_params"
40
40
  require "steep/ast/annotation"
41
41
  require "steep/ast/annotation/collection"
42
- require "steep/ast/buffer"
43
42
  require "steep/ast/builtin"
44
43
  require "steep/ast/types/factory"
45
44
 
@@ -72,6 +71,7 @@ require "steep/type_inference/type_env"
72
71
  require "steep/type_inference/local_variable_type_env"
73
72
  require "steep/type_inference/logic"
74
73
  require "steep/type_inference/logic_type_interpreter"
74
+ require "steep/type_inference/method_call"
75
75
  require "steep/ast/types"
76
76
 
77
77
  require "steep/server/utils"
@@ -93,6 +93,7 @@ require "steep/project/hover_content"
93
93
  require "steep/project/completion_provider"
94
94
  require "steep/drivers/utils/driver_helper"
95
95
  require "steep/drivers/check"
96
+ require "steep/drivers/stats"
96
97
  require "steep/drivers/validate"
97
98
  require "steep/drivers/annotations"
98
99
  require "steep/drivers/watch"
@@ -68,7 +68,7 @@ module Steep
68
68
  name = match[:name]
69
69
  type = match[:type]
70
70
 
71
- method_type = factory.method_type(RBS::Parser.parse_method_type(type), self_type: AST::Types::Self.new)
71
+ method_type = factory.method_type(RBS::Parser.parse_method_type(type), self_type: AST::Types::Self.new, method_decls: Set[])
72
72
 
73
73
  AST::Annotation::MethodType.new(name: name.to_sym,
74
74
  type: method_type,
@@ -149,6 +149,8 @@ module Steep
149
149
  type: function_1(type.params, type.return_type),
150
150
  location: nil
151
151
  )
152
+ when Logic::Base
153
+ RBS::Types::Bases::Bool.new(location: nil)
152
154
  else
153
155
  raise "Unexpected type given: #{type} (#{type.class})"
154
156
  end
@@ -178,7 +180,7 @@ module Steep
178
180
  )
179
181
  end
180
182
 
181
- def method_type(method_type, self_type:, subst2: nil, method_def: nil)
183
+ def method_type(method_type, self_type:, subst2: nil, method_decls:)
182
184
  fvs = self_type.free_variables()
183
185
 
184
186
  type_params = []
@@ -209,8 +211,7 @@ module Steep
209
211
  return_type: type(block.type.return_type).subst(subst), location: nil)
210
212
  )
211
213
  end,
212
- method_def: method_def,
213
- location: method_def&.member&.location
214
+ method_decls: method_decls
214
215
  )
215
216
 
216
217
  if block_given?
@@ -342,48 +343,46 @@ module Steep
342
343
  end
343
344
  end
344
345
 
345
- def setup_primitives(method_name, method_type)
346
- if method_def = method_type.method_def
347
- defined_in = method_def.defined_in
348
- member = method_def.member
349
-
350
- if member.is_a?(RBS::AST::Members::MethodDefinition)
351
- case
352
- when defined_in == RBS::BuiltinNames::Object.name && member.instance?
353
- case method_name
354
- when :is_a?, :kind_of?, :instance_of?
355
- return method_type.with(
356
- return_type: AST::Types::Logic::ReceiverIsArg.new(location: method_type.return_type.location)
357
- )
358
- when :nil?
359
- return method_type.with(
360
- return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
361
- )
362
- end
346
+ NilClassName = TypeName("::NilClass")
363
347
 
364
- when defined_in == AST::Builtin::NilClass.module_name && member.instance?
365
- case method_name
366
- when :nil?
367
- return method_type.with(
368
- return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
369
- )
370
- end
348
+ def setup_primitives(method_name, method_def, method_type)
349
+ defined_in = method_def.defined_in
350
+ member = method_def.member
371
351
 
372
- when defined_in == RBS::BuiltinNames::BasicObject.name && member.instance?
373
- case method_name
374
- when :!
375
- return method_type.with(
376
- return_type: AST::Types::Logic::Not.new(location: method_type.return_type.location)
377
- )
378
- end
352
+ if member.is_a?(RBS::AST::Members::MethodDefinition)
353
+ case method_name
354
+ when :is_a?, :kind_of?, :instance_of?
355
+ if defined_in == RBS::BuiltinNames::Object.name && member.instance?
356
+ return method_type.with(
357
+ return_type: AST::Types::Logic::ReceiverIsArg.new(location: method_type.return_type.location)
358
+ )
359
+ end
379
360
 
380
- when defined_in == RBS::BuiltinNames::Module.name && member.instance?
381
- case method_name
382
- when :===
383
- return method_type.with(
384
- return_type: AST::Types::Logic::ArgIsReceiver.new(location: method_type.return_type.location)
385
- )
386
- end
361
+ when :nil?
362
+ case defined_in
363
+ when RBS::BuiltinNames::Object.name,
364
+ NilClassName
365
+ return method_type.with(
366
+ return_type: AST::Types::Logic::ReceiverIsNil.new(location: method_type.return_type.location)
367
+ )
368
+ end
369
+
370
+ when :!
371
+ case defined_in
372
+ when RBS::BuiltinNames::BasicObject.name,
373
+ RBS::BuiltinNames::TrueClass.name,
374
+ RBS::BuiltinNames::FalseClass.name
375
+ return method_type.with(
376
+ return_type: AST::Types::Logic::Not.new(location: method_type.return_type.location)
377
+ )
378
+ end
379
+
380
+ when :===
381
+ case defined_in
382
+ when RBS::BuiltinNames::Module.name
383
+ return method_type.with(
384
+ return_type: AST::Types::Logic::ArgIsReceiver.new(location: method_type.return_type.location)
385
+ )
387
386
  end
388
387
  end
389
388
  end
@@ -409,7 +408,7 @@ module Steep
409
408
  else
410
409
  raise "Unexpected `self` type interface"
411
410
  end
412
-
411
+
413
412
  when Name::Instance
414
413
  Interface::Interface.new(type: self_type, private: private).tap do |interface|
415
414
  definition = definition_builder.build_instance(type.name)
@@ -433,10 +432,13 @@ module Steep
433
432
 
434
433
  interface.methods[name] = Interface::Interface::Entry.new(
435
434
  method_types: method.defs.map do |type_def|
435
+ method_name = InstanceMethodName.new(type_name: type_def.implemented_in || type_def.defined_in, method_name: name)
436
+ decl = TypeInference::MethodCall::MethodDecl.new(method_name: method_name, method_def: type_def)
436
437
  setup_primitives(
437
438
  name,
439
+ type_def,
438
440
  method_type(type_def.type,
439
- method_def: type_def,
441
+ method_decls: Set[decl],
440
442
  self_type: self_type,
441
443
  subst2: subst)
442
444
  )
@@ -460,7 +462,11 @@ module Steep
460
462
  definition.methods.each do |name, method|
461
463
  interface.methods[name] = Interface::Interface::Entry.new(
462
464
  method_types: method.defs.map do |type_def|
463
- method_type(type_def.type, method_def: type_def, self_type: self_type, subst2: subst)
465
+ decls = Set[TypeInference::MethodCall::MethodDecl.new(
466
+ method_name: InstanceMethodName.new(type_name: type_def.implemented_in || type_def.defined_in, method_name: name),
467
+ method_def: type_def
468
+ )]
469
+ method_type(type_def.type, method_decls: decls, self_type: self_type, subst2: subst)
464
470
  end
465
471
  )
466
472
  end
@@ -485,10 +491,16 @@ module Steep
485
491
 
486
492
  interface.methods[name] = Interface::Interface::Entry.new(
487
493
  method_types: method.defs.map do |type_def|
494
+ decl = TypeInference::MethodCall::MethodDecl.new(
495
+ method_name: SingletonMethodName.new(type_name: type_def.implemented_in || type_def.defined_in,
496
+ method_name: name),
497
+ method_def: type_def
498
+ )
488
499
  setup_primitives(
489
500
  name,
501
+ type_def,
490
502
  method_type(type_def.type,
491
- method_def: type_def,
503
+ method_decls: Set[decl],
492
504
  self_type: self_type,
493
505
  subst2: subst)
494
506
  )
@@ -568,8 +580,7 @@ module Steep
568
580
  rest_keywords: nil),
569
581
  block: nil,
570
582
  return_type: elem_type,
571
- method_def: nil,
572
- location: nil
583
+ method_decls: Set[]
573
584
  )
574
585
  } + aref.method_types
575
586
  )
@@ -588,8 +599,7 @@ module Steep
588
599
  rest_keywords: nil),
589
600
  block: nil,
590
601
  return_type: elem_type,
591
- method_def: nil,
592
- location: nil
602
+ method_decls: Set[]
593
603
  )
594
604
  } + update.method_types
595
605
  )
@@ -603,8 +613,7 @@ module Steep
603
613
  params: Interface::Params.empty,
604
614
  block: nil,
605
615
  return_type: type.types[0] || AST::Builtin.nil_type,
606
- method_def: nil,
607
- location: nil
616
+ method_decls: Set[]
608
617
  )
609
618
  ]
610
619
  )
@@ -618,8 +627,7 @@ module Steep
618
627
  params: Interface::Params.empty,
619
628
  block: nil,
620
629
  return_type: type.types.last || AST::Builtin.nil_type,
621
- method_def: nil,
622
- location: nil
630
+ method_decls: Set[]
623
631
  )
624
632
  ]
625
633
  )
@@ -640,6 +648,7 @@ module Steep
640
648
  Interface::Interface::Entry.new(
641
649
  method_types: type.elements.map {|key_value, value_type|
642
650
  key_type = Literal.new(value: key_value, location: nil)
651
+
643
652
  Interface::MethodType.new(
644
653
  type_params: [],
645
654
  params: Interface::Params.new(required: [key_type],
@@ -650,8 +659,7 @@ module Steep
650
659
  rest_keywords: nil),
651
660
  block: nil,
652
661
  return_type: value_type,
653
- method_def: nil,
654
- location: nil
662
+ method_decls: Set[]
655
663
  )
656
664
  } + ref.method_types
657
665
  )
@@ -671,8 +679,7 @@ module Steep
671
679
  rest_keywords: nil),
672
680
  block: nil,
673
681
  return_type: value_type,
674
- method_def: nil,
675
- location: nil
682
+ method_decls: Set[]
676
683
  )
677
684
  } + update.method_types
678
685
  )
@@ -687,8 +694,7 @@ module Steep
687
694
  params: type.params,
688
695
  return_type: type.return_type,
689
696
  block: nil,
690
- method_def: nil,
691
- location: nil
697
+ method_decls: Set[]
692
698
  )
693
699
 
694
700
  interface.methods[:[]] = Interface::Interface::Entry.new(method_types: [method_type])
@@ -18,7 +18,7 @@ module Steep
18
18
  end
19
19
 
20
20
  def ==(other)
21
- other.class ==self.class
21
+ other.class == self.class
22
22
  end
23
23
 
24
24
  alias eql? ==
@@ -57,6 +57,25 @@ module Steep
57
57
  @location = location
58
58
  end
59
59
  end
60
+
61
+ class Env < Base
62
+ attr_reader :truthy, :falsy
63
+
64
+ def initialize(truthy:, falsy:, location: nil)
65
+ @truthy = truthy
66
+ @falsy = falsy
67
+ end
68
+
69
+ def ==(other)
70
+ other.is_a?(Env) && other.truthy == truthy && other.falsy == falsy
71
+ end
72
+
73
+ alias eql? ==
74
+
75
+ def hash
76
+ self.class.hash ^ truthy.hash ^ falsy.hash
77
+ end
78
+ end
60
79
  end
61
80
  end
62
81
  end
@@ -16,7 +16,7 @@ module Steep
16
16
  end
17
17
 
18
18
  def self.available_commands
19
- [:init, :check, :validate, :annotations, :version, :project, :watch, :langserver, :vendor]
19
+ [:init, :check, :validate, :annotations, :version, :project, :watch, :langserver, :stats]
20
20
  end
21
21
 
22
22
  def process_global_options
@@ -34,7 +34,7 @@ module Steep
34
34
 
35
35
  def setup_command
36
36
  @command = argv.shift&.to_sym
37
- if CLI.available_commands.include?(@command) || @command == :worker
37
+ if CLI.available_commands.include?(@command) || @command == :worker || @command == :vendor
38
38
  true
39
39
  else
40
40
  stderr.puts "Unknown command: #{command}"
@@ -91,6 +91,19 @@ module Steep
91
91
  end.run
92
92
  end
93
93
 
94
+ def process_stats
95
+ Drivers::Stats.new(stdout: stdout, stderr: stderr).tap do |check|
96
+ OptionParser.new do |opts|
97
+ opts.banner = "Usage: steep stats [options] [sources]"
98
+
99
+ opts.on("--steepfile=PATH") {|path| check.steepfile = Pathname(path) }
100
+ handle_logging_options opts
101
+ end.parse!(argv)
102
+
103
+ check.command_line_patterns.push *argv
104
+ end.run
105
+ end
106
+
94
107
  def process_validate
95
108
  Drivers::Validate.new(stdout: stdout, stderr: stderr).tap do |command|
96
109
  OptionParser.new do |opts|
@@ -47,6 +47,17 @@ module Steep
47
47
  target.options.libraries.each do |lib|
48
48
  stdout.puts " - #{lib}"
49
49
  end
50
+ stdout.puts " library dirs:"
51
+ Project::Target.construct_env_loader(options: target.options).tap do |loader|
52
+ loader.each_dir do |lib, path|
53
+ case lib
54
+ when :core
55
+ stdout.puts " - core: #{path}"
56
+ else
57
+ stdout.puts " - #{lib.name}: #{path}"
58
+ end
59
+ end
60
+ end
50
61
  end
51
62
 
52
63
  0
@@ -0,0 +1,85 @@
1
+ require "csv"
2
+
3
+ module Steep
4
+ module Drivers
5
+ class Stats
6
+ attr_reader :stdout
7
+ attr_reader :stderr
8
+ attr_reader :command_line_patterns
9
+
10
+ include Utils::DriverHelper
11
+
12
+ def initialize(stdout:, stderr:)
13
+ @stdout = stdout
14
+ @stderr = stderr
15
+ @command_line_patterns = []
16
+ end
17
+
18
+ def run
19
+ project = load_config()
20
+
21
+ loader = Project::FileLoader.new(project: project)
22
+ loader.load_sources(command_line_patterns)
23
+ loader.load_signatures()
24
+
25
+ type_check(project)
26
+
27
+ stdout.puts(
28
+ CSV.generate do |csv|
29
+ csv << ["Target", "File", "Status", "Typed calls", "Untyped calls", "All calls", "Typed %"]
30
+
31
+ project.targets.each do |target|
32
+ case (status = target.status)
33
+ when Project::Target::TypeCheckStatus
34
+ status.type_check_sources.each do |source_file|
35
+ case source_file.status
36
+ when Project::SourceFile::TypeCheckStatus
37
+ typing = source_file.status.typing
38
+
39
+ typed = 0
40
+ untyped = 0
41
+ total = 0
42
+ typing.method_calls.each_value do |call|
43
+ case call
44
+ when TypeInference::MethodCall::Typed
45
+ typed += 1
46
+ when TypeInference::MethodCall::Untyped
47
+ untyped += 1
48
+ end
49
+
50
+ total += 1
51
+ end
52
+
53
+ csv << format_stats(target, source_file.path, "success", typed, untyped, total)
54
+ when Project::SourceFile::TypeCheckErrorStatus
55
+ csv << format_stats(target, source_file.path, "error", 0, 0, 0)
56
+ else
57
+ csv << format_stats(target, source_file.path, "unknown (#{source_file.status.class.to_s.split(/::/).last})", 0, 0, 0)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
63
+ )
64
+
65
+ 0
66
+ end
67
+
68
+ def format_stats(target, path, status, typed, untyped, total)
69
+ [
70
+ target.name,
71
+ path.to_s,
72
+ status,
73
+ typed,
74
+ untyped,
75
+ total,
76
+ if total.nonzero?
77
+ format("%.2f", (typed.to_f/total)*100)
78
+ else
79
+ 0
80
+ end
81
+ ]
82
+ end
83
+ end
84
+ end
85
+ end