rubocop 0.9.1 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of rubocop might be problematic. Click here for more details.
- data/.travis.yml +3 -1
- data/CHANGELOG.md +38 -0
- data/README.md +34 -0
- data/Rakefile +3 -0
- data/config/default.yml +14 -1
- data/config/enabled.yml +30 -7
- data/lib/rubocop.rb +15 -0
- data/lib/rubocop/cli.rb +48 -154
- data/lib/rubocop/config.rb +19 -22
- data/lib/rubocop/config_store.rb +2 -4
- data/lib/rubocop/cop/commissioner.rb +90 -0
- data/lib/rubocop/cop/cop.rb +38 -31
- data/lib/rubocop/cop/corrector.rb +84 -0
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +0 -3
- data/lib/rubocop/cop/lint/block_alignment.rb +151 -0
- data/lib/rubocop/cop/lint/empty_ensure.rb +18 -0
- data/lib/rubocop/cop/lint/end_alignment.rb +0 -124
- data/lib/rubocop/cop/lint/end_in_method.rb +0 -2
- data/lib/rubocop/cop/lint/ensure_return.rb +3 -3
- data/lib/rubocop/cop/lint/eval.rb +0 -2
- data/lib/rubocop/cop/lint/handle_exceptions.rb +0 -2
- data/lib/rubocop/cop/lint/literal_in_condition.rb +0 -10
- data/lib/rubocop/cop/lint/loop.rb +0 -2
- data/lib/rubocop/cop/lint/rescue_exception.rb +0 -2
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +2 -2
- data/lib/rubocop/cop/lint/unreachable_code.rb +0 -2
- data/lib/rubocop/cop/lint/unused_local_variable.rb +2 -2
- data/lib/rubocop/cop/lint/void.rb +0 -2
- data/lib/rubocop/cop/offence.rb +9 -0
- data/lib/rubocop/cop/rails/validation.rb +2 -1
- data/lib/rubocop/cop/style/access_control.rb +4 -3
- data/lib/rubocop/cop/style/alias.rb +2 -4
- data/lib/rubocop/cop/style/align_parameters.rb +0 -2
- data/lib/rubocop/cop/style/and_or.rb +4 -6
- data/lib/rubocop/cop/style/ascii_comments.rb +2 -2
- data/lib/rubocop/cop/style/ascii_identifiers.rb +2 -2
- data/lib/rubocop/cop/style/attr.rb +0 -2
- data/lib/rubocop/cop/style/avoid_class_vars.rb +0 -1
- data/lib/rubocop/cop/style/avoid_for.rb +0 -2
- data/lib/rubocop/cop/style/avoid_global_vars.rb +3 -7
- data/lib/rubocop/cop/style/avoid_perl_backrefs.rb +0 -2
- data/lib/rubocop/cop/style/avoid_perlisms.rb +2 -4
- data/lib/rubocop/cop/style/begin_block.rb +0 -2
- data/lib/rubocop/cop/style/block_comments.rb +2 -2
- data/lib/rubocop/cop/style/block_nesting.rb +3 -3
- data/lib/rubocop/cop/style/blocks.rb +0 -2
- data/lib/rubocop/cop/style/case_equality.rb +0 -2
- data/lib/rubocop/cop/style/case_indentation.rb +0 -2
- data/lib/rubocop/cop/style/character_literal.rb +10 -6
- data/lib/rubocop/cop/style/class_and_module_camel_case.rb +0 -4
- data/lib/rubocop/cop/style/class_methods.rb +1 -1
- data/lib/rubocop/cop/style/collection_methods.rb +3 -5
- data/lib/rubocop/cop/style/colon_method_call.rb +3 -3
- data/lib/rubocop/cop/style/comment_annotation.rb +44 -0
- data/lib/rubocop/cop/style/constant_name.rb +0 -2
- data/lib/rubocop/cop/style/def_parentheses.rb +0 -8
- data/lib/rubocop/cop/style/documentation.rb +6 -2
- data/lib/rubocop/cop/style/dot_position.rb +0 -2
- data/lib/rubocop/cop/style/empty_line_between_defs.rb +0 -2
- data/lib/rubocop/cop/style/empty_lines.rb +10 -8
- data/lib/rubocop/cop/style/empty_literal.rb +3 -1
- data/lib/rubocop/cop/style/encoding.rb +7 -6
- data/lib/rubocop/cop/style/end_block.rb +0 -2
- data/lib/rubocop/cop/style/end_of_line.rb +4 -3
- data/lib/rubocop/cop/style/favor_join.rb +0 -2
- data/lib/rubocop/cop/style/favor_modifier.rb +9 -9
- data/lib/rubocop/cop/style/favor_sprintf.rb +0 -2
- data/lib/rubocop/cop/style/favor_unless_over_negated_if.rb +0 -2
- data/lib/rubocop/cop/style/hash_syntax.rb +0 -2
- data/lib/rubocop/cop/style/if_then_else.rb +0 -2
- data/lib/rubocop/cop/style/lambda.rb +0 -2
- data/lib/rubocop/cop/style/leading_comment_space.rb +2 -2
- data/lib/rubocop/cop/style/line_continuation.rb +4 -3
- data/lib/rubocop/cop/style/line_length.rb +4 -3
- data/lib/rubocop/cop/style/method_and_variable_snake_case.rb +4 -3
- data/lib/rubocop/cop/style/method_call_parentheses.rb +0 -2
- data/lib/rubocop/cop/style/method_length.rb +0 -4
- data/lib/rubocop/cop/style/not.rb +0 -2
- data/lib/rubocop/cop/style/op_method.rb +0 -2
- data/lib/rubocop/cop/style/parameter_lists.rb +0 -2
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +12 -6
- data/lib/rubocop/cop/style/proc.rb +0 -2
- data/lib/rubocop/cop/style/reduce_arguments.rb +0 -2
- data/lib/rubocop/cop/style/redundant_begin.rb +45 -0
- data/lib/rubocop/cop/style/redundant_return.rb +59 -0
- data/lib/rubocop/cop/style/redundant_self.rb +83 -0
- data/lib/rubocop/cop/style/regexp_literal.rb +0 -2
- data/lib/rubocop/cop/style/rescue_modifier.rb +13 -21
- data/lib/rubocop/cop/style/semicolon.rb +15 -9
- data/lib/rubocop/cop/style/single_line_methods.rb +0 -4
- data/lib/rubocop/cop/style/space_after_comma_etc.rb +2 -2
- data/lib/rubocop/cop/style/space_after_control_keyword.rb +0 -1
- data/lib/rubocop/cop/style/string_literals.rb +5 -2
- data/lib/rubocop/cop/style/surrounding_space.rb +106 -91
- data/lib/rubocop/cop/style/tab.rb +4 -3
- data/lib/rubocop/cop/style/ternary_operator.rb +0 -4
- data/lib/rubocop/cop/style/trailing_whitespace.rb +4 -3
- data/lib/rubocop/cop/style/trivial_accessors.rb +51 -6
- data/lib/rubocop/cop/style/unless_else.rb +0 -2
- data/lib/rubocop/cop/style/variable_interpolation.rb +0 -2
- data/lib/rubocop/cop/style/when_then.rb +3 -3
- data/lib/rubocop/cop/style/while_until_do.rb +3 -5
- data/lib/rubocop/cop/style/word_array.rb +0 -2
- data/lib/rubocop/cop/util.rb +0 -4
- data/lib/rubocop/formatter/file_list_formatter.rb +18 -0
- data/lib/rubocop/formatter/formatter_set.rb +2 -1
- data/lib/rubocop/processed_source.rb +27 -0
- data/lib/rubocop/rake_task.rb +50 -0
- data/lib/rubocop/source_parser.rb +105 -0
- data/lib/rubocop/target_finder.rb +67 -0
- data/lib/rubocop/token.rb +22 -0
- data/lib/rubocop/version.rb +1 -1
- data/rubocop.gemspec +5 -3
- data/spec/project_spec.rb +0 -11
- data/spec/rubocop/cli_spec.rb +112 -6
- data/spec/rubocop/config_spec.rb +13 -17
- data/spec/rubocop/config_store_spec.rb +8 -23
- data/spec/rubocop/cops/commissioner_spec.rb +72 -0
- data/spec/rubocop/cops/corrector_spec.rb +63 -0
- data/spec/rubocop/cops/lint/assignment_in_condition_spec.rb +2 -2
- data/spec/rubocop/cops/lint/block_alignment_spec.rb +357 -0
- data/spec/rubocop/cops/lint/empty_ensure_spec.rb +33 -0
- data/spec/rubocop/cops/lint/end_alignment_spec.rb +0 -263
- data/spec/rubocop/cops/lint/ensure_return_spec.rb +6 -9
- data/spec/rubocop/cops/offence_spec.rb +28 -0
- data/spec/rubocop/cops/style/and_or_spec.rb +21 -11
- data/spec/rubocop/cops/style/ascii_identifiers_spec.rb +14 -0
- data/spec/rubocop/cops/style/avoid_global_vars_spec.rb +10 -14
- data/spec/rubocop/cops/style/character_literal_spec.rb +17 -2
- data/spec/rubocop/cops/style/colon_method_call_spec.rb +20 -15
- data/spec/rubocop/cops/style/comment_annotation_spec.rb +62 -0
- data/spec/rubocop/cops/style/encoding_spec.rb +7 -0
- data/spec/rubocop/cops/style/parentheses_around_condition_spec.rb +37 -9
- data/spec/rubocop/cops/style/redundant_begin_spec.rb +63 -0
- data/spec/rubocop/cops/style/redundant_return_spec.rb +64 -0
- data/spec/rubocop/cops/style/redundant_self_spec.rb +76 -0
- data/spec/rubocop/cops/style/string_literals_spec.rb +18 -13
- data/spec/rubocop/cops/style/trivial_accessors_spec.rb +110 -52
- data/spec/rubocop/cops/style/when_then_spec.rb +14 -7
- data/spec/rubocop/cops/style/while_until_do_spec.rb +12 -0
- data/spec/rubocop/cops/variable_inspector_spec.rb +3 -5
- data/spec/rubocop/formatter/file_list_formatter_spec.rb +33 -0
- data/spec/rubocop/processed_source_spec.rb +67 -0
- data/spec/rubocop/source_parser_spec.rb +141 -0
- data/spec/rubocop/target_finder_spec.rb +180 -0
- data/spec/rubocop/token_spec.rb +27 -0
- data/spec/spec_helper.rb +24 -4
- metadata +108 -18
- checksums.yaml +0 -7
    
        data/lib/rubocop/config.rb
    CHANGED
    
    | @@ -25,11 +25,11 @@ module Rubocop | |
| 25 25 | 
             
                    hash = YAML.load_file(path)
         | 
| 26 26 |  | 
| 27 27 | 
             
                    base_configs(path, hash['inherit_from']).reverse.each do |base_config|
         | 
| 28 | 
            +
                      if File.basename(base_config.loaded_path) == DOTFILE
         | 
| 29 | 
            +
                        make_excludes_absolute(base_config)
         | 
| 30 | 
            +
                      end
         | 
| 28 31 | 
             
                      base_config.each do |key, value|
         | 
| 29 32 | 
             
                        if value.is_a?(Hash)
         | 
| 30 | 
            -
                          if key == 'AllCops' && value['Excludes']
         | 
| 31 | 
            -
                            correct_relative_excludes(value, base_config, path)
         | 
| 32 | 
            -
                          end
         | 
| 33 33 | 
             
                          hash[key] = hash.has_key?(key) ? merge(value, hash[key]) : value
         | 
| 34 34 | 
             
                        end
         | 
| 35 35 | 
             
                      end
         | 
| @@ -41,14 +41,14 @@ module Rubocop | |
| 41 41 | 
             
                    config
         | 
| 42 42 | 
             
                  end
         | 
| 43 43 |  | 
| 44 | 
            -
                  def  | 
| 45 | 
            -
                     | 
| 46 | 
            -
                       | 
| 47 | 
            -
                         | 
| 48 | 
            -
             | 
| 49 | 
            -
                         | 
| 50 | 
            -
             | 
| 51 | 
            -
                         | 
| 44 | 
            +
                  def make_excludes_absolute(config)
         | 
| 45 | 
            +
                    if config['AllCops'] && config['AllCops']['Excludes']
         | 
| 46 | 
            +
                      config['AllCops']['Excludes'].map! do |exclude_elem|
         | 
| 47 | 
            +
                        if exclude_elem.is_a?(String) && !exclude_elem.start_with?('/')
         | 
| 48 | 
            +
                          File.join(File.dirname(config.loaded_path), exclude_elem)
         | 
| 49 | 
            +
                        else
         | 
| 50 | 
            +
                          exclude_elem
         | 
| 51 | 
            +
                        end
         | 
| 52 52 | 
             
                      end
         | 
| 53 53 | 
             
                    end
         | 
| 54 54 | 
             
                  end
         | 
| @@ -58,15 +58,15 @@ module Rubocop | |
| 58 58 | 
             
                    path_name.relative_path_from(Pathname.new(base)).to_s
         | 
| 59 59 | 
             
                  end
         | 
| 60 60 |  | 
| 61 | 
            -
                  # Return  | 
| 62 | 
            -
                  #  | 
| 63 | 
            -
                  #  | 
| 61 | 
            +
                  # Return an extended merge of two hashes. That is, a normal hash merge,
         | 
| 62 | 
            +
                  # with the addition that any value that is a hash, and occurs in both
         | 
| 63 | 
            +
                  # arguments (i.e., cop names), will also be merged.
         | 
| 64 64 | 
             
                  def merge(base_hash, derived_hash)
         | 
| 65 65 | 
             
                    result = {}
         | 
| 66 66 | 
             
                    base_hash.each do |key, value|
         | 
| 67 67 | 
             
                      result[key] = if derived_hash.has_key?(key)
         | 
| 68 68 | 
             
                                      if value.is_a?(Hash)
         | 
| 69 | 
            -
                                        merge( | 
| 69 | 
            +
                                        value.merge(derived_hash[key])
         | 
| 70 70 | 
             
                                      else
         | 
| 71 71 | 
             
                                        derived_hash[key]
         | 
| 72 72 | 
             
                                      end
         | 
| @@ -107,6 +107,7 @@ module Rubocop | |
| 107 107 | 
             
                    if found_files.any? && found_files.last != config_file
         | 
| 108 108 | 
             
                      add_excludes_from_higher_level(config, load_file(found_files.last))
         | 
| 109 109 | 
             
                    end
         | 
| 110 | 
            +
                    make_excludes_absolute(config)
         | 
| 110 111 | 
             
                    merge_with_default(config, config_file)
         | 
| 111 112 | 
             
                  end
         | 
| 112 113 |  | 
| @@ -116,9 +117,7 @@ module Rubocop | |
| 116 117 | 
             
                      config['AllCops']['Excludes'] ||= []
         | 
| 117 118 | 
             
                      highest_config['AllCops']['Excludes'].each do |path|
         | 
| 118 119 | 
             
                        unless path.is_a?(Regexp) || path.start_with?('/')
         | 
| 119 | 
            -
                           | 
| 120 | 
            -
                            highest_config.loaded_path.count('/')
         | 
| 121 | 
            -
                          path = '../' * diff_in_level + path
         | 
| 120 | 
            +
                          path = File.join(File.dirname(highest_config.loaded_path), path)
         | 
| 122 121 | 
             
                        end
         | 
| 123 122 | 
             
                        config['AllCops']['Excludes'] << path
         | 
| 124 123 | 
             
                      end
         | 
| @@ -209,10 +208,8 @@ module Rubocop | |
| 209 208 | 
             
                end
         | 
| 210 209 |  | 
| 211 210 | 
             
                def file_to_exclude?(file)
         | 
| 212 | 
            -
                   | 
| 213 | 
            -
                  patterns_to_exclude.any?  | 
| 214 | 
            -
                    match_path?(pattern, relative_file_path)
         | 
| 215 | 
            -
                  end
         | 
| 211 | 
            +
                  file = File.join(Dir.pwd, file) unless file.start_with?('/')
         | 
| 212 | 
            +
                  patterns_to_exclude.any? { |pattern| match_path?(pattern, file) }
         | 
| 216 213 | 
             
                end
         | 
| 217 214 |  | 
| 218 215 | 
             
                def patterns_to_include
         | 
    
        data/lib/rubocop/config_store.rb
    CHANGED
    
    | @@ -3,10 +3,8 @@ | |
| 3 3 | 
             
            module Rubocop
         | 
| 4 4 | 
             
              # Handles chaching of configurations and association of inspected
         | 
| 5 5 | 
             
              # ruby files to configurations.
         | 
| 6 | 
            -
               | 
| 7 | 
            -
                 | 
| 8 | 
            -
             | 
| 9 | 
            -
                def prepare
         | 
| 6 | 
            +
              class ConfigStore
         | 
| 7 | 
            +
                def initialize
         | 
| 10 8 | 
             
                  # @options_config stores a config that is specified in the command line.
         | 
| 11 9 | 
             
                  # This takes precedence over configs located in any directories
         | 
| 12 10 | 
             
                  @options_config = nil
         | 
| @@ -0,0 +1,90 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Rubocop
         | 
| 4 | 
            +
              module Cop
         | 
| 5 | 
            +
                # Commissioner class is responsible for processing the AST and delagating
         | 
| 6 | 
            +
                # work to the specified cops.
         | 
| 7 | 
            +
                class Commissioner < Parser::AST::Processor
         | 
| 8 | 
            +
                  attr_reader :errors
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                  METHODS_NOT_DEFINED_IN_PARSER_PROCESSOR = [
         | 
| 11 | 
            +
                    :on_sym, :on_str, :on_int, :on_float
         | 
| 12 | 
            +
                  ]
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                  def self.callback_methods
         | 
| 15 | 
            +
                    Parser::AST::Processor.instance_methods.select do |method|
         | 
| 16 | 
            +
                      method.to_s =~ /^on_/
         | 
| 17 | 
            +
                    end + METHODS_NOT_DEFINED_IN_PARSER_PROCESSOR
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                  # Methods that are not defined in Parser::AST::Processor
         | 
| 21 | 
            +
                  # won't have a `super` to call. So we should not attempt
         | 
| 22 | 
            +
                  # to invoke `super` when defining them.
         | 
| 23 | 
            +
                  def self.call_super(callback)
         | 
| 24 | 
            +
                    if METHODS_NOT_DEFINED_IN_PARSER_PROCESSOR.include?(callback)
         | 
| 25 | 
            +
                      ''
         | 
| 26 | 
            +
                    else
         | 
| 27 | 
            +
                      'super'
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                  def initialize(cops, options = {})
         | 
| 32 | 
            +
                    @cops = cops
         | 
| 33 | 
            +
                    @options = options
         | 
| 34 | 
            +
                    reset_errors
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  callback_methods.each do |callback|
         | 
| 38 | 
            +
                    class_eval <<-EOS
         | 
| 39 | 
            +
                      def #{callback}(node)
         | 
| 40 | 
            +
                        @cops.each do |cop|
         | 
| 41 | 
            +
                          if cop.respond_to?(:#{callback})
         | 
| 42 | 
            +
                            delegate_to(cop, :#{callback}, node)
         | 
| 43 | 
            +
                          end
         | 
| 44 | 
            +
                        end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                        #{call_super(callback)}
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
                    EOS
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def investigate(processed_source)
         | 
| 52 | 
            +
                    reset_errors
         | 
| 53 | 
            +
                    invoke_cops_callback(processed_source)
         | 
| 54 | 
            +
                    process(processed_source.ast) if processed_source.ast
         | 
| 55 | 
            +
                    @cops.reduce([]) do |offences, cop|
         | 
| 56 | 
            +
                      offences.concat(cop.offences)
         | 
| 57 | 
            +
                      offences
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
                  end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  private
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  def reset_errors
         | 
| 64 | 
            +
                    @errors = Hash.new { |hash, k| hash[k] = [] }
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  # There are cops that require their own custom processing.
         | 
| 68 | 
            +
                  # If they define the #investigate method all input parameters passed
         | 
| 69 | 
            +
                  # to the commissioner will be passed to the cop too in order to do
         | 
| 70 | 
            +
                  # its own processing.
         | 
| 71 | 
            +
                  def invoke_cops_callback(processed_source)
         | 
| 72 | 
            +
                    @cops.each do |cop|
         | 
| 73 | 
            +
                      if cop.respond_to?(:investigate)
         | 
| 74 | 
            +
                        cop.investigate(processed_source)
         | 
| 75 | 
            +
                      end
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  def delegate_to(cop, callback, node)
         | 
| 80 | 
            +
                    cop.send callback, node
         | 
| 81 | 
            +
                  rescue => e
         | 
| 82 | 
            +
                    if @options[:raise_error]
         | 
| 83 | 
            +
                      fail e
         | 
| 84 | 
            +
                    else
         | 
| 85 | 
            +
                      @errors[cop] << e
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
                  end
         | 
| 88 | 
            +
                end
         | 
| 89 | 
            +
              end
         | 
| 90 | 
            +
            end
         | 
    
        data/lib/rubocop/cop/cop.rb
    CHANGED
    
    | @@ -2,31 +2,33 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module Rubocop
         | 
| 4 4 | 
             
              module Cop
         | 
| 5 | 
            -
                # A basic wrapper around Parser's tokens.
         | 
| 6 | 
            -
                class Token
         | 
| 7 | 
            -
                  attr_reader :pos, :type, :text
         | 
| 8 | 
            -
             | 
| 9 | 
            -
                  def initialize(pos, type, text)
         | 
| 10 | 
            -
                    @pos, @type, @text = pos, type, text
         | 
| 11 | 
            -
                  end
         | 
| 12 | 
            -
             | 
| 13 | 
            -
                  def to_s
         | 
| 14 | 
            -
                    "[[#{@pos.line}, #{@pos.column}], #{@type}, #{@text.inspect}]"
         | 
| 15 | 
            -
                  end
         | 
| 16 | 
            -
                end
         | 
| 17 | 
            -
             | 
| 18 5 | 
             
                # A scaffold for concrete cops.
         | 
| 19 6 | 
             
                #
         | 
| 20 7 | 
             
                # The Cop class is meant to be extended.
         | 
| 21 8 | 
             
                #
         | 
| 22 9 | 
             
                # Cops track offences and can autocorrect them of the fly.
         | 
| 23 | 
            -
                 | 
| 10 | 
            +
                #
         | 
| 11 | 
            +
                # A commissioner object is responsible for traversing the AST and invoking
         | 
| 12 | 
            +
                # the specific callbacks on each cop.
         | 
| 13 | 
            +
                # If a cop needs to do its own processing of the AST or depends on
         | 
| 14 | 
            +
                # something else it should define the #investigate method and do
         | 
| 15 | 
            +
                # the processing there.
         | 
| 16 | 
            +
                #
         | 
| 17 | 
            +
                # @example
         | 
| 18 | 
            +
                #
         | 
| 19 | 
            +
                # class CustomCop < Cop
         | 
| 20 | 
            +
                #   def investigate(processed_source)
         | 
| 21 | 
            +
                #     # Do custom processing
         | 
| 22 | 
            +
                #   end
         | 
| 23 | 
            +
                # end
         | 
| 24 | 
            +
                class Cop
         | 
| 24 25 | 
             
                  extend AST::Sexp
         | 
| 25 26 |  | 
| 26 27 | 
             
                  attr_accessor :offences
         | 
| 27 28 | 
             
                  attr_accessor :debug
         | 
| 28 29 | 
             
                  attr_accessor :autocorrect
         | 
| 29 30 | 
             
                  attr_writer :disabled_lines
         | 
| 31 | 
            +
                  attr_reader :corrections
         | 
| 30 32 |  | 
| 31 33 | 
             
                  @all = []
         | 
| 32 34 | 
             
                  @config = {}
         | 
| @@ -67,20 +69,8 @@ module Rubocop | |
| 67 69 | 
             
                    @offences = []
         | 
| 68 70 | 
             
                    @debug = false
         | 
| 69 71 | 
             
                    @autocorrect = false
         | 
| 70 | 
            -
             | 
| 71 | 
            -
             | 
| 72 | 
            -
                  def inspect(source_buffer, source, tokens, ast, comments)
         | 
| 73 | 
            -
                    if autocorrect
         | 
| 74 | 
            -
                      filename = source_buffer.instance_variable_get(:@name)
         | 
| 75 | 
            -
                      new_source = rewrite(source_buffer, ast)
         | 
| 76 | 
            -
                      unless new_source == source_buffer.source
         | 
| 77 | 
            -
                        File.open(filename, 'w') { |f| f.write(new_source) }
         | 
| 78 | 
            -
                        source_buffer.instance_variable_set(:@source, nil)
         | 
| 79 | 
            -
                        source_buffer.read
         | 
| 80 | 
            -
                      end
         | 
| 81 | 
            -
                    else
         | 
| 82 | 
            -
                      process(ast)
         | 
| 83 | 
            -
                    end
         | 
| 72 | 
            +
                    @ignored_nodes = []
         | 
| 73 | 
            +
                    @corrections = []
         | 
| 84 74 | 
             
                  end
         | 
| 85 75 |  | 
| 86 76 | 
             
                  def do_autocorrect(node)
         | 
| @@ -90,9 +80,6 @@ module Rubocop | |
| 90 80 | 
             
                  def autocorrect_action(node)
         | 
| 91 81 | 
             
                  end
         | 
| 92 82 |  | 
| 93 | 
            -
                  def ignore_node(node)
         | 
| 94 | 
            -
                  end
         | 
| 95 | 
            -
             | 
| 96 83 | 
             
                  def add_offence(severity, location, message)
         | 
| 97 84 | 
             
                    unless @disabled_lines && @disabled_lines.include?(location.line)
         | 
| 98 85 | 
             
                      message = debug ? "#{name}: #{message}" : message
         | 
| @@ -104,8 +91,28 @@ module Rubocop | |
| 104 91 | 
             
                    self.class.cop_name
         | 
| 105 92 | 
             
                  end
         | 
| 106 93 |  | 
| 94 | 
            +
                  def ignore_node(node)
         | 
| 95 | 
            +
                    @ignored_nodes << node
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
             | 
| 107 98 | 
             
                  private
         | 
| 108 99 |  | 
| 100 | 
            +
                  def part_of_ignored_node?(node)
         | 
| 101 | 
            +
                    expression = node.loc.expression
         | 
| 102 | 
            +
                    @ignored_nodes.each do |ignored_node|
         | 
| 103 | 
            +
                      if ignored_node.loc.expression.begin_pos <= expression.begin_pos &&
         | 
| 104 | 
            +
                        ignored_node.loc.expression.end_pos >= expression.end_pos
         | 
| 105 | 
            +
                        return true
         | 
| 106 | 
            +
                      end
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
             | 
| 109 | 
            +
                    false
         | 
| 110 | 
            +
                  end
         | 
| 111 | 
            +
             | 
| 112 | 
            +
                  def ignored_node?(node)
         | 
| 113 | 
            +
                    @ignored_nodes.include?(node)
         | 
| 114 | 
            +
                  end
         | 
| 115 | 
            +
             | 
| 109 116 | 
             
                  def on_node(syms, sexp, excludes = [])
         | 
| 110 117 | 
             
                    yield sexp if Array(syms).include?(sexp.type)
         | 
| 111 118 |  | 
| @@ -0,0 +1,84 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Rubocop
         | 
| 4 | 
            +
              module Cop
         | 
| 5 | 
            +
                # This class takes a source buffer and rewrite its source
         | 
| 6 | 
            +
                # based on the different correction rules supplied.
         | 
| 7 | 
            +
                #
         | 
| 8 | 
            +
                # Important!
         | 
| 9 | 
            +
                # The nodes modified by the corrections should be part of the
         | 
| 10 | 
            +
                # AST of the source_buffer.
         | 
| 11 | 
            +
                class Corrector
         | 
| 12 | 
            +
                  #
         | 
| 13 | 
            +
                  # @param source_buffer [Parser::Source::Buffer]
         | 
| 14 | 
            +
                  # @param corrections [Array(#call)]
         | 
| 15 | 
            +
                  #   Array of Objects that respond to #call. They will receive the
         | 
| 16 | 
            +
                  #   corrector itself and should use its method to modify the source.
         | 
| 17 | 
            +
                  #
         | 
| 18 | 
            +
                  # @example
         | 
| 19 | 
            +
                  #
         | 
| 20 | 
            +
                  # class AndOrCorrector
         | 
| 21 | 
            +
                  #   def initialize(node)
         | 
| 22 | 
            +
                  #     @node = node
         | 
| 23 | 
            +
                  #   end
         | 
| 24 | 
            +
                  #
         | 
| 25 | 
            +
                  #   def call(corrector)
         | 
| 26 | 
            +
                  #     replacement = (@node.type == :and ? '&&' : '||')
         | 
| 27 | 
            +
                  #     corrector.replace(@node.loc.operator, replacement)
         | 
| 28 | 
            +
                  #   end
         | 
| 29 | 
            +
                  # end
         | 
| 30 | 
            +
                  #
         | 
| 31 | 
            +
                  # corrections = [AndOrCorrector.new(node)]
         | 
| 32 | 
            +
                  # corrector = Corrector.new(source_buffer, corrections)
         | 
| 33 | 
            +
                  def initialize(source_buffer, corrections)
         | 
| 34 | 
            +
                    @source_buffer = source_buffer
         | 
| 35 | 
            +
                    @corrections = corrections
         | 
| 36 | 
            +
                    @source_rewriter = Parser::Source::Rewriter.new(source_buffer)
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  # Does the actual rewrite and returns string corresponding to
         | 
| 40 | 
            +
                  # the rewritten source.
         | 
| 41 | 
            +
                  #
         | 
| 42 | 
            +
                  # @return [String]
         | 
| 43 | 
            +
                  # TODO: Handle conflict exceptions raised from the Source::Rewriter
         | 
| 44 | 
            +
                  def rewrite
         | 
| 45 | 
            +
                    @corrections.each do |correction|
         | 
| 46 | 
            +
                      correction.call(self)
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                    @source_rewriter.process
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  # Removes the source range.
         | 
| 53 | 
            +
                  #
         | 
| 54 | 
            +
                  # @param [Parser::Source::Range] range
         | 
| 55 | 
            +
                  def remove(range)
         | 
| 56 | 
            +
                    @source_rewriter.remove(range)
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  # Inserts new code before the given source range.
         | 
| 60 | 
            +
                  #
         | 
| 61 | 
            +
                  # @param [Parser::Source::Range] range
         | 
| 62 | 
            +
                  # @param [String] content
         | 
| 63 | 
            +
                  def insert_before(range, content)
         | 
| 64 | 
            +
                    @source_rewriter.insert_before(range, content)
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  # Inserts new code after the given source range.
         | 
| 68 | 
            +
                  #
         | 
| 69 | 
            +
                  # @param [Parser::Source::Range] range
         | 
| 70 | 
            +
                  # @param [String] content
         | 
| 71 | 
            +
                  def insert_after(range, content)
         | 
| 72 | 
            +
                    @source_rewriter.insert_after(range, content)
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  # Replaces the code of the source range `range` with `content`.
         | 
| 76 | 
            +
                  #
         | 
| 77 | 
            +
                  # @param [Parser::Source::Range] range
         | 
| 78 | 
            +
                  # @param [String] content
         | 
| 79 | 
            +
                  def replace(range, content)
         | 
| 80 | 
            +
                    @source_rewriter.replace(range, content)
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
            end
         | 
| @@ -0,0 +1,151 @@ | |
| 1 | 
            +
            # encoding: utf-8
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module Rubocop
         | 
| 4 | 
            +
              module Cop
         | 
| 5 | 
            +
                module Lint
         | 
| 6 | 
            +
                  # This cop checks whether the end keywords are aligned properly for do
         | 
| 7 | 
            +
                  # end blocks.
         | 
| 8 | 
            +
                  #
         | 
| 9 | 
            +
                  # @example
         | 
| 10 | 
            +
                  #
         | 
| 11 | 
            +
                  #   variable = lambda do |i|
         | 
| 12 | 
            +
                  #     i
         | 
| 13 | 
            +
                  #   end
         | 
| 14 | 
            +
                  class BlockAlignment < Cop
         | 
| 15 | 
            +
                    MSG = 'end at %d, %d is not aligned with %s at %d, %d'
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    def initialize
         | 
| 18 | 
            +
                      super
         | 
| 19 | 
            +
                      @inspected_blocks = []
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    def on_block(node)
         | 
| 23 | 
            +
                      return if already_processed_node?(node)
         | 
| 24 | 
            +
                      method, = *node
         | 
| 25 | 
            +
                      start_node = method.loc.expression.source =~ /\n/ ? method : node
         | 
| 26 | 
            +
                      check_block_alignment(start_node.loc.expression, node.loc)
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    def on_and(node)
         | 
| 30 | 
            +
                      return if already_processed_node?(node)
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                      _left, right = *node
         | 
| 33 | 
            +
                      if right.type == :block
         | 
| 34 | 
            +
                        check_block_alignment(node.loc.expression, right.loc)
         | 
| 35 | 
            +
                        @inspected_blocks << right
         | 
| 36 | 
            +
                      end
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    alias_method :on_or, :on_and
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    def on_lvasgn(node)
         | 
| 42 | 
            +
                      _, children = *node
         | 
| 43 | 
            +
                      process_block_assignment(node, children)
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                    alias_method :on_ivasgn,   :on_lvasgn
         | 
| 47 | 
            +
                    alias_method :on_cvasgn,   :on_lvasgn
         | 
| 48 | 
            +
                    alias_method :on_gvasgn,   :on_lvasgn
         | 
| 49 | 
            +
                    alias_method :on_and_asgn, :on_lvasgn
         | 
| 50 | 
            +
                    alias_method :on_or_asgn,  :on_lvasgn
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    def on_casgn(node)
         | 
| 53 | 
            +
                      _, _, children = *node
         | 
| 54 | 
            +
                      process_block_assignment(node, children)
         | 
| 55 | 
            +
                    end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                    def on_op_asgn(node)
         | 
| 58 | 
            +
                      variable, _op, args = *node
         | 
| 59 | 
            +
                      process_block_assignment(variable, args)
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    def on_send(node)
         | 
| 63 | 
            +
                      _receiver, _method, *args = *node
         | 
| 64 | 
            +
                      process_block_assignment(node, args.last)
         | 
| 65 | 
            +
                    end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                    def on_masgn(node)
         | 
| 68 | 
            +
                      variables, args = *node
         | 
| 69 | 
            +
                      process_block_assignment(variables, args)
         | 
| 70 | 
            +
                    end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    private
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    def process_block_assignment(begin_node, other_node)
         | 
| 75 | 
            +
                      return unless other_node
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                      block_node = find_block_node(other_node)
         | 
| 78 | 
            +
                      return unless block_node.type == :block
         | 
| 79 | 
            +
             | 
| 80 | 
            +
                      # If the block is an argument in a function call, align end with
         | 
| 81 | 
            +
                      # the block itself, and not with the function.
         | 
| 82 | 
            +
                      if begin_node.type == :send
         | 
| 83 | 
            +
                        _receiver, method, *_args = *begin_node
         | 
| 84 | 
            +
                        begin_node = block_node if method.to_s =~ /^\w+$/
         | 
| 85 | 
            +
                      end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                      # Align with the expression that is on the same line
         | 
| 88 | 
            +
                      # where the block is defined
         | 
| 89 | 
            +
                      if begin_node.type != :mlhs && block_is_on_next_line?(begin_node,
         | 
| 90 | 
            +
                                                                            block_node)
         | 
| 91 | 
            +
                        return
         | 
| 92 | 
            +
                      end
         | 
| 93 | 
            +
                      return if already_processed_node?(block_node)
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                      @inspected_blocks << block_node
         | 
| 96 | 
            +
                      check_block_alignment(begin_node.loc.expression, block_node.loc)
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                    def find_block_node(node)
         | 
| 100 | 
            +
                      while [:send, :lvasgn].include?(node.type)
         | 
| 101 | 
            +
                        n = case node.type
         | 
| 102 | 
            +
                            when :send
         | 
| 103 | 
            +
                              find_block_or_send_node(node) || break
         | 
| 104 | 
            +
                            when :lvasgn
         | 
| 105 | 
            +
                              _variable, value = *node
         | 
| 106 | 
            +
                              value
         | 
| 107 | 
            +
                            end
         | 
| 108 | 
            +
                        node = n if n
         | 
| 109 | 
            +
                      end
         | 
| 110 | 
            +
                      node
         | 
| 111 | 
            +
                    end
         | 
| 112 | 
            +
             | 
| 113 | 
            +
                    def find_block_or_send_node(send_node)
         | 
| 114 | 
            +
                      receiver, _method, args = *send_node
         | 
| 115 | 
            +
                      [receiver, args].find do |subnode|
         | 
| 116 | 
            +
                        subnode && [:block, :send].include?(subnode.type)
         | 
| 117 | 
            +
                      end
         | 
| 118 | 
            +
                    end
         | 
| 119 | 
            +
             | 
| 120 | 
            +
                    def check_block_alignment(start_loc, block_loc)
         | 
| 121 | 
            +
                      match = start_loc.source.match(/\n(\s*)((end)?\.\S+)\Z/)
         | 
| 122 | 
            +
                      if match
         | 
| 123 | 
            +
                        start_line = start_loc.line + start_loc.source.count("\n")
         | 
| 124 | 
            +
                        start_column = match.captures[0].length
         | 
| 125 | 
            +
                        start_source = match.captures[1]
         | 
| 126 | 
            +
                      else
         | 
| 127 | 
            +
                        start_line = start_loc.line
         | 
| 128 | 
            +
                        start_column = start_loc.column
         | 
| 129 | 
            +
                        start_source = start_loc.source.lines.to_a.first.chomp
         | 
| 130 | 
            +
                      end
         | 
| 131 | 
            +
                      end_loc = block_loc.end
         | 
| 132 | 
            +
                      if block_loc.begin.line != end_loc.line &&
         | 
| 133 | 
            +
                          start_column != end_loc.column
         | 
| 134 | 
            +
                        add_offence(:warning,
         | 
| 135 | 
            +
                                    end_loc,
         | 
| 136 | 
            +
                                    sprintf(MSG, end_loc.line, end_loc.column,
         | 
| 137 | 
            +
                                            start_source, start_line, start_column))
         | 
| 138 | 
            +
                      end
         | 
| 139 | 
            +
                    end
         | 
| 140 | 
            +
             | 
| 141 | 
            +
                    def already_processed_node?(node)
         | 
| 142 | 
            +
                      @inspected_blocks.include?(node)
         | 
| 143 | 
            +
                    end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                    def block_is_on_next_line?(begin_node, block_node)
         | 
| 146 | 
            +
                      begin_node.loc.line != block_node.loc.line
         | 
| 147 | 
            +
                    end
         | 
| 148 | 
            +
                  end
         | 
| 149 | 
            +
                end
         | 
| 150 | 
            +
              end
         | 
| 151 | 
            +
            end
         |