Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: ruby-syntax-tree/syntax_tree
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v6.0.1
Choose a base ref
...
head repository: ruby-syntax-tree/syntax_tree
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: v6.0.2
Choose a head ref
  • 16 commits
  • 10 files changed
  • 6 contributors

Commits on Feb 27, 2023

  1. Copy the full SHA
    ed6e206 View commit details
  2. Disable reflection type verification on truffleruby

    * It fails transiently and Ripper does not seem to provide any way to
      investigate the actual error/exception.
    eregon committed Feb 27, 2023
    Copy the full SHA
    d3410ff View commit details
  3. Merge pull request #321 from eregon/fix-truffleruby-ci

    Fix CI for TruffleRuby
    kddnewton authored Feb 27, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    adde5da View commit details
  4. Copy the full SHA
    58d2ab4 View commit details
  5. Merge pull request #323 from ruby-syntax-tree/fix-ast-output

    Fix AST output for Command and CommandCall nodes
    kddnewton authored Feb 27, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    451085c View commit details

Commits on Mar 1, 2023

  1. Bump rubocop from 1.46.0 to 1.47.0

    Bumps [rubocop](https://github.com/rubocop/rubocop) from 1.46.0 to 1.47.0.
    - [Release notes](https://github.com/rubocop/rubocop/releases)
    - [Changelog](https://github.com/rubocop/rubocop/blob/master/CHANGELOG.md)
    - [Commits](rubocop/rubocop@v1.46.0...v1.47.0)
    
    ---
    updated-dependencies:
    - dependency-name: rubocop
      dependency-type: direct:development
      update-type: version-update:semver-minor
    ...
    
    Signed-off-by: dependabot[bot] <support@github.com>
    dependabot[bot] authored Mar 1, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    ead8874 View commit details
  2. Merge pull request #325 from ruby-syntax-tree/dependabot/bundler/rubo…

    …cop-1.47.0
    
    Bump rubocop from 1.46.0 to 1.47.0
    github-actions[bot] authored Mar 1, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    654ebcb View commit details

Commits on Mar 2, 2023

  1. Fix lambda local locations

    Co-authored-by: Kevin Newton <kddnewton@users.noreply.github.com>
    vinistock and kddnewton committed Mar 2, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    vinistock Vinicius Stock
    Copy the full SHA
    2367cb4 View commit details
  2. Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    8a51e7c View commit details
  3. Add support for regexp locals

    Co-authored-by: Kevin Newton <kddnewton@users.noreply.github.com>
    vinistock and kddnewton committed Mar 2, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    vinistock Vinicius Stock
    Copy the full SHA
    ac63bef View commit details
  4. Add support for lambda and block locals

    Co-authored-by: Kevin Newton <kddnewton@users.noreply.github.com>
    vinistock and kddnewton committed Mar 2, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    vinistock Vinicius Stock
    Copy the full SHA
    039c087 View commit details
  5. Fix test for pinned variable

    Co-authored-by: Kevin Newton <kddnewton@users.noreply.github.com>
    vinistock and kddnewton committed Mar 2, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    vinistock Vinicius Stock
    Copy the full SHA
    48a630a View commit details
  6. Fix WithScope visits for deconstructed block params

    Co-authored-by: Kevin Newton <kddnewton@users.noreply.github.com>
    vinistock and kddnewton committed Mar 2, 2023

    Verified

    This commit was signed with the committer’s verified signature.
    vinistock Vinicius Stock
    Copy the full SHA
    77bdc12 View commit details
  7. Merge pull request #329 from vinistock/vs/add_support_for_more_locals

    Add support for more locals in WithScope
    kddnewton authored Mar 2, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    384a0d2 View commit details
  8. Merge pull request #328 from vinistock/vs/fix_deconstructed_block_params

    Fix WithScope visits for deconstructed block params
    kddnewton authored Mar 2, 2023

    Verified

    This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
    Copy the full SHA
    1510e4f View commit details

Commits on Mar 3, 2023

  1. Bump to version 6.0.2

    kddnewton committed Mar 3, 2023
    Copy the full SHA
    4864692 View commit details
Showing with 264 additions and 46 deletions.
  1. +14 −1 CHANGELOG.md
  2. +4 −4 Gemfile.lock
  3. +2 −0 lib/syntax_tree/field_visitor.rb
  4. +6 −0 lib/syntax_tree/parser.rb
  5. +2 −1 lib/syntax_tree/reflection.rb
  6. +1 −1 lib/syntax_tree/version.rb
  7. +71 −1 lib/syntax_tree/with_scope.rb
  8. +48 −0 test/parser_test.rb
  9. +37 −33 test/test_helper.rb
  10. +79 −5 test/with_scope_test.rb
15 changes: 14 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -6,6 +6,18 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a

## [Unreleased]

## [6.0.2] - 2023-03-03

### Added

- The `WithScope` visitor mixin will now additionally report local variables defined through regular expression named captures.
- The `WithScope` visitor mixin now properly handles destructured splat arguments in required positions.

### Changed

- Fixed the AST output by adding blocks to `Command` and `CommandCall` nodes in the `FieldVisitor`.
- Fixed the location of lambda local variables (e.g., `->(; a) {}`).

## [6.0.1] - 2023-02-26

### Added
@@ -572,7 +584,8 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a

- 🎉 Initial release! 🎉

[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.1...HEAD
[unreleased]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.2...HEAD
[6.0.2]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.1...v6.0.2
[6.0.1]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v6.0.0...v6.0.1
[6.0.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.3.0...v6.0.0
[5.3.0]: https://github.com/ruby-syntax-tree/syntax_tree/compare/v5.2.0...v5.3.0
8 changes: 4 additions & 4 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
syntax_tree (6.0.1)
syntax_tree (6.0.2)
prettier_print (>= 1.2.0)

GEM
@@ -19,7 +19,7 @@ GEM
rake (13.0.6)
regexp_parser (2.7.0)
rexml (3.2.5)
rubocop (1.46.0)
rubocop (1.47.0)
json (~> 2.3)
parallel (~> 1.10)
parser (>= 3.2.0.0)
@@ -29,9 +29,9 @@ GEM
rubocop-ast (>= 1.26.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 3.0)
rubocop-ast (1.26.0)
rubocop-ast (1.27.0)
parser (>= 3.2.1.0)
ruby-progressbar (1.11.0)
ruby-progressbar (1.12.0)
simplecov (0.22.0)
docile (~> 1.1)
simplecov-html (~> 0.11)
2 changes: 2 additions & 0 deletions lib/syntax_tree/field_visitor.rb
Original file line number Diff line number Diff line change
@@ -263,6 +263,7 @@ def visit_command(node)
node(node, "command") do
field("message", node.message)
field("arguments", node.arguments)
field("block", node.block) if node.block
comments(node)
end
end
@@ -273,6 +274,7 @@ def visit_command_call(node)
field("operator", node.operator)
field("message", node.message)
field("arguments", node.arguments) if node.arguments
field("block", node.block) if node.block
comments(node)
end
end
6 changes: 6 additions & 0 deletions lib/syntax_tree/parser.rb
Original file line number Diff line number Diff line change
@@ -2391,8 +2391,14 @@ def lambda_locals(source)
}
}

parent_line = lineno - 1
parent_column =
consume_token(Semicolon).location.start_column - tokens[index][0][1]

tokens[(index + 1)..].each_with_object([]) do |token, locals|
(lineno, column), type, value, = token
column += parent_column if lineno == 1
lineno += parent_line

# Make the state transition for the parser. If there isn't a transition
# from the current state to a new state for this type, then we're in a
3 changes: 2 additions & 1 deletion lib/syntax_tree/reflection.rb
Original file line number Diff line number Diff line change
@@ -176,7 +176,8 @@ def parse_comments(statements, index)
program =
SyntaxTree.parse(SyntaxTree.read(File.expand_path("node.rb", __dir__)))

main_statements = program.statements.body.last.bodystmt.statements.body
program_statements = program.statements
main_statements = program_statements.body.last.bodystmt.statements.body
main_statements.each_with_index do |main_statement, main_statement_index|
# Ensure we are only looking at class declarations.
next unless main_statement.is_a?(SyntaxTree::ClassDeclaration)
2 changes: 1 addition & 1 deletion lib/syntax_tree/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# frozen_string_literal: true

module SyntaxTree
VERSION = "6.0.1"
VERSION = "6.0.2"
end
72 changes: 71 additions & 1 deletion lib/syntax_tree/with_scope.rb
Original file line number Diff line number Diff line change
@@ -189,6 +189,15 @@ def visit_blockarg(node)
super
end

def visit_block_var(node)
node.locals.each do |local|
current_scope.add_local_definition(local, :variable)
end

super
end
alias visit_lambda_var visit_block_var

# Visit for keeping track of local variable definitions
def visit_var_field(node)
value = node.value
@@ -217,11 +226,72 @@ def visit_var_ref(node)
super
end

# When using regex named capture groups, vcalls might actually be a variable
def visit_vcall(node)
value = node.value
definition = current_scope.find_local(value.value)
current_scope.add_local_usage(value, definition.type) if definition

super
end

# Visit for capturing local variables defined in regex named capture groups
def visit_binary(node)
if node.operator == :=~
left = node.left

if left.is_a?(RegexpLiteral) && left.parts.length == 1 &&
left.parts.first.is_a?(TStringContent)
content = left.parts.first

value = content.value
location = content.location
start_line = location.start_line

Regexp
.new(value, Regexp::FIXEDENCODING)
.names
.each do |name|
offset = value.index(/\(\?<#{Regexp.escape(name)}>/)
line = start_line + value[0...offset].count("\n")

# We need to add 3 to account for these three characters
# prefixing a named capture (?<
column = location.start_column + offset + 3
if value[0...offset].include?("\n")
column =
value[0...offset].length - value[0...offset].rindex("\n") +
3 - 1
end

ident_location =
Location.new(
start_line: line,
start_char: location.start_char + offset,
start_column: column,
end_line: line,
end_char: location.start_char + offset + name.length,
end_column: column + name.length
)

identifier = Ident.new(value: name, location: ident_location)
current_scope.add_local_definition(identifier, :variable)
end
end
end

super
end

private

def add_argument_definitions(list)
list.each do |param|
if param.is_a?(SyntaxTree::MLHSParen)
case param
when ArgStar
value = param.value
current_scope.add_local_definition(value, :argument) if value
when MLHSParen
add_argument_definitions(param.contents.parts)
else
current_scope.add_local_definition(param, :argument)
48 changes: 48 additions & 0 deletions test/parser_test.rb
Original file line number Diff line number Diff line change
@@ -74,5 +74,53 @@ def test_does_not_choke_on_invalid_characters_in_source_string
\xC5
RUBY
end

def test_lambda_vars_with_parameters_location
tree = SyntaxTree.parse(<<~RUBY)
# comment
# comment
->(_i; a) { a }
RUBY

local_location =
tree.statements.body.last.params.contents.locals.first.location

assert_equal(3, local_location.start_line)
assert_equal(3, local_location.end_line)
assert_equal(7, local_location.start_column)
assert_equal(8, local_location.end_column)
end

def test_lambda_vars_location
tree = SyntaxTree.parse(<<~RUBY)
# comment
# comment
->(; a) { a }
RUBY

local_location =
tree.statements.body.last.params.contents.locals.first.location

assert_equal(3, local_location.start_line)
assert_equal(3, local_location.end_line)
assert_equal(5, local_location.start_column)
assert_equal(6, local_location.end_column)
end

def test_multiple_lambda_vars_location
tree = SyntaxTree.parse(<<~RUBY)
# comment
# comment
->(; a, b, c) { a }
RUBY

local_location =
tree.statements.body.last.params.contents.locals.last.location

assert_equal(3, local_location.start_line)
assert_equal(3, local_location.end_line)
assert_equal(11, local_location.start_column)
assert_equal(12, local_location.end_column)
end
end
end
70 changes: 37 additions & 33 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
@@ -1,46 +1,50 @@
# frozen_string_literal: true

require "simplecov"
SimpleCov.start do
add_filter("idempotency_test.rb") unless ENV["CI"]
add_group("lib", "lib")
add_group("test", "test")
unless RUBY_ENGINE == "truffleruby"
require "simplecov"
SimpleCov.start do
add_filter("idempotency_test.rb") unless ENV["CI"]
add_group("lib", "lib")
add_group("test", "test")
end
end

$LOAD_PATH.unshift(File.expand_path("../lib", __dir__))
require "syntax_tree"
require "syntax_tree/cli"

# Here we are going to establish type verification whenever a new node is
# created. We do this through the reflection module, which in turn parses the
# source code of the node classes.
require "syntax_tree/reflection"
SyntaxTree::Reflection.nodes.each do |name, node|
next if name == :Statements

clazz = SyntaxTree.const_get(name)
parameters = clazz.instance_method(:initialize).parameters

# First, verify that all of the parameters listed in the list of attributes.
# If there are any parameters that aren't listed in the attributes, then
# something went wrong with the parsing in the reflection module.
raise unless (parameters.map(&:last) - node.attributes.keys).empty?

# Now we're going to use an alias chain to redefine the initialize method to
# include type checking.
clazz.alias_method(:initialize_without_verify, :initialize)
clazz.define_method(:initialize) do |**kwargs|
kwargs.each do |kwarg, value|
attribute = node.attributes.fetch(kwarg)

unless attribute.type === value
raise TypeError,
"invalid type for #{name}##{kwarg}, expected " \
"#{attribute.type.inspect}, got #{value.inspect}"
unless RUBY_ENGINE == "truffleruby"
# Here we are going to establish type verification whenever a new node is
# created. We do this through the reflection module, which in turn parses the
# source code of the node classes.
require "syntax_tree/reflection"
SyntaxTree::Reflection.nodes.each do |name, node|
next if name == :Statements

clazz = SyntaxTree.const_get(name)
parameters = clazz.instance_method(:initialize).parameters

# First, verify that all of the parameters listed in the list of attributes.
# If there are any parameters that aren't listed in the attributes, then
# something went wrong with the parsing in the reflection module.
raise unless (parameters.map(&:last) - node.attributes.keys).empty?

# Now we're going to use an alias chain to redefine the initialize method to
# include type checking.
clazz.alias_method(:initialize_without_verify, :initialize)
clazz.define_method(:initialize) do |**kwargs|
kwargs.each do |kwarg, value|
attribute = node.attributes.fetch(kwarg)

unless attribute.type === value
raise TypeError,
"invalid type for #{name}##{kwarg}, expected " \
"#{attribute.type.inspect}, got #{value.inspect}"
end
end
end

initialize_without_verify(**kwargs)
initialize_without_verify(**kwargs)
end
end
end

Loading