solargraph 0.44.0 → 0.45.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.
Files changed (36) hide show
  1. checksums.yaml +4 -4
  2. data/.github/workflows/rspec.yml +41 -0
  3. data/CHANGELOG.md +32 -0
  4. data/README.md +10 -2
  5. data/SPONSORS.md +2 -0
  6. data/lib/solargraph/api_map.rb +2 -2
  7. data/lib/solargraph/complex_type.rb +4 -0
  8. data/lib/solargraph/convention/rspec.rb +13 -4
  9. data/lib/solargraph/diagnostics/rubocop_helpers.rb +1 -1
  10. data/lib/solargraph/language_server/uri_helpers.rb +1 -1
  11. data/lib/solargraph/parser/legacy/class_methods.rb +9 -8
  12. data/lib/solargraph/parser/legacy/node_chainer.rb +17 -0
  13. data/lib/solargraph/parser/legacy/node_processors/block_node.rb +22 -2
  14. data/lib/solargraph/parser/rubyvm/class_methods.rb +8 -14
  15. data/lib/solargraph/parser/rubyvm/node_chainer.rb +5 -0
  16. data/lib/solargraph/parser/rubyvm/node_processors/block_node.rb +22 -2
  17. data/lib/solargraph/parser/rubyvm/node_processors.rb +1 -0
  18. data/lib/solargraph/pin/block.rb +19 -9
  19. data/lib/solargraph/pin/documenting.rb +1 -1
  20. data/lib/solargraph/pin/local_variable.rb +2 -14
  21. data/lib/solargraph/pin/namespace.rb +2 -2
  22. data/lib/solargraph/pin/parameter.rb +1 -4
  23. data/lib/solargraph/range.rb +1 -1
  24. data/lib/solargraph/source/chain/link.rb +4 -0
  25. data/lib/solargraph/source/chain/q_call.rb +11 -0
  26. data/lib/solargraph/source/chain.rb +14 -1
  27. data/lib/solargraph/source.rb +1 -4
  28. data/lib/solargraph/source_map/clip.rb +5 -5
  29. data/lib/solargraph/source_map/mapper.rb +4 -4
  30. data/lib/solargraph/type_checker.rb +20 -1
  31. data/lib/solargraph/version.rb +1 -1
  32. data/lib/solargraph/yard_map/core_fills.rb +7 -2
  33. data/lib/solargraph/yard_map.rb +4 -2
  34. data/lib/solargraph.rb +2 -2
  35. data/solargraph.gemspec +1 -1
  36. metadata +9 -7
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: '0827e2be47eea923c2f61688ce4e97efa11d3d801f094ffb8a6cdb73a7fb52b3'
4
- data.tar.gz: dcbad1eaa64acab63ccc511a93c0e1370fc469815e2e67735fc79c2f4dc08038
3
+ metadata.gz: 99cf1c663bb1a93324ff9c860b82e60d47988a4c48e9c0e977176cb94852f29c
4
+ data.tar.gz: 9b10324bcf7493f9ed5f8a67a8d47e4f0a379b76ed2aeb31514d1fa06b88dbf6
5
5
  SHA512:
6
- metadata.gz: fa050d901186e2385685d2aab7934f611517d68b549f4cb064ab7c3524f99e890488675a06483c2b1f2e2cafc0417a3367972a1ea8b6c6614a3465e867313b03
7
- data.tar.gz: 4b12d6b0f65f53afd0ccbbdfc84a9fe8159f2f3a4bb3c625a84f1a9ee80be53f8c8b1d1ecfbae9bcf01ceff691388af5d0cef7271bb5470730fc4aa7a2471df9
6
+ metadata.gz: a981e45a500d2469972d0fab1e4bcfdbde99dc46de7ef7b377d486a9a027ce617b8806790ed3565a673fbf3d3fd206d246408b5ce744a0b575e02ee3a3a9b699
7
+ data.tar.gz: 0bcb7a9957581cac9c066076938dbbdcbcc596bf7ddc8f4cd4539d71dadabab68cfdbc18f59df8daedd02dcff8f45b671a0a742c6ec1afbd39ede15fb3db381b
@@ -0,0 +1,41 @@
1
+ # This workflow uses actions that are not certified by GitHub.
2
+ # They are provided by a third-party and are governed by
3
+ # separate terms of service, privacy policy, and support
4
+ # documentation.
5
+ # This workflow will download a prebuilt Ruby version, install dependencies and run tests with rspec.
6
+ # For more information see: https://github.com/marketplace/actions/setup-ruby-jruby-and-truffleruby
7
+
8
+ name: RSpec
9
+
10
+ on:
11
+ push:
12
+ branches: [ master ]
13
+ pull_request:
14
+ branches: [ master ]
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ jobs:
20
+ test:
21
+
22
+ runs-on: ubuntu-latest
23
+ strategy:
24
+ matrix:
25
+ ruby-version: ['2.4', '2.5', '2.6', '2.7', '3.0']
26
+
27
+ steps:
28
+ - uses: actions/checkout@v3
29
+ - name: Set up Ruby
30
+ # To automatically get bug fixes and new Ruby versions for ruby/setup-ruby,
31
+ # change this to (see https://github.com/ruby/setup-ruby#versioning):
32
+ # uses: ruby/setup-ruby@v1
33
+ uses: ruby/setup-ruby@2b019609e2b0f1ea1a2bc8ca11cb82ab46ada124
34
+ with:
35
+ ruby-version: ${{ matrix.ruby-version }}
36
+ bundler-cache: false
37
+ - run: bundle install
38
+ - name: Set up yardocs
39
+ run: bundle exec yard gems
40
+ - name: Run tests
41
+ run: bundle exec rspec
data/CHANGELOG.md CHANGED
@@ -1,3 +1,35 @@
1
+ ## 0.45.0 - May 23, 2022
2
+ - Basic support for RSpec #describe and #it
3
+ - fix: domain can complete private method (#490)
4
+ - Update README.md (#533)
5
+ - Doc: update readme.md for add solargraph support (#536)
6
+ - Process DASGN node in Ruby 3
7
+ - File.open core fill
8
+ - replace with_unbundled_env with with_original_env (#489)
9
+ - Require specific version of gem (#509)
10
+ - Support URIs prefixed with single slashed file scheme (#529)
11
+ - Fix typo in README.md (#549)
12
+ - details on config behavior (#556)
13
+ - Consider overloads in arity checks
14
+ - ENV core fill for Hash-like methods (#537)
15
+ - Fix string ranges with substitutions (#463)
16
+
17
+ ## 0.44.3 - January 22, 2022
18
+ - TypeChecker validates aliased namespaces (#497)
19
+ - Always use reference YARD tags when resolving param types (#515) (#516)
20
+ - Skip method aliases in strict type checking
21
+
22
+ ## 0.44.2 - November 23, 2021
23
+ - Scope local variables in class_eval blocks (#503)
24
+ - Fix invalid UTF-8 in node comments (#504)
25
+
26
+ ## 0.44.1 - November 18, 2021
27
+ - Chain nil safety navigation operator (#420)
28
+ - Update closure and context for class_eval receiver (#487)
29
+ - SourceMap::Mapper handles invalid byte sequences (#474)
30
+ - Special handle var= references, which may use as var = (have space) (#498)
31
+ - Rebind define_method to self (#494)
32
+
1
33
  ## 0.44.0 - September 27, 2021
2
34
  - Allow opening parenthesis, space, and comma when using Diff::LCS (#465)
3
35
  - Support textDocument/documentHighlight
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # Solargraph
2
2
 
3
+ [![RSpec](https://github.com/castwide/solargraph/actions/workflows/rspec.yml/badge.svg)](https://github.com/castwide/solargraph/actions/workflows/rspec.yml)
4
+
3
5
  ## A Ruby Language Server
4
6
 
5
7
  Solargraph provides a comprehensive suite of tools for Ruby programming: intellisense, diagnostics, inline documentation, and type checking.
@@ -31,7 +33,9 @@ Plug-ins and extensions are available for the following editors:
31
33
  * GitHub: https://github.com/castwide/atom-solargraph
32
34
 
33
35
  * **Vim**
34
- * GitHub: https://github.com/autozimu/LanguageClient-neovim
36
+ * GitHub: `LanguageClient-neovim`, https://github.com/autozimu/LanguageClient-neovim
37
+ * GitHub: `coc`, https://github.com/neoclide/coc-solargraph
38
+ * GitHub: `Vim-EasyComplete`, https://github.com/jayli/vim-easycomplete
35
39
 
36
40
  * **Emacs**
37
41
  * GitHub: `eglot.el`, https://github.com/joaotavora/eglot
@@ -41,13 +45,17 @@ Plug-ins and extensions are available for the following editors:
41
45
  * Plugin: https://marketplace.eclipse.org/content/ruby-solargraph
42
46
  * GitHub: https://github.com/PyvesB/eclipse-solargraph
43
47
 
48
+ ### Configuration
49
+
50
+ Solargraph's behavior can be controlled via optional [configuration](https://solargraph.org/guides/configuration) files. The highest priority file is a `.solargraph.yml` file at the root of the project. If not present, any global configuration at `~/.config/solargraph/config.yml` will apply. The path to the global configuration can be overridden with the `SOLARGRAPH_GLOBAL_CONFIG` environment variable.
51
+
44
52
  ### Gem Support
45
53
 
46
54
  Solargraph is capable of providing code completion and documentation for gems that have YARD documentation. You can make sure your gems are documented by running `yard gems` from the command line. (YARD is included as one of Solargraph's gem dependencies. The first time you run it might take a while if you have a lot of gems installed).
47
55
 
48
56
  When editing code, a `require` call that references a gem will pull the documentation into the code maps and include the gem's API in code completion and intellisense.
49
57
 
50
- If your project automatically requires bundled gems (e.g., `require 'bundler/require'`), Solargraph will add all of the Gemfile's default dependecies to the map.
58
+ If your project automatically requires bundled gems (e.g., `require 'bundler/require'`), Solargraph will add all of the Gemfile's default dependencies to the map.
51
59
 
52
60
  ### Type Checking
53
61
 
data/SPONSORS.md CHANGED
@@ -13,3 +13,5 @@ The following people and organizations provide funding or other resources. [Beco
13
13
  - Emily Strickland
14
14
  - Tom de Grunt
15
15
  - Akira Yamada
16
+ - Erlend Finvåg
17
+ - Matt Massicotte
@@ -255,7 +255,7 @@ module Solargraph
255
255
  implicit.domains.each do |domain|
256
256
  type = ComplexType.try_parse(domain)
257
257
  next if type.undefined?
258
- result.concat inner_get_methods(type.name, type.scope, [:public], deep, skip)
258
+ result.concat inner_get_methods(type.name, type.scope, visibility, deep, skip)
259
259
  end
260
260
  result.concat inner_get_methods(fqns, :class, visibility, deep, skip)
261
261
  result.concat inner_get_methods(fqns, :instance, visibility, deep, skip)
@@ -525,7 +525,7 @@ module Solargraph
525
525
  end
526
526
  store.domains(fqns).each do |d|
527
527
  dt = ComplexType.try_parse(d)
528
- result.concat inner_get_methods(dt.namespace, dt.scope, [:public], deep, skip)
528
+ result.concat inner_get_methods(dt.namespace, dt.scope, visibility, deep, skip)
529
529
  end
530
530
  end
531
531
  result
@@ -87,6 +87,10 @@ module Solargraph
87
87
  ComplexType.parse(*result.map(&:tag))
88
88
  end
89
89
 
90
+ def nullable?
91
+ @items.any?(&:nil_type?)
92
+ end
93
+
90
94
  private
91
95
 
92
96
  # @todo This is a quick and dirty hack that forces `self` keywords
@@ -8,14 +8,23 @@ module Solargraph
8
8
  @environ ||= Environ.new(
9
9
  requires: ['rspec'],
10
10
  domains: ['RSpec::Matchers', 'RSpec::ExpectationGroups'],
11
- # This override is necessary due to an erroneous @return tag in
12
- # rspec's YARD documentation.
13
- # @todo The return types have been fixed (https://github.com/rspec/rspec-expectations/pull/1121)
14
11
  pins: [
12
+ # This override is necessary due to an erroneous @return tag in
13
+ # rspec's YARD documentation.
14
+ # @todo The return types have been fixed (https://github.com/rspec/rspec-expectations/pull/1121)
15
15
  Solargraph::Pin::Reference::Override.method_return('RSpec::Matchers#expect', 'RSpec::Expectations::ExpectationTarget')
16
- ]
16
+ ].concat(extras)
17
17
  )
18
18
  end
19
+
20
+ private
21
+
22
+ def extras
23
+ @@extras ||= SourceMap.load_string(%(
24
+ def describe(*args); end
25
+ def it(*args); end
26
+ )).pins
27
+ end
19
28
  end
20
29
  end
21
30
  end
@@ -31,7 +31,7 @@ module Solargraph
31
31
  # @param code [String]
32
32
  # @return [Array(Array<String>, Array<String>)]
33
33
  def generate_options filename, code
34
- args = ['-f', 'j', filename]
34
+ args = ['-f', 'j', '--force-exclusion', filename]
35
35
  base_options = RuboCop::Options.new
36
36
  options, paths = base_options.parse(args)
37
37
  options[:stdin] = code
@@ -14,7 +14,7 @@ module Solargraph
14
14
  # @param uri [String]
15
15
  # @return [String]
16
16
  def uri_to_file uri
17
- decode(uri).sub(/^file\:\/\//, '').sub(/^\/([a-z]\:)/i, '\1')
17
+ decode(uri).sub(/^file\:(?:\/\/)?/, '').sub(/^\/([a-z]\:)/i, '\1')
18
18
  end
19
19
 
20
20
  # Convert a file path to a URI.
@@ -48,10 +48,16 @@ module Solargraph
48
48
  end
49
49
 
50
50
  def references source, name
51
+ if name.end_with?("=")
52
+ reg = /#{Regexp.escape name[0..-2]}\s*=/
53
+ extract_offset = ->(code, offset) { reg.match(code, offset).offset(0) }
54
+ else
55
+ extract_offset = ->(code, offset) { [soff = code.index(name, offset), soff + name.length] }
56
+ end
51
57
  inner_node_references(name, source.node).map do |n|
52
- offset = Position.to_offset(source.code, NodeMethods.get_node_start_position(n))
53
- soff = source.code.index(name, offset)
54
- eoff = soff + name.length
58
+ rng = Range.from_node(n)
59
+ offset = Position.to_offset(source.code, rng.start)
60
+ soff, eoff = extract_offset[source.code, offset]
55
61
  Location.new(
56
62
  source.filename,
57
63
  Range.new(
@@ -109,16 +115,11 @@ module Solargraph
109
115
  result = []
110
116
  if node.type == :str
111
117
  result.push Range.from_node(node)
112
- elsif node.type == :dstr
113
- here = Range.from_node(node)
114
- there = Range.from_node(node.children[1])
115
- result.push Range.new(here.start, there.start)
116
118
  end
117
119
  node.children.each do |child|
118
120
  result.concat string_ranges(child)
119
121
  end
120
122
  if node.type == :dstr && node.children.last.nil?
121
- # result.push Range.new(result.last.ending, result.last.ending)
122
123
  last = node.children[-2]
123
124
  unless last.nil?
124
125
  rng = Range.from_node(last)
@@ -71,6 +71,23 @@ module Solargraph
71
71
  else
72
72
  raise "No idea what to do with #{n}"
73
73
  end
74
+ elsif n.type == :csend
75
+ if n.children[0].is_a?(::Parser::AST::Node)
76
+ result.concat generate_links(n.children[0])
77
+ args = []
78
+ n.children[2..-1].each do |c|
79
+ args.push NodeChainer.chain(c)
80
+ end
81
+ result.push Chain::QCall.new(n.children[1].to_s, args, @in_block > 0 || block_passed?(n))
82
+ elsif n.children[0].nil?
83
+ args = []
84
+ n.children[2..-1].each do |c|
85
+ args.push NodeChainer.chain(c)
86
+ end
87
+ result.push Chain::QCall.new(n.children[1].to_s, args, @in_block > 0 || block_passed?(n))
88
+ else
89
+ raise "No idea what to do with #{n}"
90
+ end
74
91
  elsif n.type == :self
75
92
  result.push Chain::Head.new('self')
76
93
  elsif n.type == :zsuper
@@ -5,16 +5,36 @@ module Solargraph
5
5
  module Legacy
6
6
  module NodeProcessors
7
7
  class BlockNode < Parser::NodeProcessor::Base
8
+ include Legacy::NodeMethods
9
+
8
10
  def process
11
+ location = get_node_location(node)
12
+ parent = if other_class_eval?
13
+ Solargraph::Pin::Namespace.new(
14
+ location: location,
15
+ type: :class,
16
+ name: unpack_name(node.children[0].children[0])
17
+ )
18
+ else
19
+ region.closure
20
+ end
9
21
  pins.push Solargraph::Pin::Block.new(
10
- location: get_node_location(node),
11
- closure: region.closure,
22
+ location: location,
23
+ closure: parent,
12
24
  receiver: node.children[0],
13
25
  comments: comments_for(node),
14
26
  scope: region.scope || region.closure.context.scope
15
27
  )
16
28
  process_children region.update(closure: pins.last)
17
29
  end
30
+
31
+ private
32
+
33
+ def other_class_eval?
34
+ node.children[0].type == :send &&
35
+ node.children[0].children[1] == :class_eval &&
36
+ [:cbase, :const].include?(node.children[0].children[0]&.type)
37
+ end
18
38
  end
19
39
  end
20
40
  end
@@ -29,22 +29,17 @@ module Solargraph
29
29
  NodeProcessor.process(source.node, Region.new(source: source))
30
30
  end
31
31
 
32
- # def returns_from node
33
- # return [] unless Parser.is_ast_node?(node)
34
- # if node.type == :SCOPE
35
- # # node.children.select { |n| n.is_a?(RubyVM::AbstractSyntaxTree::Node) }.map { |n| DeepInference.get_return_nodes(n) }.flatten
36
- # DeepInference.get_return_nodes(node.children[2])
37
- # else
38
- # DeepInference.get_return_nodes(node)
39
- # end
40
- # end
41
-
42
32
  def references source, name
33
+ if name.end_with?("=")
34
+ reg = /#{Regexp.escape name[0..-2]}\s*=/
35
+ extract_offset = ->(code, offset) { reg.match(code, offset).offset(0) }
36
+ else
37
+ extract_offset = ->(code, offset) { [soff = code.index(name, offset), soff + name.length] }
38
+ end
43
39
  inner_node_references(name, source.node).map do |n|
44
40
  rng = Range.from_node(n)
45
41
  offset = Position.to_offset(source.code, rng.start)
46
- soff = source.code.index(name, offset)
47
- eoff = soff + name.length
42
+ soff, eoff = extract_offset[source.code, offset]
48
43
  Location.new(
49
44
  source.filename,
50
45
  Range.new(
@@ -128,13 +123,12 @@ module Solargraph
128
123
  elsif node.type == :DSTR
129
124
  here = Range.from_node(node)
130
125
  there = Range.from_node(node.children[1])
131
- result.push Range.new(here.start, there.start)
126
+ result.push Range.new(here.start, there&.start || here.ending)
132
127
  end
133
128
  node.children.each do |child|
134
129
  result.concat string_ranges(child)
135
130
  end
136
131
  if node.type == :DSTR && node.children.last.nil?
137
- # result.push Range.new(result.last.ending, result.last.ending)
138
132
  last = node.children[-2]
139
133
  unless last.nil?
140
134
  rng = Range.from_node(last)
@@ -60,6 +60,11 @@ module Solargraph
60
60
  result.concat generate_links(c)
61
61
  end
62
62
  result.push Chain::Call.new(n.children[-2].to_s, node_to_argchains(n.children.last), @in_block > 0 || block_passed?(n))
63
+ elsif n.type == :QCALL
64
+ n.children[0..-3].each do |c|
65
+ result.concat generate_links(c)
66
+ end
67
+ result.push Chain::QCall.new(n.children[-2].to_s, node_to_argchains(n.children.last), @in_block > 0 || block_passed?(n))
63
68
  elsif n.type == :ATTRASGN
64
69
  result.concat generate_links(n.children[0])
65
70
  result.push Chain::Call.new(n.children[1].to_s, node_to_argchains(n.children[2]), @in_block > 0 || block_passed?(n))
@@ -5,16 +5,36 @@ module Solargraph
5
5
  module Rubyvm
6
6
  module NodeProcessors
7
7
  class BlockNode < Parser::NodeProcessor::Base
8
+ include NodeMethods
9
+
8
10
  def process
11
+ location = get_node_location(node)
12
+ parent = if other_class_eval?
13
+ Solargraph::Pin::Namespace.new(
14
+ location: location,
15
+ type: :class,
16
+ name: unpack_name(node.children[0].children[0])
17
+ )
18
+ else
19
+ region.closure
20
+ end
9
21
  pins.push Solargraph::Pin::Block.new(
10
- location: get_node_location(node),
11
- closure: region.closure,
22
+ location: location,
23
+ closure: parent,
12
24
  receiver: node.children[0],
13
25
  comments: comments_for(node),
14
26
  scope: region.scope || region.closure.context.scope
15
27
  )
16
28
  process_children region.update(closure: pins.last)
17
29
  end
30
+
31
+ private
32
+
33
+ def other_class_eval?
34
+ node.children[0].type == :CALL &&
35
+ node.children[0].children[1] == :class_eval &&
36
+ [:COLON2, :CONST].include?(node.children[0].children[0].type)
37
+ end
18
38
  end
19
39
  end
20
40
  end
@@ -45,6 +45,7 @@ module Solargraph
45
45
  register :IASGN, Rubyvm::NodeProcessors::IvasgnNode
46
46
  register :CVASGN, Rubyvm::NodeProcessors::CvasgnNode
47
47
  register :LASGN, Rubyvm::NodeProcessors::LvasgnNode
48
+ register :DASGN, Rubyvm::NodeProcessors::LvasgnNode
48
49
  register :DASGN_CURR, Rubyvm::NodeProcessors::LvasgnNode
49
50
  register :GASGN, Rubyvm::NodeProcessors::GvasgnNode
50
51
  register :CDECL, Rubyvm::NodeProcessors::CasgnNode
@@ -8,9 +8,10 @@ module Solargraph
8
8
  # @return [Parser::AST::Node]
9
9
  attr_reader :receiver
10
10
 
11
- def initialize receiver: nil, args: [], **splat
11
+ def initialize receiver: nil, args: [], context: nil, **splat
12
12
  super(**splat)
13
13
  @receiver = receiver
14
+ @context = context
14
15
  @parameters = args
15
16
  end
16
17
 
@@ -44,15 +45,24 @@ module Solargraph
44
45
  return nil unless api_map.rebindable_method_names.include?(word)
45
46
  chain = Parser.chain(receiver, location.filename)
46
47
  locals = api_map.source_map(location.filename).locals_at(location)
47
- if ['instance_eval', 'instance_exec', 'class_eval', 'class_exec', 'module_eval', 'module_exec'].include?(chain.links.last.word)
48
+ links_last_word = chain.links.last.word
49
+ if %w[instance_eval instance_exec class_eval class_exec module_eval module_exec].include?(links_last_word)
48
50
  return chain.base.infer(api_map, self, locals)
49
- else
50
- receiver_pin = chain.define(api_map, self, locals).first
51
- if receiver_pin && receiver_pin.docstring
52
- ys = receiver_pin.docstring.tag(:yieldself)
53
- if ys && ys.types && !ys.types.empty?
54
- return ComplexType.try_parse(*ys.types).qualify(api_map, receiver_pin.context.namespace)
55
- end
51
+ end
52
+ if 'define_method' == links_last_word and chain.define(api_map, self, locals).first&.path == 'Module#define_method' # change class type to instance type
53
+ if chain.links.size > 1 # Class.define_method
54
+ ty = chain.base.infer(api_map, self, locals)
55
+ return Solargraph::ComplexType.parse(ty.namespace)
56
+ else # define_method without self
57
+ return Solargraph::ComplexType.parse(closure.binder.namespace)
58
+ end
59
+ end
60
+ # other case without early return, read block yieldself tags
61
+ receiver_pin = chain.define(api_map, self, locals).first
62
+ if receiver_pin && receiver_pin.docstring
63
+ ys = receiver_pin.docstring.tag(:yieldself)
64
+ if ys && ys.types && !ys.types.empty?
65
+ return ComplexType.try_parse(*ys.types).qualify(api_map, receiver_pin.context.namespace)
56
66
  end
57
67
  end
58
68
  nil
@@ -80,7 +80,7 @@ module Solargraph
80
80
  end
81
81
  end
82
82
  end
83
- sections.map(&:to_s).join
83
+ sections.map(&:to_s).join.strip
84
84
  end
85
85
  end
86
86
 
@@ -18,19 +18,7 @@ module Solargraph
18
18
  true
19
19
  end
20
20
 
21
- # @param other [Pin::Base] The caller's block
22
- # @param position [Position, Array(Integer, Integer)] The caller's position
23
- # @return [Boolean]
24
- def visible_from?(other, position)
25
- position = Position.normalize(position)
26
- other.filename == filename &&
27
- match_tags(other.full_context.tag, full_context.tag) &&
28
- (other == closure ||
29
- (closure.location.range.contain?(other.location.range.start) && closure.location.range.contain?(other.location.range.ending))
30
- ) &&
31
- presence.contain?(position)
32
- end
33
-
21
+ # @param other_closure [Pin::Closure]
34
22
  # @param other_loc [Location]
35
23
  def visible_at?(other_closure, other_loc)
36
24
  return true if location.filename == other_loc.filename &&
@@ -53,7 +41,7 @@ module Solargraph
53
41
  end
54
42
 
55
43
  def match_named_closure needle, haystack
56
- return true if needle == haystack
44
+ return true if needle == haystack || haystack.is_a?(Pin::Block)
57
45
  cursor = haystack
58
46
  until cursor.nil?
59
47
  return true if needle.path == cursor.path
@@ -9,8 +9,8 @@ module Solargraph
9
9
  # @return [::Symbol] :class or :module
10
10
  attr_reader :type
11
11
 
12
- # @param type [Symbol] :class or :module
13
- # @param visibility [Symbol] :public or :private
12
+ # @param type [::Symbol] :class or :module
13
+ # @param visibility [::Symbol] :public or :private
14
14
  # @param gates [Array<String>]
15
15
  def initialize type: :class, visibility: :public, gates: [''], **splat
16
16
  # super(location, namespace, name, comments)
@@ -145,10 +145,7 @@ module Solargraph
145
145
  # meths.shift # Ignore the first one
146
146
  meths.each do |meth|
147
147
  found = nil
148
- params = meth.docstring.tags(:param)
149
- if params.empty?
150
- params = see_reference(docstring, api_map)
151
- end
148
+ params = meth.docstring.tags(:param) + see_reference(docstring, api_map)
152
149
  params.each do |p|
153
150
  next unless p.name == name
154
151
  found = p
@@ -69,7 +69,7 @@ module Solargraph
69
69
  if node.is_a?(RubyVM::AbstractSyntaxTree::Node)
70
70
  Solargraph::Range.from_to(node.first_lineno - 1, node.first_column, node.last_lineno - 1, node.last_column)
71
71
  end
72
- elsif node.loc && node.loc.expression
72
+ elsif node&.loc && node.loc.expression
73
73
  from_expr(node.loc.expression)
74
74
  end
75
75
  end
@@ -51,6 +51,10 @@ module Solargraph
51
51
  clone.mark_head(false)
52
52
  end
53
53
 
54
+ def nullable?
55
+ false
56
+ end
57
+
54
58
  protected
55
59
 
56
60
  # Mark whether this link is the head of a chain
@@ -0,0 +1,11 @@
1
+ module Solargraph
2
+ class Source
3
+ class Chain
4
+ class QCall < Call
5
+ def nullable?
6
+ true
7
+ end
8
+ end
9
+ end
10
+ end
11
+ end
@@ -11,6 +11,7 @@ module Solargraph
11
11
  class Chain
12
12
  autoload :Link, 'solargraph/source/chain/link'
13
13
  autoload :Call, 'solargraph/source/chain/call'
14
+ autoload :QCall, 'solargraph/source/chain/q_call'
14
15
  autoload :Variable, 'solargraph/source/chain/variable'
15
16
  autoload :ClassVariable, 'solargraph/source/chain/class_variable'
16
17
  autoload :Constant, 'solargraph/source/chain/constant'
@@ -76,7 +77,8 @@ module Solargraph
76
77
  # @return [ComplexType]
77
78
  def infer api_map, name_pin, locals
78
79
  pins = define(api_map, name_pin, locals)
79
- infer_first_defined(pins, links.last.last_context, api_map)
80
+ type = infer_first_defined(pins, links.last.last_context, api_map)
81
+ maybe_nil(type)
80
82
  end
81
83
 
82
84
  # @return [Boolean]
@@ -101,6 +103,10 @@ module Solargraph
101
103
  @splat
102
104
  end
103
105
 
106
+ def nullable?
107
+ links.any?(&:nullable?)
108
+ end
109
+
104
110
  private
105
111
 
106
112
  # @param pins [Array<Pin::Base>]
@@ -146,6 +152,13 @@ module Solargraph
146
152
  return type if context.nil? || context.return_type.undefined?
147
153
  type.self_to(context.return_type.namespace)
148
154
  end
155
+
156
+ # @param type [ComplexType]
157
+ def maybe_nil type
158
+ return type if type.undefined? || type.void? || type.nullable?
159
+ return type unless nullable?
160
+ ComplexType.try_parse("#{type}, nil")
161
+ end
149
162
  end
150
163
  end
151
164
  end
@@ -42,7 +42,6 @@ module Solargraph
42
42
  @version = version
43
43
  @domains = []
44
44
  begin
45
- # @node, @comments = Source.parse_with_comments(@code, filename)
46
45
  @node, @comments = Solargraph::Parser.parse_with_comments(@code, filename)
47
46
  @parsed = true
48
47
  rescue Parser::SyntaxError, EncodingError => e
@@ -336,7 +335,6 @@ module Solargraph
336
335
  # @param parent [Symbol]
337
336
  # @return [void]
338
337
  def inner_folding_ranges top, result = [], parent = nil
339
- # return unless top.is_a?(::Parser::AST::Node)
340
338
  return unless Parser.is_ast_node?(top)
341
339
  if FOLDING_NODE_TYPES.include?(top.type)
342
340
  # @todo Smelly exception for hash's first-level array in RubyVM
@@ -362,7 +360,7 @@ module Solargraph
362
360
  skip = nil
363
361
  comments.lines.each { |l|
364
362
  # Trim the comment and minimum leading whitespace
365
- p = l.gsub(/^#+/, '')
363
+ p = l.encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#+/, '')
366
364
  if p.strip.empty?
367
365
  next unless started
368
366
  ctxt.concat p
@@ -433,7 +431,6 @@ module Solargraph
433
431
  # @return [void]
434
432
  def inner_tree_at node, position, stack
435
433
  return if node.nil?
436
- # here = Range.from_to(node.loc.expression.line, node.loc.expression.column, node.loc.expression.last_line, node.loc.expression.last_column)
437
434
  here = Range.from_node(node)
438
435
  if here.contain?(position) || colonized(here, position, node)
439
436
  stack.unshift node
@@ -53,11 +53,7 @@ module Solargraph
53
53
  #
54
54
  # @return [Array<Solargraph::Pin::Base>]
55
55
  def locals
56
- loc_pos = context_pin.location.range.contain?(cursor.position) ? cursor.position : context_pin.location.range.ending
57
- adj_pos = Position.new(loc_pos.line, (loc_pos.column.zero? ? 0 : loc_pos.column - 1))
58
- @locals ||= source_map.locals.select { |pin|
59
- pin.visible_from?(block, adj_pos)
60
- }.reverse
56
+ @locals ||= source_map.locals_at(location)
61
57
  end
62
58
 
63
59
  def gates
@@ -92,6 +88,10 @@ module Solargraph
92
88
  @source_map ||= api_map.source_map(cursor.filename)
93
89
  end
94
90
 
91
+ def location
92
+ Location.new(source_map.filename, Solargraph::Range.new(cursor.position, cursor.position))
93
+ end
94
+
95
95
  # @return [Solargraph::Pin::Base]
96
96
  def block
97
97
  @block ||= source_map.locate_block_pin(cursor.node_position.line, cursor.node_position.character)
@@ -61,7 +61,7 @@ module Solargraph
61
61
  end
62
62
 
63
63
  def process_comment source_position, comment_position, comment
64
- return unless comment =~ MACRO_REGEXP
64
+ return unless comment.encode('UTF-8', invalid: :replace, replace: '?') =~ MACRO_REGEXP
65
65
  cmnt = remove_inline_comment_hashes(comment)
66
66
  parse = Solargraph::Source.parse_docstring(cmnt)
67
67
  last_line = 0
@@ -169,7 +169,7 @@ module Solargraph
169
169
  # @todo Handle parser errors in !parse directives
170
170
  end
171
171
  when 'domain'
172
- namespace = closure_at(source_position)
172
+ namespace = closure_at(source_position) || Pin::ROOT_PIN
173
173
  namespace.domains.concat directive.tag.types unless directive.tag.types.nil?
174
174
  when 'override'
175
175
  pins.push Pin::Reference::Override.new(location, directive.tag.name, docstring.tags)
@@ -182,7 +182,7 @@ module Solargraph
182
182
  started = false
183
183
  comment.lines.each { |l|
184
184
  # Trim the comment and minimum leading whitespace
185
- p = l.gsub(/^#/, '')
185
+ p = l.encode('UTF-8', invalid: :replace, replace: '?').gsub(/^#/, '')
186
186
  if num.nil? && !p.strip.empty?
187
187
  num = p.index(/[^ ]/)
188
188
  started = true
@@ -197,7 +197,7 @@ module Solargraph
197
197
 
198
198
  # @return [void]
199
199
  def process_comment_directives
200
- return unless @code =~ MACRO_REGEXP
200
+ return unless @code.encode('UTF-8', invalid: :replace, replace: '?') =~ MACRO_REGEXP
201
201
  code_lines = @code.lines
202
202
  @source.associated_comments.each do |line, comments|
203
203
  src_pos = line ? Position.new(line, code_lines[line].to_s.chomp.index(/[^\s]/) || 0) : Position.new(code_lines.length, 0)
@@ -84,12 +84,13 @@ module Solargraph
84
84
  # @param pin [Pin::Method]
85
85
  # @return [Array<Problem>]
86
86
  def method_return_type_problems_for pin
87
+ return [] if pin.is_a?(Pin::MethodAlias)
87
88
  result = []
88
89
  declared = pin.typify(api_map).self_to(pin.full_context.namespace)
89
90
  if declared.undefined?
90
91
  if pin.return_type.undefined? && rules.require_type_tags?
91
92
  result.push Problem.new(pin.location, "Missing @return tag for #{pin.path}", pin: pin)
92
- elsif pin.return_type.defined?
93
+ elsif pin.return_type.defined? && !resolved_constant?(pin)
93
94
  result.push Problem.new(pin.location, "Unresolved return type #{pin.return_type} for #{pin.path}", pin: pin)
94
95
  elsif rules.must_tag_or_infer? && pin.probe(api_map).undefined?
95
96
  result.push Problem.new(pin.location, "Untyped method #{pin.path} could not be inferred")
@@ -111,6 +112,15 @@ module Solargraph
111
112
  result
112
113
  end
113
114
 
115
+ # @todo This is not optimal. A better solution would probably be to mix
116
+ # namespace alias into types at the ApiMap level.
117
+ #
118
+ # @param pin [Pin::Base]
119
+ # @return [Boolean]
120
+ def resolved_constant? pin
121
+ api_map.get_constants('', pin.binder.tag).any? { |pin| pin.name == pin.return_type.namespace && ['Class', 'Module'].include?(pin.return_type.name) }
122
+ end
123
+
114
124
  def virtual_pin? pin
115
125
  pin.location && source_map.source.comment_at?(pin.location.range.ending)
116
126
  end
@@ -402,6 +412,15 @@ module Solargraph
402
412
 
403
413
  # @param pin [Pin::Method]
404
414
  def arity_problems_for(pin, arguments, location)
415
+ ([pin] + pin.overloads).map do |p|
416
+ result = pin_arity_problems_for(p, arguments, location)
417
+ return [] if result.empty?
418
+ result
419
+ end.flatten.uniq(&:message)
420
+ end
421
+
422
+ # @param pin [Pin::Method]
423
+ def pin_arity_problems_for(pin, arguments, location)
405
424
  return [] unless pin.explicit?
406
425
  return [] if pin.parameters.empty? && arguments.empty?
407
426
  if pin.parameters.empty?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Solargraph
4
- VERSION = '0.44.0'
4
+ VERSION = '0.45.0'
5
5
  end
@@ -101,6 +101,7 @@ module Solargraph
101
101
  Override.method_return('File.dirname', 'String'),
102
102
  Override.method_return('File.extname', 'String'),
103
103
  Override.method_return('File.join', 'String'),
104
+ Override.method_return('File.open', 'File'),
104
105
 
105
106
  Override.from_comment('Float#+', %(
106
107
  @param y [Numeric]
@@ -110,14 +111,15 @@ module Solargraph
110
111
  Override.from_comment('Hash#[]', %(
111
112
  @return_value_parameter
112
113
  )),
113
-
114
114
  # @todo This override isn't robust enough. It needs to allow for
115
115
  # parameterized Hash types, e.g., [Hash{Symbol => String}].
116
116
  Override.from_comment('Hash#[]=', %(
117
117
  @param_tuple
118
118
  )),
119
-
120
119
  Override.method_return('Hash#merge', 'Hash'),
120
+ Override.from_comment('Hash#store', %{
121
+ @overload store(key, value)
122
+ }),
121
123
 
122
124
  Override.from_comment('Integer#+', %(
123
125
  @param y [Numeric]
@@ -181,6 +183,9 @@ module Solargraph
181
183
  )
182
184
 
183
185
  PINS = [
186
+ # HACK: Extending Hash is not accurate to the implementation, but
187
+ # accurate enough to the behavior
188
+ Pin::Reference::Extend.new(closure: Pin::Namespace.new(name: 'ENV'), name: 'Hash'),
184
189
  Pin::Reference::Superclass.new(closure: Pin::Namespace.new(name: 'File'), name: 'IO'),
185
190
  Pin::Reference::Superclass.new(closure: Pin::Namespace.new(name: 'Integer'), name: 'Numeric'),
186
191
  Pin::Reference::Superclass.new(closure: Pin::Namespace.new(name: 'Float'), name: 'Numeric')
@@ -88,7 +88,7 @@ module Solargraph
88
88
  @rebindable_method_names ||= pins_by_class(Pin::Method)
89
89
  .select { |pin| pin.comments && pin.comments.include?('@yieldself') }
90
90
  .map(&:name)
91
- .concat(['instance_eval', 'instance_exec', 'class_eval', 'class_exec', 'module_eval', 'module_exec'])
91
+ .concat(['instance_eval', 'instance_exec', 'class_eval', 'class_exec', 'module_eval', 'module_exec', 'define_method'])
92
92
  .to_set
93
93
  end
94
94
 
@@ -343,7 +343,9 @@ module Solargraph
343
343
  # @param path [String]
344
344
  # @return [Gem::Specification]
345
345
  def spec_for_require path
346
- spec = Gem::Specification.find_by_path(path) || Gem::Specification.find_by_name(path.split('/').first)
346
+ name = path.split('/').first
347
+ spec = Gem::Specification.find_by_name(name, @gemset[name])
348
+
347
349
  # Avoid loading the spec again if it's going to be skipped anyway
348
350
  return spec if @source_gems.include?(spec.name)
349
351
  # Avoid loading the spec again if it's already the correct version
data/lib/solargraph.rb CHANGED
@@ -59,8 +59,8 @@ module Solargraph
59
59
  #
60
60
  # @return [void]
61
61
  def self.with_clean_env &block
62
- meth = if Bundler.respond_to?(:with_unbundled_env)
63
- :with_unbundled_env
62
+ meth = if Bundler.respond_to?(:with_original_env)
63
+ :with_original_env
64
64
  else
65
65
  :with_clean_env
66
66
  end
data/solargraph.gemspec CHANGED
@@ -34,7 +34,7 @@ Gem::Specification.new do |s|
34
34
  s.add_runtime_dependency 'tilt', '~> 2.0'
35
35
  s.add_runtime_dependency 'yard', '~> 0.9', '>= 0.9.24'
36
36
 
37
- s.add_development_dependency 'pry', '~> 0.11.3'
37
+ s.add_development_dependency 'pry'
38
38
  s.add_development_dependency 'public_suffix', '~> 3.1'
39
39
  s.add_development_dependency 'rspec', '~> 3.5', '>= 3.5.0'
40
40
  s.add_development_dependency 'simplecov', '~> 0.14'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: solargraph
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.44.0
4
+ version: 0.45.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fred Snyder
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-09-27 00:00:00.000000000 Z
11
+ date: 2022-05-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: backport
@@ -222,16 +222,16 @@ dependencies:
222
222
  name: pry
223
223
  requirement: !ruby/object:Gem::Requirement
224
224
  requirements:
225
- - - "~>"
225
+ - - ">="
226
226
  - !ruby/object:Gem::Version
227
- version: 0.11.3
227
+ version: '0'
228
228
  type: :development
229
229
  prerelease: false
230
230
  version_requirements: !ruby/object:Gem::Requirement
231
231
  requirements:
232
- - - "~>"
232
+ - - ">="
233
233
  - !ruby/object:Gem::Version
234
- version: 0.11.3
234
+ version: '0'
235
235
  - !ruby/object:Gem::Dependency
236
236
  name: public_suffix
237
237
  requirement: !ruby/object:Gem::Requirement
@@ -301,6 +301,7 @@ executables:
301
301
  extensions: []
302
302
  extra_rdoc_files: []
303
303
  files:
304
+ - ".github/workflows/rspec.yml"
304
305
  - ".gitignore"
305
306
  - ".rspec"
306
307
  - ".travis.yml"
@@ -510,6 +511,7 @@ files:
510
511
  - lib/solargraph/source/chain/link.rb
511
512
  - lib/solargraph/source/chain/literal.rb
512
513
  - lib/solargraph/source/chain/or.rb
514
+ - lib/solargraph/source/chain/q_call.rb
513
515
  - lib/solargraph/source/chain/variable.rb
514
516
  - lib/solargraph/source/chain/z_super.rb
515
517
  - lib/solargraph/source/change.rb
@@ -571,7 +573,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
571
573
  - !ruby/object:Gem::Version
572
574
  version: '0'
573
575
  requirements: []
574
- rubygems_version: 3.1.6
576
+ rubygems_version: 3.3.7
575
577
  signing_key:
576
578
  specification_version: 4
577
579
  summary: A Ruby language server