rubocop 1.91.0 → 1.92.2
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 +4 -4
- data/README.md +1 -1
- data/config/default.yml +4 -1
- data/lib/rubocop/cached_data.rb +47 -7
- data/lib/rubocop/changed_files.rb +35 -13
- data/lib/rubocop/cli/command/cop_names.rb +57 -0
- data/lib/rubocop/cli/command/execute_runner.rb +2 -2
- data/lib/rubocop/cli/command/explain.rb +34 -0
- data/lib/rubocop/cli/command/show_cops.rb +6 -0
- data/lib/rubocop/cli/command/show_docs_url.rb +6 -4
- data/lib/rubocop/cli.rb +3 -2
- data/lib/rubocop/comment_config.rb +17 -8
- data/lib/rubocop/config.rb +0 -4
- data/lib/rubocop/config_loader.rb +6 -3
- data/lib/rubocop/config_loader_resolver.rb +37 -12
- data/lib/rubocop/config_obsoletion/removed_cop.rb +1 -1
- data/lib/rubocop/config_obsoletion.rb +9 -0
- data/lib/rubocop/config_validator.rb +11 -0
- data/lib/rubocop/cop/autocorrect_logic.rb +10 -5
- data/lib/rubocop/cop/badge.rb +1 -1
- data/lib/rubocop/cop/base.rb +42 -21
- data/lib/rubocop/cop/bundler/gem_comment.rb +0 -1
- data/lib/rubocop/cop/bundler/ordered_gems.rb +0 -8
- data/lib/rubocop/cop/cop_documentation.rb +81 -0
- data/lib/rubocop/cop/cop_explanation.rb +140 -0
- data/lib/rubocop/cop/correctors/alignment_corrector.rb +13 -1
- data/lib/rubocop/cop/correctors/each_to_for_corrector.rb +37 -7
- data/lib/rubocop/cop/correctors/empty_line_corrector.rb +1 -1
- data/lib/rubocop/cop/correctors/lambda_literal_to_method_corrector.rb +25 -4
- data/lib/rubocop/cop/correctors/multiline_literal_brace_corrector.rb +34 -0
- data/lib/rubocop/cop/correctors/parentheses_corrector.rb +45 -8
- data/lib/rubocop/cop/documentation.rb +1 -1
- data/lib/rubocop/cop/gemspec/dependency_version.rb +0 -6
- data/lib/rubocop/cop/gemspec/ordered_dependencies.rb +0 -6
- data/lib/rubocop/cop/internal_affairs/node_matcher_directive.rb +2 -5
- data/lib/rubocop/cop/internal_affairs/node_type_group.rb +0 -7
- data/lib/rubocop/cop/internal_affairs/redundant_context_config_parameter.rb +1 -7
- data/lib/rubocop/cop/internal_affairs/style_detected_api_use.rb +1 -7
- data/lib/rubocop/cop/layout/argument_alignment.rb +5 -1
- data/lib/rubocop/cop/layout/block_alignment.rb +27 -0
- data/lib/rubocop/cop/layout/case_indentation.rb +25 -9
- data/lib/rubocop/cop/layout/class_structure.rb +29 -7
- data/lib/rubocop/cop/layout/def_end_alignment.rb +4 -0
- data/lib/rubocop/cop/layout/dot_position.rb +25 -15
- data/lib/rubocop/cop/layout/empty_line_after_multiline_condition.rb +21 -8
- data/lib/rubocop/cop/layout/empty_lines.rb +5 -2
- data/lib/rubocop/cop/layout/empty_lines_after_module_inclusion.rb +1 -1
- data/lib/rubocop/cop/layout/empty_lines_around_attribute_accessor.rb +1 -0
- data/lib/rubocop/cop/layout/empty_lines_around_block_body.rb +9 -3
- data/lib/rubocop/cop/layout/empty_lines_around_exception_handling_keywords.rb +23 -1
- data/lib/rubocop/cop/layout/end_alignment.rb +21 -25
- data/lib/rubocop/cop/layout/extra_spacing.rb +21 -6
- data/lib/rubocop/cop/layout/first_argument_indentation.rb +27 -1
- data/lib/rubocop/cop/layout/first_array_element_line_break.rb +15 -0
- data/lib/rubocop/cop/layout/first_hash_element_indentation.rb +16 -8
- data/lib/rubocop/cop/layout/first_method_argument_line_break.rb +6 -0
- data/lib/rubocop/cop/layout/first_method_parameter_line_break.rb +4 -0
- data/lib/rubocop/cop/layout/hash_alignment.rb +16 -12
- data/lib/rubocop/cop/layout/heredoc_argument_closing_parenthesis.rb +7 -3
- data/lib/rubocop/cop/layout/heredoc_indentation.rb +15 -0
- data/lib/rubocop/cop/layout/indentation_consistency.rb +22 -5
- data/lib/rubocop/cop/layout/indentation_style.rb +4 -1
- data/lib/rubocop/cop/layout/indentation_width.rb +2 -2
- data/lib/rubocop/cop/layout/line_continuation_spacing.rb +8 -1
- data/lib/rubocop/cop/layout/line_end_string_concatenation_indentation.rb +25 -9
- data/lib/rubocop/cop/layout/line_length.rb +176 -26
- data/lib/rubocop/cop/layout/multiline_array_brace_layout.rb +12 -0
- data/lib/rubocop/cop/layout/multiline_block_layout.rb +8 -1
- data/lib/rubocop/cop/layout/multiline_hash_brace_layout.rb +9 -0
- data/lib/rubocop/cop/layout/multiline_method_call_brace_layout.rb +6 -0
- data/lib/rubocop/cop/layout/multiline_method_call_indentation.rb +30 -5
- data/lib/rubocop/cop/layout/multiline_method_definition_brace_layout.rb +4 -0
- data/lib/rubocop/cop/layout/redundant_line_break.rb +22 -1
- data/lib/rubocop/cop/layout/rescue_ensure_alignment.rb +16 -3
- data/lib/rubocop/cop/layout/single_line_block_chain.rb +10 -2
- data/lib/rubocop/cop/layout/space_after_not.rb +5 -0
- data/lib/rubocop/cop/layout/space_around_block_parameters.rb +6 -11
- data/lib/rubocop/cop/layout/space_around_keyword.rb +10 -0
- data/lib/rubocop/cop/layout/space_around_operators.rb +31 -4
- data/lib/rubocop/cop/layout/space_before_block_braces.rb +16 -1
- data/lib/rubocop/cop/layout/space_inside_array_literal_brackets.rb +15 -2
- data/lib/rubocop/cop/layout/space_inside_block_braces.rb +32 -9
- data/lib/rubocop/cop/layout/space_inside_range_literal.rb +29 -11
- data/lib/rubocop/cop/layout/space_inside_reference_brackets.rb +2 -12
- data/lib/rubocop/cop/lint/ambiguous_operator.rb +1 -1
- data/lib/rubocop/cop/lint/ambiguous_operator_precedence.rb +2 -1
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +6 -1
- data/lib/rubocop/cop/lint/empty_conditional_body.rb +3 -1
- data/lib/rubocop/cop/lint/heredoc_method_call_position.rb +4 -0
- data/lib/rubocop/cop/lint/interpolation_check.rb +20 -17
- data/lib/rubocop/cop/lint/literal_as_condition.rb +15 -0
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +63 -19
- data/lib/rubocop/cop/lint/loop.rb +24 -2
- data/lib/rubocop/cop/lint/misplaced_magic_comment.rb +71 -11
- data/lib/rubocop/cop/lint/missing_cop_enable_directive.rb +18 -3
- data/lib/rubocop/cop/lint/nested_percent_literal.rb +3 -0
- data/lib/rubocop/cop/lint/number_conversion.rb +3 -1
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +22 -1
- data/lib/rubocop/cop/lint/percent_string_array.rb +75 -9
- data/lib/rubocop/cop/lint/redundant_cop_disable_directive.rb +39 -9
- data/lib/rubocop/cop/lint/redundant_dir_glob_sort.rb +13 -2
- data/lib/rubocop/cop/lint/redundant_splat_expansion.rb +11 -4
- data/lib/rubocop/cop/lint/safe_navigation_consistency.rb +47 -1
- data/lib/rubocop/cop/lint/symbol_conversion.rb +1 -0
- data/lib/rubocop/cop/lint/unescaped_bracket_in_regexp.rb +18 -1
- data/lib/rubocop/cop/lint/useless_assignment.rb +14 -7
- data/lib/rubocop/cop/lint/useless_setter_call.rb +7 -4
- data/lib/rubocop/cop/lint/void.rb +8 -1
- data/lib/rubocop/cop/metrics/class_length.rb +4 -2
- data/lib/rubocop/cop/metrics/cyclomatic_complexity.rb +0 -4
- data/lib/rubocop/cop/metrics/module_length.rb +4 -2
- data/lib/rubocop/cop/mixin/alignment.rb +4 -0
- data/lib/rubocop/cop/mixin/check_line_breakable.rb +23 -19
- data/lib/rubocop/cop/mixin/check_single_line_suitability.rb +8 -2
- data/lib/rubocop/cop/mixin/code_length.rb +10 -2
- data/lib/rubocop/cop/mixin/comments_help.rb +1 -1
- data/lib/rubocop/cop/mixin/empty_lines_around_body.rb +12 -1
- data/lib/rubocop/cop/mixin/endless_method_body_help.rb +105 -0
- data/lib/rubocop/cop/mixin/forbidden_identifiers.rb +2 -2
- data/lib/rubocop/cop/mixin/hash_alignment_styles.rb +41 -6
- data/lib/rubocop/cop/mixin/hash_shorthand_syntax.rb +26 -2
- data/lib/rubocop/cop/mixin/min_branches_count.rb +3 -1
- data/lib/rubocop/cop/mixin/multiline_element_line_breaks.rb +11 -1
- data/lib/rubocop/cop/mixin/multiline_literal_brace_layout.rb +9 -0
- data/lib/rubocop/cop/mixin/ordered_gem_node.rb +1 -1
- data/lib/rubocop/cop/mixin/pattern_matching_help.rb +35 -0
- data/lib/rubocop/cop/mixin/preceding_following_alignment.rb +17 -2
- data/lib/rubocop/cop/mixin/project_index_help.rb +15 -0
- data/lib/rubocop/cop/mixin/reparsed_equivalence.rb +13 -3
- data/lib/rubocop/cop/mixin/statement_modifier.rb +39 -5
- data/lib/rubocop/cop/mixin/trailing_comma.rb +1 -1
- data/lib/rubocop/cop/mixin/visibility_help.rb +2 -1
- data/lib/rubocop/cop/mixin.rb +2 -0
- data/lib/rubocop/cop/naming/accessor_method_name.rb +0 -16
- data/lib/rubocop/cop/naming/block_forwarding.rb +1 -1
- data/lib/rubocop/cop/naming/heredoc_delimiter_case.rb +9 -2
- data/lib/rubocop/cop/naming/heredoc_delimiter_naming.rb +5 -3
- data/lib/rubocop/cop/naming/predicate_prefix.rb +7 -22
- data/lib/rubocop/cop/naming/rescued_exceptions_variable_name.rb +1 -1
- data/lib/rubocop/cop/offense.rb +59 -3
- data/lib/rubocop/cop/registry.rb +1 -1
- data/lib/rubocop/cop/security/json_load.rb +16 -4
- data/lib/rubocop/cop/style/access_modifier_declarations.rb +37 -2
- data/lib/rubocop/cop/style/alias.rb +18 -5
- data/lib/rubocop/cop/style/ambiguous_endless_method_definition.rb +4 -3
- data/lib/rubocop/cop/style/and_or.rb +37 -10
- data/lib/rubocop/cop/style/arguments_forwarding.rb +32 -3
- data/lib/rubocop/cop/style/attr.rb +3 -4
- data/lib/rubocop/cop/style/bitwise_predicate.rb +6 -4
- data/lib/rubocop/cop/style/block_delimiters.rb +35 -3
- data/lib/rubocop/cop/style/case_equality.rb +14 -3
- data/lib/rubocop/cop/style/case_like_if.rb +6 -3
- data/lib/rubocop/cop/style/class_and_module_children.rb +57 -16
- data/lib/rubocop/cop/style/class_methods_definitions.rb +15 -0
- data/lib/rubocop/cop/style/collection_querying.rb +79 -46
- data/lib/rubocop/cop/style/combinable_loops.rb +89 -11
- data/lib/rubocop/cop/style/concat_array_literals.rb +58 -7
- data/lib/rubocop/cop/style/conditional_assignment.rb +4 -0
- data/lib/rubocop/cop/style/disable_cops_within_source_code_directive.rb +65 -9
- data/lib/rubocop/cop/style/double_negation.rb +11 -0
- data/lib/rubocop/cop/style/empty_literal.rb +1 -1
- data/lib/rubocop/cop/style/empty_method.rb +3 -1
- data/lib/rubocop/cop/style/empty_string_inside_interpolation.rb +53 -11
- data/lib/rubocop/cop/style/endless_method.rb +104 -11
- data/lib/rubocop/cop/style/exact_regexp_match.rb +45 -8
- data/lib/rubocop/cop/style/explicit_block_argument.rb +50 -10
- data/lib/rubocop/cop/style/float_division.rb +37 -2
- data/lib/rubocop/cop/style/for.rb +53 -2
- data/lib/rubocop/cop/style/format_string.rb +28 -6
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +2 -1
- data/lib/rubocop/cop/style/guard_clause.rb +31 -22
- data/lib/rubocop/cop/style/hash_as_last_array_item.rb +4 -0
- data/lib/rubocop/cop/style/hash_conversion.rb +28 -8
- data/lib/rubocop/cop/style/hash_lookup_method.rb +42 -9
- data/lib/rubocop/cop/style/hash_syntax.rb +16 -3
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +22 -3
- data/lib/rubocop/cop/style/if_inside_else.rb +21 -1
- data/lib/rubocop/cop/style/if_unless_modifier.rb +32 -26
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +4 -0
- data/lib/rubocop/cop/style/if_with_semicolon.rb +47 -4
- data/lib/rubocop/cop/style/inverse_methods.rb +24 -2
- data/lib/rubocop/cop/style/it_block_parameter.rb +94 -4
- data/lib/rubocop/cop/style/keyword_parameters_order.rb +3 -1
- data/lib/rubocop/cop/style/lambda.rb +34 -1
- data/lib/rubocop/cop/style/lambda_call.rb +7 -2
- data/lib/rubocop/cop/style/map_into_array.rb +1 -2
- data/lib/rubocop/cop/style/method_call_with_args_parentheses/omit_parentheses.rb +80 -13
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +26 -2
- data/lib/rubocop/cop/style/method_call_without_args_parentheses.rb +26 -3
- data/lib/rubocop/cop/style/method_def_parentheses.rb +38 -4
- data/lib/rubocop/cop/style/missing_else.rb +7 -1
- data/lib/rubocop/cop/style/mixin_grouping.rb +12 -3
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +45 -5
- data/lib/rubocop/cop/style/multiline_in_pattern_then.rb +7 -0
- data/lib/rubocop/cop/style/multiline_method_signature.rb +2 -5
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +7 -3
- data/lib/rubocop/cop/style/multiline_when_then.rb +7 -0
- data/lib/rubocop/cop/style/mutable_constant.rb +52 -7
- data/lib/rubocop/cop/style/negated_if_else_condition.rb +2 -1
- data/lib/rubocop/cop/style/negative_array_index.rb +9 -1
- data/lib/rubocop/cop/style/nested_modifier.rb +27 -8
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +26 -8
- data/lib/rubocop/cop/style/next.rb +24 -10
- data/lib/rubocop/cop/style/nil_comparison.rb +16 -4
- data/lib/rubocop/cop/style/non_nil_check.rb +1 -1
- data/lib/rubocop/cop/style/not.rb +10 -5
- data/lib/rubocop/cop/style/numeric_predicate.rb +28 -5
- data/lib/rubocop/cop/style/one_line_conditional.rb +13 -2
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +19 -1
- data/lib/rubocop/cop/style/percent_literal_delimiters.rb +14 -3
- data/lib/rubocop/cop/style/quoted_symbols.rb +5 -7
- data/lib/rubocop/cop/style/raise_args.rb +80 -11
- data/lib/rubocop/cop/style/reduce_to_hash.rb +47 -33
- data/lib/rubocop/cop/style/redundant_begin.rb +45 -5
- data/lib/rubocop/cop/style/redundant_condition.rb +30 -13
- data/lib/rubocop/cop/style/redundant_double_splat_hash_braces.rb +6 -0
- data/lib/rubocop/cop/style/redundant_exception.rb +27 -11
- data/lib/rubocop/cop/style/redundant_fetch_block.rb +9 -1
- data/lib/rubocop/cop/style/redundant_freeze.rb +11 -3
- data/lib/rubocop/cop/style/redundant_interpolation_unfreeze.rb +24 -1
- data/lib/rubocop/cop/style/redundant_parentheses.rb +12 -1
- data/lib/rubocop/cop/style/redundant_regexp_argument.rb +7 -0
- data/lib/rubocop/cop/style/redundant_regexp_constructor.rb +20 -0
- data/lib/rubocop/cop/style/redundant_regexp_escape.rb +4 -0
- data/lib/rubocop/cop/style/redundant_self.rb +9 -1
- data/lib/rubocop/cop/style/redundant_self_assignment_branch.rb +28 -0
- data/lib/rubocop/cop/style/redundant_sort.rb +38 -4
- data/lib/rubocop/cop/style/regexp_literal.rb +75 -26
- data/lib/rubocop/cop/style/require_order.rb +25 -8
- data/lib/rubocop/cop/style/rescue_modifier.rb +11 -9
- data/lib/rubocop/cop/style/return_nil.rb +17 -2
- data/lib/rubocop/cop/style/return_nil_in_predicate_method_definition.rb +11 -1
- data/lib/rubocop/cop/style/sample.rb +9 -8
- data/lib/rubocop/cop/style/self_assignment.rb +12 -15
- data/lib/rubocop/cop/style/semicolon.rb +16 -5
- data/lib/rubocop/cop/style/send_with_literal_method_name.rb +4 -0
- data/lib/rubocop/cop/style/single_line_methods.rb +42 -4
- data/lib/rubocop/cop/style/stabby_lambda_parentheses.rb +29 -5
- data/lib/rubocop/cop/style/string_concatenation.rb +21 -4
- data/lib/rubocop/cop/style/struct_inheritance.rb +6 -0
- data/lib/rubocop/cop/style/super_arguments.rb +3 -0
- data/lib/rubocop/cop/style/symbol_proc.rb +24 -6
- data/lib/rubocop/cop/style/ternary_parentheses.rb +23 -1
- data/lib/rubocop/cop/style/trailing_underscore_variable.rb +23 -19
- data/lib/rubocop/cop/style/while_until_modifier.rb +1 -1
- data/lib/rubocop/cop/style/yoda_condition.rb +24 -9
- data/lib/rubocop/cop/style/yoda_expression.rb +10 -1
- data/lib/rubocop/cop/team.rb +10 -5
- data/lib/rubocop/cop/variable_force/assignment.rb +1 -1
- data/lib/rubocop/cop/variable_force/variable.rb +14 -1
- data/lib/rubocop/formatter/base_formatter.rb +22 -0
- data/lib/rubocop/formatter/formatter_set.rb +4 -0
- data/lib/rubocop/formatter/json_formatter.rb +71 -1
- data/lib/rubocop/formatter/offense_count_formatter.rb +3 -1
- data/lib/rubocop/formatter/pacman_formatter.rb +3 -1
- data/lib/rubocop/formatter/sarif_formatter.rb +12 -7
- data/lib/rubocop/formatter/worst_offenders_formatter.rb +3 -1
- data/lib/rubocop/lsp/routes.rb +23 -23
- data/lib/rubocop/lsp/runtime.rb +47 -11
- data/lib/rubocop/lsp/stdin_runner.rb +11 -5
- data/lib/rubocop/lsp.rb +5 -2
- data/lib/rubocop/mcp/autocorrection_request.rb +52 -0
- data/lib/rubocop/mcp/explain_tool.rb +68 -0
- data/lib/rubocop/mcp/scope.rb +65 -0
- data/lib/rubocop/mcp/server.rb +184 -76
- data/lib/rubocop/mcp/source_files.rb +35 -0
- data/lib/rubocop/offense_limit.rb +30 -0
- data/lib/rubocop/options.rb +45 -10
- data/lib/rubocop/plugin/configuration_integrator.rb +36 -8
- data/lib/rubocop/remote_config.rb +4 -1
- data/lib/rubocop/result_cache.rb +20 -13
- data/lib/rubocop/runner.rb +110 -42
- data/lib/rubocop/server/cache.rb +6 -6
- data/lib/rubocop/target_ruby.rb +21 -9
- data/lib/rubocop/util.rb +69 -0
- data/lib/rubocop/version.rb +1 -1
- data/lib/rubocop.rb +5 -0
- metadata +16 -5
|
@@ -59,15 +59,22 @@ module RuboCop
|
|
|
59
59
|
|
|
60
60
|
plugin_config.make_excludes_absolute
|
|
61
61
|
|
|
62
|
-
|
|
63
|
-
# (e.g. `TargetRailsVersion: ~` of rubocop-rails); `merge_all_cop_settings` above
|
|
64
|
-
# carries such keys into each subsequent plugin's config, where `unset_nil: true`
|
|
65
|
-
# would delete them from the combined configuration.
|
|
66
|
-
ConfigLoader.merge_with_default(plugin_config, plugin_config_path, unset_nil: false)
|
|
62
|
+
merge_plugin_config_with_default(plugin_config, plugin_config_path)
|
|
67
63
|
end
|
|
68
64
|
end
|
|
69
65
|
end
|
|
70
66
|
|
|
67
|
+
# `unset_nil: false` keeps `AllCops` keys declared with a nil value by a plugin
|
|
68
|
+
# (e.g. `TargetRailsVersion: ~` of rubocop-rails); `merge_all_cop_settings` carries
|
|
69
|
+
# such keys into each subsequent plugin's config, where `unset_nil: true` would
|
|
70
|
+
# delete them from the combined configuration. Preview is left to be applied when
|
|
71
|
+
# a project configuration is resolved against the defaults.
|
|
72
|
+
def merge_plugin_config_with_default(plugin_config, plugin_config_path)
|
|
73
|
+
ConfigLoader.merge_with_default(
|
|
74
|
+
plugin_config, plugin_config_path, unset_nil: false, resolve_preview: false
|
|
75
|
+
)
|
|
76
|
+
end
|
|
77
|
+
|
|
71
78
|
def merge_plugin_config_into_all_cops!(rubocop_config, plugin_config)
|
|
72
79
|
rubocop_config['AllCops'].merge!(plugin_config['AllCops'])
|
|
73
80
|
end
|
|
@@ -98,18 +105,39 @@ module RuboCop
|
|
|
98
105
|
|
|
99
106
|
case rules.type
|
|
100
107
|
when :path
|
|
101
|
-
|
|
108
|
+
config = ConfigLoader.load_file(rules.value, check: false)
|
|
109
|
+
warn_about_obsolete_config(plugin, config)
|
|
110
|
+
[config, rules.value]
|
|
102
111
|
when :object
|
|
103
112
|
path = plugin.method(:rules).source_location[0]
|
|
104
113
|
[Config.create(rules.value, path, check: true), path]
|
|
105
114
|
when :error
|
|
106
|
-
plugin_name = plugin.about&.name || plugin.inspect
|
|
107
115
|
error_message = rules.value.respond_to?(:message) ? rules.value.message : rules.value
|
|
108
116
|
|
|
109
|
-
raise "Plugin `#{plugin_name}' failed to load with error: #{error_message}"
|
|
117
|
+
raise "Plugin `#{plugin_name(plugin)}' failed to load with error: #{error_message}"
|
|
110
118
|
end
|
|
111
119
|
end
|
|
112
120
|
|
|
121
|
+
# Plugin configuration is loaded without validation, so an obsolete key in a plugin's
|
|
122
|
+
# defaults used to surface only later, against the user's own file, once the two had
|
|
123
|
+
# been merged. Report it here, while the path is still the plugin's, and name the
|
|
124
|
+
# plugin: the file lives inside an installed gem, so the fix is to report it upstream.
|
|
125
|
+
# It is a warning whatever the rule's severity: a stale plugin is not the user's
|
|
126
|
+
# mistake and must not stop the run.
|
|
127
|
+
def warn_about_obsolete_config(plugin, config)
|
|
128
|
+
messages = ConfigObsoletion.new(config).messages
|
|
129
|
+
return if messages.empty?
|
|
130
|
+
|
|
131
|
+
warn Rainbow(<<~WARNING).yellow
|
|
132
|
+
Warning: obsolete configuration found in the `#{plugin_name(plugin)}` plugin:
|
|
133
|
+
#{messages.join("\n")}
|
|
134
|
+
WARNING
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def plugin_name(plugin)
|
|
138
|
+
plugin.about&.name || plugin.inspect
|
|
139
|
+
end
|
|
140
|
+
|
|
113
141
|
# This is how we ensure "first-in wins": plugins can override AllCops settings that are
|
|
114
142
|
# set by RuboCop's default configuration, but once a plugin sets an AllCop setting, they
|
|
115
143
|
# have exclusive first-in-wins rights to that setting.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
-
require 'net/http'
|
|
4
3
|
require 'time'
|
|
4
|
+
require 'uri'
|
|
5
5
|
|
|
6
6
|
module RuboCop
|
|
7
7
|
# Common methods and behaviors for dealing with remote config files.
|
|
@@ -45,6 +45,9 @@ module RuboCop
|
|
|
45
45
|
def request(uri = @uri, limit = 10, &block)
|
|
46
46
|
raise ArgumentError, 'HTTP redirect too deep' if limit.zero?
|
|
47
47
|
|
|
48
|
+
# Loaded here rather than at the top, since most runs never fetch a remote config.
|
|
49
|
+
require 'net/http'
|
|
50
|
+
|
|
48
51
|
http = Net::HTTP.new(uri.hostname, uri.port)
|
|
49
52
|
http.use_ssl = uri.instance_of?(URI::HTTPS)
|
|
50
53
|
|
data/lib/rubocop/result_cache.rb
CHANGED
|
@@ -59,9 +59,9 @@ module RuboCop
|
|
|
59
59
|
puts "Removing the #{remove_count} oldest files from #{rubocop_cache_dir}" if verbose
|
|
60
60
|
sorted = files.sort_by { |path| File.mtime(path) }
|
|
61
61
|
remove_files(sorted, remove_count)
|
|
62
|
-
rescue Errno::ENOENT
|
|
62
|
+
rescue Errno::ENOENT, Errno::EACCES, Errno::EPERM, Errno::EROFS
|
|
63
63
|
# This can happen if parallel RuboCop invocations try to remove the
|
|
64
|
-
# same files. No problem.
|
|
64
|
+
# same files, or if the cache can't be written to. No problem.
|
|
65
65
|
puts $ERROR_INFO if verbose
|
|
66
66
|
end
|
|
67
67
|
|
|
@@ -138,26 +138,17 @@ module RuboCop
|
|
|
138
138
|
|
|
139
139
|
begin
|
|
140
140
|
FileUtils.mkdir_p(dir)
|
|
141
|
-
rescue Errno::EACCES, Errno::EROFS => e
|
|
141
|
+
rescue Errno::EACCES, Errno::EPERM, Errno::EROFS => e
|
|
142
142
|
warn "Couldn't create cache directory. Continuing without cache.\n #{e.message}"
|
|
143
143
|
return
|
|
144
144
|
end
|
|
145
145
|
|
|
146
|
-
preliminary_path = "#{@path}_#{rand(1_000_000_000)}"
|
|
147
146
|
# RuboCop must be in control of where its cached data is stored. A
|
|
148
147
|
# symbolic link anywhere in the cache directory tree can be an
|
|
149
148
|
# indication that a symlink attack is being waged.
|
|
150
149
|
return if symlink_protection_triggered?(dir)
|
|
151
150
|
|
|
152
|
-
|
|
153
|
-
f.write(@cached_data.to_json(offenses))
|
|
154
|
-
end
|
|
155
|
-
# The preliminary path is used so that if there are multiple RuboCop
|
|
156
|
-
# processes trying to save data for the same inspected file
|
|
157
|
-
# simultaneously, the only problem we run in to is a competition who gets
|
|
158
|
-
# to write to the final file. The contents are the same, so no corruption
|
|
159
|
-
# of data should occur.
|
|
160
|
-
FileUtils.mv(preliminary_path, @path)
|
|
151
|
+
write_cache_file(offenses)
|
|
161
152
|
end
|
|
162
153
|
|
|
163
154
|
private
|
|
@@ -177,6 +168,22 @@ module RuboCop
|
|
|
177
168
|
false
|
|
178
169
|
end
|
|
179
170
|
|
|
171
|
+
def write_cache_file(offenses)
|
|
172
|
+
preliminary_path = "#{@path}_#{rand(1_000_000_000)}"
|
|
173
|
+
File.open(preliminary_path, 'w', encoding: Encoding::UTF_8) do |f|
|
|
174
|
+
f.write(@cached_data.to_json(offenses))
|
|
175
|
+
end
|
|
176
|
+
# The preliminary path is used so that if there are multiple RuboCop
|
|
177
|
+
# processes trying to save data for the same inspected file
|
|
178
|
+
# simultaneously, the only problem we run in to is a competition who gets
|
|
179
|
+
# to write to the final file. The contents are the same, so no corruption
|
|
180
|
+
# of data should occur.
|
|
181
|
+
FileUtils.mv(preliminary_path, @path)
|
|
182
|
+
rescue Errno::EACCES, Errno::EPERM, Errno::EROFS => e
|
|
183
|
+
FileUtils.rm_f(preliminary_path)
|
|
184
|
+
warn "Couldn't write cache file. Continuing without cache.\n #{e.message}"
|
|
185
|
+
end
|
|
186
|
+
|
|
180
187
|
def file_checksum(file, config_store)
|
|
181
188
|
stat = File.stat(file)
|
|
182
189
|
return unavailable_checksum unless stat.file?
|
data/lib/rubocop/runner.rb
CHANGED
|
@@ -53,7 +53,7 @@ module RuboCop
|
|
|
53
53
|
Lint/RedundantCopDisableDirective RedundantCopDisableDirective Lint
|
|
54
54
|
].freeze
|
|
55
55
|
|
|
56
|
-
attr_reader :errors, :warnings
|
|
56
|
+
attr_reader :errors, :warnings
|
|
57
57
|
attr_writer :aborting
|
|
58
58
|
|
|
59
59
|
def initialize(options, config_store)
|
|
@@ -63,10 +63,20 @@ module RuboCop
|
|
|
63
63
|
@warnings = []
|
|
64
64
|
@aborting = false
|
|
65
65
|
@inspected_files = []
|
|
66
|
-
@
|
|
66
|
+
@diff_sources = {}
|
|
67
67
|
@report_queue = {}
|
|
68
68
|
end
|
|
69
69
|
|
|
70
|
+
# The `--diff` patches, one per file, from the source on disk (or stdin) to
|
|
71
|
+
# the source once every correction, redundant directive removal included,
|
|
72
|
+
# has been applied.
|
|
73
|
+
def diffs
|
|
74
|
+
@diffs ||= @diff_sources.filter_map do |file, (original_source, corrected_source)|
|
|
75
|
+
diff = UnifiedDiff.new(PathUtil.smart_path(file), original_source, corrected_source).to_s
|
|
76
|
+
diff unless diff.empty?
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
|
|
70
80
|
def run(paths)
|
|
71
81
|
@inspection_team_by_config = {}.compare_by_identity
|
|
72
82
|
|
|
@@ -101,9 +111,7 @@ module RuboCop
|
|
|
101
111
|
def only_changed(target_files)
|
|
102
112
|
return target_files unless @options[:changed]
|
|
103
113
|
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
target_files.select { |file| changed.include?(file) }.freeze
|
|
114
|
+
ChangedFiles.new(@options[:changed]).filter(target_files).freeze
|
|
107
115
|
end
|
|
108
116
|
|
|
109
117
|
def find_target_files(paths)
|
|
@@ -158,11 +166,12 @@ module RuboCop
|
|
|
158
166
|
|
|
159
167
|
def inspect_files(files) # rubocop:disable Metrics/AbcSize
|
|
160
168
|
formatter_set.started(files)
|
|
169
|
+
formatters_started = true
|
|
161
170
|
file_iterator(files) do |file|
|
|
162
|
-
offenses = process_file(file)
|
|
171
|
+
offenses, problems = collect_problems { process_file(file) }
|
|
163
172
|
succeeded = offenses.none? { |o| considered_failure?(o) && offense_displayed?(o) }
|
|
164
173
|
|
|
165
|
-
[offenses, succeeded]
|
|
174
|
+
[offenses, succeeded, problems]
|
|
166
175
|
end
|
|
167
176
|
ensure
|
|
168
177
|
# OPTIMIZE: Calling `ResultCache.cleanup` takes time. This optimization
|
|
@@ -171,17 +180,18 @@ module RuboCop
|
|
|
171
180
|
if files.size > 1 && cached_run?
|
|
172
181
|
ResultCache.cleanup(@config_store, @options[:debug], @options[:cache_root])
|
|
173
182
|
end
|
|
174
|
-
formatter_set.finished(@inspected_files.freeze)
|
|
183
|
+
formatter_set.finished(@inspected_files.freeze) if formatters_started
|
|
175
184
|
formatter_set.close_output_files
|
|
185
|
+
warn_about_elided_offenses
|
|
176
186
|
end
|
|
177
187
|
|
|
178
188
|
def file_iterator(files, &block)
|
|
179
189
|
all_passed = true
|
|
180
190
|
|
|
181
191
|
on_start = ->(file, _index) { file_started(file) }
|
|
182
|
-
on_finish = lambda do |file, index, (offenses, passed)|
|
|
192
|
+
on_finish = lambda do |file, index, (offenses, passed, problems)|
|
|
183
193
|
all_passed &&= passed
|
|
184
|
-
finished_report(file, index, offenses)
|
|
194
|
+
finished_report(file, index, offenses, problems)
|
|
185
195
|
end
|
|
186
196
|
|
|
187
197
|
if run_in_parallel?(files)
|
|
@@ -195,8 +205,8 @@ module RuboCop
|
|
|
195
205
|
all_passed
|
|
196
206
|
end
|
|
197
207
|
|
|
198
|
-
def finished_report(file, index, offenses)
|
|
199
|
-
@report_queue[index] = [file, offenses]
|
|
208
|
+
def finished_report(file, index, offenses, problems)
|
|
209
|
+
@report_queue[index] = [file, offenses, problems]
|
|
200
210
|
@next_index_to_report ||= 0
|
|
201
211
|
while @report_queue.key?(@next_index_to_report)
|
|
202
212
|
process_report_queue_entry(@next_index_to_report)
|
|
@@ -205,7 +215,10 @@ module RuboCop
|
|
|
205
215
|
end
|
|
206
216
|
|
|
207
217
|
def process_report_queue_entry(index)
|
|
208
|
-
file, offenses = @report_queue.delete(index)
|
|
218
|
+
file, offenses, (errors, warnings) = @report_queue.delete(index)
|
|
219
|
+
@errors.concat(errors)
|
|
220
|
+
@warnings.concat(warnings)
|
|
221
|
+
file_problems(file, errors, warnings) unless errors.empty? && warnings.empty?
|
|
209
222
|
file_finished(file, offenses)
|
|
210
223
|
end
|
|
211
224
|
|
|
@@ -269,6 +282,21 @@ module RuboCop
|
|
|
269
282
|
paths.each { |path| puts PathUtil.relative_path(path) }
|
|
270
283
|
end
|
|
271
284
|
|
|
285
|
+
# Keeps the errors and warnings a file causes apart from the rest of the
|
|
286
|
+
# run's. A parallel worker inspects files in a copy of the runner that is
|
|
287
|
+
# thrown away, so they have to travel back with the file's offenses.
|
|
288
|
+
def collect_problems
|
|
289
|
+
errors = @errors
|
|
290
|
+
warnings = @warnings
|
|
291
|
+
@errors = []
|
|
292
|
+
@warnings = []
|
|
293
|
+
|
|
294
|
+
[yield, [@errors, @warnings]]
|
|
295
|
+
ensure
|
|
296
|
+
@errors = errors
|
|
297
|
+
@warnings = warnings
|
|
298
|
+
end
|
|
299
|
+
|
|
272
300
|
def process_file(file)
|
|
273
301
|
file_offenses(file)
|
|
274
302
|
rescue InfiniteCorrectionLoop => e
|
|
@@ -321,9 +349,11 @@ module RuboCop
|
|
|
321
349
|
if redundant_updated
|
|
322
350
|
# Do one extra inspection loop if any redundant disables were
|
|
323
351
|
# removed. This is done in order to find rubocop:enable directives that
|
|
324
|
-
# have now become useless.
|
|
325
|
-
|
|
326
|
-
|
|
352
|
+
# have now become useless. It finds the offenses that are left again,
|
|
353
|
+
# located in the corrected source, so only the corrected ones are kept
|
|
354
|
+
# from before, as `inspect_and_correct` does.
|
|
355
|
+
_source, new_offenses = do_inspection_loop(file, team.updated_source)
|
|
356
|
+
offenses = offenses.select(&:corrected?) | new_offenses
|
|
327
357
|
end
|
|
328
358
|
end
|
|
329
359
|
offenses
|
|
@@ -336,7 +366,9 @@ module RuboCop
|
|
|
336
366
|
team = Cop::Team.mobilize([Cop::Lint::RedundantCopDisableDirective], config, @options)
|
|
337
367
|
return if team.cops.empty?
|
|
338
368
|
|
|
339
|
-
team.
|
|
369
|
+
team.defer_corrections = @options[:diff]
|
|
370
|
+
team.cops.first.offenses_to_check = offenses.reject(&:corrected?)
|
|
371
|
+
|
|
340
372
|
yield team
|
|
341
373
|
end
|
|
342
374
|
|
|
@@ -369,6 +401,15 @@ module RuboCop
|
|
|
369
401
|
formatter_set.file_started(file, cli_options: @options, config_store: @config_store)
|
|
370
402
|
end
|
|
371
403
|
|
|
404
|
+
# Formatters get the messages without the colors added for the terminal.
|
|
405
|
+
def file_problems(file, errors, warnings)
|
|
406
|
+
formatter_set.file_problems(file, plain_messages(errors), plain_messages(warnings))
|
|
407
|
+
end
|
|
408
|
+
|
|
409
|
+
def plain_messages(problems)
|
|
410
|
+
problems.map { |problem| Rainbow::StringUtils.uncolor(problem.to_s) }
|
|
411
|
+
end
|
|
412
|
+
|
|
372
413
|
def file_finished(file, offenses)
|
|
373
414
|
@inspected_files << file
|
|
374
415
|
offenses = offenses_to_report(offenses)
|
|
@@ -387,15 +428,15 @@ module RuboCop
|
|
|
387
428
|
return unless cache
|
|
388
429
|
# Caching results when a cop has crashed would prevent the crash in the
|
|
389
430
|
# next run, since the cop would not be called then. We want crashes to
|
|
390
|
-
# show up the same in each run.
|
|
431
|
+
# show up the same in each run. Only this file's errors and warnings are
|
|
432
|
+
# visible here, see `collect_problems`.
|
|
391
433
|
return if errors.any? || warnings.any?
|
|
392
434
|
|
|
393
435
|
cache.save(offenses)
|
|
394
436
|
end
|
|
395
437
|
|
|
396
|
-
def do_inspection_loop(file)
|
|
397
|
-
|
|
398
|
-
processed_source = get_processed_source(file, @prism_result)
|
|
438
|
+
def do_inspection_loop(file, source = nil)
|
|
439
|
+
processed_source = initial_processed_source(file, source)
|
|
399
440
|
original_source = processed_source.raw_source
|
|
400
441
|
# This variable is 2d array used to track corrected offenses after each
|
|
401
442
|
# inspection iteration. This is used to output meaningful infinite loop
|
|
@@ -428,6 +469,15 @@ module RuboCop
|
|
|
428
469
|
finalize_corrections(file, original_source, corrected_source)
|
|
429
470
|
end
|
|
430
471
|
|
|
472
|
+
# `source` is the corrected source to continue from when the corrections
|
|
473
|
+
# are kept in memory rather than written to the file (`--diff`).
|
|
474
|
+
def initial_processed_source(file, source)
|
|
475
|
+
return get_processed_source(file, nil, source: source) if source
|
|
476
|
+
|
|
477
|
+
# We can reuse the prism result since the source did not change yet.
|
|
478
|
+
get_processed_source(file, @prism_result)
|
|
479
|
+
end
|
|
480
|
+
|
|
431
481
|
def inspect_and_correct(processed_source, offenses_by_iteration)
|
|
432
482
|
# The offenses that couldn't be corrected will be found again so we
|
|
433
483
|
# only keep the corrected ones in order to avoid duplicate reporting.
|
|
@@ -445,24 +495,26 @@ module RuboCop
|
|
|
445
495
|
if @options[:diff]
|
|
446
496
|
record_diff(file, original_source, corrected_source)
|
|
447
497
|
elsif corrected_source
|
|
448
|
-
|
|
498
|
+
Util.replace_file_contents(file, corrected_source)
|
|
449
499
|
end
|
|
450
500
|
end
|
|
451
501
|
|
|
452
502
|
# `--diff` reports what autocorrection would do instead of doing it. With
|
|
453
503
|
# `--stdin` the corrected source is handed back through the options rather
|
|
454
|
-
# than deferred, so that is where the new source comes from.
|
|
504
|
+
# than deferred, so that is where the new source comes from. A file can go
|
|
505
|
+
# through more than one inspection loop, so the diff runs from the source
|
|
506
|
+
# the first loop started with to the one the last loop ended with.
|
|
455
507
|
def record_diff(file, original_source, corrected_source)
|
|
456
|
-
|
|
457
|
-
return unless original_source && new_source
|
|
508
|
+
return unless original_source
|
|
458
509
|
|
|
510
|
+
new_source = @options[:stdin] || corrected_source
|
|
459
511
|
# The original source is the file as it is on disk, so the corrected one
|
|
460
512
|
# has to go through the same line ending conversion `File.write` would
|
|
461
513
|
# apply, or on Windows every single line reads as changed.
|
|
462
|
-
new_source = emulate_write_read_cycle(new_source)
|
|
514
|
+
new_source = new_source ? Util.emulate_write_read_cycle(new_source) : original_source
|
|
463
515
|
|
|
464
|
-
|
|
465
|
-
|
|
516
|
+
sources = (@diff_sources[file] ||= [original_source])
|
|
517
|
+
sources[1] = new_source
|
|
466
518
|
end
|
|
467
519
|
|
|
468
520
|
def inspect_iteration(processed_source)
|
|
@@ -472,17 +524,6 @@ module RuboCop
|
|
|
472
524
|
[team, offenses, updated_source_file]
|
|
473
525
|
end
|
|
474
526
|
|
|
475
|
-
# When corrections were written to disk and read back between iterations,
|
|
476
|
-
# the text-mode write converted LF to CRLF on Windows, and cops like
|
|
477
|
-
# `Layout/EndOfLine` rely on seeing the source as it would be on disk.
|
|
478
|
-
# Apply the same conversion to the in-memory source. The final `File.write`
|
|
479
|
-
# still performs it for the file itself.
|
|
480
|
-
def emulate_write_read_cycle(source)
|
|
481
|
-
return source unless Platform.windows?
|
|
482
|
-
|
|
483
|
-
source.encode(source.encoding, crlf_newline: true)
|
|
484
|
-
end
|
|
485
|
-
|
|
486
527
|
# Custom ruby extractors may derive their fragments from the file on
|
|
487
528
|
# disk rather than from the passed processed source, so corrections can
|
|
488
529
|
# only be kept in memory when the default extractor is used.
|
|
@@ -573,6 +614,12 @@ module RuboCop
|
|
|
573
614
|
end
|
|
574
615
|
end
|
|
575
616
|
|
|
617
|
+
# Lets a runner that is reused from run to run pick up a different `--only`
|
|
618
|
+
# or `--except`.
|
|
619
|
+
def forget_mobilized_cops
|
|
620
|
+
@mobilized_cop_classes = nil
|
|
621
|
+
end
|
|
622
|
+
|
|
576
623
|
def mobilize_cop_badge?(badge, config)
|
|
577
624
|
return false if badge.department == :Test
|
|
578
625
|
return false if badge.match_name?(@options[:except])
|
|
@@ -589,7 +636,7 @@ module RuboCop
|
|
|
589
636
|
%i[only except].each do |option|
|
|
590
637
|
next unless @options[option]
|
|
591
638
|
|
|
592
|
-
@options[option].map
|
|
639
|
+
@options[option] = @options[option].map do |cop_name|
|
|
593
640
|
Cop::Registry.qualified_cop_name(cop_name, "--#{option} option")
|
|
594
641
|
end
|
|
595
642
|
end
|
|
@@ -630,7 +677,28 @@ module RuboCop
|
|
|
630
677
|
end
|
|
631
678
|
|
|
632
679
|
def offenses_to_report(offenses)
|
|
633
|
-
offenses.select { |o| offense_displayed?(o) }
|
|
680
|
+
offenses = offenses.select { |o| offense_displayed?(o) }
|
|
681
|
+
offenses = offense_limit.filter(offenses) if offense_limit
|
|
682
|
+
offenses
|
|
683
|
+
end
|
|
684
|
+
|
|
685
|
+
# Files are reported in order, so which offenses survive the limit does not
|
|
686
|
+
# depend on how the run was parallelized.
|
|
687
|
+
def offense_limit
|
|
688
|
+
return @offense_limit if defined?(@offense_limit)
|
|
689
|
+
|
|
690
|
+
max = @options[:max_offenses_per_cop]
|
|
691
|
+
@offense_limit = (OffenseLimit.new(max.to_i) if max)
|
|
692
|
+
end
|
|
693
|
+
|
|
694
|
+
def warn_about_elided_offenses
|
|
695
|
+
elided = offense_limit&.elided_per_cop
|
|
696
|
+
return unless elided&.any?
|
|
697
|
+
|
|
698
|
+
warn Rainbow(
|
|
699
|
+
"#{elided.values.sum} more offenses were not reported because of " \
|
|
700
|
+
"--max-offenses-per-cop=#{offense_limit.max} (#{elided.keys.join(', ')})."
|
|
701
|
+
).yellow
|
|
634
702
|
end
|
|
635
703
|
|
|
636
704
|
def supports_safe_autocorrect?(offense)
|
|
@@ -662,7 +730,7 @@ module RuboCop
|
|
|
662
730
|
|
|
663
731
|
processed_source = if source
|
|
664
732
|
ProcessedSource.new(
|
|
665
|
-
emulate_write_read_cycle(source),
|
|
733
|
+
Util.emulate_write_read_cycle(source),
|
|
666
734
|
ruby_version,
|
|
667
735
|
file,
|
|
668
736
|
parser_engine: parser_engine
|
data/lib/rubocop/server/cache.rb
CHANGED
|
@@ -51,9 +51,9 @@ module RuboCop
|
|
|
51
51
|
Pathname(project_dir).join(lockfile_name)
|
|
52
52
|
end.find(&:exist?)
|
|
53
53
|
version_data = lockfile_path&.read || RuboCop::Version::STRING
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
54
|
+
config_path = Pathname(args_config_file_path || ConfigFinder.find_config_path(Dir.pwd))
|
|
55
|
+
# NOTE: A missing config file is reported by `RuboCop::CLI` with a friendly message.
|
|
56
|
+
config_data = config_path.exist? ? config_path.read : ''
|
|
57
57
|
yaml = load_erb_templated_yaml(config_data)
|
|
58
58
|
|
|
59
59
|
inherit_from_data = inherit_from_data(yaml)
|
|
@@ -112,8 +112,8 @@ module RuboCop
|
|
|
112
112
|
|
|
113
113
|
def pid_running?
|
|
114
114
|
Process.kill(0, pid_path(create_dir: false).read.to_i) == 1
|
|
115
|
-
rescue Errno::ESRCH, Errno::ENOENT, Errno::EACCES, Errno::
|
|
116
|
-
Errno::ENOTDIR
|
|
115
|
+
rescue Errno::ESRCH, Errno::ENOENT, Errno::EACCES, Errno::EPERM, Errno::EROFS,
|
|
116
|
+
Errno::ENAMETOOLONG, Errno::ENOTDIR
|
|
117
117
|
false
|
|
118
118
|
end
|
|
119
119
|
|
|
@@ -181,7 +181,7 @@ module RuboCop
|
|
|
181
181
|
yaml_code = ERB.new(content).result
|
|
182
182
|
|
|
183
183
|
require 'yaml'
|
|
184
|
-
YAML.safe_load(yaml_code, permitted_classes: [Regexp, Symbol], aliases: true)
|
|
184
|
+
YAML.safe_load(yaml_code, permitted_classes: [Regexp, Symbol], aliases: true) || {}
|
|
185
185
|
end
|
|
186
186
|
end
|
|
187
187
|
end
|
data/lib/rubocop/target_ruby.rb
CHANGED
|
@@ -100,15 +100,23 @@ module RuboCop
|
|
|
100
100
|
def gemspec_filepath
|
|
101
101
|
return @gemspec_filepath if defined?(@gemspec_filepath)
|
|
102
102
|
|
|
103
|
-
@gemspec_filepath =
|
|
104
|
-
@config.
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
103
|
+
@gemspec_filepath = @config.traverse_directories_upwards(
|
|
104
|
+
@config.base_dir_for_path_parameters, ConfigFinder.project_root
|
|
105
|
+
) do |dir|
|
|
106
|
+
candidates = gemspec_candidates(dir)
|
|
107
|
+
|
|
108
|
+
# Bundler will use a gemspec whatever the filename is, as long as its the only one in
|
|
109
|
+
# the folder.
|
|
110
|
+
break candidates.first if candidates.one?
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
def gemspec_candidates(dir)
|
|
115
|
+
# NOTE: Can't use `dir.glob` because of JRuby 9.4.8.0 incompatibility:
|
|
116
|
+
# https://github.com/jruby/jruby/issues/8358
|
|
117
|
+
Pathname.glob("#{dir}/*.gemspec")
|
|
118
|
+
rescue SystemCallError
|
|
119
|
+
[]
|
|
112
120
|
end
|
|
113
121
|
|
|
114
122
|
def version_from_gemspec_file(file)
|
|
@@ -179,6 +187,8 @@ module RuboCop
|
|
|
179
187
|
return unless file && File.file?(file)
|
|
180
188
|
|
|
181
189
|
File.read(file).match(pattern) { |md| md[:version].to_f }
|
|
190
|
+
rescue SystemCallError
|
|
191
|
+
nil
|
|
182
192
|
end
|
|
183
193
|
|
|
184
194
|
def version_file
|
|
@@ -250,6 +260,8 @@ module RuboCop
|
|
|
250
260
|
result = line.match(/^\s*ruby\s+(\d+\.\d+)[p.\d]*\s*$/)
|
|
251
261
|
return result.captures.first.to_f if result
|
|
252
262
|
end
|
|
263
|
+
rescue SystemCallError
|
|
264
|
+
nil
|
|
253
265
|
end
|
|
254
266
|
|
|
255
267
|
def bundler_lock_file_path
|
data/lib/rubocop/util.rb
CHANGED
|
@@ -12,5 +12,74 @@ module RuboCop
|
|
|
12
12
|
ensure
|
|
13
13
|
$VERBOSE = old_verbose
|
|
14
14
|
end
|
|
15
|
+
|
|
16
|
+
# The source as it reads back after `File.write`, which in text mode turns
|
|
17
|
+
# each LF into CRLF on Windows. Cops like `Layout/EndOfLine` have to see
|
|
18
|
+
# corrected code that way between correction passes, and so does anything
|
|
19
|
+
# that compares it with what a write leaves on disk. The final `File.write`
|
|
20
|
+
# still performs the conversion for the file itself.
|
|
21
|
+
def self.emulate_write_read_cycle(source)
|
|
22
|
+
return source unless Platform.windows?
|
|
23
|
+
|
|
24
|
+
source.encode(source.encoding, crlf_newline: true)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Replaces the contents of `path` without a window in which the file is truncated
|
|
28
|
+
# but not yet rewritten, so that an interrupted run or a full disk cannot lose
|
|
29
|
+
# the original: the new contents are written next to the file and renamed over it,
|
|
30
|
+
# like `ResultCache#save`. `File.write` is used for the temporary file so text-mode
|
|
31
|
+
# semantics (the CRLF conversion on Windows) stay identical to a direct write.
|
|
32
|
+
# Where the temporary file cannot be created or renamed for lack of permission,
|
|
33
|
+
# a direct write is used as before.
|
|
34
|
+
def self.replace_file_contents(path, contents)
|
|
35
|
+
# Write through symlinks so that the rename replaces the real file and
|
|
36
|
+
# the symlink survives.
|
|
37
|
+
path = File.realpath(path)
|
|
38
|
+
|
|
39
|
+
# Opening a read-only file fails without truncating it, keeping the behavior
|
|
40
|
+
# a direct write always had.
|
|
41
|
+
return File.write(path, contents) unless File.writable?(path)
|
|
42
|
+
|
|
43
|
+
temp_path = "#{path}.#{Process.pid}.#{rand(1_000_000_000)}.rubocop.tmp"
|
|
44
|
+
begin
|
|
45
|
+
replace_via_temporary_file(path, temp_path, contents)
|
|
46
|
+
ensure
|
|
47
|
+
FileUtils.rm_f(temp_path)
|
|
48
|
+
end
|
|
49
|
+
rescue Errno::ENOENT
|
|
50
|
+
# The file vanished mid-run or is a dangling symlink: recreate it the way
|
|
51
|
+
# a direct write always has.
|
|
52
|
+
File.write(path, contents)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def self.replace_via_temporary_file(path, temp_path, contents)
|
|
56
|
+
# The directory refuses new files even though the file itself is writable,
|
|
57
|
+
# so a direct write is the only option left.
|
|
58
|
+
return File.write(path, contents) unless write_temporary_file(temp_path, contents)
|
|
59
|
+
|
|
60
|
+
begin
|
|
61
|
+
# A fresh file would get umask-default permissions on rename;
|
|
62
|
+
# keep the original's.
|
|
63
|
+
File.chmod(File.stat(path).mode, temp_path)
|
|
64
|
+
File.rename(temp_path, path)
|
|
65
|
+
rescue SystemCallError
|
|
66
|
+
# The replacement was refused, e.g. the file is open in another process on Windows.
|
|
67
|
+
# The contents already fit on disk, so a direct write is safe once the temporary copy
|
|
68
|
+
# no longer takes up that space.
|
|
69
|
+
FileUtils.rm_f(temp_path)
|
|
70
|
+
File.write(path, contents)
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
private_class_method :replace_via_temporary_file
|
|
74
|
+
|
|
75
|
+
# Space errors such as `ENOSPC` must propagate: a direct write would then
|
|
76
|
+
# truncate the file and fail, which is exactly the loss being avoided.
|
|
77
|
+
def self.write_temporary_file(temp_path, contents)
|
|
78
|
+
File.write(temp_path, contents)
|
|
79
|
+
true
|
|
80
|
+
rescue Errno::EACCES, Errno::EPERM
|
|
81
|
+
false
|
|
82
|
+
end
|
|
83
|
+
private_class_method :write_temporary_file
|
|
15
84
|
end
|
|
16
85
|
end
|
data/lib/rubocop/version.rb
CHANGED
data/lib/rubocop.rb
CHANGED
|
@@ -55,6 +55,8 @@ require_relative 'rubocop/cop/base'
|
|
|
55
55
|
require_relative 'rubocop/cop/cop'
|
|
56
56
|
require_relative 'rubocop/cop/commissioner'
|
|
57
57
|
require_relative 'rubocop/cop/documentation'
|
|
58
|
+
require_relative 'rubocop/cop/cop_documentation'
|
|
59
|
+
require_relative 'rubocop/cop/cop_explanation'
|
|
58
60
|
require_relative 'rubocop/cop/corrector'
|
|
59
61
|
require_relative 'rubocop/cop/correctors'
|
|
60
62
|
require_relative 'rubocop/cop/force'
|
|
@@ -117,12 +119,15 @@ require_relative 'rubocop/comment_config/disable_next'
|
|
|
117
119
|
require_relative 'rubocop/comment_config/push_pop'
|
|
118
120
|
require_relative 'rubocop/comment_config'
|
|
119
121
|
require_relative 'rubocop/result_cache'
|
|
122
|
+
require_relative 'rubocop/offense_limit'
|
|
120
123
|
require_relative 'rubocop/runner'
|
|
121
124
|
require_relative 'rubocop/cli'
|
|
122
125
|
require_relative 'rubocop/cli/command'
|
|
123
126
|
require_relative 'rubocop/cli/environment'
|
|
124
127
|
require_relative 'rubocop/cli/command/base'
|
|
125
128
|
require_relative 'rubocop/cli/command/auto_generate_config'
|
|
129
|
+
require_relative 'rubocop/cli/command/cop_names'
|
|
130
|
+
require_relative 'rubocop/cli/command/explain'
|
|
126
131
|
require_relative 'rubocop/cli/command/execute_runner'
|
|
127
132
|
require_relative 'rubocop/cli/command/init_dotfile'
|
|
128
133
|
require_relative 'rubocop/cli/command/lsp'
|