rubbycop 0.49.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE.txt +21 -0
- data/README.md +211 -0
- data/assets/logo.png +0 -0
- data/assets/output.html.erb +261 -0
- data/bin/rubbycop +17 -0
- data/config/default.yml +1548 -0
- data/config/disabled.yml +119 -0
- data/config/enabled.yml +1734 -0
- data/lib/rubbycop.rb +510 -0
- data/lib/rubbycop/ast/builder.rb +64 -0
- data/lib/rubbycop/ast/node.rb +610 -0
- data/lib/rubbycop/ast/node/and_node.rb +37 -0
- data/lib/rubbycop/ast/node/array_node.rb +48 -0
- data/lib/rubbycop/ast/node/case_node.rb +64 -0
- data/lib/rubbycop/ast/node/ensure_node.rb +25 -0
- data/lib/rubbycop/ast/node/for_node.rb +53 -0
- data/lib/rubbycop/ast/node/hash_node.rb +109 -0
- data/lib/rubbycop/ast/node/if_node.rb +138 -0
- data/lib/rubbycop/ast/node/keyword_splat_node.rb +45 -0
- data/lib/rubbycop/ast/node/mixin/binary_operator_node.rb +23 -0
- data/lib/rubbycop/ast/node/mixin/conditional_node.rb +45 -0
- data/lib/rubbycop/ast/node/mixin/hash_element_node.rb +125 -0
- data/lib/rubbycop/ast/node/mixin/modifier_node.rb +17 -0
- data/lib/rubbycop/ast/node/mixin/predicate_operator_node.rb +35 -0
- data/lib/rubbycop/ast/node/or_node.rb +37 -0
- data/lib/rubbycop/ast/node/pair_node.rb +64 -0
- data/lib/rubbycop/ast/node/resbody_node.rb +25 -0
- data/lib/rubbycop/ast/node/send_node.rb +209 -0
- data/lib/rubbycop/ast/node/until_node.rb +43 -0
- data/lib/rubbycop/ast/node/when_node.rb +61 -0
- data/lib/rubbycop/ast/node/while_node.rb +43 -0
- data/lib/rubbycop/ast/sexp.rb +16 -0
- data/lib/rubbycop/ast/traversal.rb +171 -0
- data/lib/rubbycop/cached_data.rb +63 -0
- data/lib/rubbycop/cli.rb +199 -0
- data/lib/rubbycop/comment_config.rb +155 -0
- data/lib/rubbycop/config.rb +444 -0
- data/lib/rubbycop/config_loader.rb +244 -0
- data/lib/rubbycop/config_loader_resolver.rb +43 -0
- data/lib/rubbycop/config_store.rb +48 -0
- data/lib/rubbycop/cop/autocorrect_logic.rb +26 -0
- data/lib/rubbycop/cop/badge.rb +73 -0
- data/lib/rubbycop/cop/bundler/duplicated_gem.rb +69 -0
- data/lib/rubbycop/cop/bundler/ordered_gems.rb +113 -0
- data/lib/rubbycop/cop/commissioner.rb +118 -0
- data/lib/rubbycop/cop/cop.rb +222 -0
- data/lib/rubbycop/cop/corrector.rb +135 -0
- data/lib/rubbycop/cop/force.rb +41 -0
- data/lib/rubbycop/cop/ignored_node.rb +38 -0
- data/lib/rubbycop/cop/layout/access_modifier_indentation.rb +109 -0
- data/lib/rubbycop/cop/layout/align_array.rb +35 -0
- data/lib/rubbycop/cop/layout/align_hash.rb +235 -0
- data/lib/rubbycop/cop/layout/align_parameters.rb +97 -0
- data/lib/rubbycop/cop/layout/block_end_newline.rb +56 -0
- data/lib/rubbycop/cop/layout/case_indentation.rb +163 -0
- data/lib/rubbycop/cop/layout/closing_parenthesis_indentation.rb +88 -0
- data/lib/rubbycop/cop/layout/comment_indentation.rb +71 -0
- data/lib/rubbycop/cop/layout/dot_position.rb +84 -0
- data/lib/rubbycop/cop/layout/else_alignment.rb +105 -0
- data/lib/rubbycop/cop/layout/empty_line_after_magic_comment.rb +63 -0
- data/lib/rubbycop/cop/layout/empty_line_between_defs.rb +143 -0
- data/lib/rubbycop/cop/layout/empty_lines.rb +60 -0
- data/lib/rubbycop/cop/layout/empty_lines_around_access_modifier.rb +90 -0
- data/lib/rubbycop/cop/layout/empty_lines_around_begin_body.rb +42 -0
- data/lib/rubbycop/cop/layout/empty_lines_around_block_body.rb +41 -0
- data/lib/rubbycop/cop/layout/empty_lines_around_class_body.rb +39 -0
- data/lib/rubbycop/cop/layout/empty_lines_around_exception_handling_keywords.rb +127 -0
- data/lib/rubbycop/cop/layout/empty_lines_around_method_body.rb +41 -0
- data/lib/rubbycop/cop/layout/empty_lines_around_module_body.rb +44 -0
- data/lib/rubbycop/cop/layout/end_of_line.rb +52 -0
- data/lib/rubbycop/cop/layout/extra_spacing.rb +237 -0
- data/lib/rubbycop/cop/layout/first_array_element_line_break.rb +41 -0
- data/lib/rubbycop/cop/layout/first_hash_element_line_break.rb +33 -0
- data/lib/rubbycop/cop/layout/first_method_argument_line_break.rb +49 -0
- data/lib/rubbycop/cop/layout/first_method_parameter_line_break.rb +42 -0
- data/lib/rubbycop/cop/layout/first_parameter_indentation.rb +109 -0
- data/lib/rubbycop/cop/layout/indent_array.rb +114 -0
- data/lib/rubbycop/cop/layout/indent_assignment.rb +42 -0
- data/lib/rubbycop/cop/layout/indent_hash.rb +134 -0
- data/lib/rubbycop/cop/layout/indent_heredoc.rb +173 -0
- data/lib/rubbycop/cop/layout/indentation_consistency.rb +51 -0
- data/lib/rubbycop/cop/layout/indentation_width.rb +303 -0
- data/lib/rubbycop/cop/layout/initial_indentation.rb +42 -0
- data/lib/rubbycop/cop/layout/leading_comment_space.rb +43 -0
- data/lib/rubbycop/cop/layout/multiline_array_brace_layout.rb +81 -0
- data/lib/rubbycop/cop/layout/multiline_assignment_layout.rb +88 -0
- data/lib/rubbycop/cop/layout/multiline_block_layout.rb +134 -0
- data/lib/rubbycop/cop/layout/multiline_hash_brace_layout.rb +81 -0
- data/lib/rubbycop/cop/layout/multiline_method_call_brace_layout.rb +97 -0
- data/lib/rubbycop/cop/layout/multiline_method_call_indentation.rb +215 -0
- data/lib/rubbycop/cop/layout/multiline_method_definition_brace_layout.rb +82 -0
- data/lib/rubbycop/cop/layout/multiline_operation_indentation.rb +89 -0
- data/lib/rubbycop/cop/layout/rescue_ensure_alignment.rb +86 -0
- data/lib/rubbycop/cop/layout/space_after_colon.rb +40 -0
- data/lib/rubbycop/cop/layout/space_after_comma.rb +21 -0
- data/lib/rubbycop/cop/layout/space_after_method_name.rb +37 -0
- data/lib/rubbycop/cop/layout/space_after_not.rb +38 -0
- data/lib/rubbycop/cop/layout/space_after_semicolon.rb +21 -0
- data/lib/rubbycop/cop/layout/space_around_block_parameters.rb +109 -0
- data/lib/rubbycop/cop/layout/space_around_equals_in_parameter_default.rb +68 -0
- data/lib/rubbycop/cop/layout/space_around_keyword.rb +224 -0
- data/lib/rubbycop/cop/layout/space_around_operators.rb +142 -0
- data/lib/rubbycop/cop/layout/space_before_block_braces.rb +54 -0
- data/lib/rubbycop/cop/layout/space_before_comma.rb +16 -0
- data/lib/rubbycop/cop/layout/space_before_comment.rb +27 -0
- data/lib/rubbycop/cop/layout/space_before_first_arg.rb +64 -0
- data/lib/rubbycop/cop/layout/space_before_semicolon.rb +16 -0
- data/lib/rubbycop/cop/layout/space_in_lambda_literal.rb +87 -0
- data/lib/rubbycop/cop/layout/space_inside_array_percent_literal.rb +53 -0
- data/lib/rubbycop/cop/layout/space_inside_block_braces.rb +158 -0
- data/lib/rubbycop/cop/layout/space_inside_brackets.rb +20 -0
- data/lib/rubbycop/cop/layout/space_inside_hash_literal_braces.rb +150 -0
- data/lib/rubbycop/cop/layout/space_inside_parens.rb +16 -0
- data/lib/rubbycop/cop/layout/space_inside_percent_literal_delimiters.rb +64 -0
- data/lib/rubbycop/cop/layout/space_inside_range_literal.rb +63 -0
- data/lib/rubbycop/cop/layout/space_inside_string_interpolation.rb +65 -0
- data/lib/rubbycop/cop/layout/tab.rb +57 -0
- data/lib/rubbycop/cop/layout/trailing_blank_lines.rb +78 -0
- data/lib/rubbycop/cop/layout/trailing_whitespace.rb +28 -0
- data/lib/rubbycop/cop/lint/ambiguous_block_association.rb +66 -0
- data/lib/rubbycop/cop/lint/ambiguous_operator.rb +55 -0
- data/lib/rubbycop/cop/lint/ambiguous_regexp_literal.rb +43 -0
- data/lib/rubbycop/cop/lint/assignment_in_condition.rb +80 -0
- data/lib/rubbycop/cop/lint/block_alignment.rb +229 -0
- data/lib/rubbycop/cop/lint/circular_argument_reference.rb +83 -0
- data/lib/rubbycop/cop/lint/condition_position.rb +52 -0
- data/lib/rubbycop/cop/lint/debugger.rb +72 -0
- data/lib/rubbycop/cop/lint/def_end_alignment.rb +78 -0
- data/lib/rubbycop/cop/lint/deprecated_class_methods.rb +90 -0
- data/lib/rubbycop/cop/lint/duplicate_case_condition.rb +53 -0
- data/lib/rubbycop/cop/lint/duplicate_methods.rb +151 -0
- data/lib/rubbycop/cop/lint/duplicated_key.rb +38 -0
- data/lib/rubbycop/cop/lint/each_with_object_argument.rb +39 -0
- data/lib/rubbycop/cop/lint/else_layout.rb +65 -0
- data/lib/rubbycop/cop/lint/empty_ensure.rb +60 -0
- data/lib/rubbycop/cop/lint/empty_expression.rb +42 -0
- data/lib/rubbycop/cop/lint/empty_interpolation.rb +36 -0
- data/lib/rubbycop/cop/lint/empty_when.rb +38 -0
- data/lib/rubbycop/cop/lint/end_alignment.rb +157 -0
- data/lib/rubbycop/cop/lint/end_in_method.rb +40 -0
- data/lib/rubbycop/cop/lint/ensure_return.rb +43 -0
- data/lib/rubbycop/cop/lint/float_out_of_range.rb +35 -0
- data/lib/rubbycop/cop/lint/format_parameter_mismatch.rb +182 -0
- data/lib/rubbycop/cop/lint/handle_exceptions.rb +56 -0
- data/lib/rubbycop/cop/lint/implicit_string_concatenation.rb +95 -0
- data/lib/rubbycop/cop/lint/ineffective_access_modifier.rb +143 -0
- data/lib/rubbycop/cop/lint/inherit_exception.rb +83 -0
- data/lib/rubbycop/cop/lint/invalid_character_literal.rb +41 -0
- data/lib/rubbycop/cop/lint/literal_in_condition.rb +127 -0
- data/lib/rubbycop/cop/lint/literal_in_interpolation.rb +76 -0
- data/lib/rubbycop/cop/lint/loop.rb +63 -0
- data/lib/rubbycop/cop/lint/multiple_compare.rb +48 -0
- data/lib/rubbycop/cop/lint/nested_method_definition.rb +105 -0
- data/lib/rubbycop/cop/lint/next_without_accumulator.rb +50 -0
- data/lib/rubbycop/cop/lint/non_local_exit_from_iterator.rb +85 -0
- data/lib/rubbycop/cop/lint/parentheses_as_grouped_expression.rb +60 -0
- data/lib/rubbycop/cop/lint/percent_string_array.rb +84 -0
- data/lib/rubbycop/cop/lint/percent_symbol_array.rb +66 -0
- data/lib/rubbycop/cop/lint/rand_one.rb +39 -0
- data/lib/rubbycop/cop/lint/require_parentheses.rb +61 -0
- data/lib/rubbycop/cop/lint/rescue_exception.rb +45 -0
- data/lib/rubbycop/cop/lint/safe_navigation_chain.rb +70 -0
- data/lib/rubbycop/cop/lint/shadowed_exception.rb +132 -0
- data/lib/rubbycop/cop/lint/shadowing_outer_local_variable.rb +53 -0
- data/lib/rubbycop/cop/lint/string_conversion_in_interpolation.rb +58 -0
- data/lib/rubbycop/cop/lint/syntax.rb +55 -0
- data/lib/rubbycop/cop/lint/underscore_prefixed_variable_name.rb +62 -0
- data/lib/rubbycop/cop/lint/unified_integer.rb +42 -0
- data/lib/rubbycop/cop/lint/unneeded_disable.rb +231 -0
- data/lib/rubbycop/cop/lint/unneeded_splat_expansion.rb +141 -0
- data/lib/rubbycop/cop/lint/unreachable_code.rb +52 -0
- data/lib/rubbycop/cop/lint/unused_block_argument.rb +145 -0
- data/lib/rubbycop/cop/lint/unused_method_argument.rb +61 -0
- data/lib/rubbycop/cop/lint/useless_access_modifier.rb +229 -0
- data/lib/rubbycop/cop/lint/useless_assignment.rb +132 -0
- data/lib/rubbycop/cop/lint/useless_comparison.rb +28 -0
- data/lib/rubbycop/cop/lint/useless_else_without_rescue.rb +46 -0
- data/lib/rubbycop/cop/lint/useless_setter_call.rb +162 -0
- data/lib/rubbycop/cop/lint/void.rb +108 -0
- data/lib/rubbycop/cop/message_annotator.rb +116 -0
- data/lib/rubbycop/cop/metrics/abc_size.rb +39 -0
- data/lib/rubbycop/cop/metrics/block_length.rb +32 -0
- data/lib/rubbycop/cop/metrics/block_nesting.rb +64 -0
- data/lib/rubbycop/cop/metrics/class_length.rb +24 -0
- data/lib/rubbycop/cop/metrics/cyclomatic_complexity.rb +31 -0
- data/lib/rubbycop/cop/metrics/line_length.rb +168 -0
- data/lib/rubbycop/cop/metrics/method_length.rb +27 -0
- data/lib/rubbycop/cop/metrics/module_length.rb +24 -0
- data/lib/rubbycop/cop/metrics/parameter_lists.rb +45 -0
- data/lib/rubbycop/cop/metrics/perceived_complexity.rb +61 -0
- data/lib/rubbycop/cop/mixin/access_modifier_node.rb +41 -0
- data/lib/rubbycop/cop/mixin/annotation_comment.rb +36 -0
- data/lib/rubbycop/cop/mixin/array_hash_indentation.rb +82 -0
- data/lib/rubbycop/cop/mixin/array_min_size.rb +59 -0
- data/lib/rubbycop/cop/mixin/array_syntax.rb +15 -0
- data/lib/rubbycop/cop/mixin/autocorrect_alignment.rb +149 -0
- data/lib/rubbycop/cop/mixin/check_assignment.rb +40 -0
- data/lib/rubbycop/cop/mixin/classish_length.rb +36 -0
- data/lib/rubbycop/cop/mixin/code_length.rb +32 -0
- data/lib/rubbycop/cop/mixin/configurable_enforced_style.rb +97 -0
- data/lib/rubbycop/cop/mixin/configurable_formatting.rb +48 -0
- data/lib/rubbycop/cop/mixin/configurable_max.rb +19 -0
- data/lib/rubbycop/cop/mixin/configurable_naming.rb +16 -0
- data/lib/rubbycop/cop/mixin/configurable_numbering.rb +17 -0
- data/lib/rubbycop/cop/mixin/def_node.rb +27 -0
- data/lib/rubbycop/cop/mixin/documentation_comment.rb +46 -0
- data/lib/rubbycop/cop/mixin/duplication.rb +46 -0
- data/lib/rubbycop/cop/mixin/empty_lines_around_body.rb +161 -0
- data/lib/rubbycop/cop/mixin/end_keyword_alignment.rb +85 -0
- data/lib/rubbycop/cop/mixin/enforce_superclass.rb +36 -0
- data/lib/rubbycop/cop/mixin/first_element_line_break.rb +41 -0
- data/lib/rubbycop/cop/mixin/frozen_string_literal.rb +37 -0
- data/lib/rubbycop/cop/mixin/hash_alignment.rb +116 -0
- data/lib/rubbycop/cop/mixin/ignored_pattern.rb +27 -0
- data/lib/rubbycop/cop/mixin/integer_node.rb +12 -0
- data/lib/rubbycop/cop/mixin/match_range.rb +22 -0
- data/lib/rubbycop/cop/mixin/method_complexity.rb +30 -0
- data/lib/rubbycop/cop/mixin/method_preference.rb +30 -0
- data/lib/rubbycop/cop/mixin/min_body_length.rb +19 -0
- data/lib/rubbycop/cop/mixin/multiline_expression_indentation.rb +183 -0
- data/lib/rubbycop/cop/mixin/multiline_literal_brace_layout.rb +152 -0
- data/lib/rubbycop/cop/mixin/negative_conditional.rb +43 -0
- data/lib/rubbycop/cop/mixin/on_method_def.rb +44 -0
- data/lib/rubbycop/cop/mixin/on_normal_if_unless.rb +14 -0
- data/lib/rubbycop/cop/mixin/parentheses.rb +22 -0
- data/lib/rubbycop/cop/mixin/parser_diagnostic.rb +34 -0
- data/lib/rubbycop/cop/mixin/percent_literal.rb +100 -0
- data/lib/rubbycop/cop/mixin/preceding_following_alignment.rb +89 -0
- data/lib/rubbycop/cop/mixin/rescue_node.rb +21 -0
- data/lib/rubbycop/cop/mixin/safe_assignment.rb +20 -0
- data/lib/rubbycop/cop/mixin/safe_mode.rb +22 -0
- data/lib/rubbycop/cop/mixin/space_after_punctuation.rb +55 -0
- data/lib/rubbycop/cop/mixin/space_before_punctuation.rb +48 -0
- data/lib/rubbycop/cop/mixin/space_inside.rb +76 -0
- data/lib/rubbycop/cop/mixin/statement_modifier.rb +69 -0
- data/lib/rubbycop/cop/mixin/string_help.rb +33 -0
- data/lib/rubbycop/cop/mixin/string_literals_help.rb +33 -0
- data/lib/rubbycop/cop/mixin/surrounding_space.rb +40 -0
- data/lib/rubbycop/cop/mixin/target_rails_version.rb +16 -0
- data/lib/rubbycop/cop/mixin/target_ruby_version.rb +16 -0
- data/lib/rubbycop/cop/mixin/too_many_lines.rb +39 -0
- data/lib/rubbycop/cop/mixin/trailing_comma.rb +161 -0
- data/lib/rubbycop/cop/mixin/unused_argument.rb +42 -0
- data/lib/rubbycop/cop/offense.rb +188 -0
- data/lib/rubbycop/cop/performance/caller.rb +41 -0
- data/lib/rubbycop/cop/performance/case_when_splat.rb +176 -0
- data/lib/rubbycop/cop/performance/casecmp.rb +107 -0
- data/lib/rubbycop/cop/performance/compare_with_block.rb +107 -0
- data/lib/rubbycop/cop/performance/count.rb +98 -0
- data/lib/rubbycop/cop/performance/detect.rb +107 -0
- data/lib/rubbycop/cop/performance/double_start_end_with.rb +102 -0
- data/lib/rubbycop/cop/performance/end_with.rb +55 -0
- data/lib/rubbycop/cop/performance/fixed_size.rb +56 -0
- data/lib/rubbycop/cop/performance/flat_map.rb +73 -0
- data/lib/rubbycop/cop/performance/hash_each_methods.rb +84 -0
- data/lib/rubbycop/cop/performance/lstrip_rstrip.rb +41 -0
- data/lib/rubbycop/cop/performance/range_include.rb +41 -0
- data/lib/rubbycop/cop/performance/redundant_block_call.rb +93 -0
- data/lib/rubbycop/cop/performance/redundant_match.rb +55 -0
- data/lib/rubbycop/cop/performance/redundant_merge.rb +149 -0
- data/lib/rubbycop/cop/performance/redundant_sort_by.rb +45 -0
- data/lib/rubbycop/cop/performance/regexp_match.rb +215 -0
- data/lib/rubbycop/cop/performance/reverse_each.rb +40 -0
- data/lib/rubbycop/cop/performance/sample.rb +140 -0
- data/lib/rubbycop/cop/performance/size.rb +71 -0
- data/lib/rubbycop/cop/performance/start_with.rb +58 -0
- data/lib/rubbycop/cop/performance/string_replacement.rb +170 -0
- data/lib/rubbycop/cop/performance/times_map.rb +61 -0
- data/lib/rubbycop/cop/rails/action_filter.rb +96 -0
- data/lib/rubbycop/cop/rails/active_support_aliases.rb +68 -0
- data/lib/rubbycop/cop/rails/application_job.rb +32 -0
- data/lib/rubbycop/cop/rails/application_record.rb +32 -0
- data/lib/rubbycop/cop/rails/blank.rb +138 -0
- data/lib/rubbycop/cop/rails/date.rb +127 -0
- data/lib/rubbycop/cop/rails/delegate.rb +106 -0
- data/lib/rubbycop/cop/rails/delegate_allow_blank.rb +51 -0
- data/lib/rubbycop/cop/rails/dynamic_find_by.rb +81 -0
- data/lib/rubbycop/cop/rails/enum_uniqueness.rb +43 -0
- data/lib/rubbycop/cop/rails/exit.rb +61 -0
- data/lib/rubbycop/cop/rails/file_path.rb +75 -0
- data/lib/rubbycop/cop/rails/find_by.rb +51 -0
- data/lib/rubbycop/cop/rails/find_each.rb +47 -0
- data/lib/rubbycop/cop/rails/has_and_belongs_to_many.rb +18 -0
- data/lib/rubbycop/cop/rails/http_positional_arguments.rb +106 -0
- data/lib/rubbycop/cop/rails/not_null_column.rb +67 -0
- data/lib/rubbycop/cop/rails/output.rb +23 -0
- data/lib/rubbycop/cop/rails/output_safety.rb +58 -0
- data/lib/rubbycop/cop/rails/pluralization_grammar.rb +106 -0
- data/lib/rubbycop/cop/rails/present.rb +143 -0
- data/lib/rubbycop/cop/rails/read_write_attribute.rb +65 -0
- data/lib/rubbycop/cop/rails/relative_date_constant.rb +88 -0
- data/lib/rubbycop/cop/rails/request_referer.rb +56 -0
- data/lib/rubbycop/cop/rails/reversible_migration.rb +216 -0
- data/lib/rubbycop/cop/rails/safe_navigation.rb +91 -0
- data/lib/rubbycop/cop/rails/save_bang.rb +160 -0
- data/lib/rubbycop/cop/rails/scope_args.rb +29 -0
- data/lib/rubbycop/cop/rails/skips_model_validations.rb +63 -0
- data/lib/rubbycop/cop/rails/time_zone.rb +197 -0
- data/lib/rubbycop/cop/rails/uniq_before_pluck.rb +93 -0
- data/lib/rubbycop/cop/rails/validation.rb +64 -0
- data/lib/rubbycop/cop/registry.rb +171 -0
- data/lib/rubbycop/cop/security/eval.rb +30 -0
- data/lib/rubbycop/cop/security/json_load.rb +44 -0
- data/lib/rubbycop/cop/security/marshal_load.rb +37 -0
- data/lib/rubbycop/cop/security/yaml_load.rb +37 -0
- data/lib/rubbycop/cop/severity.rb +76 -0
- data/lib/rubbycop/cop/style/accessor_method_name.rb +45 -0
- data/lib/rubbycop/cop/style/alias.rb +119 -0
- data/lib/rubbycop/cop/style/and_or.rb +125 -0
- data/lib/rubbycop/cop/style/array_join.rb +30 -0
- data/lib/rubbycop/cop/style/ascii_comments.rb +38 -0
- data/lib/rubbycop/cop/style/ascii_identifiers.rb +36 -0
- data/lib/rubbycop/cop/style/attr.rb +50 -0
- data/lib/rubbycop/cop/style/auto_resource_cleanup.rb +42 -0
- data/lib/rubbycop/cop/style/bare_percent_literals.rb +57 -0
- data/lib/rubbycop/cop/style/begin_block.rb +16 -0
- data/lib/rubbycop/cop/style/block_comments.rb +46 -0
- data/lib/rubbycop/cop/style/block_delimiters.rb +228 -0
- data/lib/rubbycop/cop/style/braces_around_hash_parameters.rb +138 -0
- data/lib/rubbycop/cop/style/case_equality.rb +18 -0
- data/lib/rubbycop/cop/style/character_literal.rb +43 -0
- data/lib/rubbycop/cop/style/class_and_module_camel_case.rb +29 -0
- data/lib/rubbycop/cop/style/class_and_module_children.rb +69 -0
- data/lib/rubbycop/cop/style/class_check.rb +40 -0
- data/lib/rubbycop/cop/style/class_methods.rb +67 -0
- data/lib/rubbycop/cop/style/class_vars.rb +23 -0
- data/lib/rubbycop/cop/style/collection_methods.rb +51 -0
- data/lib/rubbycop/cop/style/colon_method_call.rb +33 -0
- data/lib/rubbycop/cop/style/command_literal.rb +119 -0
- data/lib/rubbycop/cop/style/comment_annotation.rb +62 -0
- data/lib/rubbycop/cop/style/conditional_assignment.rb +691 -0
- data/lib/rubbycop/cop/style/constant_name.rb +29 -0
- data/lib/rubbycop/cop/style/copyright.rb +89 -0
- data/lib/rubbycop/cop/style/def_with_parentheses.rb +31 -0
- data/lib/rubbycop/cop/style/documentation.rb +79 -0
- data/lib/rubbycop/cop/style/documentation_method.rb +80 -0
- data/lib/rubbycop/cop/style/double_negation.rb +35 -0
- data/lib/rubbycop/cop/style/each_for_simple_loop.rb +57 -0
- data/lib/rubbycop/cop/style/each_with_object.rb +91 -0
- data/lib/rubbycop/cop/style/empty_case_condition.rb +84 -0
- data/lib/rubbycop/cop/style/empty_else.rb +138 -0
- data/lib/rubbycop/cop/style/empty_literal.rb +108 -0
- data/lib/rubbycop/cop/style/empty_method.rb +102 -0
- data/lib/rubbycop/cop/style/encoding.rb +92 -0
- data/lib/rubbycop/cop/style/end_block.rb +17 -0
- data/lib/rubbycop/cop/style/even_odd.rb +56 -0
- data/lib/rubbycop/cop/style/file_name.rb +183 -0
- data/lib/rubbycop/cop/style/flip_flop.rb +20 -0
- data/lib/rubbycop/cop/style/for.rb +50 -0
- data/lib/rubbycop/cop/style/format_string.rb +46 -0
- data/lib/rubbycop/cop/style/format_string_token.rb +141 -0
- data/lib/rubbycop/cop/style/frozen_string_literal_comment.rb +96 -0
- data/lib/rubbycop/cop/style/global_vars.rb +70 -0
- data/lib/rubbycop/cop/style/guard_clause.rb +90 -0
- data/lib/rubbycop/cop/style/hash_syntax.rb +214 -0
- data/lib/rubbycop/cop/style/identical_conditional_branches.rb +130 -0
- data/lib/rubbycop/cop/style/if_inside_else.rb +45 -0
- data/lib/rubbycop/cop/style/if_unless_modifier.rb +80 -0
- data/lib/rubbycop/cop/style/if_unless_modifier_of_if_unless.rb +38 -0
- data/lib/rubbycop/cop/style/if_with_semicolon.rb +20 -0
- data/lib/rubbycop/cop/style/implicit_runtime_error.rb +31 -0
- data/lib/rubbycop/cop/style/infinite_loop.rb +91 -0
- data/lib/rubbycop/cop/style/inline_comment.rb +32 -0
- data/lib/rubbycop/cop/style/inverse_methods.rb +130 -0
- data/lib/rubbycop/cop/style/lambda.rb +209 -0
- data/lib/rubbycop/cop/style/lambda_call.rb +66 -0
- data/lib/rubbycop/cop/style/line_end_concatenation.rb +115 -0
- data/lib/rubbycop/cop/style/method_call_with_args_parentheses.rb +107 -0
- data/lib/rubbycop/cop/style/method_call_without_args_parentheses.rb +75 -0
- data/lib/rubbycop/cop/style/method_called_on_do_end_block.rb +44 -0
- data/lib/rubbycop/cop/style/method_def_parentheses.rb +83 -0
- data/lib/rubbycop/cop/style/method_missing.rb +81 -0
- data/lib/rubbycop/cop/style/method_name.rb +28 -0
- data/lib/rubbycop/cop/style/missing_else.rb +100 -0
- data/lib/rubbycop/cop/style/mixin_grouping.rb +135 -0
- data/lib/rubbycop/cop/style/module_function.rb +64 -0
- data/lib/rubbycop/cop/style/multiline_block_chain.rb +42 -0
- data/lib/rubbycop/cop/style/multiline_if_modifier.rb +63 -0
- data/lib/rubbycop/cop/style/multiline_if_then.rb +47 -0
- data/lib/rubbycop/cop/style/multiline_memoization.rb +77 -0
- data/lib/rubbycop/cop/style/multiline_ternary_operator.rb +19 -0
- data/lib/rubbycop/cop/style/mutable_constant.rb +68 -0
- data/lib/rubbycop/cop/style/negated_if.rb +103 -0
- data/lib/rubbycop/cop/style/negated_while.rb +32 -0
- data/lib/rubbycop/cop/style/nested_modifier.rb +87 -0
- data/lib/rubbycop/cop/style/nested_parenthesized_calls.rb +61 -0
- data/lib/rubbycop/cop/style/nested_ternary_operator.rb +21 -0
- data/lib/rubbycop/cop/style/next.rb +225 -0
- data/lib/rubbycop/cop/style/nil_comparison.rb +35 -0
- data/lib/rubbycop/cop/style/non_nil_check.rb +121 -0
- data/lib/rubbycop/cop/style/not.rb +69 -0
- data/lib/rubbycop/cop/style/numeric_literal_prefix.rb +97 -0
- data/lib/rubbycop/cop/style/numeric_literals.rb +101 -0
- data/lib/rubbycop/cop/style/numeric_predicate.rb +140 -0
- data/lib/rubbycop/cop/style/one_line_conditional.rb +75 -0
- data/lib/rubbycop/cop/style/op_method.rb +41 -0
- data/lib/rubbycop/cop/style/option_hash.rb +58 -0
- data/lib/rubbycop/cop/style/optional_arguments.rb +62 -0
- data/lib/rubbycop/cop/style/parallel_assignment.rb +287 -0
- data/lib/rubbycop/cop/style/parentheses_around_condition.rb +56 -0
- data/lib/rubbycop/cop/style/percent_literal_delimiters.rb +100 -0
- data/lib/rubbycop/cop/style/percent_q_literals.rb +52 -0
- data/lib/rubbycop/cop/style/perl_backrefs.rb +31 -0
- data/lib/rubbycop/cop/style/predicate_name.rb +67 -0
- data/lib/rubbycop/cop/style/preferred_hash_methods.rb +78 -0
- data/lib/rubbycop/cop/style/proc.rb +26 -0
- data/lib/rubbycop/cop/style/raise_args.rb +140 -0
- data/lib/rubbycop/cop/style/redundant_begin.rb +47 -0
- data/lib/rubbycop/cop/style/redundant_exception.rb +55 -0
- data/lib/rubbycop/cop/style/redundant_freeze.rb +45 -0
- data/lib/rubbycop/cop/style/redundant_parentheses.rb +199 -0
- data/lib/rubbycop/cop/style/redundant_return.rb +121 -0
- data/lib/rubbycop/cop/style/redundant_self.rb +144 -0
- data/lib/rubbycop/cop/style/regexp_literal.rb +114 -0
- data/lib/rubbycop/cop/style/rescue_modifier.rb +37 -0
- data/lib/rubbycop/cop/style/safe_navigation.rb +145 -0
- data/lib/rubbycop/cop/style/self_assignment.rb +93 -0
- data/lib/rubbycop/cop/style/semicolon.rb +70 -0
- data/lib/rubbycop/cop/style/send.rb +21 -0
- data/lib/rubbycop/cop/style/signal_exception.rb +109 -0
- data/lib/rubbycop/cop/style/single_line_block_params.rb +68 -0
- data/lib/rubbycop/cop/style/single_line_methods.rb +77 -0
- data/lib/rubbycop/cop/style/special_global_vars.rb +156 -0
- data/lib/rubbycop/cop/style/stabby_lambda_parentheses.rb +113 -0
- data/lib/rubbycop/cop/style/string_literals.rb +102 -0
- data/lib/rubbycop/cop/style/string_literals_in_interpolation.rb +30 -0
- data/lib/rubbycop/cop/style/string_methods.rb +34 -0
- data/lib/rubbycop/cop/style/struct_inheritance.rb +32 -0
- data/lib/rubbycop/cop/style/symbol_array.rb +109 -0
- data/lib/rubbycop/cop/style/symbol_literal.rb +32 -0
- data/lib/rubbycop/cop/style/symbol_proc.rb +143 -0
- data/lib/rubbycop/cop/style/ternary_parentheses.rb +200 -0
- data/lib/rubbycop/cop/style/trailing_comma_in_arguments.rb +64 -0
- data/lib/rubbycop/cop/style/trailing_comma_in_literal.rb +56 -0
- data/lib/rubbycop/cop/style/trailing_underscore_variable.rb +113 -0
- data/lib/rubbycop/cop/style/trivial_accessors.rb +176 -0
- data/lib/rubbycop/cop/style/unless_else.rb +39 -0
- data/lib/rubbycop/cop/style/unneeded_capital_w.rb +41 -0
- data/lib/rubbycop/cop/style/unneeded_interpolation.rb +98 -0
- data/lib/rubbycop/cop/style/unneeded_percent_q.rb +96 -0
- data/lib/rubbycop/cop/style/variable_interpolation.rb +44 -0
- data/lib/rubbycop/cop/style/variable_name.rb +39 -0
- data/lib/rubbycop/cop/style/variable_number.rb +78 -0
- data/lib/rubbycop/cop/style/when_then.rb +24 -0
- data/lib/rubbycop/cop/style/while_until_do.rb +36 -0
- data/lib/rubbycop/cop/style/while_until_modifier.rb +41 -0
- data/lib/rubbycop/cop/style/word_array.rb +114 -0
- data/lib/rubbycop/cop/style/zero_length_predicate.rb +90 -0
- data/lib/rubbycop/cop/team.rb +193 -0
- data/lib/rubbycop/cop/util.rb +309 -0
- data/lib/rubbycop/cop/variable_force.rb +458 -0
- data/lib/rubbycop/cop/variable_force/assignment.rb +90 -0
- data/lib/rubbycop/cop/variable_force/branch.rb +318 -0
- data/lib/rubbycop/cop/variable_force/branchable.rb +21 -0
- data/lib/rubbycop/cop/variable_force/reference.rb +49 -0
- data/lib/rubbycop/cop/variable_force/scope.rb +107 -0
- data/lib/rubbycop/cop/variable_force/variable.rb +103 -0
- data/lib/rubbycop/cop/variable_force/variable_table.rb +128 -0
- data/lib/rubbycop/error.rb +11 -0
- data/lib/rubbycop/formatter/base_formatter.rb +123 -0
- data/lib/rubbycop/formatter/clang_style_formatter.rb +54 -0
- data/lib/rubbycop/formatter/colorizable.rb +41 -0
- data/lib/rubbycop/formatter/disabled_config_formatter.rb +181 -0
- data/lib/rubbycop/formatter/disabled_lines_formatter.rb +57 -0
- data/lib/rubbycop/formatter/emacs_style_formatter.rb +24 -0
- data/lib/rubbycop/formatter/file_list_formatter.rb +19 -0
- data/lib/rubbycop/formatter/formatter_set.rb +102 -0
- data/lib/rubbycop/formatter/fuubar_style_formatter.rb +80 -0
- data/lib/rubbycop/formatter/html_formatter.rb +134 -0
- data/lib/rubbycop/formatter/json_formatter.rb +74 -0
- data/lib/rubbycop/formatter/offense_count_formatter.rb +55 -0
- data/lib/rubbycop/formatter/progress_formatter.rb +63 -0
- data/lib/rubbycop/formatter/simple_text_formatter.rb +136 -0
- data/lib/rubbycop/formatter/text_util.rb +20 -0
- data/lib/rubbycop/formatter/worst_offenders_formatter.rb +60 -0
- data/lib/rubbycop/magic_comment.rb +210 -0
- data/lib/rubbycop/name_similarity.rb +21 -0
- data/lib/rubbycop/node_pattern.rb +543 -0
- data/lib/rubbycop/options.rb +355 -0
- data/lib/rubbycop/path_util.rb +36 -0
- data/lib/rubbycop/platform.rb +11 -0
- data/lib/rubbycop/processed_source.rb +151 -0
- data/lib/rubbycop/rake_task.rb +86 -0
- data/lib/rubbycop/remote_config.rb +78 -0
- data/lib/rubbycop/result_cache.rb +176 -0
- data/lib/rubbycop/rspec/cop_helper.rb +98 -0
- data/lib/rubbycop/rspec/host_environment_simulation_helper.rb +32 -0
- data/lib/rubbycop/rspec/shared_contexts.rb +98 -0
- data/lib/rubbycop/rspec/shared_examples.rb +92 -0
- data/lib/rubbycop/rspec/support.rb +8 -0
- data/lib/rubbycop/runner.rb +338 -0
- data/lib/rubbycop/string_interpreter.rb +57 -0
- data/lib/rubbycop/string_util.rb +156 -0
- data/lib/rubbycop/target_finder.rb +201 -0
- data/lib/rubbycop/token.rb +25 -0
- data/lib/rubbycop/version.rb +19 -0
- data/lib/rubbycop/warning.rb +11 -0
- metadata +663 -0
@@ -0,0 +1,20 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubbyCop
|
4
|
+
module Formatter
|
5
|
+
# Common logic for UI texts.
|
6
|
+
module TextUtil
|
7
|
+
module_function
|
8
|
+
|
9
|
+
def pluralize(number, thing, options = {})
|
10
|
+
if number.zero? && options[:no_for_zero]
|
11
|
+
"no #{thing}s"
|
12
|
+
elsif number == 1
|
13
|
+
"1 #{thing}"
|
14
|
+
else
|
15
|
+
"#{number} #{thing}s"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'pathname'
|
4
|
+
|
5
|
+
module RubbyCop
|
6
|
+
module Formatter
|
7
|
+
# This formatter displays the list of offensive files, sorted by number of
|
8
|
+
# offenses with the worst offenders first.
|
9
|
+
#
|
10
|
+
# Here's the format:
|
11
|
+
#
|
12
|
+
# 26 this/file/is/really/bad.rb
|
13
|
+
# 3 just/ok.rb
|
14
|
+
# --
|
15
|
+
# 29 Total
|
16
|
+
class WorstOffendersFormatter < BaseFormatter
|
17
|
+
attr_reader :offense_counts
|
18
|
+
|
19
|
+
def started(target_files)
|
20
|
+
super
|
21
|
+
@offense_counts = {}
|
22
|
+
end
|
23
|
+
|
24
|
+
def file_finished(file, offenses)
|
25
|
+
return if offenses.empty?
|
26
|
+
|
27
|
+
path = Pathname.new(file).relative_path_from(Pathname.new(Dir.pwd))
|
28
|
+
@offense_counts[path] = offenses.size
|
29
|
+
end
|
30
|
+
|
31
|
+
def finished(_inspected_files)
|
32
|
+
report_summary(@offense_counts)
|
33
|
+
end
|
34
|
+
|
35
|
+
def report_summary(offense_counts)
|
36
|
+
per_file_counts = ordered_offense_counts(offense_counts)
|
37
|
+
total_count = total_offense_count(offense_counts)
|
38
|
+
|
39
|
+
output.puts
|
40
|
+
|
41
|
+
per_file_counts.each do |file_name, count|
|
42
|
+
output.puts "#{count.to_s.ljust(total_count.to_s.length + 2)}" \
|
43
|
+
"#{file_name}\n"
|
44
|
+
end
|
45
|
+
output.puts '--'
|
46
|
+
output.puts "#{total_count} Total"
|
47
|
+
|
48
|
+
output.puts
|
49
|
+
end
|
50
|
+
|
51
|
+
def ordered_offense_counts(offense_counts)
|
52
|
+
Hash[offense_counts.sort_by { |k, v| [-v, k] }]
|
53
|
+
end
|
54
|
+
|
55
|
+
def total_offense_count(offense_counts)
|
56
|
+
offense_counts.values.inject(0, :+)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,210 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubbyCop
|
4
|
+
# Parse different formats of magic comments.
|
5
|
+
#
|
6
|
+
# @abstract parent of three different magic comment handlers
|
7
|
+
class MagicComment
|
8
|
+
# @see https://git.io/vMC1C IRB's pattern for matching magic comment tokens
|
9
|
+
TOKEN = /[[:alnum:]\-_]+/
|
10
|
+
|
11
|
+
# Detect magic comment format and pass it to the appropriate wrapper.
|
12
|
+
#
|
13
|
+
# @param comment [String]
|
14
|
+
#
|
15
|
+
# @return [RubbyCop::MagicComment]
|
16
|
+
def self.parse(comment)
|
17
|
+
case comment
|
18
|
+
when EmacsComment::FORMAT then EmacsComment.new(comment)
|
19
|
+
when VimComment::FORMAT then VimComment.new(comment)
|
20
|
+
else
|
21
|
+
SimpleComment.new(comment)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def initialize(comment)
|
26
|
+
@comment = comment
|
27
|
+
end
|
28
|
+
|
29
|
+
def any?
|
30
|
+
frozen_string_literal_specified? || encoding_specified?
|
31
|
+
end
|
32
|
+
|
33
|
+
# Does the magic comment enable the frozen string literal feature.
|
34
|
+
#
|
35
|
+
# Test whether the frozen string literal value is `true`. Cannot
|
36
|
+
# just return `frozen_string_literal` since an invalid magic comment
|
37
|
+
# like `# frozen_string_literal: yes` is possible and the truthy value
|
38
|
+
# `'yes'` does not actually enable the feature
|
39
|
+
#
|
40
|
+
# @return [Boolean]
|
41
|
+
def frozen_string_literal?
|
42
|
+
frozen_string_literal == true
|
43
|
+
end
|
44
|
+
|
45
|
+
# Was a magic comment for the frozen string literal found?
|
46
|
+
#
|
47
|
+
# @return [Boolean]
|
48
|
+
def frozen_string_literal_specified?
|
49
|
+
specified?(frozen_string_literal)
|
50
|
+
end
|
51
|
+
|
52
|
+
# Expose the `frozen_string_literal` value coerced to a boolean if possible.
|
53
|
+
#
|
54
|
+
# @return [Boolean] if value is `true` or `false`
|
55
|
+
# @return [nil] if frozen_string_literal comment isn't found
|
56
|
+
# @return [String] if comment is found but isn't true or false
|
57
|
+
def frozen_string_literal
|
58
|
+
return unless (setting = extract_frozen_string_literal)
|
59
|
+
|
60
|
+
case setting
|
61
|
+
when 'true' then true
|
62
|
+
when 'false' then false
|
63
|
+
else
|
64
|
+
setting
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def encoding_specified?
|
69
|
+
specified?(encoding)
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def specified?(value)
|
75
|
+
!value.nil?
|
76
|
+
end
|
77
|
+
|
78
|
+
# Match the entire comment string with a pattern and take the first capture.
|
79
|
+
#
|
80
|
+
# @param pattern [Regexp]
|
81
|
+
#
|
82
|
+
# @return [String] if pattern matched
|
83
|
+
# @return [nil] otherwise
|
84
|
+
def extract(pattern)
|
85
|
+
@comment[pattern, 1]
|
86
|
+
end
|
87
|
+
|
88
|
+
# Parent to Vim and Emacs magic comment handling.
|
89
|
+
#
|
90
|
+
# @abstract
|
91
|
+
class EditorComment < MagicComment
|
92
|
+
private
|
93
|
+
|
94
|
+
# Find a token starting with the provided keyword and extract its value.
|
95
|
+
#
|
96
|
+
# @param keyword [String]
|
97
|
+
#
|
98
|
+
# @return [String] extracted value if it is found
|
99
|
+
# @return [nil] otherwise
|
100
|
+
def match(keyword)
|
101
|
+
pattern = /\A#{keyword}\s*#{self.class::OPERATOR}\s*(#{TOKEN})\z/
|
102
|
+
|
103
|
+
tokens.each do |token|
|
104
|
+
next unless (value = token[pattern, 1])
|
105
|
+
|
106
|
+
return value.downcase
|
107
|
+
end
|
108
|
+
|
109
|
+
nil
|
110
|
+
end
|
111
|
+
|
112
|
+
# Individual tokens composing an editor specific comment string.
|
113
|
+
#
|
114
|
+
# @return [Array<String>]
|
115
|
+
def tokens
|
116
|
+
extract(self.class::FORMAT).split(self.class::SEPARATOR).map(&:strip)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# Wrapper for Emacs style magic comments.
|
121
|
+
#
|
122
|
+
# @example Emacs style comment
|
123
|
+
# comment = RubbyCop::MagicComment.parse(
|
124
|
+
# '# -*- encoding: ASCII-8BIT -*-'
|
125
|
+
# )
|
126
|
+
#
|
127
|
+
# comment.encoding # => 'ascii-8bit'
|
128
|
+
#
|
129
|
+
# @see https://www.gnu.org/software/emacs/manual/html_node/emacs/Specify-Coding.html
|
130
|
+
# @see https://git.io/vMCXh Emacs handling in Ruby's parse.y
|
131
|
+
class EmacsComment < EditorComment
|
132
|
+
FORMAT = /\-\*\-(.+)\-\*\-/
|
133
|
+
SEPARATOR = ';'.freeze
|
134
|
+
OPERATOR = ':'.freeze
|
135
|
+
|
136
|
+
def encoding
|
137
|
+
match('encoding')
|
138
|
+
end
|
139
|
+
|
140
|
+
private
|
141
|
+
|
142
|
+
def extract_frozen_string_literal
|
143
|
+
match('frozen_string_literal')
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
# Wrapper for Vim style magic comments.
|
148
|
+
#
|
149
|
+
# @example Vim style comment
|
150
|
+
# comment = RubbyCop::MagicComment.parse(
|
151
|
+
# '# vim: filetype=ruby, fileencoding=ascii-8bit'
|
152
|
+
# )
|
153
|
+
#
|
154
|
+
# comment.encoding # => 'ascii-8bit'
|
155
|
+
class VimComment < EditorComment
|
156
|
+
FORMAT = /#\s*vim:\s*(.+)/
|
157
|
+
SEPARATOR = ', '.freeze
|
158
|
+
OPERATOR = '='.freeze
|
159
|
+
|
160
|
+
# For some reason the fileencoding keyword only works if there
|
161
|
+
# is at least one other token included in the string. For example
|
162
|
+
#
|
163
|
+
# # works
|
164
|
+
# # vim: foo=bar, fileencoding=ascii-8bit
|
165
|
+
#
|
166
|
+
# # does nothing
|
167
|
+
# # vim: foo=bar, fileencoding=ascii-8bit
|
168
|
+
#
|
169
|
+
def encoding
|
170
|
+
match('fileencoding') if tokens.size > 1
|
171
|
+
end
|
172
|
+
|
173
|
+
# Vim comments cannot specify frozen string literal behavior.
|
174
|
+
def frozen_string_literal; end
|
175
|
+
end
|
176
|
+
|
177
|
+
# Wrapper for regular magic comments not bound to an editor.
|
178
|
+
#
|
179
|
+
# Simple comments can only specify one setting per comment.
|
180
|
+
#
|
181
|
+
# @example frozen string literal comments
|
182
|
+
# comment1 = RubbyCop::MagicComment.parse('# frozen_string_literal: true')
|
183
|
+
# comment1.frozen_string_literal # => true
|
184
|
+
# comment1.encoding # => nil
|
185
|
+
#
|
186
|
+
# @example encoding comments
|
187
|
+
# comment2 = RubbyCop::MagicComment.parse('# encoding: utf-8')
|
188
|
+
# comment2.frozen_string_literal # => nil
|
189
|
+
# comment2.encoding # => 'utf-8'
|
190
|
+
class SimpleComment < MagicComment
|
191
|
+
# Match `encoding` or `coding`
|
192
|
+
def encoding
|
193
|
+
extract(/\b(?:en)?coding: (#{TOKEN})/i)
|
194
|
+
end
|
195
|
+
|
196
|
+
private
|
197
|
+
|
198
|
+
# Extract `frozen_string_literal`.
|
199
|
+
#
|
200
|
+
# The `frozen_string_literal` magic comment only works if it
|
201
|
+
# is the only text in the comment.
|
202
|
+
#
|
203
|
+
# Case-insensitive and dashes/underscores are acceptable.
|
204
|
+
# @see https://git.io/vM7Mg
|
205
|
+
def extract_frozen_string_literal
|
206
|
+
extract(/\A#\s*frozen[_-]string[_-]literal:\s*(#{TOKEN})\s*\z/i)
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
210
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RubbyCop
|
4
|
+
# Common functionality for finding names that are similar to a given name.
|
5
|
+
module NameSimilarity
|
6
|
+
MINIMUM_SIMILARITY_TO_SUGGEST = 0.9
|
7
|
+
|
8
|
+
def find_similar_name(target_name, scope)
|
9
|
+
names = collect_variable_like_names(scope)
|
10
|
+
names.delete(target_name)
|
11
|
+
|
12
|
+
scores = names.each_with_object({}) do |name, hash|
|
13
|
+
score = StringUtil.similarity(target_name, name)
|
14
|
+
hash[name] = score if score >= MINIMUM_SIMILARITY_TO_SUGGEST
|
15
|
+
end
|
16
|
+
|
17
|
+
most_similar_name, _max_score = scores.max_by { |_, score| score }
|
18
|
+
most_similar_name
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,543 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# rubbycop:disable Metrics/ClassLength
|
4
|
+
# rubbycop:disable Metrics/CyclomaticComplexity
|
5
|
+
|
6
|
+
module RubbyCop
|
7
|
+
# This class performs a pattern-matching operation on an AST node.
|
8
|
+
#
|
9
|
+
# Initialize a new `NodePattern` with `NodePattern.new(pattern_string)`, then
|
10
|
+
# pass an AST node to `NodePattern#match`. Alternatively, use one of the class
|
11
|
+
# macros in `NodePattern::Macros` to define your own pattern-matching method.
|
12
|
+
#
|
13
|
+
# If the match fails, `nil` will be returned. If the match succeeds, the
|
14
|
+
# return value depends on whether a block was provided to `#match`, and
|
15
|
+
# whether the pattern contained any "captures" (values which are extracted
|
16
|
+
# from a matching AST.)
|
17
|
+
#
|
18
|
+
# - With block: #match yields the captures (if any) and passes the return
|
19
|
+
# value of the block through.
|
20
|
+
# - With no block, but one capture: the capture is returned.
|
21
|
+
# - With no block, but multiple captures: captures are returned as an array.
|
22
|
+
# - With no block and no captures: #match returns `true`.
|
23
|
+
#
|
24
|
+
# ## Pattern string format examples
|
25
|
+
#
|
26
|
+
# ':sym' # matches a literal symbol
|
27
|
+
# '1' # matches a literal integer
|
28
|
+
# 'nil' # matches a literal nil
|
29
|
+
# 'send' # matches (send ...)
|
30
|
+
# '(send)' # matches (send)
|
31
|
+
# '(send ...)' # matches (send ...)
|
32
|
+
# '(op-asgn)' # node types with hyphenated names also work
|
33
|
+
# '{send class}' # matches (send ...) or (class ...)
|
34
|
+
# '({send class})' # matches (send) or (class)
|
35
|
+
# '(send const)' # matches (send (const ...))
|
36
|
+
# '(send _ :new)' # matches (send <anything> :new)
|
37
|
+
# '(send $_ :new)' # as above, but whatever matches the $_ is captured
|
38
|
+
# '(send $_ $_)' # you can use as many captures as you want
|
39
|
+
# '(send !const ...)' # ! negates the next part of the pattern
|
40
|
+
# '$(send const ...)' # arbitrary matching can be performed on a capture
|
41
|
+
# '(send _recv _msg)' # wildcards can be named (for readability)
|
42
|
+
# '(send ... :new)' # you can specifically match against the last child
|
43
|
+
# # (this only works for the very last)
|
44
|
+
# '(send $...)' # capture all the children as an array
|
45
|
+
# '(send $... int)' # capture all children but the last as an array
|
46
|
+
# '(send _x :+ _x)' # unification is performed on named wildcards
|
47
|
+
# # (like Prolog variables...)
|
48
|
+
# # (#== is used to see if values unify)
|
49
|
+
# '(int odd?)' # words which end with a ? are predicate methods,
|
50
|
+
# # are are called on the target to see if it matches
|
51
|
+
# # any Ruby method which the matched object supports
|
52
|
+
# # can be used
|
53
|
+
# # if a truthy value is returned, the match succeeds
|
54
|
+
# '(int [!1 !2])' # [] contains multiple patterns, ALL of which must
|
55
|
+
# # match in that position
|
56
|
+
# # in other words, while {} is pattern union (logical
|
57
|
+
# # OR), [] is intersection (logical AND)
|
58
|
+
# '(send %1 _)' # % stands for a parameter which must be supplied to
|
59
|
+
# # #match at matching time
|
60
|
+
# # it will be compared to the corresponding value in
|
61
|
+
# # the AST using #==
|
62
|
+
# # a bare '%' is the same as '%1'
|
63
|
+
# # the number of extra parameters passed to #match
|
64
|
+
# # must equal the highest % value in the pattern
|
65
|
+
# # for consistency, %0 is the 'root node' which is
|
66
|
+
# # passed as the 1st argument to #match, where the
|
67
|
+
# # matching process starts
|
68
|
+
# '^^send' # each ^ ascends one level in the AST
|
69
|
+
# # so this matches against the grandparent node
|
70
|
+
# '#method' # we call this a 'funcall'; it calls a method in the
|
71
|
+
# # context where a pattern-matching method is defined
|
72
|
+
# # if that returns a truthy value, the match succeeds
|
73
|
+
# 'equal?(%1)' # predicates can be given 1 or more extra args
|
74
|
+
# '#method(%0, 1)' # funcalls can also be given 1 or more extra args
|
75
|
+
#
|
76
|
+
# You can nest arbitrarily deep:
|
77
|
+
#
|
78
|
+
# # matches node parsed from 'Const = Class.new' or 'Const = Module.new':
|
79
|
+
# '(casgn nil const (send (const nil {:Class :Module}) :new)))'
|
80
|
+
# # matches a node parsed from an 'if', with a '==' comparison,
|
81
|
+
# # and no 'else' branch:
|
82
|
+
# '(if (send _ :== _) _ nil)'
|
83
|
+
#
|
84
|
+
# Note that patterns like 'send' are implemented by calling `#send_type?` on
|
85
|
+
# the node being matched, 'const' by `#const_type?`, 'int' by `#int_type?`,
|
86
|
+
# and so on. Therefore, if you add methods which are named like
|
87
|
+
# `#prefix_type?` to the AST node class, then 'prefix' will become usable as
|
88
|
+
# a pattern.
|
89
|
+
#
|
90
|
+
# Also note that if you need a "guard clause" to protect against possible nils
|
91
|
+
# in a certain place in the AST, you can do it like this: `[!nil <pattern>]`
|
92
|
+
#
|
93
|
+
# The compiler code is very simple; don't be afraid to read through it!
|
94
|
+
class NodePattern
|
95
|
+
# @private
|
96
|
+
Invalid = Class.new(StandardError)
|
97
|
+
|
98
|
+
# @private
|
99
|
+
# Builds Ruby code which implements a pattern
|
100
|
+
class Compiler
|
101
|
+
RSYM = %r{:(?:[\w+@*/?!<>=~|%^-]+|\[\]=?)}
|
102
|
+
ID_CHAR = /[a-zA-Z_-]/
|
103
|
+
META = /\(|\)|\{|\}|\[|\]|\$\.\.\.|\$|!|\^|\.\.\./
|
104
|
+
NUMBER = /-?\d+(?:\.\d+)?/
|
105
|
+
TOKEN =
|
106
|
+
/\G(?:[\s,]+|#{META}|%\d*|#{NUMBER}|\#?#{ID_CHAR}+[\!\?]?\(?|#{RSYM}|.)/
|
107
|
+
|
108
|
+
NODE = /\A#{ID_CHAR}+\Z/
|
109
|
+
PREDICATE = /\A#{ID_CHAR}+\?\(?\Z/
|
110
|
+
WILDCARD = /\A_#{ID_CHAR}*\Z/
|
111
|
+
FUNCALL = /\A\##{ID_CHAR}+[\!\?]?\(?\Z/
|
112
|
+
LITERAL = /\A(?:#{RSYM}|#{NUMBER}|nil)\Z/
|
113
|
+
PARAM = /\A%\d*\Z/
|
114
|
+
CLOSING = /\A(?:\)|\}|\])\Z/
|
115
|
+
|
116
|
+
attr_reader :match_code
|
117
|
+
|
118
|
+
def initialize(str, node_var = 'node0')
|
119
|
+
@string = str
|
120
|
+
@root = node_var
|
121
|
+
|
122
|
+
@temps = 0 # avoid name clashes between temp variables
|
123
|
+
@captures = 0 # number of captures seen
|
124
|
+
@unify = {} # named wildcard -> temp variable number
|
125
|
+
@params = 0 # highest % (param) number seen
|
126
|
+
|
127
|
+
run(node_var)
|
128
|
+
end
|
129
|
+
|
130
|
+
def run(node_var)
|
131
|
+
tokens = @string.scan(TOKEN)
|
132
|
+
tokens.reject! { |token| token =~ /\A[\s,]+\Z/ } # drop whitespace
|
133
|
+
@match_code = compile_expr(tokens, node_var, false)
|
134
|
+
fail_due_to('unbalanced pattern') unless tokens.empty?
|
135
|
+
end
|
136
|
+
|
137
|
+
# rubbycop:disable Metrics/MethodLength, Metrics/AbcSize
|
138
|
+
def compile_expr(tokens, cur_node, seq_head)
|
139
|
+
# read a single pattern-matching expression from the token stream,
|
140
|
+
# return Ruby code which performs the corresponding matching operation
|
141
|
+
# on 'cur_node' (which is Ruby code which evaluates to an AST node)
|
142
|
+
#
|
143
|
+
# the 'pattern-matching' expression may be a composite which
|
144
|
+
# contains an arbitrary number of sub-expressions
|
145
|
+
token = tokens.shift
|
146
|
+
case token
|
147
|
+
when '(' then compile_seq(tokens, cur_node, seq_head)
|
148
|
+
when '{' then compile_union(tokens, cur_node, seq_head)
|
149
|
+
when '[' then compile_intersect(tokens, cur_node, seq_head)
|
150
|
+
when '!' then compile_negation(tokens, cur_node, seq_head)
|
151
|
+
when '$' then compile_capture(tokens, cur_node, seq_head)
|
152
|
+
when '^' then compile_ascend(tokens, cur_node, seq_head)
|
153
|
+
when WILDCARD then compile_wildcard(cur_node, token[1..-1], seq_head)
|
154
|
+
when FUNCALL then compile_funcall(tokens, cur_node, token, seq_head)
|
155
|
+
when LITERAL then compile_literal(cur_node, token, seq_head)
|
156
|
+
when PREDICATE then compile_predicate(tokens, cur_node, token, seq_head)
|
157
|
+
when NODE then compile_nodetype(cur_node, token)
|
158
|
+
when PARAM then compile_param(cur_node, token[1..-1], seq_head)
|
159
|
+
when CLOSING then fail_due_to("#{token} in invalid position")
|
160
|
+
when nil then fail_due_to('pattern ended prematurely')
|
161
|
+
else fail_due_to("invalid token #{token.inspect}")
|
162
|
+
end
|
163
|
+
end
|
164
|
+
# rubbycop:enable Metrics/MethodLength, Metrics/AbcSize
|
165
|
+
|
166
|
+
def compile_seq(tokens, cur_node, seq_head)
|
167
|
+
fail_due_to('empty parentheses') if tokens.first == ')'
|
168
|
+
fail_due_to('parentheses at sequence head') if seq_head
|
169
|
+
|
170
|
+
# 'cur_node' is a Ruby expression which evaluates to an AST node,
|
171
|
+
# but we don't know how expensive it is
|
172
|
+
# to be safe, cache the node in a temp variable and then use the
|
173
|
+
# temp variable as 'cur_node'
|
174
|
+
with_temp_node(cur_node) do |init, temp_node|
|
175
|
+
terms = compile_seq_terms(tokens, temp_node)
|
176
|
+
|
177
|
+
join_terms(init, terms, ' && ')
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
def compile_seq_terms(tokens, cur_node)
|
182
|
+
ret, size =
|
183
|
+
compile_seq_terms_with_size(tokens, cur_node) do |token, terms, index|
|
184
|
+
case token
|
185
|
+
when '...'.freeze
|
186
|
+
return compile_ellipsis(tokens, cur_node, terms, index)
|
187
|
+
when '$...'.freeze
|
188
|
+
return compile_capt_ellip(tokens, cur_node, terms, index)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
|
192
|
+
ret << "(#{cur_node}.children.size == #{size})"
|
193
|
+
end
|
194
|
+
|
195
|
+
def compile_seq_terms_with_size(tokens, cur_node)
|
196
|
+
index = nil
|
197
|
+
terms = []
|
198
|
+
until tokens.first == ')'
|
199
|
+
yield tokens.first, terms, index || 0
|
200
|
+
term, index = compile_expr_with_index(tokens, cur_node, index)
|
201
|
+
terms << term
|
202
|
+
end
|
203
|
+
|
204
|
+
tokens.shift # drop concluding )
|
205
|
+
[terms, index]
|
206
|
+
end
|
207
|
+
|
208
|
+
def compile_expr_with_index(tokens, cur_node, index)
|
209
|
+
if index.nil?
|
210
|
+
# in 'sequence head' position; some expressions are compiled
|
211
|
+
# differently at 'sequence head' (notably 'node type' expressions)
|
212
|
+
# grep for seq_head to see where it makes a difference
|
213
|
+
[compile_expr(tokens, cur_node, true), 0]
|
214
|
+
else
|
215
|
+
child_node = "#{cur_node}.children[#{index}]"
|
216
|
+
[compile_expr(tokens, child_node, false), index + 1]
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def compile_ellipsis(tokens, cur_node, terms, index)
|
221
|
+
if (term = compile_seq_tail(tokens, "#{cur_node}.children.last"))
|
222
|
+
terms << "(#{cur_node}.children.size > #{index})"
|
223
|
+
terms << term
|
224
|
+
elsif index > 0
|
225
|
+
terms << "(#{cur_node}.children.size >= #{index})"
|
226
|
+
end
|
227
|
+
terms
|
228
|
+
end
|
229
|
+
|
230
|
+
def compile_capt_ellip(tokens, cur_node, terms, index)
|
231
|
+
capture = next_capture
|
232
|
+
if (term = compile_seq_tail(tokens, "#{cur_node}.children.last"))
|
233
|
+
terms << "(#{cur_node}.children.size > #{index})"
|
234
|
+
terms << term
|
235
|
+
terms << "(#{capture} = #{cur_node}.children[#{index}..-2])"
|
236
|
+
else
|
237
|
+
terms << "(#{cur_node}.children.size >= #{index})" if index > 0
|
238
|
+
terms << "(#{capture} = #{cur_node}.children[#{index}..-1])"
|
239
|
+
end
|
240
|
+
terms
|
241
|
+
end
|
242
|
+
|
243
|
+
def compile_seq_tail(tokens, cur_node)
|
244
|
+
tokens.shift
|
245
|
+
if tokens.first == ')'
|
246
|
+
tokens.shift
|
247
|
+
nil
|
248
|
+
else
|
249
|
+
expr = compile_expr(tokens, cur_node, false)
|
250
|
+
fail_due_to('missing )') unless tokens.shift == ')'
|
251
|
+
expr
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
def compile_union(tokens, cur_node, seq_head)
|
256
|
+
fail_due_to('empty union') if tokens.first == '}'
|
257
|
+
|
258
|
+
with_temp_node(cur_node) do |init, temp_node|
|
259
|
+
terms = union_terms(tokens, temp_node, seq_head)
|
260
|
+
join_terms(init, terms, ' || ')
|
261
|
+
end
|
262
|
+
end
|
263
|
+
|
264
|
+
def union_terms(tokens, temp_node, seq_head)
|
265
|
+
# we need to ensure that each branch of the {} contains the same
|
266
|
+
# number of captures (since only one branch of the {} can actually
|
267
|
+
# match, the same variables are used to hold the captures for each
|
268
|
+
# branch)
|
269
|
+
compile_expr_with_captures(tokens,
|
270
|
+
temp_node, seq_head) do |term, before, after|
|
271
|
+
terms = [term]
|
272
|
+
until tokens.first == '}'
|
273
|
+
terms << compile_expr_with_capture_check(tokens, temp_node,
|
274
|
+
seq_head, before, after)
|
275
|
+
end
|
276
|
+
tokens.shift
|
277
|
+
|
278
|
+
terms
|
279
|
+
end
|
280
|
+
end
|
281
|
+
|
282
|
+
def compile_expr_with_captures(tokens, temp_node, seq_head)
|
283
|
+
captures_before = @captures
|
284
|
+
expr = compile_expr(tokens, temp_node, seq_head)
|
285
|
+
|
286
|
+
yield expr, captures_before, @captures
|
287
|
+
end
|
288
|
+
|
289
|
+
def compile_expr_with_capture_check(tokens, temp_node, seq_head, before,
|
290
|
+
after)
|
291
|
+
@captures = before
|
292
|
+
expr = compile_expr(tokens, temp_node, seq_head)
|
293
|
+
if @captures != after
|
294
|
+
fail_due_to('each branch of {} must have same # of captures')
|
295
|
+
end
|
296
|
+
|
297
|
+
expr
|
298
|
+
end
|
299
|
+
|
300
|
+
def compile_intersect(tokens, cur_node, seq_head)
|
301
|
+
fail_due_to('empty intersection') if tokens.first == ']'
|
302
|
+
|
303
|
+
with_temp_node(cur_node) do |init, temp_node|
|
304
|
+
terms = []
|
305
|
+
until tokens.first == ']'
|
306
|
+
terms << compile_expr(tokens, temp_node, seq_head)
|
307
|
+
end
|
308
|
+
tokens.shift
|
309
|
+
|
310
|
+
join_terms(init, terms, ' && ')
|
311
|
+
end
|
312
|
+
end
|
313
|
+
|
314
|
+
def compile_capture(tokens, cur_node, seq_head)
|
315
|
+
"(#{next_capture} = #{cur_node}#{'.type' if seq_head}; " \
|
316
|
+
"#{compile_expr(tokens, cur_node, seq_head)})"
|
317
|
+
end
|
318
|
+
|
319
|
+
def compile_negation(tokens, cur_node, seq_head)
|
320
|
+
"(!#{compile_expr(tokens, cur_node, seq_head)})"
|
321
|
+
end
|
322
|
+
|
323
|
+
def compile_ascend(tokens, cur_node, seq_head)
|
324
|
+
"(#{cur_node}.parent && " \
|
325
|
+
"#{compile_expr(tokens, "#{cur_node}.parent", seq_head)})"
|
326
|
+
end
|
327
|
+
|
328
|
+
def compile_wildcard(cur_node, name, seq_head)
|
329
|
+
if name.empty?
|
330
|
+
'true'
|
331
|
+
elsif @unify.key?(name)
|
332
|
+
# we have already seen a wildcard with this name before
|
333
|
+
# so the value it matched the first time will already be stored
|
334
|
+
# in a temp. check if this value matches the one stored in the temp
|
335
|
+
"(#{cur_node}#{'.type' if seq_head} == temp#{@unify[name]})"
|
336
|
+
else
|
337
|
+
n = @unify[name] = next_temp_value
|
338
|
+
# double assign to temp#{n} to avoid "assigned but unused variable"
|
339
|
+
"(temp#{n} = temp#{n} = #{cur_node}#{'.type' if seq_head}; true)"
|
340
|
+
end
|
341
|
+
end
|
342
|
+
|
343
|
+
def compile_literal(cur_node, literal, seq_head)
|
344
|
+
"(#{cur_node}#{'.type' if seq_head} == #{literal})"
|
345
|
+
end
|
346
|
+
|
347
|
+
def compile_predicate(tokens, cur_node, predicate, seq_head)
|
348
|
+
if predicate.end_with?('(') # is there an arglist?
|
349
|
+
args = compile_args(tokens)
|
350
|
+
predicate = predicate[0..-2] # drop the trailing (
|
351
|
+
"(#{cur_node}#{'.type' if seq_head}.#{predicate}(#{args.join(',')}))"
|
352
|
+
else
|
353
|
+
"(#{cur_node}#{'.type' if seq_head}.#{predicate})"
|
354
|
+
end
|
355
|
+
end
|
356
|
+
|
357
|
+
def compile_funcall(tokens, cur_node, method, seq_head)
|
358
|
+
# call a method in the context which this pattern-matching
|
359
|
+
# code is used in. pass target value as an argument
|
360
|
+
method = method[1..-1] # drop the leading #
|
361
|
+
if method.end_with?('(') # is there an arglist?
|
362
|
+
args = compile_args(tokens)
|
363
|
+
method = method[0..-2] # drop the trailing (
|
364
|
+
"(#{method}(#{cur_node}#{'.type' if seq_head}),#{args.join(',')})"
|
365
|
+
else
|
366
|
+
"(#{method}(#{cur_node}#{'.type' if seq_head}))"
|
367
|
+
end
|
368
|
+
end
|
369
|
+
|
370
|
+
def compile_nodetype(cur_node, type)
|
371
|
+
"(#{cur_node} && #{cur_node}.#{type.tr('-', '_')}_type?)"
|
372
|
+
end
|
373
|
+
|
374
|
+
def compile_param(cur_node, number, seq_head)
|
375
|
+
"(#{cur_node}#{'.type' if seq_head} == #{get_param(number)})"
|
376
|
+
end
|
377
|
+
|
378
|
+
def compile_args(tokens)
|
379
|
+
args = []
|
380
|
+
args << compile_arg(tokens.shift) until tokens.first == ')'
|
381
|
+
tokens.shift # drop the )
|
382
|
+
args
|
383
|
+
end
|
384
|
+
|
385
|
+
def compile_arg(token)
|
386
|
+
case token
|
387
|
+
when WILDCARD then
|
388
|
+
name = token[1..-1]
|
389
|
+
number = @unify[name] || fail_due_to('invalid in arglist: ' + token)
|
390
|
+
"temp#{number}"
|
391
|
+
when LITERAL then token
|
392
|
+
when PARAM then get_param(token[1..-1])
|
393
|
+
when CLOSING then fail_due_to("#{token} in invalid position")
|
394
|
+
when nil then fail_due_to('pattern ended prematurely')
|
395
|
+
else fail_due_to("invalid token in arglist: #{token.inspect}")
|
396
|
+
end
|
397
|
+
end
|
398
|
+
|
399
|
+
def next_capture
|
400
|
+
"capture#{@captures += 1}"
|
401
|
+
end
|
402
|
+
|
403
|
+
def get_param(number)
|
404
|
+
number = number.empty? ? 1 : Integer(number)
|
405
|
+
@params = number if number > @params
|
406
|
+
number.zero? ? @root : "param#{number}"
|
407
|
+
end
|
408
|
+
|
409
|
+
def join_terms(init, terms, operator)
|
410
|
+
"(#{init};#{terms.join(operator)})"
|
411
|
+
end
|
412
|
+
|
413
|
+
def emit_capture_list
|
414
|
+
(1..@captures).map { |n| "capture#{n}" }.join(',')
|
415
|
+
end
|
416
|
+
|
417
|
+
def emit_retval
|
418
|
+
if @captures.zero?
|
419
|
+
'true'
|
420
|
+
elsif @captures == 1
|
421
|
+
'capture1'
|
422
|
+
else
|
423
|
+
"[#{emit_capture_list}]"
|
424
|
+
end
|
425
|
+
end
|
426
|
+
|
427
|
+
def emit_param_list
|
428
|
+
(1..@params).map { |n| "param#{n}" }.join(',')
|
429
|
+
end
|
430
|
+
|
431
|
+
def emit_trailing_params
|
432
|
+
params = emit_param_list
|
433
|
+
params.empty? ? '' : ",#{params}"
|
434
|
+
end
|
435
|
+
|
436
|
+
def emit_method_code
|
437
|
+
<<-CODE
|
438
|
+
return nil unless #{@match_code}
|
439
|
+
block_given? ? yield(#{emit_capture_list}) : (return #{emit_retval})
|
440
|
+
CODE
|
441
|
+
end
|
442
|
+
|
443
|
+
def fail_due_to(message)
|
444
|
+
raise Invalid, "Couldn't compile due to #{message}. Pattern: #{@string}"
|
445
|
+
end
|
446
|
+
|
447
|
+
def with_temp_node(cur_node)
|
448
|
+
with_temp_variable do |temp_var|
|
449
|
+
# double assign to temp#{n} to avoid "assigned but unused variable"
|
450
|
+
yield "#{temp_var} = #{temp_var} = #{cur_node}", temp_var
|
451
|
+
end
|
452
|
+
end
|
453
|
+
|
454
|
+
def with_temp_variable
|
455
|
+
yield "temp#{next_temp_value}"
|
456
|
+
end
|
457
|
+
|
458
|
+
def next_temp_value
|
459
|
+
@temps += 1
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
# Helpers for defining methods based on a pattern string
|
464
|
+
module Macros
|
465
|
+
# Define a method which applies a pattern to an AST node
|
466
|
+
#
|
467
|
+
# The new method will return nil if the node does not match
|
468
|
+
# If the node matches, and a block is provided, the new method will
|
469
|
+
# yield to the block (passing any captures as block arguments).
|
470
|
+
# If the node matches, and no block is provided, the new method will
|
471
|
+
# return the captures, or `true` if there were none.
|
472
|
+
def def_node_matcher(method_name, pattern_str)
|
473
|
+
compiler = RubbyCop::NodePattern::Compiler.new(pattern_str, 'node')
|
474
|
+
src = "def #{method_name}(node" \
|
475
|
+
"#{compiler.emit_trailing_params});" \
|
476
|
+
"#{compiler.emit_method_code};end"
|
477
|
+
|
478
|
+
location = caller_locations(1, 1).first
|
479
|
+
class_eval(src, location.path, location.lineno)
|
480
|
+
end
|
481
|
+
|
482
|
+
# Define a method which recurses over the descendants of an AST node,
|
483
|
+
# checking whether any of them match the provided pattern
|
484
|
+
#
|
485
|
+
# If the method name ends with '?', the new method will return `true`
|
486
|
+
# as soon as it finds a descendant which matches. Otherwise, it will
|
487
|
+
# yield all descendants which match.
|
488
|
+
def def_node_search(method_name, pattern_str)
|
489
|
+
compiler = RubbyCop::NodePattern::Compiler.new(pattern_str, 'node')
|
490
|
+
called_from = caller(1..1).first.split(':')
|
491
|
+
|
492
|
+
if method_name.to_s.end_with?('?')
|
493
|
+
node_search_first(method_name, compiler, called_from)
|
494
|
+
else
|
495
|
+
node_search_all(method_name, compiler, called_from)
|
496
|
+
end
|
497
|
+
end
|
498
|
+
|
499
|
+
def node_search_first(method_name, compiler, called_from)
|
500
|
+
node_search(method_name, compiler, 'return true', '', called_from)
|
501
|
+
end
|
502
|
+
|
503
|
+
def node_search_all(method_name, compiler, called_from)
|
504
|
+
yieldval = compiler.emit_capture_list
|
505
|
+
yieldval = 'node' if yieldval.empty?
|
506
|
+
prelude = "return enum_for(:#{method_name}, node0" \
|
507
|
+
"#{compiler.emit_trailing_params}) unless block_given?"
|
508
|
+
|
509
|
+
node_search(method_name, compiler, "yield(#{yieldval})", prelude,
|
510
|
+
called_from)
|
511
|
+
end
|
512
|
+
|
513
|
+
def node_search(method_name, compiler, on_match, prelude, called_from)
|
514
|
+
src = node_search_body(method_name, compiler.emit_trailing_params,
|
515
|
+
prelude, compiler.match_code, on_match)
|
516
|
+
filename, lineno = *called_from
|
517
|
+
class_eval(src, filename, lineno.to_i)
|
518
|
+
end
|
519
|
+
|
520
|
+
def node_search_body(method_name, trailing_params, prelude, match_code,
|
521
|
+
on_match)
|
522
|
+
<<-END
|
523
|
+
def #{method_name}(node0#{trailing_params})
|
524
|
+
#{prelude}
|
525
|
+
node0.each_node do |node|
|
526
|
+
if #{match_code}
|
527
|
+
#{on_match}
|
528
|
+
end
|
529
|
+
end
|
530
|
+
nil
|
531
|
+
end
|
532
|
+
END
|
533
|
+
end
|
534
|
+
end
|
535
|
+
|
536
|
+
def initialize(str)
|
537
|
+
compiler = Compiler.new(str)
|
538
|
+
src = "def match(node0#{compiler.emit_trailing_params});" \
|
539
|
+
"#{compiler.emit_method_code}end"
|
540
|
+
instance_eval(src)
|
541
|
+
end
|
542
|
+
end
|
543
|
+
end
|