rubocop 0.46.0 → 0.47.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubocop might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +77 -2
- data/config/default.yml +151 -74
- data/config/disabled.yml +9 -0
- data/config/enabled.yml +49 -9
- data/lib/rubocop.rb +36 -8
- data/lib/rubocop/ast/builder.rb +59 -0
- data/lib/rubocop/ast/node.rb +607 -0
- data/lib/rubocop/ast/node/array_node.rb +45 -0
- data/lib/rubocop/ast/node/case_node.rb +63 -0
- data/lib/rubocop/ast/node/for_node.rb +53 -0
- data/lib/rubocop/ast/node/hash_node.rb +102 -0
- data/lib/rubocop/ast/node/if_node.rb +136 -0
- data/lib/rubocop/ast/node/keyword_splat_node.rb +45 -0
- data/lib/rubocop/ast/node/mixin/conditional_node.rb +45 -0
- data/lib/rubocop/ast/node/mixin/hash_element_node.rb +125 -0
- data/lib/rubocop/ast/node/mixin/modifier_node.rb +17 -0
- data/lib/rubocop/ast/node/pair_node.rb +64 -0
- data/lib/rubocop/ast/node/until_node.rb +43 -0
- data/lib/rubocop/ast/node/when_node.rb +61 -0
- data/lib/rubocop/ast/node/while_node.rb +43 -0
- data/lib/rubocop/ast/sexp.rb +16 -0
- data/lib/rubocop/{ast_node → ast}/traversal.rb +1 -1
- data/lib/rubocop/cli.rb +18 -14
- data/lib/rubocop/comment_config.rb +1 -3
- data/lib/rubocop/config.rb +93 -35
- data/lib/rubocop/config_loader.rb +1 -1
- data/lib/rubocop/cop/badge.rb +73 -0
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
- data/lib/rubocop/cop/bundler/ordered_gems.rb +43 -3
- data/lib/rubocop/cop/commissioner.rb +17 -6
- data/lib/rubocop/cop/cop.rb +25 -112
- data/lib/rubocop/cop/lint/ambiguous_operator.rb +9 -4
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +7 -0
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +18 -4
- data/lib/rubocop/cop/lint/block_alignment.rb +40 -9
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +14 -0
- data/lib/rubocop/cop/lint/condition_position.rb +14 -16
- data/lib/rubocop/cop/lint/debugger.rb +28 -0
- data/lib/rubocop/cop/lint/def_end_alignment.rb +21 -1
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +13 -1
- data/lib/rubocop/cop/lint/duplicate_case_condition.rb +26 -22
- data/lib/rubocop/cop/lint/duplicate_methods.rb +15 -1
- data/lib/rubocop/cop/lint/duplicated_key.rb +16 -8
- data/lib/rubocop/cop/lint/each_with_object_argument.rb +9 -0
- data/lib/rubocop/cop/lint/else_layout.rb +26 -29
- data/lib/rubocop/cop/lint/empty_ensure.rb +38 -0
- data/lib/rubocop/cop/lint/empty_expression.rb +11 -1
- data/lib/rubocop/cop/lint/empty_interpolation.rb +8 -0
- data/lib/rubocop/cop/lint/empty_when.rb +14 -16
- data/lib/rubocop/cop/lint/end_alignment.rb +48 -28
- data/lib/rubocop/cop/lint/end_in_method.rb +23 -0
- data/lib/rubocop/cop/lint/ensure_return.rb +21 -0
- data/lib/rubocop/cop/lint/float_out_of_range.rb +5 -0
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +29 -4
- data/lib/rubocop/cop/lint/handle_exceptions.rb +40 -0
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +7 -2
- data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +11 -2
- data/lib/rubocop/cop/lint/invalid_character_literal.rb +3 -0
- data/lib/rubocop/cop/lint/literal_in_condition.rb +34 -36
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -0
- data/lib/rubocop/cop/lint/loop.rb +36 -0
- data/lib/rubocop/cop/lint/multiple_compare.rb +46 -0
- data/lib/rubocop/cop/lint/nested_method_definition.rb +22 -0
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +5 -0
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +8 -0
- data/lib/rubocop/cop/lint/percent_string_array.rb +27 -13
- data/lib/rubocop/cop/lint/percent_symbol_array.rb +14 -4
- data/lib/rubocop/cop/lint/rand_one.rb +7 -3
- data/lib/rubocop/cop/lint/require_parentheses.rb +20 -19
- data/lib/rubocop/cop/lint/rescue_exception.rb +20 -0
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +66 -0
- data/lib/rubocop/cop/lint/shadowed_exception.rb +6 -1
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +24 -0
- data/lib/rubocop/cop/lint/string_conversion_in_interpolation.rb +8 -0
- data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +24 -0
- data/lib/rubocop/cop/lint/unified_integer.rb +5 -0
- data/lib/rubocop/cop/lint/unneeded_disable.rb +2 -2
- data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +5 -0
- data/lib/rubocop/cop/lint/unreachable_code.rb +17 -0
- data/lib/rubocop/cop/lint/unused_block_argument.rb +2 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +28 -1
- data/lib/rubocop/cop/lint/useless_assignment.rb +18 -0
- data/lib/rubocop/cop/lint/useless_comparison.rb +3 -1
- data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +16 -1
- data/lib/rubocop/cop/lint/useless_setter_call.rb +16 -4
- data/lib/rubocop/cop/lint/void.rb +52 -0
- data/lib/rubocop/cop/message_annotator.rb +102 -0
- data/lib/rubocop/cop/metrics/block_length.rb +6 -0
- data/lib/rubocop/cop/metrics/block_nesting.rb +17 -5
- data/lib/rubocop/cop/metrics/line_length.rb +11 -4
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
- data/lib/rubocop/cop/mixin/array_syntax.rb +2 -11
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +12 -5
- data/lib/rubocop/cop/mixin/configurable_formatting.rb +48 -0
- data/lib/rubocop/cop/mixin/configurable_max.rb +3 -3
- data/lib/rubocop/cop/mixin/configurable_naming.rb +5 -33
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +6 -47
- data/lib/rubocop/cop/mixin/documentation_comment.rb +7 -1
- data/lib/rubocop/cop/mixin/duplication.rb +46 -0
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +2 -2
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +14 -11
- data/lib/rubocop/cop/mixin/hash_alignment.rb +114 -0
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -3
- data/lib/rubocop/cop/mixin/negative_conditional.rb +21 -7
- data/lib/rubocop/cop/mixin/on_method_def.rb +14 -0
- data/lib/rubocop/cop/mixin/on_normal_if_unless.rb +1 -24
- data/lib/rubocop/cop/mixin/statement_modifier.rb +8 -13
- data/lib/rubocop/cop/mixin/target_ruby_version.rb +16 -0
- data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -3
- data/lib/rubocop/cop/offense.rb +1 -1
- data/lib/rubocop/cop/performance/case_when_splat.rb +56 -59
- data/lib/rubocop/cop/performance/detect.rb +2 -2
- data/lib/rubocop/cop/performance/flat_map.rb +3 -3
- data/lib/rubocop/cop/performance/redundant_merge.rb +3 -6
- data/lib/rubocop/cop/performance/regexp_match.rb +201 -0
- data/lib/rubocop/cop/rails/delegate.rb +2 -2
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +10 -19
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +12 -40
- data/lib/rubocop/cop/rails/file_path.rb +80 -0
- data/lib/rubocop/cop/rails/find_each.rb +5 -14
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +30 -24
- data/lib/rubocop/cop/rails/not_null_column.rb +23 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +217 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +4 -2
- data/lib/rubocop/cop/rails/skips_model_validations.rb +46 -0
- data/lib/rubocop/cop/rails/time_zone.rb +1 -1
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +7 -5
- data/lib/rubocop/cop/registry.rb +170 -0
- data/lib/rubocop/cop/{lint → security}/eval.rb +7 -1
- data/lib/rubocop/cop/security/marshal_load.rb +33 -0
- data/lib/rubocop/cop/security/yaml_load.rb +37 -0
- data/lib/rubocop/cop/style/align_hash.rb +138 -169
- data/lib/rubocop/cop/style/and_or.rb +1 -1
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +10 -15
- data/lib/rubocop/cop/style/case_indentation.rb +36 -27
- data/lib/rubocop/cop/style/conditional_assignment.rb +64 -47
- data/lib/rubocop/cop/style/each_with_object.rb +4 -1
- data/lib/rubocop/cop/style/else_alignment.rb +14 -20
- data/lib/rubocop/cop/style/empty_case_condition.rb +16 -25
- data/lib/rubocop/cop/style/empty_else.rb +20 -22
- data/lib/rubocop/cop/style/empty_literal.rb +4 -4
- data/lib/rubocop/cop/style/empty_method.rb +12 -6
- data/lib/rubocop/cop/style/encoding.rb +1 -1
- data/lib/rubocop/cop/style/file_name.rb +24 -4
- data/lib/rubocop/cop/style/first_method_argument_line_break.rb +1 -1
- data/lib/rubocop/cop/style/format_string.rb +17 -48
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +40 -11
- data/lib/rubocop/cop/style/guard_clause.rb +11 -17
- data/lib/rubocop/cop/style/hash_syntax.rb +24 -42
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +40 -28
- data/lib/rubocop/cop/style/if_inside_else.rb +6 -9
- data/lib/rubocop/cop/style/if_unless_modifier.rb +16 -25
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +3 -9
- data/lib/rubocop/cop/style/indent_array.rb +1 -1
- data/lib/rubocop/cop/style/indentation_width.rb +29 -60
- data/lib/rubocop/cop/style/infinite_loop.rb +21 -22
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +86 -0
- data/lib/rubocop/cop/style/{method_call_parentheses.rb → method_call_without_args_parentheses.rb} +8 -1
- data/lib/rubocop/cop/style/missing_else.rb +40 -14
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +5 -15
- data/lib/rubocop/cop/style/multiline_if_then.rb +14 -8
- data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +3 -3
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -5
- data/lib/rubocop/cop/style/mutable_constant.rb +3 -2
- data/lib/rubocop/cop/style/negated_if.rb +3 -19
- data/lib/rubocop/cop/style/negated_while.rb +2 -17
- data/lib/rubocop/cop/style/nested_modifier.rb +16 -43
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -5
- data/lib/rubocop/cop/style/next.rb +23 -21
- data/lib/rubocop/cop/style/non_nil_check.rb +2 -3
- data/lib/rubocop/cop/style/not.rb +1 -3
- data/lib/rubocop/cop/style/numeric_literals.rb +2 -2
- data/lib/rubocop/cop/style/one_line_conditional.rb +12 -22
- data/lib/rubocop/cop/style/option_hash.rb +4 -15
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -3
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -12
- data/lib/rubocop/cop/style/percent_q_literals.rb +15 -12
- data/lib/rubocop/cop/style/redundant_freeze.rb +3 -2
- data/lib/rubocop/cop/style/redundant_parentheses.rb +27 -4
- data/lib/rubocop/cop/style/redundant_return.rb +4 -8
- data/lib/rubocop/cop/style/safe_navigation.rb +13 -6
- data/lib/rubocop/cop/style/space_after_colon.rb +2 -4
- data/lib/rubocop/cop/style/space_around_block_parameters.rb +1 -1
- data/lib/rubocop/cop/style/space_around_operators.rb +15 -13
- data/lib/rubocop/cop/style/string_methods.rb +1 -3
- data/lib/rubocop/cop/style/symbol_array.rb +1 -5
- data/lib/rubocop/cop/style/ternary_parentheses.rb +5 -6
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +2 -5
- data/lib/rubocop/cop/style/trailing_comma_in_literal.rb +1 -1
- data/lib/rubocop/cop/style/unless_else.rb +1 -5
- data/lib/rubocop/cop/style/when_then.rb +4 -2
- data/lib/rubocop/cop/style/while_until_do.rb +9 -13
- data/lib/rubocop/cop/style/while_until_modifier.rb +12 -11
- data/lib/rubocop/cop/style/word_array.rb +5 -9
- data/lib/rubocop/cop/team.rb +16 -15
- data/lib/rubocop/cop/util.rb +13 -3
- data/lib/rubocop/formatter/clang_style_formatter.rb +2 -2
- data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
- data/lib/rubocop/magic_comment.rb +196 -0
- data/lib/rubocop/options.rb +5 -4
- data/lib/rubocop/processed_source.rb +1 -1
- data/lib/rubocop/rspec/cop_helper.rb +9 -0
- data/lib/rubocop/rspec/shared_examples.rb +1 -1
- data/lib/rubocop/runner.rb +7 -2
- data/lib/rubocop/version.rb +1 -1
- metadata +41 -14
- data/lib/rubocop/ast_node.rb +0 -624
- data/lib/rubocop/ast_node/builder.rb +0 -30
- data/lib/rubocop/ast_node/sexp.rb +0 -13
- data/lib/rubocop/cop/mixin/hash_node.rb +0 -14
- data/lib/rubocop/cop/mixin/if_node.rb +0 -42
@@ -40,15 +40,17 @@ module RuboCop
|
|
40
40
|
# foo&.bar(baz)
|
41
41
|
# foo&.bar { |e| e.baz }
|
42
42
|
class SafeNavigation < Cop
|
43
|
+
extend TargetRubyVersion
|
44
|
+
|
43
45
|
MSG = 'Use safe navigation (`&.`) instead of `%s`.'.freeze
|
44
46
|
|
45
47
|
def_node_matcher :try_call, <<-PATTERN
|
46
48
|
(send !nil ${:try :try!} $_ ...)
|
47
49
|
PATTERN
|
48
50
|
|
49
|
-
|
50
|
-
return if target_ruby_version < 2.3
|
51
|
+
minimum_target_ruby_version 2.3
|
51
52
|
|
53
|
+
def on_send(node)
|
52
54
|
try_call(node) do |try_method, method_to_try|
|
53
55
|
return if try_method == :try && !cop_config['ConvertTry']
|
54
56
|
return unless method_to_try.sym_type?
|
@@ -0,0 +1,46 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Rails
|
6
|
+
# This cop checks for the use of methods which skip
|
7
|
+
# validations which are listed in
|
8
|
+
# http://guides.rubyonrails.org/active_record_validations.html#skipping-validations
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# Article.first.decrement!(:view_count)
|
13
|
+
# DiscussionBoard.decrement_counter(:post_count, 5)
|
14
|
+
# Article.first.increment!(:view_count)
|
15
|
+
# DiscussionBoard.increment_counter(:post_count, 5)
|
16
|
+
# person.toggle :active
|
17
|
+
# product.touch
|
18
|
+
# Billing.update_all("category = 'authorized', author = 'David'")
|
19
|
+
# user.update_attribute(website: 'example.com')
|
20
|
+
# user.update_columns(last_request_at: Time.current)
|
21
|
+
# Post.update_counters 5, comment_count: -1, action_count: 1
|
22
|
+
#
|
23
|
+
# # good
|
24
|
+
# user.update_attributes(website: 'example.com')
|
25
|
+
class SkipsModelValidations < Cop
|
26
|
+
MSG = 'Avoid using `%s` because it skips validations.'.freeze
|
27
|
+
|
28
|
+
def on_send(node)
|
29
|
+
_receiver, method_name = *node
|
30
|
+
|
31
|
+
return unless blacklist.include?(method_name.to_s)
|
32
|
+
|
33
|
+
add_offense(node,
|
34
|
+
node.loc.selector,
|
35
|
+
format(MSG, method_name))
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def blacklist
|
41
|
+
cop_config['Blacklist']
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -109,7 +109,7 @@ module RuboCop
|
|
109
109
|
# called is part of the time class.
|
110
110
|
def method_from_time_class?(node)
|
111
111
|
receiver, method_name, *_args = *node
|
112
|
-
if (receiver.is_a? RuboCop::Node) && !receiver.cbase_type?
|
112
|
+
if (receiver.is_a? RuboCop::AST::Node) && !receiver.cbase_type?
|
113
113
|
method_from_time_class?(receiver)
|
114
114
|
else
|
115
115
|
TIMECLASS.include? method_name
|
@@ -15,11 +15,11 @@ module RuboCop
|
|
15
15
|
# # good
|
16
16
|
# Model.uniq.pluck(:id)
|
17
17
|
#
|
18
|
-
# This cop has two different enforcement modes. When the
|
18
|
+
# This cop has two different enforcement modes. When the EnforcedStyle
|
19
19
|
# is conservative (the default) then only calls to pluck on a constant
|
20
20
|
# (i.e. a model class) before uniq are added as offenses.
|
21
21
|
#
|
22
|
-
# When the
|
22
|
+
# When the EnforcedStyle is aggressive then all calls to pluck before
|
23
23
|
# uniq are added as offenses. This may lead to false positives as the cop
|
24
24
|
# cannot distinguish between calls to pluck on an ActiveRecord::Relation
|
25
25
|
# vs a call to pluck on an ActiveRecord::Associations::CollectionProxy.
|
@@ -35,6 +35,8 @@ module RuboCop
|
|
35
35
|
# false positives.
|
36
36
|
#
|
37
37
|
class UniqBeforePluck < RuboCop::Cop::Cop
|
38
|
+
include ConfigurableEnforcedStyle
|
39
|
+
|
38
40
|
MSG = 'Use `%s` before `pluck`.'.freeze
|
39
41
|
NEWLINE = "\n".freeze
|
40
42
|
PATTERN = '[!^block (send (send %s :pluck ...) ${:uniq :distinct} ...)]'
|
@@ -47,7 +49,7 @@ module RuboCop
|
|
47
49
|
format(PATTERN, '_')
|
48
50
|
|
49
51
|
def on_send(node)
|
50
|
-
method = if
|
52
|
+
method = if style == :conservative
|
51
53
|
conservative_node_match(node)
|
52
54
|
else
|
53
55
|
aggressive_node_match(node)
|
@@ -66,8 +68,8 @@ module RuboCop
|
|
66
68
|
|
67
69
|
private
|
68
70
|
|
69
|
-
def
|
70
|
-
|
71
|
+
def style_parameter_name
|
72
|
+
'EnforcedStyle'
|
71
73
|
end
|
72
74
|
|
73
75
|
def dot_method_with_whitespace(method, node)
|
@@ -0,0 +1,170 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
# Error raised when an unqualified cop name is used that could
|
6
|
+
# refer to two or more cops under different departments
|
7
|
+
class AmbiguousCopName < RuboCop::Error
|
8
|
+
MSG = 'Ambiguous cop name `%<name>s` used in %<origin>s needs ' \
|
9
|
+
'department qualifier. Did you mean %<options>s?'.freeze
|
10
|
+
|
11
|
+
def initialize(name, origin, badges)
|
12
|
+
super(
|
13
|
+
format(
|
14
|
+
MSG,
|
15
|
+
name: name,
|
16
|
+
origin: origin,
|
17
|
+
options: badges.to_a.join(' or ')
|
18
|
+
)
|
19
|
+
)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Registry that tracks all cops by their badge and department.
|
24
|
+
class Registry
|
25
|
+
def initialize(cops = [])
|
26
|
+
@registry = {}
|
27
|
+
@departments = {}
|
28
|
+
|
29
|
+
cops.each { |cop| enlist(cop) }
|
30
|
+
end
|
31
|
+
|
32
|
+
def enlist(cop)
|
33
|
+
@registry[cop.badge] = cop
|
34
|
+
@departments[cop.department] ||= []
|
35
|
+
@departments[cop.department] << cop
|
36
|
+
end
|
37
|
+
|
38
|
+
# @return [Array<Symbol>] list of departments for current cops.
|
39
|
+
def departments
|
40
|
+
@departments.keys
|
41
|
+
end
|
42
|
+
|
43
|
+
# @return [Registry] Cops for that specific department.
|
44
|
+
def with_department(department)
|
45
|
+
with(@departments.fetch(department, []))
|
46
|
+
end
|
47
|
+
|
48
|
+
# @return [Registry] Cops not for a specific department.
|
49
|
+
def without_department(department)
|
50
|
+
without_department = @departments.dup
|
51
|
+
without_department.delete(department)
|
52
|
+
|
53
|
+
with(without_department.values.flatten)
|
54
|
+
end
|
55
|
+
|
56
|
+
def contains_cop_matching?(names)
|
57
|
+
cops.any? { |cop| cop.match?(names) }
|
58
|
+
end
|
59
|
+
|
60
|
+
# Convert a user provided cop name into a properly namespaced name
|
61
|
+
#
|
62
|
+
# @example gives back a correctly qualified cop name
|
63
|
+
#
|
64
|
+
# cops = RuboCop::Cop::Cop.all
|
65
|
+
# cops.qualified_cop_name('Style/IndentArray') # => 'Style/IndentArray'
|
66
|
+
#
|
67
|
+
# @example fixes incorrect namespaces
|
68
|
+
#
|
69
|
+
# cops = RuboCop::Cop::Cop.all
|
70
|
+
# cops.qualified_cop_name('Lint/IndentArray') # => 'Style/IndentArray'
|
71
|
+
#
|
72
|
+
# @example namespaces bare cop identifiers
|
73
|
+
#
|
74
|
+
# cops = RuboCop::Cop::Cop.all
|
75
|
+
# cops.qualified_cop_name('IndentArray') # => 'IndentArray'
|
76
|
+
#
|
77
|
+
# @example passes back unrecognized cop names
|
78
|
+
#
|
79
|
+
# cops = RuboCop::Cop::Cop.all
|
80
|
+
# cops.qualified_cop_name('NotACop') # => 'NotACop'
|
81
|
+
#
|
82
|
+
# @param name [String] Cop name extracted from config
|
83
|
+
# @param path [String] Path of file that `name` was extracted from
|
84
|
+
#
|
85
|
+
# @raise [AmbiguousCopName]
|
86
|
+
# if a bare identifier with two possible namespaces is provided
|
87
|
+
#
|
88
|
+
# @note Emits a warning if the provided name has an incorrect namespace
|
89
|
+
#
|
90
|
+
# @return [String] Qualified cop name
|
91
|
+
def qualified_cop_name(name, path)
|
92
|
+
badge = Badge.parse(name)
|
93
|
+
return name if registered?(badge)
|
94
|
+
|
95
|
+
potential_badges = qualify_badge(badge)
|
96
|
+
|
97
|
+
case potential_badges.size
|
98
|
+
when 0 then name # No namespace found. Deal with it later in caller.
|
99
|
+
when 1 then resolve_badge(badge, potential_badges.first, path)
|
100
|
+
else raise AmbiguousCopName.new(badge, path, potential_badges)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
def to_h
|
105
|
+
cops.group_by(&:cop_name)
|
106
|
+
end
|
107
|
+
|
108
|
+
def cops
|
109
|
+
@registry.values
|
110
|
+
end
|
111
|
+
|
112
|
+
def length
|
113
|
+
@registry.size
|
114
|
+
end
|
115
|
+
|
116
|
+
def enabled(config, only)
|
117
|
+
select do |cop|
|
118
|
+
config.cop_enabled?(cop) || only.include?(cop.cop_name)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
def names
|
123
|
+
cops.map(&:cop_name)
|
124
|
+
end
|
125
|
+
|
126
|
+
def ==(other)
|
127
|
+
cops == other.cops
|
128
|
+
end
|
129
|
+
|
130
|
+
def sort!
|
131
|
+
@registry = Hash[@registry.sort_by { |badge, _| badge.cop_name }]
|
132
|
+
|
133
|
+
self
|
134
|
+
end
|
135
|
+
|
136
|
+
def select(&block)
|
137
|
+
cops.select(&block)
|
138
|
+
end
|
139
|
+
|
140
|
+
def each(&block)
|
141
|
+
cops.each(&block)
|
142
|
+
end
|
143
|
+
|
144
|
+
private
|
145
|
+
|
146
|
+
def with(cops)
|
147
|
+
self.class.new(cops)
|
148
|
+
end
|
149
|
+
|
150
|
+
def qualify_badge(badge)
|
151
|
+
@departments
|
152
|
+
.map { |department, _| badge.with_department(department) }
|
153
|
+
.select { |potential_badge| registered?(potential_badge) }
|
154
|
+
end
|
155
|
+
|
156
|
+
def resolve_badge(given_badge, real_badge, source_path)
|
157
|
+
unless given_badge.match?(real_badge)
|
158
|
+
warn "#{source_path}: #{given_badge} has the wrong namespace - " \
|
159
|
+
"should be #{real_badge.department}"
|
160
|
+
end
|
161
|
+
|
162
|
+
real_badge.to_s
|
163
|
+
end
|
164
|
+
|
165
|
+
def registered?(badge)
|
166
|
+
@registry.key?(badge)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
@@ -2,8 +2,14 @@
|
|
2
2
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
|
-
module
|
5
|
+
module Security
|
6
6
|
# This cop checks for the use of *Kernel#eval*.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
#
|
10
|
+
# # bad
|
11
|
+
#
|
12
|
+
# eval(something)
|
7
13
|
class Eval < Cop
|
8
14
|
MSG = 'The use of `eval` is a serious security risk.'.freeze
|
9
15
|
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Security
|
6
|
+
# This cop checks for the use of Marshal class methods which have
|
7
|
+
# potential security issues leading to remote code execution when
|
8
|
+
# loading from an untrusted source.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# Marshal.load("{}")
|
13
|
+
# Marshal.restore("{}")
|
14
|
+
#
|
15
|
+
# # good
|
16
|
+
# Marshal.dump("{}")
|
17
|
+
#
|
18
|
+
class MarshalLoad < Cop
|
19
|
+
MSG = 'Avoid using `Marshal.%s`.'.freeze
|
20
|
+
|
21
|
+
def_node_matcher :marshal_load, <<-END
|
22
|
+
(send (const nil :Marshal) ${:load :restore} ...)
|
23
|
+
END
|
24
|
+
|
25
|
+
def on_send(node)
|
26
|
+
marshal_load(node) do |method|
|
27
|
+
add_offense(node, :selector, format(MSG, method))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module Cop
|
5
|
+
module Security
|
6
|
+
# This cop checks for the use of YAML class methods which have
|
7
|
+
# potential security issues leading to remote code execution when
|
8
|
+
# loading from an untrusted source.
|
9
|
+
#
|
10
|
+
# @example
|
11
|
+
# # bad
|
12
|
+
# YAML.load("--- foo")
|
13
|
+
#
|
14
|
+
# # good
|
15
|
+
# YAML.safe_load("--- foo")
|
16
|
+
# YAML.dump("foo")
|
17
|
+
#
|
18
|
+
class YAMLLoad < Cop
|
19
|
+
MSG = 'Prefer using `YAML.safe_load` over `YAML.load`.'.freeze
|
20
|
+
|
21
|
+
def_node_matcher :yaml_load, <<-END
|
22
|
+
(send (const nil :YAML) :load ...)
|
23
|
+
END
|
24
|
+
|
25
|
+
def on_send(node)
|
26
|
+
yaml_load(node) do |method|
|
27
|
+
add_offense(node, :selector, format(MSG, method))
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def autocorrect(node)
|
32
|
+
->(corrector) { corrector.replace(node.loc.selector, 'safe_load') }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -3,217 +3,186 @@
|
|
3
3
|
module RuboCop
|
4
4
|
module Cop
|
5
5
|
module Style
|
6
|
-
#
|
7
|
-
# literal are aligned.
|
6
|
+
# Check that the keys, separators, and values of a multi-line hash
|
7
|
+
# literal are aligned according to configuration. The configuration
|
8
|
+
# options are:
|
9
|
+
#
|
10
|
+
# - key (left align keys)
|
11
|
+
# - separator (align hash rockets and colons, right align keys)
|
12
|
+
# - table (left align keys, hash rockets, and values)
|
13
|
+
#
|
14
|
+
# The treatment of hashes passed as the last argument to a method call
|
15
|
+
# can also be configured. The options are:
|
16
|
+
#
|
17
|
+
# - always_inspect
|
18
|
+
# - always_ignore
|
19
|
+
# - ignore_implicit (without curly braces)
|
20
|
+
# - ignore_explicit (with curly braces)
|
21
|
+
#
|
22
|
+
# @example
|
23
|
+
#
|
24
|
+
# # EnforcedHashRocketStyle: key (default)
|
25
|
+
# # EnforcedColonStyle: key (default)
|
26
|
+
#
|
27
|
+
# # good
|
28
|
+
# {
|
29
|
+
# foo: bar,
|
30
|
+
# ba: baz
|
31
|
+
# }
|
32
|
+
# {
|
33
|
+
# :foo => bar,
|
34
|
+
# :ba => baz
|
35
|
+
# }
|
36
|
+
#
|
37
|
+
# # bad
|
38
|
+
# {
|
39
|
+
# foo: bar,
|
40
|
+
# ba: baz
|
41
|
+
# }
|
42
|
+
# {
|
43
|
+
# :foo => bar,
|
44
|
+
# :ba => baz
|
45
|
+
# }
|
46
|
+
#
|
47
|
+
# @example
|
48
|
+
#
|
49
|
+
# # EnforcedHashRocketStyle: separator
|
50
|
+
# # EnforcedColonStyle: separator
|
51
|
+
#
|
52
|
+
# #good
|
53
|
+
# {
|
54
|
+
# foo: bar,
|
55
|
+
# ba: baz
|
56
|
+
# }
|
57
|
+
# {
|
58
|
+
# :foo => bar,
|
59
|
+
# :ba => baz
|
60
|
+
# }
|
61
|
+
#
|
62
|
+
# #bad
|
63
|
+
# {
|
64
|
+
# foo: bar,
|
65
|
+
# ba: baz
|
66
|
+
# }
|
67
|
+
# {
|
68
|
+
# :foo => bar,
|
69
|
+
# :ba => baz
|
70
|
+
# }
|
71
|
+
# {
|
72
|
+
# :foo => bar,
|
73
|
+
# :ba => baz
|
74
|
+
# }
|
75
|
+
#
|
76
|
+
# @example
|
77
|
+
#
|
78
|
+
# # EnforcedHashRocketStyle: table
|
79
|
+
# # EnforcedColonStyle: table
|
80
|
+
#
|
81
|
+
# #good
|
82
|
+
# {
|
83
|
+
# foo: bar,
|
84
|
+
# ba: baz
|
85
|
+
# }
|
86
|
+
# {
|
87
|
+
# :foo => bar,
|
88
|
+
# :ba => baz
|
89
|
+
# }
|
90
|
+
#
|
91
|
+
# #bad
|
92
|
+
# {
|
93
|
+
# foo: bar,
|
94
|
+
# ba: baz
|
95
|
+
# }
|
96
|
+
# {
|
97
|
+
# :foo => bar,
|
98
|
+
# :ba => baz
|
99
|
+
# }
|
8
100
|
class AlignHash < Cop
|
9
|
-
|
10
|
-
# when the enforced style is 'key'.
|
11
|
-
class KeyAlignment
|
12
|
-
def checkable_layout(_node)
|
13
|
-
true
|
14
|
-
end
|
15
|
-
|
16
|
-
def deltas_for_first_pair(*)
|
17
|
-
{} # The first pair is always considered correct.
|
18
|
-
end
|
19
|
-
|
20
|
-
def deltas(first_pair, current_pair)
|
21
|
-
if Util.begins_its_line?(current_pair.source_range)
|
22
|
-
{ key: first_pair.loc.column - current_pair.loc.column }
|
23
|
-
else
|
24
|
-
{}
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# Common functionality for the styles where not only keys, but also
|
30
|
-
# values are aligned.
|
31
|
-
class AlignmentOfValues
|
32
|
-
include HashNode # any_pairs_on_the_same_line?
|
33
|
-
|
34
|
-
def checkable_layout(node)
|
35
|
-
!any_pairs_on_the_same_line?(node) && all_have_same_separator?(node)
|
36
|
-
end
|
37
|
-
|
38
|
-
def deltas(first_pair, current_pair)
|
39
|
-
key_delta = key_delta(first_pair, current_pair)
|
40
|
-
current_separator = current_pair.loc.operator
|
41
|
-
separator_delta = separator_delta(first_pair, current_separator,
|
42
|
-
key_delta)
|
43
|
-
value_delta = value_delta(first_pair, current_pair) -
|
44
|
-
key_delta - separator_delta
|
45
|
-
|
46
|
-
{ key: key_delta, separator: separator_delta, value: value_delta }
|
47
|
-
end
|
48
|
-
|
49
|
-
private
|
50
|
-
|
51
|
-
def separator_delta(first_pair, current_separator, key_delta)
|
52
|
-
if current_separator.is?(':')
|
53
|
-
0 # Colon follows directly after key
|
54
|
-
else
|
55
|
-
hash_rocket_delta(first_pair, current_separator) - key_delta
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
def all_have_same_separator?(node)
|
60
|
-
first_separator = node.children.first.loc.operator.source
|
61
|
-
node.children.butfirst.all? do |pair|
|
62
|
-
pair.loc.operator.is?(first_separator)
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
66
|
-
|
67
|
-
# Handles calculation of deltas when the enforced style is 'table'.
|
68
|
-
class TableAlignment < AlignmentOfValues
|
69
|
-
# The table style is the only one where the first key-value pair can
|
70
|
-
# be considered to have bad alignment.
|
71
|
-
def deltas_for_first_pair(first_pair, node)
|
72
|
-
key_widths = node.children.map do |pair|
|
73
|
-
key, _value = *pair
|
74
|
-
key.source.length
|
75
|
-
end
|
76
|
-
@max_key_width = key_widths.max
|
77
|
-
|
78
|
-
separator_delta = separator_delta(first_pair,
|
79
|
-
first_pair.loc.operator, 0)
|
80
|
-
{
|
81
|
-
separator: separator_delta,
|
82
|
-
value: value_delta(first_pair, first_pair) - separator_delta
|
83
|
-
}
|
84
|
-
end
|
85
|
-
|
86
|
-
private
|
87
|
-
|
88
|
-
def key_delta(first_pair, current_pair)
|
89
|
-
first_pair.loc.column - current_pair.loc.column
|
90
|
-
end
|
91
|
-
|
92
|
-
def hash_rocket_delta(first_pair, current_separator)
|
93
|
-
first_pair.loc.column + @max_key_width + 1 -
|
94
|
-
current_separator.column
|
95
|
-
end
|
96
|
-
|
97
|
-
def value_delta(first_pair, current_pair)
|
98
|
-
first_key, = *first_pair
|
99
|
-
_, current_value = *current_pair
|
100
|
-
correct_value_column = first_key.loc.column +
|
101
|
-
spaced_separator(current_pair).length +
|
102
|
-
@max_key_width
|
103
|
-
correct_value_column - current_value.loc.column
|
104
|
-
end
|
105
|
-
|
106
|
-
def spaced_separator(node)
|
107
|
-
node.loc.operator.is?('=>') ? ' => ' : ': '
|
108
|
-
end
|
109
|
-
end
|
110
|
-
|
111
|
-
# Handles calculation of deltas when the enforced style is 'separator'.
|
112
|
-
class SeparatorAlignment < AlignmentOfValues
|
113
|
-
def deltas_for_first_pair(*)
|
114
|
-
{} # The first pair is always considered correct.
|
115
|
-
end
|
116
|
-
|
117
|
-
private
|
118
|
-
|
119
|
-
def key_delta(first_pair, current_pair)
|
120
|
-
key_end_column(first_pair) - key_end_column(current_pair)
|
121
|
-
end
|
122
|
-
|
123
|
-
def key_end_column(pair)
|
124
|
-
key, _value = *pair
|
125
|
-
key.loc.column + key.source.length
|
126
|
-
end
|
127
|
-
|
128
|
-
def hash_rocket_delta(first_pair, current_separator)
|
129
|
-
first_pair.loc.operator.column - current_separator.column
|
130
|
-
end
|
131
|
-
|
132
|
-
def value_delta(first_pair, current_pair)
|
133
|
-
_, first_value = *first_pair
|
134
|
-
_, current_value = *current_pair
|
135
|
-
first_value.loc.column - current_value.loc.column
|
136
|
-
end
|
137
|
-
end
|
101
|
+
include HashAlignment
|
138
102
|
|
139
103
|
MSG = 'Align the elements of a hash literal if they span more than ' \
|
140
104
|
'one line.'.freeze
|
141
105
|
|
142
106
|
def on_send(node)
|
143
|
-
return
|
144
|
-
|
145
|
-
|
107
|
+
return if double_splat?(node)
|
108
|
+
|
109
|
+
last_argument = node.children.last
|
110
|
+
|
111
|
+
return unless last_argument.hash_type? &&
|
112
|
+
ignore_hash_argument?(last_argument)
|
146
113
|
|
147
|
-
ignore_node(
|
114
|
+
ignore_node(last_argument)
|
148
115
|
end
|
149
116
|
|
150
117
|
def on_hash(node)
|
151
118
|
return if ignored_node?(node)
|
152
|
-
return if node.
|
153
|
-
return unless node.multiline?
|
119
|
+
return if node.pairs.empty? || node.single_line?
|
154
120
|
|
155
|
-
|
156
|
-
|
157
|
-
@alignment_for_colons ||= new_alignment('EnforcedColonStyle')
|
158
|
-
|
159
|
-
unless @alignment_for_hash_rockets.checkable_layout(node) &&
|
160
|
-
@alignment_for_colons.checkable_layout(node)
|
161
|
-
return
|
162
|
-
end
|
121
|
+
return unless alignment_for_hash_rockets.checkable_layout?(node) &&
|
122
|
+
alignment_for_colons.checkable_layout?(node)
|
163
123
|
|
164
124
|
check_pairs(node)
|
165
125
|
end
|
166
126
|
|
167
127
|
private
|
168
128
|
|
129
|
+
attr_accessor :column_deltas
|
130
|
+
|
131
|
+
def double_splat?(node)
|
132
|
+
node.children.last.is_a?(Symbol)
|
133
|
+
end
|
134
|
+
|
169
135
|
def check_pairs(node)
|
170
|
-
first_pair = node.
|
171
|
-
|
172
|
-
|
136
|
+
first_pair = node.pairs.first
|
137
|
+
self.column_deltas = alignment_for(first_pair)
|
138
|
+
.deltas_for_first_pair(first_pair, node)
|
173
139
|
add_offense(first_pair, :expression) unless good_alignment?
|
174
140
|
|
175
141
|
node.children.each do |current|
|
176
|
-
|
142
|
+
self.column_deltas = alignment_for(current)
|
143
|
+
.deltas(first_pair, current)
|
177
144
|
add_offense(current, :expression) unless good_alignment?
|
178
145
|
end
|
179
146
|
end
|
180
147
|
|
181
|
-
def
|
148
|
+
def ignore_hash_argument?(node)
|
182
149
|
case cop_config['EnforcedLastArgumentHashStyle']
|
183
150
|
when 'always_inspect' then false
|
184
151
|
when 'always_ignore' then true
|
185
|
-
when 'ignore_explicit' then
|
186
|
-
when 'ignore_implicit' then !
|
152
|
+
when 'ignore_explicit' then node.braces?
|
153
|
+
when 'ignore_implicit' then !node.braces?
|
187
154
|
end
|
188
155
|
end
|
189
156
|
|
190
|
-
def
|
191
|
-
|
157
|
+
def alignment_for(pair)
|
158
|
+
if pair.hash_rocket?
|
159
|
+
alignment_for_hash_rockets
|
160
|
+
else
|
161
|
+
alignment_for_colons
|
162
|
+
end
|
192
163
|
end
|
193
164
|
|
194
|
-
def
|
195
|
-
|
165
|
+
def alignment_for_hash_rockets
|
166
|
+
@alignment_for_hash_rockets ||=
|
167
|
+
new_alignment('EnforcedHashRocketStyle')
|
196
168
|
end
|
197
169
|
|
198
|
-
def
|
199
|
-
|
200
|
-
|
201
|
-
else
|
202
|
-
@alignment_for_colons
|
203
|
-
end
|
170
|
+
def alignment_for_colons
|
171
|
+
@alignment_for_colons ||=
|
172
|
+
new_alignment('EnforcedColonStyle')
|
204
173
|
end
|
205
174
|
|
206
175
|
def autocorrect(node)
|
207
176
|
# We can't use the instance variable inside the lambda. That would
|
208
177
|
# just give each lambda the same reference and they would all get the
|
209
178
|
# last value of each. A local variable fixes the problem.
|
210
|
-
key_delta =
|
211
|
-
key, value = *node
|
179
|
+
key_delta = column_deltas[:key] || 0
|
212
180
|
|
213
|
-
if value
|
181
|
+
if !node.value
|
214
182
|
correct_no_value(key_delta, node.source_range)
|
215
183
|
else
|
216
|
-
correct_key_value(key_delta, key.source_range,
|
184
|
+
correct_key_value(key_delta, node.key.source_range,
|
185
|
+
node.value.source_range,
|
217
186
|
node.loc.operator)
|
218
187
|
end
|
219
188
|
end
|
@@ -226,8 +195,8 @@ module RuboCop
|
|
226
195
|
# We can't use the instance variable inside the lambda. That would
|
227
196
|
# just give each lambda the same reference and they would all get the
|
228
197
|
# last value of each. Some local variables fix the problem.
|
229
|
-
separator_delta =
|
230
|
-
value_delta =
|
198
|
+
separator_delta = column_deltas[:separator] || 0
|
199
|
+
value_delta = column_deltas[:value] || 0
|
231
200
|
|
232
201
|
key_column = key.column
|
233
202
|
key_delta = -key_column if key_delta < -key_column
|
@@ -258,7 +227,7 @@ module RuboCop
|
|
258
227
|
end
|
259
228
|
|
260
229
|
def good_alignment?
|
261
|
-
|
230
|
+
column_deltas.values.all?(&:zero?)
|
262
231
|
end
|
263
232
|
end
|
264
233
|
end
|