sonicop 26.8.111 → 26.8.113
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/CONFORMANCE.md +64 -31
- data/Cargo.lock +94 -19
- data/Cargo.toml +13 -8
- data/NOTICE +15 -3
- data/README.ja.md +145 -57
- data/README.md +140 -48
- data/build.rs +155 -0
- data/lib/sonicop/runner.rb +7 -1
- data/lib/sonicop/version.rb +1 -1
- data/src/cli.rs +216 -45
- data/src/config/inheritance.rs +637 -64
- data/src/config/mod.rs +130 -1
- data/src/config/paths.rs +550 -48
- data/src/cop_name.rs +18 -1
- data/src/diagnostic.rs +168 -3
- data/src/directives.rs +398 -77
- data/src/display_width.rs +82 -42
- data/src/display_width_table.rs +506 -0
- data/src/engine.rs +1532 -51
- data/src/formatter.rs +10 -1
- data/src/lib.rs +2 -0
- data/src/main.rs +112 -1
- data/src/nul_bytes.rs +245 -47
- data/src/parser.rs +37 -0
- data/src/profile.rs +174 -24
- data/src/ruby_version.rs +167 -21
- data/src/rules/bundler/duplicated_group.rs +3 -2
- data/src/rules/bundler/gem_comment.rs +5 -4
- data/src/rules/gemspec/attribute_assignment.rs +4 -3
- data/src/rules/gemspec/deprecated_attribute_assignment.rs +7 -6
- data/src/rules/gemspec/duplicated_assignment.rs +4 -3
- data/src/rules/gemspec/require_mfa.rs +10 -9
- data/src/rules/gemspec/required_ruby_version.rs +3 -2
- data/src/rules/gemspec/support.rs +28 -8
- data/src/rules/layout/access_modifier_indentation.rs +13 -6
- data/src/rules/layout/argument_alignment.rs +19 -7
- data/src/rules/layout/array_alignment.rs +1 -2
- data/src/rules/layout/block_alignment.rs +74 -30
- data/src/rules/layout/block_end_newline.rs +5 -5
- data/src/rules/layout/class_structure.rs +13 -4
- data/src/rules/layout/closing_heredoc_indentation.rs +24 -5
- data/src/rules/layout/closing_parenthesis_indentation.rs +3 -3
- data/src/rules/layout/comment_indentation.rs +2 -1
- data/src/rules/layout/condition_position.rs +3 -2
- data/src/rules/layout/def_end_alignment.rs +10 -6
- data/src/rules/layout/dot_position.rs +1 -2
- data/src/rules/layout/else_alignment.rs +24 -14
- data/src/rules/layout/empty_line_after_guard_clause.rs +8 -7
- data/src/rules/layout/empty_line_after_multiline_condition.rs +5 -5
- data/src/rules/layout/empty_line_between_defs.rs +8 -6
- data/src/rules/layout/empty_lines_around_access_modifier.rs +22 -10
- data/src/rules/layout/empty_lines_around_arguments.rs +4 -4
- data/src/rules/layout/empty_lines_around_body.rs +8 -2
- data/src/rules/layout/empty_lines_around_exception_handling_keywords.rs +1 -2
- data/src/rules/layout/empty_lines_around_method_body.rs +3 -2
- data/src/rules/layout/end_alignment.rs +32 -7
- data/src/rules/layout/end_of_line.rs +14 -1
- data/src/rules/layout/extra_spacing.rs +9 -11
- data/src/rules/layout/first_array_element_indentation.rs +1 -2
- data/src/rules/layout/first_hash_element_indentation.rs +8 -6
- data/src/rules/layout/hash_alignment.rs +113 -19
- data/src/rules/layout/heredoc_argument_closing_parenthesis.rs +6 -5
- data/src/rules/layout/heredoc_indentation.rs +4 -3
- data/src/rules/layout/indentation_style.rs +6 -4
- data/src/rules/layout/indentation_width.rs +76 -16
- data/src/rules/layout/initial_indentation.rs +18 -5
- data/src/rules/layout/line_continuation_leading_space.rs +4 -3
- data/src/rules/layout/line_continuation_spacing.rs +4 -3
- data/src/rules/layout/line_length.rs +417 -43
- data/src/rules/layout/mod.rs +1 -1
- data/src/rules/layout/multiline_assignment_layout.rs +20 -3
- data/src/rules/layout/multiline_block_layout.rs +1 -2
- data/src/rules/layout/multiline_expression.rs +60 -31
- data/src/rules/layout/multiline_hash_brace_layout.rs +4 -3
- data/src/rules/layout/multiline_method_call_indentation.rs +1 -1
- data/src/rules/layout/multiline_operation_indentation.rs +3 -3
- data/src/rules/layout/redundant_line_break.rs +16 -5
- data/src/rules/layout/rescue_ensure_alignment.rs +20 -12
- data/src/rules/layout/single_line_block_chain.rs +13 -4
- data/src/rules/layout/space_around_block_parameters.rs +16 -2
- data/src/rules/layout/space_around_equals_in_parameter_default.rs +1 -2
- data/src/rules/layout/space_around_keyword.rs +22 -4
- data/src/rules/layout/space_around_operators.rs +34 -2
- data/src/rules/layout/space_before_brackets.rs +13 -6
- data/src/rules/layout/space_before_comma.rs +3 -2
- data/src/rules/layout/space_inside_array_literal_brackets.rs +25 -10
- data/src/rules/layout/space_inside_array_percent_literal.rs +7 -1
- data/src/rules/layout/space_inside_parens.rs +170 -87
- data/src/rules/layout/space_inside_range_literal.rs +53 -19
- data/src/rules/layout/space_inside_reference_brackets.rs +65 -3
- data/src/rules/layout/space_inside_string_interpolation.rs +147 -48
- data/src/rules/layout/support.rs +172 -26
- data/src/rules/layout/tokens.rs +47 -7
- data/src/rules/layout/trailing_whitespace.rs +5 -2
- data/src/rules/lint/access_modifier.rs +50 -5
- data/src/rules/lint/ambiguity.rs +97 -7
- data/src/rules/lint/ambiguous_block_association.rs +29 -12
- data/src/rules/lint/ambiguous_operator.rs +8 -0
- data/src/rules/lint/ambiguous_operator_precedence.rs +18 -1
- data/src/rules/lint/ambiguous_range.rs +15 -1
- data/src/rules/lint/ambiguous_regexp_literal.rs +40 -1
- data/src/rules/lint/array_literal_in_regexp.rs +13 -5
- data/src/rules/lint/assignment_in_condition.rs +3 -2
- data/src/rules/lint/blocks.rs +2 -1
- data/src/rules/lint/boolean_symbol.rs +4 -4
- data/src/rules/lint/constant_definition_in_block.rs +1 -2
- data/src/rules/lint/constant_reassignment.rs +9 -13
- data/src/rules/lint/constant_resolution.rs +8 -0
- data/src/rules/lint/cop_directives.rs +28 -4
- data/src/rules/lint/debugger.rs +59 -9
- data/src/rules/lint/deprecated_open_ssl_constant.rs +1 -2
- data/src/rules/lint/disjunctive_assignment_in_constructor.rs +2 -2
- data/src/rules/lint/duplicate_branch.rs +16 -6
- data/src/rules/lint/duplicate_case_condition.rs +5 -5
- data/src/rules/lint/duplicate_hash_key.rs +8 -4
- data/src/rules/lint/duplicate_match_pattern.rs +7 -5
- data/src/rules/lint/duplicate_methods.rs +133 -54
- data/src/rules/lint/duplicate_require.rs +1 -2
- data/src/rules/lint/duplicate_set_element.rs +19 -5
- data/src/rules/lint/empty_conditional_body.rs +19 -3
- data/src/rules/lint/empty_expression.rs +16 -1
- data/src/rules/lint/empty_in_pattern.rs +3 -2
- data/src/rules/lint/empty_when.rs +4 -4
- data/src/rules/lint/ensure_return.rs +3 -2
- data/src/rules/lint/float_comparison.rs +8 -6
- data/src/rules/lint/flow.rs +8 -5
- data/src/rules/lint/format_parameter_mismatch.rs +4 -3
- data/src/rules/lint/hash_compare_by_identity.rs +5 -4
- data/src/rules/lint/heredoc_method_call_position.rs +3 -2
- data/src/rules/lint/implicit_string_concatenation.rs +33 -3
- data/src/rules/lint/ineffective_access_modifier.rs +5 -4
- data/src/rules/lint/inherit_exception.rs +4 -3
- data/src/rules/lint/literal_as_condition.rs +51 -31
- data/src/rules/lint/literal_assignment_in_condition.rs +4 -3
- data/src/rules/lint/literal_in_interpolation.rs +66 -5
- data/src/rules/lint/literals.rs +10 -8
- data/src/rules/lint/missing_cop_enable_directive.rs +95 -18
- data/src/rules/lint/mod.rs +1 -0
- data/src/rules/lint/nested_method_definition.rs +36 -11
- data/src/rules/lint/nil_receiver.rs +420 -0
- data/src/rules/lint/no_return_in_begin_end_blocks.rs +2 -2
- data/src/rules/lint/node_equality.rs +8 -5
- data/src/rules/lint/non_atomic_file_operation.rs +9 -3
- data/src/rules/lint/non_deterministic_require_order.rs +5 -3
- data/src/rules/lint/non_local_exit_from_iterator.rs +22 -9
- data/src/rules/lint/number_conversion.rs +1 -1
- data/src/rules/lint/out_of_range_regexp_ref.rs +6 -4
- data/src/rules/lint/parentheses_as_grouped_expression.rs +5 -0
- data/src/rules/lint/redundant_cop_enable_directive.rs +3 -1
- data/src/rules/lint/redundant_dir_glob_sort.rs +1 -2
- data/src/rules/lint/redundant_safe_navigation.rs +47 -22
- data/src/rules/lint/redundant_splat_expansion.rs +4 -2
- data/src/rules/lint/redundant_string_coercion.rs +3 -2
- data/src/rules/lint/redundant_type_conversion.rs +16 -4
- data/src/rules/lint/regexp.rs +1 -3
- data/src/rules/lint/regexp_source.rs +2 -2
- data/src/rules/lint/regexp_tree.rs +38 -5
- data/src/rules/lint/require_range_parentheses.rs +5 -5
- data/src/rules/lint/rescue_exception.rs +4 -3
- data/src/rules/lint/rescue_type.rs +3 -2
- data/src/rules/lint/return_in_void_context.rs +8 -3
- data/src/rules/lint/safe_navigation_chain.rs +47 -11
- data/src/rules/lint/self_assignment.rs +20 -2
- data/src/rules/lint/shadowed_argument.rs +2 -2
- data/src/rules/lint/shadowed_exception.rs +2 -2
- data/src/rules/lint/suppressed_exception.rs +21 -3
- data/src/rules/lint/suppressed_exception_in_number_conversion.rs +4 -3
- data/src/rules/lint/symbol_conversion.rs +56 -8
- data/src/rules/lint/syntax.rs +195 -5
- data/src/rules/lint/top_level_return_with_argument.rs +11 -1
- data/src/rules/lint/triple_quotes.rs +3 -2
- data/src/rules/lint/unescaped_bracket_in_regexp.rs +1 -1
- data/src/rules/lint/unexpected_block_arity.rs +23 -5
- data/src/rules/lint/unified_integer.rs +18 -12
- data/src/rules/lint/unmodified_reduce_accumulator.rs +36 -7
- data/src/rules/lint/unreachable_code.rs +4 -4
- data/src/rules/lint/unreachable_loop.rs +26 -10
- data/src/rules/lint/unreachable_pattern_branch.rs +7 -5
- data/src/rules/lint/unused_block_argument.rs +2 -2
- data/src/rules/lint/unused_method_argument.rs +1 -2
- data/src/rules/lint/useless_assignment.rs +3 -2
- data/src/rules/lint/useless_constant_scoping.rs +3 -2
- data/src/rules/lint/useless_else_without_rescue.rs +4 -3
- data/src/rules/lint/useless_rescue.rs +5 -4
- data/src/rules/lint/useless_ruby2_keywords.rs +2 -1
- data/src/rules/lint/useless_setter_call.rs +5 -4
- data/src/rules/lint/useless_times.rs +4 -3
- data/src/rules/lint/variable_force.rs +307 -79
- data/src/rules/lint/void.rs +32 -20
- data/src/rules/metrics/abc_size.rs +197 -7
- data/src/rules/metrics/block_length.rs +19 -10
- data/src/rules/metrics/block_nesting.rs +1 -2
- data/src/rules/metrics/class_length.rs +16 -4
- data/src/rules/metrics/collection_literal_length.rs +2 -1
- data/src/rules/metrics/complexity.rs +52 -14
- data/src/rules/metrics/fragments.rs +10 -14
- data/src/rules/metrics/locals.rs +35 -14
- data/src/rules/metrics/method_length.rs +14 -6
- data/src/rules/metrics/parameter_lists.rs +7 -6
- data/src/rules/metrics/perceived_complexity.rs +1 -3
- data/src/rules/metrics/support.rs +94 -4
- data/src/rules/mod.rs +525 -80
- data/src/rules/naming/ascii_identifiers.rs +3 -3
- data/src/rules/naming/binary_operator_parameter_name.rs +5 -4
- data/src/rules/naming/block_forwarding.rs +9 -8
- data/src/rules/naming/constant_name.rs +56 -18
- data/src/rules/naming/file_name.rs +174 -43
- data/src/rules/naming/method_name.rs +8 -1
- data/src/rules/naming/predicate_method.rs +42 -14
- data/src/rules/naming/predicate_prefix.rs +70 -4
- data/src/rules/naming/rescued_exceptions_variable_name.rs +14 -11
- data/src/rules/naming/support.rs +54 -295
- data/src/rules/naming/variable_name.rs +17 -4
- data/src/rules/naming/variable_number.rs +9 -10
- data/src/rules/node_ext.rs +1 -1
- data/src/rules/ruby_literal.rs +8 -3
- data/src/rules/security/compound_hash.rs +3 -2
- data/src/rules/security/open.rs +5 -4
- data/src/rules/send_node.rs +134 -0
- data/src/rules/single_line.rs +4 -4
- data/src/rules/style/access_modifier_declarations.rs +76 -22
- data/src/rules/style/accessor_grouping.rs +44 -6
- data/src/rules/style/alias.rs +4 -3
- data/src/rules/style/and_or.rs +2 -2
- data/src/rules/style/arguments_forwarding.rs +5 -5
- data/src/rules/style/array_coercion.rs +5 -5
- data/src/rules/style/array_first_last.rs +19 -4
- data/src/rules/style/array_intersect.rs +26 -8
- data/src/rules/style/array_intersect_with_single_element.rs +1 -1
- data/src/rules/style/ascii_comments.rs +2 -0
- data/src/rules/style/attr.rs +1 -1
- data/src/rules/style/auto_resource_cleanup.rs +1 -1
- data/src/rules/style/bitwise_predicate.rs +2 -2
- data/src/rules/style/block_args.rs +1 -1
- data/src/rules/style/block_comments.rs +11 -2
- data/src/rules/style/block_delimiters.rs +154 -10
- data/src/rules/style/case_equality.rs +7 -2
- data/src/rules/style/case_like_if.rs +26 -2
- data/src/rules/style/class_and_module_children.rs +162 -10
- data/src/rules/style/class_methods.rs +1 -1
- data/src/rules/style/class_methods_definitions.rs +9 -0
- data/src/rules/style/collection_compact.rs +4 -4
- data/src/rules/style/combinable_defined.rs +2 -2
- data/src/rules/style/combinable_loops.rs +9 -11
- data/src/rules/style/comments.rs +6 -3
- data/src/rules/style/comparable_clamp.rs +2 -2
- data/src/rules/style/concat_array_literals.rs +3 -3
- data/src/rules/style/conditional.rs +12 -1
- data/src/rules/style/conditional_assignment.rs +133 -4
- data/src/rules/style/constant_visibility.rs +3 -3
- data/src/rules/style/copyright.rs +64 -3
- data/src/rules/style/data_inheritance.rs +1 -1
- data/src/rules/style/def_with_parentheses.rs +1 -1
- data/src/rules/style/document_dynamic_eval_definition.rs +50 -12
- data/src/rules/style/documentation.rs +1 -1
- data/src/rules/style/each_for_simple_loop.rs +1 -1
- data/src/rules/style/each_with_object.rs +4 -4
- data/src/rules/style/empty_block_parameter.rs +1 -1
- data/src/rules/style/empty_case_condition.rs +6 -6
- data/src/rules/style/empty_class_definition.rs +1 -1
- data/src/rules/style/empty_else.rs +1 -1
- data/src/rules/style/empty_heredoc.rs +14 -1
- data/src/rules/style/empty_lambda_parameter.rs +1 -1
- data/src/rules/style/empty_literal.rs +34 -2
- data/src/rules/style/empty_method.rs +1 -1
- data/src/rules/style/empty_string_inside_interpolation.rs +1 -1
- data/src/rules/style/endless.rs +11 -3
- data/src/rules/style/env_home.rs +1 -1
- data/src/rules/style/eval_with_location.rs +6 -0
- data/src/rules/style/even_odd.rs +1 -1
- data/src/rules/style/exact_regexp_match.rs +1 -1
- data/src/rules/style/expand_path_arguments.rs +3 -3
- data/src/rules/style/explicit_block_argument.rs +3 -3
- data/src/rules/style/file_open.rs +5 -0
- data/src/rules/style/file_open_call.rs +3 -3
- data/src/rules/style/file_write.rs +1 -1
- data/src/rules/style/for.rs +1 -1
- data/src/rules/style/format_sequences.rs +6 -0
- data/src/rules/style/format_string.rs +75 -11
- data/src/rules/style/format_string_token.rs +61 -12
- data/src/rules/style/frozen_string_literal_comment.rs +62 -4
- data/src/rules/style/guard_clause.rs +8 -7
- data/src/rules/style/hash_as_last_array_item.rs +4 -4
- data/src/rules/style/hash_each_methods.rs +1 -1
- data/src/rules/style/hash_like_case.rs +8 -8
- data/src/rules/style/hash_lookup_method.rs +43 -3
- data/src/rules/style/hash_subset.rs +16 -10
- data/src/rules/style/hash_syntax.rs +609 -71
- data/src/rules/style/hash_transform.rs +20 -18
- data/src/rules/style/hash_transform_keys.rs +7 -0
- data/src/rules/style/hash_transform_values.rs +7 -0
- data/src/rules/style/identical_conditional_branches.rs +54 -5
- data/src/rules/style/if_inside_else.rs +19 -3
- data/src/rules/style/if_unless_modifier.rs +13 -14
- data/src/rules/style/if_with_boolean_literal_branches.rs +1 -1
- data/src/rules/style/if_with_semicolon.rs +7 -1
- data/src/rules/style/implicit_runtime_error.rs +7 -0
- data/src/rules/style/in_pattern_then.rs +2 -2
- data/src/rules/style/infinite_loop.rs +6 -0
- data/src/rules/style/inline_comment.rs +4 -1
- data/src/rules/style/inverse_methods.rs +12 -2
- data/src/rules/style/invertible_unless_condition.rs +12 -12
- data/src/rules/style/it_block_parameter.rs +5 -2
- data/src/rules/style/keyword_arguments_merging.rs +8 -2
- data/src/rules/style/keyword_parameters_order.rs +1 -1
- data/src/rules/style/line_end_concatenation.rs +1 -1
- data/src/rules/style/magic_comment_format.rs +14 -6
- data/src/rules/style/map_chain.rs +1 -1
- data/src/rules/style/map_compact_with_conditional_block.rs +3 -3
- data/src/rules/style/map_into_array.rs +7 -7
- data/src/rules/style/map_join.rs +3 -3
- data/src/rules/style/method_call_with_args_parentheses.rs +7 -7
- data/src/rules/style/method_call_without_args_parentheses.rs +2 -2
- data/src/rules/style/method_def_parentheses.rs +2 -3
- data/src/rules/style/min_max.rs +3 -3
- data/src/rules/style/min_max_comparison.rs +2 -2
- data/src/rules/style/missing_else.rs +1 -1
- data/src/rules/style/missing_respond_to_missing.rs +1 -1
- data/src/rules/style/mixin_grouping.rs +6 -7
- data/src/rules/style/multiline_block_chain.rs +6 -8
- data/src/rules/style/multiline_if_then.rs +1 -1
- data/src/rules/style/multiline_in_pattern_then.rs +16 -3
- data/src/rules/style/multiline_method_signature.rs +1 -1
- data/src/rules/style/multiline_when_then.rs +2 -2
- data/src/rules/style/mutable_constant.rs +31 -35
- data/src/rules/style/negated_if.rs +2 -2
- data/src/rules/style/negated_if_else_condition.rs +5 -3
- data/src/rules/style/negated_while.rs +1 -1
- data/src/rules/style/negative_array_index.rs +25 -3
- data/src/rules/style/nested_modifier.rs +1 -1
- data/src/rules/style/nested_parenthesized_calls.rs +2 -2
- data/src/rules/style/nested_ternary_operator.rs +2 -2
- data/src/rules/style/next.rs +12 -12
- data/src/rules/style/nil_lambda.rs +2 -2
- data/src/rules/style/nodes.rs +21 -0
- data/src/rules/style/non_nil_check.rs +82 -2
- data/src/rules/style/numbered_parameters.rs +4 -1
- data/src/rules/style/numbered_parameters_limit.rs +2 -2
- data/src/rules/style/numeric_literals.rs +17 -1
- data/src/rules/style/numeric_predicate.rs +25 -6
- data/src/rules/style/one_line_conditional.rs +24 -7
- data/src/rules/style/operator_method_call.rs +5 -0
- data/src/rules/style/optional_arguments.rs +1 -1
- data/src/rules/style/or_assignment.rs +1 -1
- data/src/rules/style/parallel_assignment.rs +20 -9
- data/src/rules/style/parens.rs +1 -1
- data/src/rules/style/parentheses_around_condition.rs +3 -3
- data/src/rules/style/partition_instead_of_double_select.rs +3 -3
- data/src/rules/style/percent.rs +18 -9
- data/src/rules/style/percent_q_literals.rs +40 -7
- data/src/rules/style/preferred_hash_methods.rs +1 -1
- data/src/rules/style/quoted_symbols.rs +6 -0
- data/src/rules/style/raise_args.rs +26 -20
- data/src/rules/style/random_with_offset.rs +1 -1
- data/src/rules/style/reduce_to_hash.rs +6 -6
- data/src/rules/style/redundant_argument.rs +1 -1
- data/src/rules/style/redundant_assignment.rs +5 -5
- data/src/rules/style/redundant_begin.rs +5 -3
- data/src/rules/style/redundant_capital_w.rs +3 -2
- data/src/rules/style/redundant_condition.rs +17 -8
- data/src/rules/style/redundant_current_directory_in_path.rs +2 -2
- data/src/rules/style/redundant_double_splat_hash_braces.rs +15 -5
- data/src/rules/style/redundant_exception.rs +3 -1
- data/src/rules/style/redundant_file_extension_in_require.rs +2 -2
- data/src/rules/style/redundant_format.rs +98 -20
- data/src/rules/style/redundant_freeze.rs +1 -1
- data/src/rules/style/redundant_heredoc_delimiter_quotes.rs +1 -1
- data/src/rules/style/redundant_initialize.rs +6 -3
- data/src/rules/style/redundant_interpolation.rs +8 -8
- data/src/rules/style/redundant_interpolation_unfreeze.rs +10 -3
- data/src/rules/style/redundant_line_continuation.rs +10 -7
- data/src/rules/style/redundant_parentheses.rs +96 -19
- data/src/rules/style/redundant_regexp_argument.rs +1 -1
- data/src/rules/style/redundant_regexp_character_class.rs +4 -3
- data/src/rules/style/redundant_regexp_escape.rs +3 -2
- data/src/rules/style/redundant_return.rs +1 -2
- data/src/rules/style/redundant_self.rs +28 -16
- data/src/rules/style/redundant_sort.rs +1 -1
- data/src/rules/style/redundant_sort_by.rs +1 -1
- data/src/rules/style/redundant_string_escape.rs +6 -5
- data/src/rules/style/regexp_literal.rs +3 -2
- data/src/rules/style/require_order.rs +2 -2
- data/src/rules/style/rescue_modifier.rs +9 -4
- data/src/rules/style/rescue_standard_error.rs +1 -1
- data/src/rules/style/return_nil.rs +2 -2
- data/src/rules/style/return_nil_in_predicate_method_definition.rs +2 -2
- data/src/rules/style/safe_navigation.rs +2 -2
- data/src/rules/style/safe_navigation_chain_length.rs +8 -1
- data/src/rules/style/sample.rs +3 -3
- data/src/rules/style/select_by.rs +6 -2
- data/src/rules/style/select_by_range.rs +1 -1
- data/src/rules/style/semicolon.rs +77 -4
- data/src/rules/style/signal_exception.rs +60 -3
- data/src/rules/style/single_argument_dig.rs +10 -2
- data/src/rules/style/single_line_block_params.rs +14 -4
- data/src/rules/style/single_line_do_end_block.rs +13 -12
- data/src/rules/style/single_line_methods.rs +2 -2
- data/src/rules/style/slicing_with_range.rs +2 -2
- data/src/rules/style/sole_nested_conditional.rs +4 -4
- data/src/rules/style/special_global_vars.rs +2 -2
- data/src/rules/style/stabby_lambda_parentheses.rs +1 -1
- data/src/rules/style/statement_modifier.rs +3 -2
- data/src/rules/style/static_class.rs +7 -1
- data/src/rules/style/string_concatenation.rs +12 -7
- data/src/rules/style/string_hash_keys.rs +3 -2
- data/src/rules/style/string_literals.rs +3 -3
- data/src/rules/style/string_literals_in_interpolation.rs +4 -1
- data/src/rules/style/struct_inheritance.rs +3 -3
- data/src/rules/style/super_arguments.rs +15 -7
- data/src/rules/style/super_with_args_parentheses.rs +49 -12
- data/src/rules/style/swap_values.rs +1 -1
- data/src/rules/style/symbol_array.rs +9 -2
- data/src/rules/style/symbol_proc.rs +3 -3
- data/src/rules/style/tally_method.rs +4 -4
- data/src/rules/style/ternary_parentheses.rs +30 -15
- data/src/rules/style/top_level_method_definition.rs +10 -5
- data/src/rules/style/trailing_body.rs +1 -1
- data/src/rules/style/trailing_comma.rs +206 -10
- data/src/rules/style/trailing_comma_in_arguments.rs +9 -10
- data/src/rules/style/trailing_comma_in_array_literal.rs +2 -1
- data/src/rules/style/trailing_comma_in_hash_literal.rs +2 -1
- data/src/rules/style/trailing_method_end_statement.rs +6 -1
- data/src/rules/style/trailing_underscore_variable.rs +4 -5
- data/src/rules/style/trivial_accessors.rs +2 -2
- data/src/rules/style/unless_logical_operators.rs +1 -1
- data/src/rules/style/unpack_first.rs +11 -2
- data/src/rules/style/when_then.rs +4 -4
- data/src/rules/style/while_until_do.rs +1 -1
- data/src/rules/style/while_until_modifier.rs +3 -3
- data/src/rules/style/word_array.rs +9 -7
- data/src/rules/style/yoda_condition.rs +2 -2
- data/src/rules/style/zero_length_predicate.rs +5 -0
- data/src/rules/support.rs +128 -11
- data/src/source.rs +18 -15
- metadata +5 -1
data/src/directives.rs
CHANGED
|
@@ -10,11 +10,56 @@ use crate::diagnostic::Offense;
|
|
|
10
10
|
use crate::engine::{Selection, is_mandatory_cop};
|
|
11
11
|
use crate::source::SourceFile;
|
|
12
12
|
|
|
13
|
+
/// What the last directive to speak for a name decided, which is the shape `analyze_cop` leaves
|
|
14
|
+
/// one cop's analysis in.
|
|
15
|
+
///
|
|
16
|
+
/// An `enable` records a verdict rather than dropping the entry, because the cop it names may sit
|
|
17
|
+
/// under a department -- or under `all` -- that a directive above it switched off, and upstream
|
|
18
|
+
/// closes that cop's range while leaving the rest of the department alone.
|
|
19
|
+
#[derive(Clone, Debug)]
|
|
20
|
+
enum Verdict {
|
|
21
|
+
/// `analyze_disabled`, carrying the `-- reason` the directive was written with.
|
|
22
|
+
Disabled(Option<String>),
|
|
23
|
+
/// `analyze_rest`.
|
|
24
|
+
Enabled,
|
|
25
|
+
}
|
|
26
|
+
|
|
13
27
|
#[derive(Clone, Debug, Default)]
|
|
14
28
|
struct Snapshot {
|
|
15
|
-
all
|
|
16
|
-
|
|
17
|
-
|
|
29
|
+
/// The verdict a directive that named `all` left in force.
|
|
30
|
+
all: Option<Verdict>,
|
|
31
|
+
/// The verdict for every name a directive spelled out, department names included. See
|
|
32
|
+
/// [`Snapshot::verdict_for`] for why a department is not expanded here.
|
|
33
|
+
cops: HashMap<String, Verdict>,
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
impl Snapshot {
|
|
37
|
+
/// The directive in force for `cop`: the one that named it, else the one that named its
|
|
38
|
+
/// department, else the one that named `all`.
|
|
39
|
+
///
|
|
40
|
+
/// `DirectiveComment#cop_names` expands a department and `all` into concrete cop names when
|
|
41
|
+
/// the directive is read, so upstream needs no lookup order at all. Expanding here instead
|
|
42
|
+
/// would mean copying a couple of hundred names into a snapshot that every line of the file
|
|
43
|
+
/// clones, and a department is exactly the prefix of the cops it holds -- so the expansion is
|
|
44
|
+
/// left implicit and read back here, with the entries it supersedes dropped by
|
|
45
|
+
/// [`record`] as the directive is applied.
|
|
46
|
+
fn verdict_for(&self, cop: &str) -> Option<&Verdict> {
|
|
47
|
+
// `parsed_cop_names` subtracts `Lint/Syntax` from every directive, `all` and departments
|
|
48
|
+
// included: a file cannot switch off the report of the parse it was not read by.
|
|
49
|
+
if cop == UNDISABLEABLE_COPS[1] {
|
|
50
|
+
return None;
|
|
51
|
+
}
|
|
52
|
+
if let Some(verdict) = self.cops.get(cop) {
|
|
53
|
+
return Some(verdict);
|
|
54
|
+
}
|
|
55
|
+
// `exclude_lint_department_cops` takes the cop that reads directives out of `all` and out
|
|
56
|
+
// of the `Lint` department, so only a directive that spells its name out reaches it:
|
|
57
|
+
// "this cop is not disabled when disabling all cops".
|
|
58
|
+
if cop == UNDISABLEABLE_COPS[0] {
|
|
59
|
+
return None;
|
|
60
|
+
}
|
|
61
|
+
self.cops.get(department(cop)).or(self.all.as_ref())
|
|
62
|
+
}
|
|
18
63
|
}
|
|
19
64
|
|
|
20
65
|
#[derive(Debug, Default)]
|
|
@@ -56,7 +101,9 @@ impl DirectiveState {
|
|
|
56
101
|
) -> Self {
|
|
57
102
|
let mut current = Snapshot::default();
|
|
58
103
|
for cop in disabled_by_config {
|
|
59
|
-
current
|
|
104
|
+
current
|
|
105
|
+
.cops
|
|
106
|
+
.insert((*cop).to_owned(), Verdict::Disabled(None));
|
|
60
107
|
}
|
|
61
108
|
let mut stack = Vec::new();
|
|
62
109
|
let mut line_states = Vec::with_capacity(source.line_count());
|
|
@@ -81,14 +128,21 @@ impl DirectiveState {
|
|
|
81
128
|
.and_then(|&start| parse_directive(line, start));
|
|
82
129
|
|
|
83
130
|
if let Some(directive) = directive {
|
|
84
|
-
if directive.inline && matches!(directive.action, Action::Disable) {
|
|
85
|
-
let mut line_only = before;
|
|
86
|
-
apply_disable(&mut line_only, &directive.cops, directive.reason);
|
|
87
|
-
line_states.push(line_only);
|
|
88
|
-
continue;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
131
|
match directive.action {
|
|
132
|
+
// `analyze_single_line`, which is where `analyze_cop` sends a directive that
|
|
133
|
+
// shares its line with code or that was written behind prose: it covers that
|
|
134
|
+
// one line and no more.
|
|
135
|
+
Action::Disable if directive.inline => {
|
|
136
|
+
let mut line_only = before;
|
|
137
|
+
apply_disable(&mut line_only, &directive.cops, directive.reason);
|
|
138
|
+
line_states.push(line_only);
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
// `return analysis unless directive.disabled?`: an `enable` written there is a
|
|
142
|
+
// no-op, and in particular does not close the range a block-form `disable`
|
|
143
|
+
// above it opened. `push` and `pop` are routed before that check upstream, so
|
|
144
|
+
// they act wherever they are written.
|
|
145
|
+
Action::Enable if directive.inline => {}
|
|
92
146
|
Action::Disable => {
|
|
93
147
|
apply_disable(&mut current, &directive.cops, directive.reason);
|
|
94
148
|
}
|
|
@@ -135,10 +189,7 @@ impl DirectiveState {
|
|
|
135
189
|
let Some(state) = self.line_states.get(line - 1) else {
|
|
136
190
|
break;
|
|
137
191
|
};
|
|
138
|
-
if !(state.
|
|
139
|
-
|| state.cops.contains_key(cop)
|
|
140
|
-
|| state.cops.contains_key(department(cop)))
|
|
141
|
-
{
|
|
192
|
+
if !matches!(state.verdict_for(cop), Some(Verdict::Disabled(_))) {
|
|
142
193
|
continue;
|
|
143
194
|
}
|
|
144
195
|
match ranges.last_mut() {
|
|
@@ -161,19 +212,10 @@ impl DirectiveState {
|
|
|
161
212
|
}
|
|
162
213
|
let (line, _) = source.line_column(offense.start);
|
|
163
214
|
let state = self.line_states.get(line.saturating_sub(1))?;
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
// `exclude_lint_department_cops` takes the cop that reads directives out of `all` and out
|
|
168
|
-
// of the `Lint` department, so only a directive that spells its name out switches it off:
|
|
169
|
-
// "this cop is not disabled when disabling all cops".
|
|
170
|
-
if offense.cop_name == UNDISABLEABLE_COPS[0] {
|
|
171
|
-
return None;
|
|
215
|
+
match state.verdict_for(offense.cop_name)? {
|
|
216
|
+
Verdict::Disabled(reason) => Some(reason.clone()),
|
|
217
|
+
Verdict::Enabled => None,
|
|
172
218
|
}
|
|
173
|
-
if let Some(reason) = state.cops.get(department(offense.cop_name)) {
|
|
174
|
-
return Some(reason.clone());
|
|
175
|
-
}
|
|
176
|
-
state.all.then(|| state.all_reason.clone())
|
|
177
219
|
}
|
|
178
220
|
}
|
|
179
221
|
|
|
@@ -206,28 +248,102 @@ pub fn opted_in_cops(source: &SourceFile, comment_ranges: &[Range<usize>]) -> Ha
|
|
|
206
248
|
}
|
|
207
249
|
|
|
208
250
|
fn apply_disable(state: &mut Snapshot, cops: &[String], reason: Option<String>) {
|
|
209
|
-
|
|
210
|
-
state
|
|
211
|
-
state.all_reason = reason;
|
|
212
|
-
} else {
|
|
213
|
-
state
|
|
214
|
-
.cops
|
|
215
|
-
.extend(cops.iter().cloned().map(|cop| (cop, reason.clone())));
|
|
251
|
+
for cop in cops {
|
|
252
|
+
record(state, cop, Verdict::Disabled(reason.clone()));
|
|
216
253
|
}
|
|
217
254
|
}
|
|
218
255
|
|
|
219
256
|
fn apply_enable(state: &mut Snapshot, cops: &[String]) {
|
|
220
|
-
|
|
221
|
-
state
|
|
222
|
-
state.all_reason = None;
|
|
223
|
-
state.cops.clear();
|
|
224
|
-
} else {
|
|
225
|
-
for cop in cops {
|
|
226
|
-
state.cops.remove(cop);
|
|
227
|
-
}
|
|
257
|
+
for cop in cops {
|
|
258
|
+
record(state, cop, Verdict::Enabled);
|
|
228
259
|
}
|
|
229
260
|
}
|
|
230
261
|
|
|
262
|
+
/// One name of one directive, applied to the snapshot.
|
|
263
|
+
///
|
|
264
|
+
/// `CommentConfig#analyze` writes the verdict against every cop `DirectiveComment#cop_names`
|
|
265
|
+
/// expanded the name into, so a directive that names `all` or a department speaks for every cop
|
|
266
|
+
/// below it and nothing an earlier directive left against one of them survives. Keeping the
|
|
267
|
+
/// expansion implicit ([`Snapshot::verdict_for`] reads a department back off the cop's own name)
|
|
268
|
+
/// means dropping those superseded entries by hand -- and leaving alone the two cops the
|
|
269
|
+
/// expansion does not reach, whose own entry outlives the broader directive that follows it.
|
|
270
|
+
fn record(state: &mut Snapshot, cop: &str, verdict: Verdict) {
|
|
271
|
+
if cop == "all" {
|
|
272
|
+
state.all = Some(verdict);
|
|
273
|
+
state.cops.retain(|name, _| undisableable(name));
|
|
274
|
+
return;
|
|
275
|
+
}
|
|
276
|
+
let cop = directive_cop_name(cop);
|
|
277
|
+
// `parsed_cop_names` ends in `cops - [LINT_SYNTAX_COP]`, so a directive that names the parse
|
|
278
|
+
// report names nothing at all -- not even enough to supersede what stands.
|
|
279
|
+
if cop == UNDISABLEABLE_COPS[1] {
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
state.cops.retain(|name, _| {
|
|
283
|
+
department(name) != cop || (cop == LINT_DEPARTMENT && undisableable(name))
|
|
284
|
+
});
|
|
285
|
+
state.cops.insert(cop.to_owned(), verdict);
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
/// `exclude_lint_department_cops`: whether `all` and the `Lint` department leave this cop alone.
|
|
289
|
+
fn undisableable(cop: &str) -> bool {
|
|
290
|
+
UNDISABLEABLE_COPS.contains(&cop)
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/// `Cop::Registry.global`: the cops a directive resolves its names against.
|
|
294
|
+
///
|
|
295
|
+
/// Both `CommentConfig#qualified_cop_name` and `DirectiveComment#initialize` read the *global*
|
|
296
|
+
/// registry rather than the one the run mobilized, so what `# rubocop:disable For` means depends
|
|
297
|
+
/// on neither the configuration nor `--only`. [`CopRegistry`] is neither -- it is built from the
|
|
298
|
+
/// configuration, and only for the cop that reviews directives -- so the names come from the cop
|
|
299
|
+
/// list itself, which is what the global registry holds upstream.
|
|
300
|
+
struct GlobalRegistry {
|
|
301
|
+
departments: HashSet<&'static str>,
|
|
302
|
+
/// `Registry#qualify_badge`, worked out once: the cop that carries a bare name, for the names
|
|
303
|
+
/// exactly one cop carries. An ambiguous one is left out, which is what makes a directive that
|
|
304
|
+
/// uses it resolve to nothing rather than to whichever cop was registered first.
|
|
305
|
+
by_cop_name: HashMap<&'static str, &'static str>,
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
fn global_registry() -> &'static GlobalRegistry {
|
|
309
|
+
static REGISTRY: OnceLock<GlobalRegistry> = OnceLock::new();
|
|
310
|
+
REGISTRY.get_or_init(|| {
|
|
311
|
+
let mut departments = HashSet::new();
|
|
312
|
+
let mut candidates: HashMap<&'static str, Option<&'static str>> = HashMap::new();
|
|
313
|
+
for name in crate::rules::rule_names() {
|
|
314
|
+
departments.insert(department(name));
|
|
315
|
+
let cop_name = name.rsplit('/').next().unwrap_or(name);
|
|
316
|
+
candidates
|
|
317
|
+
.entry(cop_name)
|
|
318
|
+
.and_modify(|found| *found = None)
|
|
319
|
+
.or_insert(Some(name));
|
|
320
|
+
}
|
|
321
|
+
GlobalRegistry {
|
|
322
|
+
departments,
|
|
323
|
+
by_cop_name: candidates
|
|
324
|
+
.into_iter()
|
|
325
|
+
.filter_map(|(cop_name, full)| Some((cop_name, full?)))
|
|
326
|
+
.collect(),
|
|
327
|
+
}
|
|
328
|
+
})
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/// `CommentConfig#qualified_cop_name`, for one name a directive spelled out.
|
|
332
|
+
///
|
|
333
|
+
/// A department is left as it is: upstream expands it into the cops it holds, each of which is
|
|
334
|
+
/// already qualified. Everything else goes through `Registry.qualified_cop_name`, which is what
|
|
335
|
+
/// makes `# rubocop:disable For` switch off `Style/For` -- RuboCop warns about the missing
|
|
336
|
+
/// department through `Migration/DepartmentName`, but it honours the directive -- and what makes
|
|
337
|
+
/// a name written under the wrong department reach the cop that really carries it.
|
|
338
|
+
fn directive_cop_name(name: &str) -> &str {
|
|
339
|
+
let registry = global_registry();
|
|
340
|
+
if registry.departments.contains(name) {
|
|
341
|
+
return name;
|
|
342
|
+
}
|
|
343
|
+
let cop_name = name.rsplit('/').next().unwrap_or(name);
|
|
344
|
+
registry.by_cop_name.get(cop_name).copied().unwrap_or(name)
|
|
345
|
+
}
|
|
346
|
+
|
|
231
347
|
#[derive(Clone, Copy, Debug)]
|
|
232
348
|
enum Action {
|
|
233
349
|
Disable,
|
|
@@ -270,7 +386,16 @@ fn parse_directive(line: &str, comment_start: usize) -> Option<Directive> {
|
|
|
270
386
|
// `!comment_only_line?(directive.line_number) || directive.single_line?`: a directive covers
|
|
271
387
|
// its own line alone when that line holds code as well, and also when it was written behind
|
|
272
388
|
// prose rather than opening its comment.
|
|
273
|
-
|
|
389
|
+
//
|
|
390
|
+
// A byte order mark is not code. It is not in `White_Space` either, so `trim` leaves it and
|
|
391
|
+
// the first line of a file that carries one would read as holding something -- upstream never
|
|
392
|
+
// sees it, because `comment_only_line?` asks the token stream and `parser` strips the mark
|
|
393
|
+
// before it lexes anything.
|
|
394
|
+
let before_marker = &line[..comment_start];
|
|
395
|
+
let before_marker = before_marker
|
|
396
|
+
.strip_prefix(crate::source::BYTE_ORDER_MARK)
|
|
397
|
+
.unwrap_or(before_marker);
|
|
398
|
+
let inline = !before_marker.trim().is_empty() || header.start != 0;
|
|
274
399
|
|
|
275
400
|
if matches!(action, Action::Push) {
|
|
276
401
|
let push_operations = arguments
|
|
@@ -367,6 +492,9 @@ pub(crate) fn cop_name_length(text: &str) -> Option<usize> {
|
|
|
367
492
|
/// directives by naming their department.
|
|
368
493
|
const UNDISABLEABLE_COPS: [&str; 2] = ["Lint/RedundantCopDisableDirective", "Lint/Syntax"];
|
|
369
494
|
|
|
495
|
+
/// `DirectiveComment::LINT_DEPARTMENT`, the one department whose expansion leaves cops out.
|
|
496
|
+
const LINT_DEPARTMENT: &str = "Lint";
|
|
497
|
+
|
|
370
498
|
/// The cop `CommentConfig` refuses to record a directive for once it is explicitly enabled, so
|
|
371
499
|
/// that a file cannot switch off the cop that objects to its directives.
|
|
372
500
|
const DISABLE_COPS_DIRECTIVE_COP: &str = "Style/DisableCopsWithinSourceCodeDirective";
|
|
@@ -1262,7 +1390,11 @@ fn lines_with_code(source: &SourceFile, comment_ranges: &[Range<usize>]) -> Hash
|
|
|
1262
1390
|
let mut lines = HashSet::new();
|
|
1263
1391
|
let mut comments = comment_ranges.iter().peekable();
|
|
1264
1392
|
let mut line = 1;
|
|
1265
|
-
|
|
1393
|
+
// `parser` strips a byte order mark before it lexes, so upstream never has a token for one and
|
|
1394
|
+
// the first line of a file that carries one is still a comment-only line. The mark is not in
|
|
1395
|
+
// `White_Space`, so nothing below would take it for a blank.
|
|
1396
|
+
let mut index = usize::from(source.text().starts_with(crate::source::BYTE_ORDER_MARK))
|
|
1397
|
+
* crate::source::BYTE_ORDER_MARK.len();
|
|
1266
1398
|
while index < text.len() {
|
|
1267
1399
|
if comments
|
|
1268
1400
|
.peek()
|
|
@@ -1374,57 +1506,246 @@ fn pop_state(
|
|
|
1374
1506
|
|
|
1375
1507
|
#[cfg(test)]
|
|
1376
1508
|
mod tests {
|
|
1509
|
+
use std::ops::Range;
|
|
1510
|
+
use std::path::Path;
|
|
1511
|
+
|
|
1512
|
+
use crate::config::Config;
|
|
1377
1513
|
use crate::diagnostic::{Offense, Severity};
|
|
1514
|
+
use crate::engine::{self, Selection};
|
|
1378
1515
|
use crate::source::SourceFile;
|
|
1379
1516
|
|
|
1380
1517
|
use super::DirectiveState;
|
|
1381
1518
|
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
"# rubocop:disable Layout -- legacy\n\
|
|
1387
|
-
a = 1 \n\
|
|
1388
|
-
# rubocop:enable Layout\n\
|
|
1389
|
-
b = 2 # rubocop:disable Layout/TrailingWhitespace\n\
|
|
1390
|
-
# rubocop:push -Layout/TrailingWhitespace\n\
|
|
1391
|
-
c = 3 \n\
|
|
1392
|
-
# rubocop:pop\n\
|
|
1393
|
-
d = 4 \n"
|
|
1394
|
-
.to_owned(),
|
|
1395
|
-
);
|
|
1396
|
-
let comment_ranges: Vec<_> = (1..=source.line_count())
|
|
1519
|
+
/// Every comment of a file whose comments are all written with a bare `#`, which is what the
|
|
1520
|
+
/// parse hands [`DirectiveState`] for real.
|
|
1521
|
+
fn comment_ranges(source: &SourceFile) -> Vec<Range<usize>> {
|
|
1522
|
+
(1..=source.line_count())
|
|
1397
1523
|
.filter_map(|line_number| {
|
|
1398
1524
|
let line = source.line(line_number);
|
|
1399
1525
|
let local_start = line.find('#')?;
|
|
1400
1526
|
let start = source.line_start(line_number) + local_start;
|
|
1401
1527
|
Some(start..start + line[local_start..].trim_end().len())
|
|
1402
1528
|
})
|
|
1403
|
-
.collect()
|
|
1404
|
-
|
|
1529
|
+
.collect()
|
|
1530
|
+
}
|
|
1531
|
+
|
|
1532
|
+
fn suppression_of(text: &str, cop: &'static str, line: usize) -> Option<Option<String>> {
|
|
1533
|
+
let source = SourceFile::new("test.rb", text.to_owned());
|
|
1534
|
+
let ranges = comment_ranges(&source);
|
|
1535
|
+
let offense = Offense::new(
|
|
1536
|
+
cop,
|
|
1537
|
+
Severity::Convention,
|
|
1538
|
+
"test",
|
|
1539
|
+
source.line_start(line),
|
|
1540
|
+
source.line_start(line) + 1,
|
|
1541
|
+
);
|
|
1542
|
+
DirectiveState::parse(&source, &ranges).suppression(&offense, &source)
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
/// The whole run, so that the directives are read the way the engine reads them rather than
|
|
1546
|
+
/// the way the test builds them: `--only Style/For` over a file of `for` loops reports one
|
|
1547
|
+
/// offense per loop the directives left switched on.
|
|
1548
|
+
fn offense_count(text: &str) -> usize {
|
|
1549
|
+
let config = Config::load_with_options(None, Path::new("/"), true)
|
|
1550
|
+
.expect("the embedded default configuration has to load");
|
|
1551
|
+
let selection = Selection {
|
|
1552
|
+
only: vec!["Style/For".to_owned()],
|
|
1553
|
+
..Selection::default()
|
|
1554
|
+
};
|
|
1555
|
+
engine::inspect_source("example.rb", text.to_owned(), &config, &selection)
|
|
1556
|
+
.expect("the source has to be inspectable")
|
|
1557
|
+
.offenses
|
|
1558
|
+
.len()
|
|
1559
|
+
}
|
|
1560
|
+
|
|
1561
|
+
const FIRST_LOOP: &str = "for i in 1..2 do\n puts i\nend\n";
|
|
1562
|
+
const SECOND_LOOP: &str = "for j in 1..2 do\n puts j\nend\n";
|
|
1563
|
+
|
|
1564
|
+
#[test]
|
|
1565
|
+
fn handles_block_inline_reason_and_push_pop_directives() {
|
|
1566
|
+
let text = "# rubocop:disable Layout -- legacy\n\
|
|
1567
|
+
a = 1 \n\
|
|
1568
|
+
# rubocop:enable Layout\n\
|
|
1569
|
+
b = 2 # rubocop:disable Layout/TrailingWhitespace\n\
|
|
1570
|
+
# rubocop:push -Layout/TrailingWhitespace\n\
|
|
1571
|
+
c = 3 \n\
|
|
1572
|
+
# rubocop:pop\n\
|
|
1573
|
+
d = 4 \n";
|
|
1405
1574
|
for (line, expected) in [(2, true), (4, true), (6, true), (8, false)] {
|
|
1406
|
-
let offense = Offense::new(
|
|
1407
|
-
"Layout/TrailingWhitespace",
|
|
1408
|
-
Severity::Convention,
|
|
1409
|
-
"test",
|
|
1410
|
-
source.line_start(line),
|
|
1411
|
-
source.line_start(line) + 1,
|
|
1412
|
-
);
|
|
1413
1575
|
assert_eq!(
|
|
1414
|
-
|
|
1576
|
+
suppression_of(text, "Layout/TrailingWhitespace", line).is_some(),
|
|
1415
1577
|
expected
|
|
1416
1578
|
);
|
|
1417
1579
|
}
|
|
1418
|
-
let offense = Offense::new(
|
|
1419
|
-
"Layout/TrailingWhitespace",
|
|
1420
|
-
Severity::Convention,
|
|
1421
|
-
"test",
|
|
1422
|
-
source.line_start(2),
|
|
1423
|
-
source.line_start(2) + 1,
|
|
1424
|
-
);
|
|
1425
1580
|
assert_eq!(
|
|
1426
|
-
|
|
1581
|
+
suppression_of(text, "Layout/TrailingWhitespace", 2),
|
|
1427
1582
|
Some(Some("legacy".to_owned()))
|
|
1428
1583
|
);
|
|
1429
1584
|
}
|
|
1585
|
+
|
|
1586
|
+
/// `analyze_single_line` is `return analysis unless directive.disabled?`, so an `enable` that
|
|
1587
|
+
/// shares its line with code changes nothing -- least of all closing the range a block-form
|
|
1588
|
+
/// `disable` above it opened, which is what it used to do here and what left the rest of the
|
|
1589
|
+
/// file reporting again.
|
|
1590
|
+
#[test]
|
|
1591
|
+
fn an_inline_enable_does_not_close_a_block_disable() {
|
|
1592
|
+
let text = format!(
|
|
1593
|
+
"# rubocop:disable Style/For\n{FIRST_LOOP}\
|
|
1594
|
+
x = 1 # rubocop:enable Style/For\n{SECOND_LOOP}"
|
|
1595
|
+
);
|
|
1596
|
+
assert!(suppression_of(&text, "Style/For", 6).is_some());
|
|
1597
|
+
assert_eq!(offense_count(&text), 0);
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
/// The same for a `push`, which `CommentConfig#analyze` routes before it ever asks whether the
|
|
1601
|
+
/// directive covers one line: it acts wherever it is written.
|
|
1602
|
+
#[test]
|
|
1603
|
+
fn an_inline_push_and_pop_still_act() {
|
|
1604
|
+
let text = format!(
|
|
1605
|
+
"x = 1 # rubocop:push -Style/For\n{FIRST_LOOP}\
|
|
1606
|
+
y = 2 # rubocop:pop\n{SECOND_LOOP}"
|
|
1607
|
+
);
|
|
1608
|
+
assert!(suppression_of(&text, "Style/For", 2).is_some());
|
|
1609
|
+
assert!(suppression_of(&text, "Style/For", 6).is_none());
|
|
1610
|
+
assert_eq!(offense_count(&text), 1);
|
|
1611
|
+
}
|
|
1612
|
+
|
|
1613
|
+
/// `DirectiveComment#cop_names` expands a department into the cops it holds, so an `enable`
|
|
1614
|
+
/// that names one of them closes that cop's range and leaves the rest of the department
|
|
1615
|
+
/// switched off. Recording the department verbatim and only ever removing the name an `enable`
|
|
1616
|
+
/// spelled out kept the cop disabled to the end of the file, which is the dangerous direction:
|
|
1617
|
+
/// it hides offenses that RuboCop reports.
|
|
1618
|
+
#[test]
|
|
1619
|
+
fn an_enable_reopens_one_cop_of_a_disabled_department() {
|
|
1620
|
+
let text = format!(
|
|
1621
|
+
"# rubocop:disable Style\n{FIRST_LOOP}\
|
|
1622
|
+
# rubocop:enable Style/For\n{SECOND_LOOP}"
|
|
1623
|
+
);
|
|
1624
|
+
assert!(suppression_of(&text, "Style/For", 2).is_some());
|
|
1625
|
+
assert!(suppression_of(&text, "Style/For", 6).is_none());
|
|
1626
|
+
// The department is still switched off for everything it did not name.
|
|
1627
|
+
assert!(suppression_of(&text, "Style/RedundantSelf", 6).is_some());
|
|
1628
|
+
assert_eq!(offense_count(&text), 1);
|
|
1629
|
+
}
|
|
1630
|
+
|
|
1631
|
+
/// `all` expands the same way.
|
|
1632
|
+
#[test]
|
|
1633
|
+
fn an_enable_reopens_one_cop_of_a_disabled_all() {
|
|
1634
|
+
let text = format!(
|
|
1635
|
+
"# rubocop:disable all\n{FIRST_LOOP}\
|
|
1636
|
+
# rubocop:enable Style/For\n{SECOND_LOOP}"
|
|
1637
|
+
);
|
|
1638
|
+
assert!(suppression_of(&text, "Style/For", 2).is_some());
|
|
1639
|
+
assert!(suppression_of(&text, "Style/For", 6).is_none());
|
|
1640
|
+
assert!(suppression_of(&text, "Layout/TrailingWhitespace", 6).is_some());
|
|
1641
|
+
assert_eq!(offense_count(&text), 1);
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
/// A directive that names a department speaks for every cop below it, so what an earlier
|
|
1645
|
+
/// directive left against one of them is gone.
|
|
1646
|
+
#[test]
|
|
1647
|
+
fn a_department_directive_supersedes_the_cops_below_it() {
|
|
1648
|
+
let reopened_then_closed = format!(
|
|
1649
|
+
"# rubocop:disable Style\n{FIRST_LOOP}\
|
|
1650
|
+
# rubocop:enable Style/For\n\
|
|
1651
|
+
# rubocop:disable Style\n{SECOND_LOOP}"
|
|
1652
|
+
);
|
|
1653
|
+
assert!(suppression_of(&reopened_then_closed, "Style/For", 7).is_some());
|
|
1654
|
+
|
|
1655
|
+
let closed_then_reopened = format!(
|
|
1656
|
+
"# rubocop:disable Style/For\n{FIRST_LOOP}\
|
|
1657
|
+
# rubocop:enable Style\n{SECOND_LOOP}"
|
|
1658
|
+
);
|
|
1659
|
+
assert!(suppression_of(&closed_then_reopened, "Style/For", 6).is_none());
|
|
1660
|
+
}
|
|
1661
|
+
|
|
1662
|
+
/// `Registry.qualified_cop_name`: RuboCop warns about the missing department through
|
|
1663
|
+
/// `Migration/DepartmentName` but honours the directive, so a name written without one -- or
|
|
1664
|
+
/// under the wrong one -- reaches the cop that carries it.
|
|
1665
|
+
#[test]
|
|
1666
|
+
fn an_unqualified_cop_name_reaches_the_cop_that_carries_it() {
|
|
1667
|
+
let bare = format!("# rubocop:disable For\n{FIRST_LOOP}");
|
|
1668
|
+
assert!(suppression_of(&bare, "Style/For", 2).is_some());
|
|
1669
|
+
assert_eq!(offense_count(&bare), 0);
|
|
1670
|
+
|
|
1671
|
+
let wrong_department = format!("# rubocop:disable Lint/For\n{FIRST_LOOP}");
|
|
1672
|
+
assert!(suppression_of(&wrong_department, "Style/For", 2).is_some());
|
|
1673
|
+
assert_eq!(offense_count(&wrong_department), 0);
|
|
1674
|
+
|
|
1675
|
+
let unqualified_enable = format!(
|
|
1676
|
+
"# rubocop:disable Style/For\n{FIRST_LOOP}\
|
|
1677
|
+
# rubocop:enable For\n{SECOND_LOOP}"
|
|
1678
|
+
);
|
|
1679
|
+
assert!(suppression_of(&unqualified_enable, "Style/For", 6).is_none());
|
|
1680
|
+
assert_eq!(offense_count(&unqualified_enable), 1);
|
|
1681
|
+
|
|
1682
|
+
// A name no cop carries stays what it was written as and switches nothing off.
|
|
1683
|
+
let unknown = format!("# rubocop:disable NotACop\n{FIRST_LOOP}");
|
|
1684
|
+
assert!(suppression_of(&unknown, "Style/For", 2).is_none());
|
|
1685
|
+
assert_eq!(offense_count(&unknown), 1);
|
|
1686
|
+
}
|
|
1687
|
+
|
|
1688
|
+
/// `parser` strips the byte order mark before it lexes, so upstream's directive comment starts
|
|
1689
|
+
/// at its `#` and its line holds no code. The mark is not in `White_Space`, so `trim` left it
|
|
1690
|
+
/// behind and the directive read as one written behind prose -- covering line one alone.
|
|
1691
|
+
#[test]
|
|
1692
|
+
fn a_byte_order_mark_leaves_a_first_line_directive_block_form() {
|
|
1693
|
+
let text = format!(
|
|
1694
|
+
"{}# rubocop:disable Style/For\n{FIRST_LOOP}",
|
|
1695
|
+
crate::source::BYTE_ORDER_MARK
|
|
1696
|
+
);
|
|
1697
|
+
assert!(suppression_of(&text, "Style/For", 2).is_some());
|
|
1698
|
+
assert_eq!(offense_count(&text), 0);
|
|
1699
|
+
}
|
|
1700
|
+
|
|
1701
|
+
/// The mark does not make line one hold code for `CommentConfig` either, which is the model
|
|
1702
|
+
/// the cops that review directives read.
|
|
1703
|
+
#[test]
|
|
1704
|
+
fn a_byte_order_mark_leaves_the_first_line_comment_only() {
|
|
1705
|
+
let text = format!(
|
|
1706
|
+
"{}# rubocop:disable Style/For\n{FIRST_LOOP}",
|
|
1707
|
+
crate::source::BYTE_ORDER_MARK
|
|
1708
|
+
);
|
|
1709
|
+
let source = SourceFile::new("test.rb", text);
|
|
1710
|
+
assert!(!super::lines_with_code(&source, &comment_ranges(&source)).contains(&1));
|
|
1711
|
+
}
|
|
1712
|
+
|
|
1713
|
+
/// `exclude_lint_department_cops`: the cop that reads directives is out of `all` and out of
|
|
1714
|
+
/// the `Lint` department, and `Lint/Syntax` is out of every directive there is.
|
|
1715
|
+
#[test]
|
|
1716
|
+
fn the_two_undisableable_cops_stay_out_of_all_and_of_their_department() {
|
|
1717
|
+
for directive in ["all", "Lint"] {
|
|
1718
|
+
let text = format!("# rubocop:disable {directive}\nx = 1\n");
|
|
1719
|
+
assert!(suppression_of(&text, "Lint/RedundantCopDisableDirective", 2).is_none());
|
|
1720
|
+
assert!(suppression_of(&text, "Lint/Syntax", 2).is_none());
|
|
1721
|
+
assert!(suppression_of(&text, "Lint/UselessAssignment", 2).is_some());
|
|
1722
|
+
}
|
|
1723
|
+
let named = "# rubocop:disable Lint/RedundantCopDisableDirective\nx = 1\n";
|
|
1724
|
+
assert!(suppression_of(named, "Lint/RedundantCopDisableDirective", 2).is_some());
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
/// The other half of `exclude_lint_department_cops`: a later `all` or `Lint` directive does not
|
|
1728
|
+
/// reach those two either, so what a directive that named one of them outright left behind
|
|
1729
|
+
/// stands. Superseding the cops a broad directive expands into must not take them with it.
|
|
1730
|
+
#[test]
|
|
1731
|
+
fn a_broad_directive_does_not_supersede_the_two_cops_it_cannot_reach() {
|
|
1732
|
+
for directive in [
|
|
1733
|
+
"# rubocop:enable Lint",
|
|
1734
|
+
"# rubocop:disable all",
|
|
1735
|
+
"# rubocop:enable all",
|
|
1736
|
+
] {
|
|
1737
|
+
let text = format!(
|
|
1738
|
+
"# rubocop:disable Lint/RedundantCopDisableDirective\n{directive}\nx = 1\n"
|
|
1739
|
+
);
|
|
1740
|
+
assert!(
|
|
1741
|
+
suppression_of(&text, "Lint/RedundantCopDisableDirective", 3).is_some(),
|
|
1742
|
+
"{directive} closed a range it does not reach"
|
|
1743
|
+
);
|
|
1744
|
+
}
|
|
1745
|
+
// Spelling the cop out does close it.
|
|
1746
|
+
let closed = "# rubocop:disable Lint/RedundantCopDisableDirective\n\
|
|
1747
|
+
# rubocop:enable Lint/RedundantCopDisableDirective\n\
|
|
1748
|
+
x = 1\n";
|
|
1749
|
+
assert!(suppression_of(closed, "Lint/RedundantCopDisableDirective", 3).is_none());
|
|
1750
|
+
}
|
|
1430
1751
|
}
|