syntax_finder 0.1.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 +7 -0
 - data/LICENSE.txt +21 -0
 - data/README.md +85 -0
 - data/Rakefile +12 -0
 - data/lib/syntax_finder/version.rb +3 -0
 - data/lib/syntax_finder.rb +272 -0
 - data/samples/all_lvar_finder.rb +27 -0
 - data/samples/all_method_name_finder.rb +22 -0
 - data/samples/all_regexp_finder.rb +13 -0
 - data/samples/assert_equal_paren.rb +17 -0
 - data/samples/call_paren_finder.rb +13 -0
 - data/samples/def_params_finder.rb +27 -0
 - data/samples/def_paren_finder.rb +13 -0
 - data/samples/if_cond_cont_indent_finder.rb +29 -0
 - data/samples/if_then_finder.rb +12 -0
 - data/samples/integer_range_finder.rb +18 -0
 - data/samples/integer_size_finder.rb +10 -0
 - data/samples/method_name_finder.rb +18 -0
 - data/samples/pragma_finder.rb +17 -0
 - data/samples/required_kwcall_wo_paren_finder.rb +18 -0
 - data/samples/required_kwdef_wo_paren_finder.rb +14 -0
 - data/samples/singleton_class_in_def_finder.rb +18 -0
 - metadata +66 -0
 
    
        checksums.yaml
    ADDED
    
    | 
         @@ -0,0 +1,7 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            ---
         
     | 
| 
      
 2 
     | 
    
         
            +
            SHA256:
         
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: f396dd84ec243335733edbe3abc5c22c0f32ea6ade48dfc16336ad385f0a2bd8
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 3e6c73de9f48e65ed5f645b7c0b8c25c139826202d739ca884545c1ff48a7b40
         
     | 
| 
      
 5 
     | 
    
         
            +
            SHA512:
         
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 0ce254cd70a3952f0a1d976cbd5ba9fe776ebb81137d8387d19b917548e4e3a7c5521ecdece26c70ebd712a67491327d1379855ad4dc058780f845b4bbaef0d0
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 78a82aa09159d3cf9e764ea342c71e60a21574f0b55acfb76510ab802bdb8a9e7b5a042c200fdf62704958dce4304aa864f3d9a66211c19f707eb55ab46c58a8
         
     | 
    
        data/LICENSE.txt
    ADDED
    
    | 
         @@ -0,0 +1,21 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            The MIT License (MIT)
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Copyright (c) 2025 Koichi Sasada
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            Permission is hereby granted, free of charge, to any person obtaining a copy
         
     | 
| 
      
 6 
     | 
    
         
            +
            of this software and associated documentation files (the "Software"), to deal
         
     | 
| 
      
 7 
     | 
    
         
            +
            in the Software without restriction, including without limitation the rights
         
     | 
| 
      
 8 
     | 
    
         
            +
            to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
         
     | 
| 
      
 9 
     | 
    
         
            +
            copies of the Software, and to permit persons to whom the Software is
         
     | 
| 
      
 10 
     | 
    
         
            +
            furnished to do so, subject to the following conditions:
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            The above copyright notice and this permission notice shall be included in
         
     | 
| 
      
 13 
     | 
    
         
            +
            all copies or substantial portions of the Software.
         
     | 
| 
      
 14 
     | 
    
         
            +
             
     | 
| 
      
 15 
     | 
    
         
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
         
     | 
| 
      
 16 
     | 
    
         
            +
            IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
         
     | 
| 
      
 17 
     | 
    
         
            +
            FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
         
     | 
| 
      
 18 
     | 
    
         
            +
            AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
         
     | 
| 
      
 19 
     | 
    
         
            +
            LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
         
     | 
| 
      
 20 
     | 
    
         
            +
            OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
         
     | 
| 
      
 21 
     | 
    
         
            +
            THE SOFTWARE.
         
     | 
    
        data/README.md
    ADDED
    
    | 
         @@ -0,0 +1,85 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # SyntaxFinder
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            Find Ruby syntax patterns with Prism.
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            ## How to use
         
     | 
| 
      
 6 
     | 
    
         
            +
             
     | 
| 
      
 7 
     | 
    
         
            +
            Write your pattern in Ruby.
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
            ```ruby
         
     | 
| 
      
 10 
     | 
    
         
            +
            # samples/if_then_finder.rb
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
            # Count up all `if` statements and `then` keywords.
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
            class IfThenFinder < SyntaxFinder
         
     | 
| 
      
 17 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 18 
     | 
    
         
            +
                if node.type == :if_node
         
     | 
| 
      
 19 
     | 
    
         
            +
                  inc :if
         
     | 
| 
      
 20 
     | 
    
         
            +
                  inc :then if node.then_keyword_loc
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
      
 24 
     | 
    
         
            +
            ```
         
     | 
| 
      
 25 
     | 
    
         
            +
             
     | 
| 
      
 26 
     | 
    
         
            +
            and run the script with Ruby script file names listed in STDIN like this.
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
            ```sh
         
     | 
| 
      
 29 
     | 
    
         
            +
            $ find ruby/ruby -name '*.rb' | ruby samples/if_then_finder.rb
         
     | 
| 
      
 30 
     | 
    
         
            +
            [[:if, 32153], [:FILES, 9558], [:then, 3627], [:FAILED_PARSE, 5]]
         
     | 
| 
      
 31 
     | 
    
         
            +
            ```
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
            In this case, with *.rb files in ruby/ruby directory, there are
         
     | 
| 
      
 34 
     | 
    
         
            +
             
     | 
| 
      
 35 
     | 
    
         
            +
            * 9,558 files
         
     | 
| 
      
 36 
     | 
    
         
            +
            * 32,153 if statements
         
     | 
| 
      
 37 
     | 
    
         
            +
            * 3,627 then keywords
         
     | 
| 
      
 38 
     | 
    
         
            +
            * 5 files are failed because of parsing.
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
            You can specify files with arguments like that:
         
     | 
| 
      
 41 
     | 
    
         
            +
             
     | 
| 
      
 42 
     | 
    
         
            +
            ```sh
         
     | 
| 
      
 43 
     | 
    
         
            +
            $ ruby samples/if_then_finder.rb samples/*.rb
         
     | 
| 
      
 44 
     | 
    
         
            +
            [[[:FILES, 16]], [[:if, 26], [:then, 1]]]
         
     | 
| 
      
 45 
     | 
    
         
            +
            ```
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
            You can specify `-j` or `-jN` for parallel processing like that:
         
     | 
| 
      
 48 
     | 
    
         
            +
             
     | 
| 
      
 49 
     | 
    
         
            +
            ```sh
         
     | 
| 
      
 50 
     | 
    
         
            +
            $ time find ruby/ruby -name '*.rb' | ruby samples/if_then_finder.rb
         
     | 
| 
      
 51 
     | 
    
         
            +
            [[:if, 32153], [:FILES, 9558], [:then, 3627], [:FAILED_PARSE, 5]]
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
            real    0m3.627s
         
     | 
| 
      
 54 
     | 
    
         
            +
            user    0m3.451s
         
     | 
| 
      
 55 
     | 
    
         
            +
            sys     0m0.219s
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
            $ time find ruby/ruby -name '*.rb' | ruby samples/if_then_finder.rb -j
         
     | 
| 
      
 58 
     | 
    
         
            +
            [[:if, 32153], [:FILES, 9558], [:then, 3627], [:FAILED_PARSE, 5]]
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
            real    0m0.962s
         
     | 
| 
      
 61 
     | 
    
         
            +
            user    0m7.944s
         
     | 
| 
      
 62 
     | 
    
         
            +
            sys     0m0.854s
         
     | 
| 
      
 63 
     | 
    
         
            +
            ```
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
            ## How to write a finder
         
     | 
| 
      
 66 
     | 
    
         
            +
             
     | 
| 
      
 67 
     | 
    
         
            +
            1. Define a class derived with `SyntaxFinder` class.
         
     | 
| 
      
 68 
     | 
    
         
            +
            2. Define a `look(node)` method with your expected pattern. `node` is Prism's node.
         
     | 
| 
      
 69 
     | 
    
         
            +
            3. Use `inc(key)` if you want to aggregate the statistics in `look()`.
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
      
 71 
     | 
    
         
            +
            See `examples/` for more examples.
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
            ## Development
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
            After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test-unit` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
         
     | 
| 
      
 76 
     | 
    
         
            +
             
     | 
| 
      
 77 
     | 
    
         
            +
            To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
            ## Contributing
         
     | 
| 
      
 80 
     | 
    
         
            +
             
     | 
| 
      
 81 
     | 
    
         
            +
            Bug reports and pull requests are welcome on GitHub at https://github.com/ko1/syntax_finder.
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
            ## License
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
            The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
         
     | 
    
        data/Rakefile
    ADDED
    
    
| 
         @@ -0,0 +1,272 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'prism'
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'optparse'
         
     | 
| 
      
 3 
     | 
    
         
            +
            require_relative 'syntax_finder/version'
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class SyntaxFinder
         
     | 
| 
      
 6 
     | 
    
         
            +
              def initialize file
         
     | 
| 
      
 7 
     | 
    
         
            +
                @file = file
         
     | 
| 
      
 8 
     | 
    
         
            +
              end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
              ### Traversing and looking methods
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
              # Please write your pattern here to investigate the node.
         
     | 
| 
      
 13 
     | 
    
         
            +
              # This method is called for each node.
         
     | 
| 
      
 14 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 15 
     | 
    
         
            +
                # override here
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
              # If you need to pass some context while traversing,
         
     | 
| 
      
 19 
     | 
    
         
            +
              # redefine this method with context like `def traverse node, context = nil`.
         
     | 
| 
      
 20 
     | 
    
         
            +
              def traverse node
         
     | 
| 
      
 21 
     | 
    
         
            +
                look node
         
     | 
| 
      
 22 
     | 
    
         
            +
                traverse_rest node
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
             
     | 
| 
      
 25 
     | 
    
         
            +
              def traverse_rest node
         
     | 
| 
      
 26 
     | 
    
         
            +
                node.child_nodes.compact.each do |child|
         
     | 
| 
      
 27 
     | 
    
         
            +
                  traverse child
         
     | 
| 
      
 28 
     | 
    
         
            +
                end
         
     | 
| 
      
 29 
     | 
    
         
            +
              end 
         
     | 
| 
      
 30 
     | 
    
         
            +
             
     | 
| 
      
 31 
     | 
    
         
            +
              ### Utility methods for look
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
              # node location information
         
     | 
| 
      
 34 
     | 
    
         
            +
              def nloc node
         
     | 
| 
      
 35 
     | 
    
         
            +
                "#{@file}:#{node.location.start_line}"
         
     | 
| 
      
 36 
     | 
    
         
            +
              end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
              # node line (1st line of node lines)
         
     | 
| 
      
 39 
     | 
    
         
            +
              def nline node
         
     | 
| 
      
 40 
     | 
    
         
            +
                node.slice_lines.lines.first.chomp
         
     | 
| 
      
 41 
     | 
    
         
            +
              end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
              # node lines
         
     | 
| 
      
 44 
     | 
    
         
            +
              def nlines node
         
     | 
| 
      
 45 
     | 
    
         
            +
                node.slice_lines
         
     | 
| 
      
 46 
     | 
    
         
            +
              end
         
     | 
| 
      
 47 
     | 
    
         
            +
             
     | 
| 
      
 48 
     | 
    
         
            +
              def success? r
         
     | 
| 
      
 49 
     | 
    
         
            +
                return true if r.success?
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                if r.errors.size == 1 &&
         
     | 
| 
      
 52 
     | 
    
         
            +
                   r.errors.first.type == :script_not_found
         
     | 
| 
      
 53 
     | 
    
         
            +
                  true
         
     | 
| 
      
 54 
     | 
    
         
            +
                else
         
     | 
| 
      
 55 
     | 
    
         
            +
                  false
         
     | 
| 
      
 56 
     | 
    
         
            +
                end
         
     | 
| 
      
 57 
     | 
    
         
            +
              end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
              # any object can be a key.
         
     | 
| 
      
 60 
     | 
    
         
            +
              # if key is [A-Z_]+, it's system key.
         
     | 
| 
      
 61 
     | 
    
         
            +
              # it's returns true if the key is already exists.
         
     | 
| 
      
 62 
     | 
    
         
            +
              def inc key
         
     | 
| 
      
 63 
     | 
    
         
            +
                has_key = @@result.has_key? key
         
     | 
| 
      
 64 
     | 
    
         
            +
                @@result[key] += 1
         
     | 
| 
      
 65 
     | 
    
         
            +
                has_key
         
     | 
| 
      
 66 
     | 
    
         
            +
              end
         
     | 
| 
      
 67 
     | 
    
         
            +
             
     | 
| 
      
 68 
     | 
    
         
            +
              def note key, note
         
     | 
| 
      
 69 
     | 
    
         
            +
                @@result_notes[key] << note
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
              def self.inc key
         
     | 
| 
      
 73 
     | 
    
         
            +
                @@result[key] += 1
         
     | 
| 
      
 74 
     | 
    
         
            +
              end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
              def self.note key, note
         
     | 
| 
      
 77 
     | 
    
         
            +
                @@result_notes[key] << note
         
     | 
| 
      
 78 
     | 
    
         
            +
              end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
              ### Execution management
         
     | 
| 
      
 81 
     | 
    
         
            +
             
     | 
| 
      
 82 
     | 
    
         
            +
              def start_traverse
         
     | 
| 
      
 83 
     | 
    
         
            +
                puts "Start #{@file}" if $DEBUG
         
     | 
| 
      
 84 
     | 
    
         
            +
             
     | 
| 
      
 85 
     | 
    
         
            +
                @root = Prism.parse_file(@file)
         
     | 
| 
      
 86 
     | 
    
         
            +
                if success?(@root)
         
     | 
| 
      
 87 
     | 
    
         
            +
                  traverse @ast = @root.value
         
     | 
| 
      
 88 
     | 
    
         
            +
                else
         
     | 
| 
      
 89 
     | 
    
         
            +
                  # pp [@file, @root.errors]
         
     | 
| 
      
 90 
     | 
    
         
            +
                  # inc :FAILED, @file
         
     | 
| 
      
 91 
     | 
    
         
            +
                  inc :FAILED_PARSE
         
     | 
| 
      
 92 
     | 
    
         
            +
                end
         
     | 
| 
      
 93 
     | 
    
         
            +
              rescue Interrupt
         
     | 
| 
      
 94 
     | 
    
         
            +
                exit
         
     | 
| 
      
 95 
     | 
    
         
            +
              rescue Exception => e
         
     | 
| 
      
 96 
     | 
    
         
            +
                pp [e, @file, e.backtrace] if $DEBUG
         
     | 
| 
      
 97 
     | 
    
         
            +
                inc [:FAILED, e.class]
         
     | 
| 
      
 98 
     | 
    
         
            +
              end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
              @@finders = [self]
         
     | 
| 
      
 101 
     | 
    
         
            +
             
     | 
| 
      
 102 
     | 
    
         
            +
              def self.inherited klass
         
     | 
| 
      
 103 
     | 
    
         
            +
                @@finders << klass
         
     | 
| 
      
 104 
     | 
    
         
            +
              end
         
     | 
| 
      
 105 
     | 
    
         
            +
             
     | 
| 
      
 106 
     | 
    
         
            +
              def self.last_finder
         
     | 
| 
      
 107 
     | 
    
         
            +
                @@finders.last
         
     | 
| 
      
 108 
     | 
    
         
            +
              end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
      
 110 
     | 
    
         
            +
              @@result = Hash.new(0)
         
     | 
| 
      
 111 
     | 
    
         
            +
              @@result_notes = Hash.new{|h, k| h[k] = []}
         
     | 
| 
      
 112 
     | 
    
         
            +
              @@opt = {}
         
     | 
| 
      
 113 
     | 
    
         
            +
             
     | 
| 
      
 114 
     | 
    
         
            +
              def self.setup_parallel finder, pn
         
     | 
| 
      
 115 
     | 
    
         
            +
                taskq = @@opt[:taskq] = Queue.new
         
     | 
| 
      
 116 
     | 
    
         
            +
                resq  = @@opt[:resq] = Queue.new
         
     | 
| 
      
 117 
     | 
    
         
            +
                term_cmd = '-- terminate'
         
     | 
| 
      
 118 
     | 
    
         
            +
             
     | 
| 
      
 119 
     | 
    
         
            +
                @@opt[:threads] = pn.times.map{
         
     | 
| 
      
 120 
     | 
    
         
            +
                  Thread.new _1 do |ti|
         
     | 
| 
      
 121 
     | 
    
         
            +
                    task_r, task_w = IO.pipe
         
     | 
| 
      
 122 
     | 
    
         
            +
                    res_r, res_w = IO.pipe
         
     | 
| 
      
 123 
     | 
    
         
            +
             
     | 
| 
      
 124 
     | 
    
         
            +
                    pid = fork do
         
     | 
| 
      
 125 
     | 
    
         
            +
                      trap(:INT, 'IGNORE')
         
     | 
| 
      
 126 
     | 
    
         
            +
             
     | 
| 
      
 127 
     | 
    
         
            +
                      loop do
         
     | 
| 
      
 128 
     | 
    
         
            +
                        file = task_r.gets.chomp
         
     | 
| 
      
 129 
     | 
    
         
            +
             
     | 
| 
      
 130 
     | 
    
         
            +
                        case file
         
     | 
| 
      
 131 
     | 
    
         
            +
                        when  term_cmd
         
     | 
| 
      
 132 
     | 
    
         
            +
                          @@result_notes.default_proc = nil
         
     | 
| 
      
 133 
     | 
    
         
            +
                          last_result = [@@result, @@result_notes]
         
     | 
| 
      
 134 
     | 
    
         
            +
                          break
         
     | 
| 
      
 135 
     | 
    
         
            +
                        else
         
     | 
| 
      
 136 
     | 
    
         
            +
                          kick finder, file
         
     | 
| 
      
 137 
     | 
    
         
            +
                          last_result = nil
         
     | 
| 
      
 138 
     | 
    
         
            +
                        end
         
     | 
| 
      
 139 
     | 
    
         
            +
                      rescue Interrupt
         
     | 
| 
      
 140 
     | 
    
         
            +
                        STDERR.puts "Worker is interrupted (pid: #{Process.pid})"
         
     | 
| 
      
 141 
     | 
    
         
            +
                        @@result_notes.default_proc = nil
         
     | 
| 
      
 142 
     | 
    
         
            +
                        last_result = [@@result, @@result_notes]
         
     | 
| 
      
 143 
     | 
    
         
            +
                        exit
         
     | 
| 
      
 144 
     | 
    
         
            +
                      ensure
         
     | 
| 
      
 145 
     | 
    
         
            +
                        res_w.puts Marshal.dump(last_result).dump
         
     | 
| 
      
 146 
     | 
    
         
            +
                      end
         
     | 
| 
      
 147 
     | 
    
         
            +
                    end
         
     | 
| 
      
 148 
     | 
    
         
            +
             
     | 
| 
      
 149 
     | 
    
         
            +
                    result = nil
         
     | 
| 
      
 150 
     | 
    
         
            +
             
     | 
| 
      
 151 
     | 
    
         
            +
                    while true
         
     | 
| 
      
 152 
     | 
    
         
            +
                      file = taskq.pop
         
     | 
| 
      
 153 
     | 
    
         
            +
                      task_w.puts file || term_cmd
         
     | 
| 
      
 154 
     | 
    
         
            +
                      result = Marshal.load(res_r.gets.chomp.undump) # wait for the result
         
     | 
| 
      
 155 
     | 
    
         
            +
                      
         
     | 
| 
      
 156 
     | 
    
         
            +
                      if result
         
     | 
| 
      
 157 
     | 
    
         
            +
                        resq << result
         
     | 
| 
      
 158 
     | 
    
         
            +
                        break
         
     | 
| 
      
 159 
     | 
    
         
            +
                      end
         
     | 
| 
      
 160 
     | 
    
         
            +
                    end
         
     | 
| 
      
 161 
     | 
    
         
            +
                  ensure
         
     | 
| 
      
 162 
     | 
    
         
            +
                    p [Thread.current, $!] if $DEBUG
         
     | 
| 
      
 163 
     | 
    
         
            +
                    Process.kill :KILL, pid
         
     | 
| 
      
 164 
     | 
    
         
            +
                    Process.waitpid pid
         
     | 
| 
      
 165 
     | 
    
         
            +
                  end
         
     | 
| 
      
 166 
     | 
    
         
            +
                }
         
     | 
| 
      
 167 
     | 
    
         
            +
              end
         
     | 
| 
      
 168 
     | 
    
         
            +
             
     | 
| 
      
 169 
     | 
    
         
            +
              def self.cleanup_parallel
         
     | 
| 
      
 170 
     | 
    
         
            +
                return unless taskq = @@opt[:taskq]
         
     | 
| 
      
 171 
     | 
    
         
            +
                trap(:INT){
         
     | 
| 
      
 172 
     | 
    
         
            +
                  trap(:INT, 'DEFAULT')
         
     | 
| 
      
 173 
     | 
    
         
            +
                }
         
     | 
| 
      
 174 
     | 
    
         
            +
                taskq.close
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
                @@opt[:parallel].times do |i|
         
     | 
| 
      
 177 
     | 
    
         
            +
                  results, notes = @@opt[:resq].pop
         
     | 
| 
      
 178 
     | 
    
         
            +
                  @@result.merge!(results){|k, a, b| a + b}
         
     | 
| 
      
 179 
     | 
    
         
            +
                  @@result_notes.merge!(notes)
         
     | 
| 
      
 180 
     | 
    
         
            +
                end
         
     | 
| 
      
 181 
     | 
    
         
            +
              end
         
     | 
| 
      
 182 
     | 
    
         
            +
             
     | 
| 
      
 183 
     | 
    
         
            +
              def self.parse_opt finder, argv
         
     | 
| 
      
 184 
     | 
    
         
            +
                o = OptionParser.new
         
     | 
| 
      
 185 
     | 
    
         
            +
                o.on '-v', '--verbose', 'VERBOSE mode' do
         
     | 
| 
      
 186 
     | 
    
         
            +
                  $VERBOSE = true
         
     | 
| 
      
 187 
     | 
    
         
            +
                end
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                o.on '-d', '--debug' do
         
     | 
| 
      
 190 
     | 
    
         
            +
                  $DEBUG = true
         
     | 
| 
      
 191 
     | 
    
         
            +
                end
         
     | 
| 
      
 192 
     | 
    
         
            +
             
     | 
| 
      
 193 
     | 
    
         
            +
                o.on '-q', '--quiet' do
         
     | 
| 
      
 194 
     | 
    
         
            +
                  @@opt[:quiet] = true
         
     | 
| 
      
 195 
     | 
    
         
            +
                end
         
     | 
| 
      
 196 
     | 
    
         
            +
             
     | 
| 
      
 197 
     | 
    
         
            +
                o.on '-j', '--jobs[=N]', 'Parallel mode' do
         
     | 
| 
      
 198 
     | 
    
         
            +
                  require 'etc'
         
     | 
| 
      
 199 
     | 
    
         
            +
             
     | 
| 
      
 200 
     | 
    
         
            +
                  pn = @@opt[:parallel] = _1&.to_i || Etc.nprocessors
         
     | 
| 
      
 201 
     | 
    
         
            +
                  setup_parallel finder, pn
         
     | 
| 
      
 202 
     | 
    
         
            +
                end
         
     | 
| 
      
 203 
     | 
    
         
            +
             
     | 
| 
      
 204 
     | 
    
         
            +
                o.parse! argv
         
     | 
| 
      
 205 
     | 
    
         
            +
                pp options: @@opt if $DEBUG
         
     | 
| 
      
 206 
     | 
    
         
            +
              end
         
     | 
| 
      
 207 
     | 
    
         
            +
             
     | 
| 
      
 208 
     | 
    
         
            +
              def self.kick finder, file
         
     | 
| 
      
 209 
     | 
    
         
            +
                finder.new(file).start_traverse
         
     | 
| 
      
 210 
     | 
    
         
            +
              end
         
     | 
| 
      
 211 
     | 
    
         
            +
             
     | 
| 
      
 212 
     | 
    
         
            +
              def self.aggregate_results
         
     | 
| 
      
 213 
     | 
    
         
            +
                if @@result_notes.size > 0
         
     | 
| 
      
 214 
     | 
    
         
            +
                  results =  @@result.sort_by{|k, v| -v}.map{|k, v|
         
     | 
| 
      
 215 
     | 
    
         
            +
                    if !(note = @@result_notes[k]).empty?
         
     | 
| 
      
 216 
     | 
    
         
            +
                      [k, v, note]
         
     | 
| 
      
 217 
     | 
    
         
            +
                    else
         
     | 
| 
      
 218 
     | 
    
         
            +
                      [k, v]
         
     | 
| 
      
 219 
     | 
    
         
            +
                    end
         
     | 
| 
      
 220 
     | 
    
         
            +
                  }
         
     | 
| 
      
 221 
     | 
    
         
            +
                else
         
     | 
| 
      
 222 
     | 
    
         
            +
                  results = @@result.sort_by{|k, v| -v}
         
     | 
| 
      
 223 
     | 
    
         
            +
                end
         
     | 
| 
      
 224 
     | 
    
         
            +
             
     | 
| 
      
 225 
     | 
    
         
            +
                results.partition{|key,|
         
     | 
| 
      
 226 
     | 
    
         
            +
                  Symbol === key && key.length > 1 && /^[A-Z_]+$/ =~ key
         
     | 
| 
      
 227 
     | 
    
         
            +
                }
         
     | 
| 
      
 228 
     | 
    
         
            +
              end
         
     | 
| 
      
 229 
     | 
    
         
            +
             
     | 
| 
      
 230 
     | 
    
         
            +
              def self.print_result
         
     | 
| 
      
 231 
     | 
    
         
            +
                system_results, user_results = aggregate_results
         
     | 
| 
      
 232 
     | 
    
         
            +
                pp [system_results, user_results]
         
     | 
| 
      
 233 
     | 
    
         
            +
              end
         
     | 
| 
      
 234 
     | 
    
         
            +
             
     | 
| 
      
 235 
     | 
    
         
            +
              def self.run finder, argv = ARGV
         
     | 
| 
      
 236 
     | 
    
         
            +
                @already_run = true
         
     | 
| 
      
 237 
     | 
    
         
            +
                parse_opt finder, argv
         
     | 
| 
      
 238 
     | 
    
         
            +
             
     | 
| 
      
 239 
     | 
    
         
            +
                files = argv.empty? ? ARGF : argv
         
     | 
| 
      
 240 
     | 
    
         
            +
                files.each do |f|
         
     | 
| 
      
 241 
     | 
    
         
            +
                  f = f.scrub.strip
         
     | 
| 
      
 242 
     | 
    
         
            +
                  next unless FileTest.file?(f)
         
     | 
| 
      
 243 
     | 
    
         
            +
              
         
     | 
| 
      
 244 
     | 
    
         
            +
                  inc :FILES
         
     | 
| 
      
 245 
     | 
    
         
            +
              
         
     | 
| 
      
 246 
     | 
    
         
            +
                  if q = @@opt[:taskq]
         
     | 
| 
      
 247 
     | 
    
         
            +
                    q << f
         
     | 
| 
      
 248 
     | 
    
         
            +
                  else
         
     | 
| 
      
 249 
     | 
    
         
            +
                    kick finder, f
         
     | 
| 
      
 250 
     | 
    
         
            +
                  end
         
     | 
| 
      
 251 
     | 
    
         
            +
                end
         
     | 
| 
      
 252 
     | 
    
         
            +
              rescue Interrupt
         
     | 
| 
      
 253 
     | 
    
         
            +
                STDERR.puts "-- Interrupted"
         
     | 
| 
      
 254 
     | 
    
         
            +
                exit
         
     | 
| 
      
 255 
     | 
    
         
            +
              ensure
         
     | 
| 
      
 256 
     | 
    
         
            +
                cleanup_parallel
         
     | 
| 
      
 257 
     | 
    
         
            +
                finder.print_result if finder
         
     | 
| 
      
 258 
     | 
    
         
            +
              end
         
     | 
| 
      
 259 
     | 
    
         
            +
             
     | 
| 
      
 260 
     | 
    
         
            +
              END{
         
     | 
| 
      
 261 
     | 
    
         
            +
                if !@already_run && @@finders.size == 2
         
     | 
| 
      
 262 
     | 
    
         
            +
                  self.run last_finder
         
     | 
| 
      
 263 
     | 
    
         
            +
                end
         
     | 
| 
      
 264 
     | 
    
         
            +
              }
         
     | 
| 
      
 265 
     | 
    
         
            +
            end
         
     | 
| 
      
 266 
     | 
    
         
            +
             
     | 
| 
      
 267 
     | 
    
         
            +
            # for pretty integer results
         
     | 
| 
      
 268 
     | 
    
         
            +
            class Integer
         
     | 
| 
      
 269 
     | 
    
         
            +
              def inspect
         
     | 
| 
      
 270 
     | 
    
         
            +
                self.to_s.gsub(/(\d)(?=\d{3}+$)/, '\\1_')
         
     | 
| 
      
 271 
     | 
    
         
            +
              end
         
     | 
| 
      
 272 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Cound up all of local variable names.
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class LVarFinder < SyntaxFinder
         
     | 
| 
      
 6 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 7 
     | 
    
         
            +
                if node.respond_to? :locals
         
     | 
| 
      
 8 
     | 
    
         
            +
                  node.locals.each{|lv|
         
     | 
| 
      
 9 
     | 
    
         
            +
                    case lv
         
     | 
| 
      
 10 
     | 
    
         
            +
                    when Symbol
         
     | 
| 
      
 11 
     | 
    
         
            +
                      inc lv
         
     | 
| 
      
 12 
     | 
    
         
            +
                    when Prism::BlockLocalVariableNode
         
     | 
| 
      
 13 
     | 
    
         
            +
                      inc lv.name
         
     | 
| 
      
 14 
     | 
    
         
            +
                    else
         
     | 
| 
      
 15 
     | 
    
         
            +
                      pp [node.type, lv.type, node.locals].inspect
         
     | 
| 
      
 16 
     | 
    
         
            +
                      exit
         
     | 
| 
      
 17 
     | 
    
         
            +
                    end
         
     | 
| 
      
 18 
     | 
    
         
            +
                  }
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
              def self.print_result
         
     | 
| 
      
 23 
     | 
    
         
            +
                @@result.sort_by{|lv, v| [-v, lv]}.each{|lv, v|
         
     | 
| 
      
 24 
     | 
    
         
            +
                  puts "#{lv}\t#{v}" 
         
     | 
| 
      
 25 
     | 
    
         
            +
                }
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Cound up all of method names.
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class MethodNameFinder < SyntaxFinder
         
     | 
| 
      
 6 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 7 
     | 
    
         
            +
                if node.type == :call_node
         
     | 
| 
      
 8 
     | 
    
         
            +
                  inc [:call, node.name]
         
     | 
| 
      
 9 
     | 
    
         
            +
                  # pp [nloc(node),nlines(node).lines.first.chomp] unless @@opt[:quiet]
         
     | 
| 
      
 10 
     | 
    
         
            +
                elsif node.type == :def_node
         
     | 
| 
      
 11 
     | 
    
         
            +
                  inc [:def, node.name]
         
     | 
| 
      
 12 
     | 
    
         
            +
                  # pp [nloc(node),nlines(node).lines.first.chomp] unless @@opt[:quiet]
         
     | 
| 
      
 13 
     | 
    
         
            +
                end
         
     | 
| 
      
 14 
     | 
    
         
            +
              end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
              def self.print_result
         
     | 
| 
      
 17 
     | 
    
         
            +
                @@result.sort_by{|(t, m), v| [t, -v]}.each{|(t, m), v|
         
     | 
| 
      
 18 
     | 
    
         
            +
                  puts "#{v}\t#{t}\t#{m}" 
         
     | 
| 
      
 19 
     | 
    
         
            +
                }
         
     | 
| 
      
 20 
     | 
    
         
            +
              end
         
     | 
| 
      
 21 
     | 
    
         
            +
            end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            relative 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Cound up all of regexps.
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class RegexpFinder < SyntaxFinder
         
     | 
| 
      
 6 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 7 
     | 
    
         
            +
                if node.type == :regular_expression_node
         
     | 
| 
      
 8 
     | 
    
         
            +
                  # pp [[nloc(node), nlines(node).lines]] unless @@opt[:quiet]
         
     | 
| 
      
 9 
     | 
    
         
            +
                  inc :regexp
         
     | 
| 
      
 10 
     | 
    
         
            +
                  inc node.unescaped
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # cound `assert*(params)` and `assert params` patterns.`
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class AssertEqualParenFinder < SyntaxFinder
         
     | 
| 
      
 6 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 7 
     | 
    
         
            +
                if node.type == :call_node && 
         
     | 
| 
      
 8 
     | 
    
         
            +
                   /^assert/ =~ node.name && node.arguments
         
     | 
| 
      
 9 
     | 
    
         
            +
                then
         
     | 
| 
      
 10 
     | 
    
         
            +
                  if node.opening_loc
         
     | 
| 
      
 11 
     | 
    
         
            +
                    inc :paren
         
     | 
| 
      
 12 
     | 
    
         
            +
                  else
         
     | 
| 
      
 13 
     | 
    
         
            +
                    inc :no_paren
         
     | 
| 
      
 14 
     | 
    
         
            +
                  end
         
     | 
| 
      
 15 
     | 
    
         
            +
                end
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Check call with paren or no paren
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class IfThenFinder < SyntaxFinder
         
     | 
| 
      
 6 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 7 
     | 
    
         
            +
                if node.type == :call_node
         
     | 
| 
      
 8 
     | 
    
         
            +
                  has_params = !node.arguments.nil?
         
     | 
| 
      
 9 
     | 
    
         
            +
                  has_paren = !node.opening_loc.nil?
         
     | 
| 
      
 10 
     | 
    
         
            +
                  inc paren: has_paren, params: has_params
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,27 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Check def with paren or no paren
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class DefParamsFinder < SyntaxFinder
         
     | 
| 
      
 6 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 7 
     | 
    
         
            +
                if node.type == :def_node
         
     | 
| 
      
 8 
     | 
    
         
            +
                  inc :def
         
     | 
| 
      
 9 
     | 
    
         
            +
                  if node.parameters
         
     | 
| 
      
 10 
     | 
    
         
            +
                    params = {
         
     | 
| 
      
 11 
     | 
    
         
            +
                      required: node.parameters.requireds.size,
         
     | 
| 
      
 12 
     | 
    
         
            +
                      optional: node.parameters.optionals.size,
         
     | 
| 
      
 13 
     | 
    
         
            +
                      rest: !node.parameters.rest.nil?,
         
     | 
| 
      
 14 
     | 
    
         
            +
                      posts: node.parameters.posts.size,
         
     | 
| 
      
 15 
     | 
    
         
            +
                      keywords: node.parameters.keywords.size,
         
     | 
| 
      
 16 
     | 
    
         
            +
                      keyword_rest: !node.parameters.keyword_rest.nil?
         
     | 
| 
      
 17 
     | 
    
         
            +
                    }.reject { |_, v| v == 0 || v == false }
         
     | 
| 
      
 18 
     | 
    
         
            +
                  else
         
     | 
| 
      
 19 
     | 
    
         
            +
                    params = {required: 0}
         
     | 
| 
      
 20 
     | 
    
         
            +
                  end
         
     | 
| 
      
 21 
     | 
    
         
            +
             
     | 
| 
      
 22 
     | 
    
         
            +
                  unless inc params
         
     | 
| 
      
 23 
     | 
    
         
            +
                    note params, nloc(node)
         
     | 
| 
      
 24 
     | 
    
         
            +
                  end
         
     | 
| 
      
 25 
     | 
    
         
            +
                end
         
     | 
| 
      
 26 
     | 
    
         
            +
              end
         
     | 
| 
      
 27 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,13 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Check def with paren or no paren
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class IfThenFinder < SyntaxFinder
         
     | 
| 
      
 6 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 7 
     | 
    
         
            +
                if node.type == :def_node
         
     | 
| 
      
 8 
     | 
    
         
            +
                  has_params = !node.parameters.nil?
         
     | 
| 
      
 9 
     | 
    
         
            +
                  has_paren = !node.lparen_loc.nil?
         
     | 
| 
      
 10 
     | 
    
         
            +
                  inc paren: has_paren, params: has_params
         
     | 
| 
      
 11 
     | 
    
         
            +
                end
         
     | 
| 
      
 12 
     | 
    
         
            +
              end
         
     | 
| 
      
 13 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,29 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # Check indentation with `if` and `and`/`or`
         
     | 
| 
      
 4 
     | 
    
         
            +
             
     | 
| 
      
 5 
     | 
    
         
            +
            class IfCondContIndnetFinder < SyntaxFinder
         
     | 
| 
      
 6 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 7 
     | 
    
         
            +
                if node.type == :if_node
         
     | 
| 
      
 8 
     | 
    
         
            +
                  inc :if
         
     | 
| 
      
 9 
     | 
    
         
            +
                  inc :then if node.then_keyword_loc
         
     | 
| 
      
 10 
     | 
    
         
            +
                  cond = node.predicate
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                  case cond.type
         
     | 
| 
      
 13 
     | 
    
         
            +
                  when :and_node, :or_node
         
     | 
| 
      
 14 
     | 
    
         
            +
                    inc op: cond.operator_loc.slice
         
     | 
| 
      
 15 
     | 
    
         
            +
                    d = cond.location.end_line - cond.location.start_line
         
     | 
| 
      
 16 
     | 
    
         
            +
                    if d > 0
         
     | 
| 
      
 17 
     | 
    
         
            +
                      base = node.location.start_column
         
     | 
| 
      
 18 
     | 
    
         
            +
                      rest_lines = nlines(cond).lines[1..]
         
     | 
| 
      
 19 
     | 
    
         
            +
                      inc indent: rest_lines.map{|line|
         
     | 
| 
      
 20 
     | 
    
         
            +
                        /^(\s*)/ =~ line
         
     | 
| 
      
 21 
     | 
    
         
            +
                        $1.size - base
         
     | 
| 
      
 22 
     | 
    
         
            +
                      }.min
         
     | 
| 
      
 23 
     | 
    
         
            +
                      # puts nlines(cond)
         
     | 
| 
      
 24 
     | 
    
         
            +
                    end
         
     | 
| 
      
 25 
     | 
    
         
            +
                  end
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
              end
         
     | 
| 
      
 28 
     | 
    
         
            +
            end
         
     | 
| 
      
 29 
     | 
    
         
            +
             
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class IntegerRangeFinder < SYntaxFinder
         
     | 
| 
      
 4 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 5 
     | 
    
         
            +
                if node.type == :range_node
         
     | 
| 
      
 6 
     | 
    
         
            +
                   left = node.left 
         
     | 
| 
      
 7 
     | 
    
         
            +
                   right = node.right
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                   if((left.nil? || left.type == :integer_node) &&
         
     | 
| 
      
 10 
     | 
    
         
            +
                      (right.nil? || right.type == :integer_node))
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                    inc :integer_range
         
     | 
| 
      
 13 
     | 
    
         
            +
                    # pp [[nloc(node), nlines(node).lines]] unless @@opt[:quiet]
         
     | 
| 
      
 14 
     | 
    
         
            +
                    inc [left&.value, right&.value].inspect
         
     | 
| 
      
 15 
     | 
    
         
            +
                   end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            # count all of given method names via comma separated environment variables,
         
     | 
| 
      
 4 
     | 
    
         
            +
            # like `NAMES=map,collect`
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
            NAMES = ENV['NAMES']&.yield_self{|m| m.split(',').map{|m| m.strip.to_sym}}.to_h{|e| [e, true]} || raise
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
            class MethodNameFinder < SyntaxFinder
         
     | 
| 
      
 9 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 10 
     | 
    
         
            +
                case t = node.type
         
     | 
| 
      
 11 
     | 
    
         
            +
                when :call_node, :def_node
         
     | 
| 
      
 12 
     | 
    
         
            +
                  if NAMES[name = node.name]
         
     | 
| 
      
 13 
     | 
    
         
            +
                    inc [t, name]
         
     | 
| 
      
 14 
     | 
    
         
            +
                    pp [nloc(node),nlines(node).lines.first.chomp] if $VERBOSE
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,17 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class PragmaFinder < SyntaxFinder
         
     | 
| 
      
 4 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 5 
     | 
    
         
            +
                @root.magic_comments.each{|c|
         
     | 
| 
      
 6 
     | 
    
         
            +
                  inc key = [c.key, c.value]
         
     | 
| 
      
 7 
     | 
    
         
            +
                  note key, @file if $VERBOSE
         
     | 
| 
      
 8 
     | 
    
         
            +
                }
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                # No need for recursive travaersal.
         
     | 
| 
      
 11 
     | 
    
         
            +
              end
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
              def traverse node
         
     | 
| 
      
 14 
     | 
    
         
            +
                look node
         
     | 
| 
      
 15 
     | 
    
         
            +
                # check only root node
         
     | 
| 
      
 16 
     | 
    
         
            +
              end
         
     | 
| 
      
 17 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class ReqKWwoParenFinder < SyntaxFinder
         
     | 
| 
      
 4 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 5 
     | 
    
         
            +
                if node.type == :call_node &&
         
     | 
| 
      
 6 
     | 
    
         
            +
                   node.opening_loc.nil? &&
         
     | 
| 
      
 7 
     | 
    
         
            +
                   node.arguments &&
         
     | 
| 
      
 8 
     | 
    
         
            +
                   node.arguments.arguments.any?{|n| n.type == :keyword_hash_node && n.elements.any?{|e|
         
     | 
| 
      
 9 
     | 
    
         
            +
                     e.type == :assoc_node &&
         
     | 
| 
      
 10 
     | 
    
         
            +
                     e.operator_loc.nil? &&
         
     | 
| 
      
 11 
     | 
    
         
            +
                     e.key.location.start_line != e.value.location.start_line}}
         
     | 
| 
      
 12 
     | 
    
         
            +
             
     | 
| 
      
 13 
     | 
    
         
            +
                  inc :found
         
     | 
| 
      
 14 
     | 
    
         
            +
                  puts "# #{nloc(node)}"
         
     | 
| 
      
 15 
     | 
    
         
            +
                  puts '> ' + nlines(node).lines.join('> ')
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,14 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class ReqKWwoParenFinder < SyntaxFinder
         
     | 
| 
      
 4 
     | 
    
         
            +
              def look node
         
     | 
| 
      
 5 
     | 
    
         
            +
                if node.type == :def_node &&
         
     | 
| 
      
 6 
     | 
    
         
            +
                   node.lparen_loc.nil? &&
         
     | 
| 
      
 7 
     | 
    
         
            +
                   node.parameters&.keywords&.last&.type == :required_keyword_parameter_node &&
         
     | 
| 
      
 8 
     | 
    
         
            +
                   node.parameters&.block.nil?
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                  inc :found
         
     | 
| 
      
 11 
     | 
    
         
            +
                  pp [nloc(node), nlines(node).lines.first.chomp]
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
              end
         
     | 
| 
      
 14 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,18 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            require 'syntax_finder'
         
     | 
| 
      
 2 
     | 
    
         
            +
             
     | 
| 
      
 3 
     | 
    
         
            +
            class SingletonClassInDefFinder < SyntaxFinder
         
     | 
| 
      
 4 
     | 
    
         
            +
              # use traverse instead of look
         
     | 
| 
      
 5 
     | 
    
         
            +
              def traverse node, in_def = nil, in_sclass = nil
         
     | 
| 
      
 6 
     | 
    
         
            +
                if node.type == :class_node && in_sclass && node.constant_path.type != :constant_read_node
         
     | 
| 
      
 7 
     | 
    
         
            +
                  pp [nloc(node), nlines(node).lines.first.chomp]
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                if node.type == :def_node
         
     | 
| 
      
 11 
     | 
    
         
            +
                  in_def = true; in_sclass = false
         
     | 
| 
      
 12 
     | 
    
         
            +
                elsif node.type == :singleton_class_node && in_def
         
     | 
| 
      
 13 
     | 
    
         
            +
                  in_sclass = true
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                node.child_nodes.compact.each{|n| traverse n, in_def, in_sclass}
         
     | 
| 
      
 17 
     | 
    
         
            +
              end
         
     | 
| 
      
 18 
     | 
    
         
            +
            end
         
     | 
    
        metadata
    ADDED
    
    | 
         @@ -0,0 +1,66 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            --- !ruby/object:Gem::Specification
         
     | 
| 
      
 2 
     | 
    
         
            +
            name: syntax_finder
         
     | 
| 
      
 3 
     | 
    
         
            +
            version: !ruby/object:Gem::Version
         
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.1.0
         
     | 
| 
      
 5 
     | 
    
         
            +
            platform: ruby
         
     | 
| 
      
 6 
     | 
    
         
            +
            authors:
         
     | 
| 
      
 7 
     | 
    
         
            +
            - Koichi Sasada
         
     | 
| 
      
 8 
     | 
    
         
            +
            autorequire:
         
     | 
| 
      
 9 
     | 
    
         
            +
            bindir: exe
         
     | 
| 
      
 10 
     | 
    
         
            +
            cert_chain: []
         
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2025-01-14 00:00:00.000000000 Z
         
     | 
| 
      
 12 
     | 
    
         
            +
            dependencies: []
         
     | 
| 
      
 13 
     | 
    
         
            +
            description: Analyze your Ruby scripts with prism.
         
     | 
| 
      
 14 
     | 
    
         
            +
            email:
         
     | 
| 
      
 15 
     | 
    
         
            +
            - ko1@atdot.net
         
     | 
| 
      
 16 
     | 
    
         
            +
            executables: []
         
     | 
| 
      
 17 
     | 
    
         
            +
            extensions: []
         
     | 
| 
      
 18 
     | 
    
         
            +
            extra_rdoc_files: []
         
     | 
| 
      
 19 
     | 
    
         
            +
            files:
         
     | 
| 
      
 20 
     | 
    
         
            +
            - LICENSE.txt
         
     | 
| 
      
 21 
     | 
    
         
            +
            - README.md
         
     | 
| 
      
 22 
     | 
    
         
            +
            - Rakefile
         
     | 
| 
      
 23 
     | 
    
         
            +
            - lib/syntax_finder.rb
         
     | 
| 
      
 24 
     | 
    
         
            +
            - lib/syntax_finder/version.rb
         
     | 
| 
      
 25 
     | 
    
         
            +
            - samples/all_lvar_finder.rb
         
     | 
| 
      
 26 
     | 
    
         
            +
            - samples/all_method_name_finder.rb
         
     | 
| 
      
 27 
     | 
    
         
            +
            - samples/all_regexp_finder.rb
         
     | 
| 
      
 28 
     | 
    
         
            +
            - samples/assert_equal_paren.rb
         
     | 
| 
      
 29 
     | 
    
         
            +
            - samples/call_paren_finder.rb
         
     | 
| 
      
 30 
     | 
    
         
            +
            - samples/def_params_finder.rb
         
     | 
| 
      
 31 
     | 
    
         
            +
            - samples/def_paren_finder.rb
         
     | 
| 
      
 32 
     | 
    
         
            +
            - samples/if_cond_cont_indent_finder.rb
         
     | 
| 
      
 33 
     | 
    
         
            +
            - samples/if_then_finder.rb
         
     | 
| 
      
 34 
     | 
    
         
            +
            - samples/integer_range_finder.rb
         
     | 
| 
      
 35 
     | 
    
         
            +
            - samples/integer_size_finder.rb
         
     | 
| 
      
 36 
     | 
    
         
            +
            - samples/method_name_finder.rb
         
     | 
| 
      
 37 
     | 
    
         
            +
            - samples/pragma_finder.rb
         
     | 
| 
      
 38 
     | 
    
         
            +
            - samples/required_kwcall_wo_paren_finder.rb
         
     | 
| 
      
 39 
     | 
    
         
            +
            - samples/required_kwdef_wo_paren_finder.rb
         
     | 
| 
      
 40 
     | 
    
         
            +
            - samples/singleton_class_in_def_finder.rb
         
     | 
| 
      
 41 
     | 
    
         
            +
            homepage: https://github.com/ko1/syntax_finder
         
     | 
| 
      
 42 
     | 
    
         
            +
            licenses:
         
     | 
| 
      
 43 
     | 
    
         
            +
            - MIT
         
     | 
| 
      
 44 
     | 
    
         
            +
            metadata:
         
     | 
| 
      
 45 
     | 
    
         
            +
              homepage_uri: https://github.com/ko1/syntax_finder
         
     | 
| 
      
 46 
     | 
    
         
            +
              source_code_uri: https://github.com/ko1/syntax_finder
         
     | 
| 
      
 47 
     | 
    
         
            +
            post_install_message:
         
     | 
| 
      
 48 
     | 
    
         
            +
            rdoc_options: []
         
     | 
| 
      
 49 
     | 
    
         
            +
            require_paths:
         
     | 
| 
      
 50 
     | 
    
         
            +
            - lib
         
     | 
| 
      
 51 
     | 
    
         
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 52 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 53 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 54 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 55 
     | 
    
         
            +
                  version: 3.0.0
         
     | 
| 
      
 56 
     | 
    
         
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         
     | 
| 
      
 57 
     | 
    
         
            +
              requirements:
         
     | 
| 
      
 58 
     | 
    
         
            +
              - - ">="
         
     | 
| 
      
 59 
     | 
    
         
            +
                - !ruby/object:Gem::Version
         
     | 
| 
      
 60 
     | 
    
         
            +
                  version: '0'
         
     | 
| 
      
 61 
     | 
    
         
            +
            requirements: []
         
     | 
| 
      
 62 
     | 
    
         
            +
            rubygems_version: 3.5.9
         
     | 
| 
      
 63 
     | 
    
         
            +
            signing_key:
         
     | 
| 
      
 64 
     | 
    
         
            +
            specification_version: 4
         
     | 
| 
      
 65 
     | 
    
         
            +
            summary: Analyze your Ruby scripts with prism.
         
     | 
| 
      
 66 
     | 
    
         
            +
            test_files: []
         
     |