rubocop 0.46.0 → 0.47.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of rubocop might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/LICENSE.txt +1 -1
- data/README.md +77 -2
- data/config/default.yml +151 -74
- data/config/disabled.yml +9 -0
- data/config/enabled.yml +49 -9
- data/lib/rubocop.rb +36 -8
- data/lib/rubocop/ast/builder.rb +59 -0
- data/lib/rubocop/ast/node.rb +607 -0
- data/lib/rubocop/ast/node/array_node.rb +45 -0
- data/lib/rubocop/ast/node/case_node.rb +63 -0
- data/lib/rubocop/ast/node/for_node.rb +53 -0
- data/lib/rubocop/ast/node/hash_node.rb +102 -0
- data/lib/rubocop/ast/node/if_node.rb +136 -0
- data/lib/rubocop/ast/node/keyword_splat_node.rb +45 -0
- data/lib/rubocop/ast/node/mixin/conditional_node.rb +45 -0
- data/lib/rubocop/ast/node/mixin/hash_element_node.rb +125 -0
- data/lib/rubocop/ast/node/mixin/modifier_node.rb +17 -0
- data/lib/rubocop/ast/node/pair_node.rb +64 -0
- data/lib/rubocop/ast/node/until_node.rb +43 -0
- data/lib/rubocop/ast/node/when_node.rb +61 -0
- data/lib/rubocop/ast/node/while_node.rb +43 -0
- data/lib/rubocop/ast/sexp.rb +16 -0
- data/lib/rubocop/{ast_node → ast}/traversal.rb +1 -1
- data/lib/rubocop/cli.rb +18 -14
- data/lib/rubocop/comment_config.rb +1 -3
- data/lib/rubocop/config.rb +93 -35
- data/lib/rubocop/config_loader.rb +1 -1
- data/lib/rubocop/cop/badge.rb +73 -0
- data/lib/rubocop/cop/bundler/duplicated_gem.rb +2 -2
- data/lib/rubocop/cop/bundler/ordered_gems.rb +43 -3
- data/lib/rubocop/cop/commissioner.rb +17 -6
- data/lib/rubocop/cop/cop.rb +25 -112
- data/lib/rubocop/cop/lint/ambiguous_operator.rb +9 -4
- data/lib/rubocop/cop/lint/ambiguous_regexp_literal.rb +7 -0
- data/lib/rubocop/cop/lint/assignment_in_condition.rb +18 -4
- data/lib/rubocop/cop/lint/block_alignment.rb +40 -9
- data/lib/rubocop/cop/lint/circular_argument_reference.rb +14 -0
- data/lib/rubocop/cop/lint/condition_position.rb +14 -16
- data/lib/rubocop/cop/lint/debugger.rb +28 -0
- data/lib/rubocop/cop/lint/def_end_alignment.rb +21 -1
- data/lib/rubocop/cop/lint/deprecated_class_methods.rb +13 -1
- data/lib/rubocop/cop/lint/duplicate_case_condition.rb +26 -22
- data/lib/rubocop/cop/lint/duplicate_methods.rb +15 -1
- data/lib/rubocop/cop/lint/duplicated_key.rb +16 -8
- data/lib/rubocop/cop/lint/each_with_object_argument.rb +9 -0
- data/lib/rubocop/cop/lint/else_layout.rb +26 -29
- data/lib/rubocop/cop/lint/empty_ensure.rb +38 -0
- data/lib/rubocop/cop/lint/empty_expression.rb +11 -1
- data/lib/rubocop/cop/lint/empty_interpolation.rb +8 -0
- data/lib/rubocop/cop/lint/empty_when.rb +14 -16
- data/lib/rubocop/cop/lint/end_alignment.rb +48 -28
- data/lib/rubocop/cop/lint/end_in_method.rb +23 -0
- data/lib/rubocop/cop/lint/ensure_return.rb +21 -0
- data/lib/rubocop/cop/lint/float_out_of_range.rb +5 -0
- data/lib/rubocop/cop/lint/format_parameter_mismatch.rb +29 -4
- data/lib/rubocop/cop/lint/handle_exceptions.rb +40 -0
- data/lib/rubocop/cop/lint/implicit_string_concatenation.rb +7 -2
- data/lib/rubocop/cop/lint/ineffective_access_modifier.rb +11 -2
- data/lib/rubocop/cop/lint/invalid_character_literal.rb +3 -0
- data/lib/rubocop/cop/lint/literal_in_condition.rb +34 -36
- data/lib/rubocop/cop/lint/literal_in_interpolation.rb +8 -0
- data/lib/rubocop/cop/lint/loop.rb +36 -0
- data/lib/rubocop/cop/lint/multiple_compare.rb +46 -0
- data/lib/rubocop/cop/lint/nested_method_definition.rb +22 -0
- data/lib/rubocop/cop/lint/next_without_accumulator.rb +5 -0
- data/lib/rubocop/cop/lint/parentheses_as_grouped_expression.rb +8 -0
- data/lib/rubocop/cop/lint/percent_string_array.rb +27 -13
- data/lib/rubocop/cop/lint/percent_symbol_array.rb +14 -4
- data/lib/rubocop/cop/lint/rand_one.rb +7 -3
- data/lib/rubocop/cop/lint/require_parentheses.rb +20 -19
- data/lib/rubocop/cop/lint/rescue_exception.rb +20 -0
- data/lib/rubocop/cop/lint/safe_navigation_chain.rb +66 -0
- data/lib/rubocop/cop/lint/shadowed_exception.rb +6 -1
- data/lib/rubocop/cop/lint/shadowing_outer_local_variable.rb +24 -0
- data/lib/rubocop/cop/lint/string_conversion_in_interpolation.rb +8 -0
- data/lib/rubocop/cop/lint/underscore_prefixed_variable_name.rb +24 -0
- data/lib/rubocop/cop/lint/unified_integer.rb +5 -0
- data/lib/rubocop/cop/lint/unneeded_disable.rb +2 -2
- data/lib/rubocop/cop/lint/unneeded_splat_expansion.rb +5 -0
- data/lib/rubocop/cop/lint/unreachable_code.rb +17 -0
- data/lib/rubocop/cop/lint/unused_block_argument.rb +2 -0
- data/lib/rubocop/cop/lint/unused_method_argument.rb +10 -0
- data/lib/rubocop/cop/lint/useless_access_modifier.rb +28 -1
- data/lib/rubocop/cop/lint/useless_assignment.rb +18 -0
- data/lib/rubocop/cop/lint/useless_comparison.rb +3 -1
- data/lib/rubocop/cop/lint/useless_else_without_rescue.rb +16 -1
- data/lib/rubocop/cop/lint/useless_setter_call.rb +16 -4
- data/lib/rubocop/cop/lint/void.rb +52 -0
- data/lib/rubocop/cop/message_annotator.rb +102 -0
- data/lib/rubocop/cop/metrics/block_length.rb +6 -0
- data/lib/rubocop/cop/metrics/block_nesting.rb +17 -5
- data/lib/rubocop/cop/metrics/line_length.rb +11 -4
- data/lib/rubocop/cop/metrics/perceived_complexity.rb +1 -2
- data/lib/rubocop/cop/mixin/array_syntax.rb +2 -11
- data/lib/rubocop/cop/mixin/configurable_enforced_style.rb +12 -5
- data/lib/rubocop/cop/mixin/configurable_formatting.rb +48 -0
- data/lib/rubocop/cop/mixin/configurable_max.rb +3 -3
- data/lib/rubocop/cop/mixin/configurable_naming.rb +5 -33
- data/lib/rubocop/cop/mixin/configurable_numbering.rb +6 -47
- data/lib/rubocop/cop/mixin/documentation_comment.rb +7 -1
- data/lib/rubocop/cop/mixin/duplication.rb +46 -0
- data/lib/rubocop/cop/mixin/end_keyword_alignment.rb +2 -2
- data/lib/rubocop/cop/mixin/frozen_string_literal.rb +14 -11
- data/lib/rubocop/cop/mixin/hash_alignment.rb +114 -0
- data/lib/rubocop/cop/mixin/multiline_expression_indentation.rb +3 -3
- data/lib/rubocop/cop/mixin/negative_conditional.rb +21 -7
- data/lib/rubocop/cop/mixin/on_method_def.rb +14 -0
- data/lib/rubocop/cop/mixin/on_normal_if_unless.rb +1 -24
- data/lib/rubocop/cop/mixin/statement_modifier.rb +8 -13
- data/lib/rubocop/cop/mixin/target_ruby_version.rb +16 -0
- data/lib/rubocop/cop/mixin/trailing_comma.rb +2 -3
- data/lib/rubocop/cop/offense.rb +1 -1
- data/lib/rubocop/cop/performance/case_when_splat.rb +56 -59
- data/lib/rubocop/cop/performance/detect.rb +2 -2
- data/lib/rubocop/cop/performance/flat_map.rb +3 -3
- data/lib/rubocop/cop/performance/redundant_merge.rb +3 -6
- data/lib/rubocop/cop/performance/regexp_match.rb +201 -0
- data/lib/rubocop/cop/rails/delegate.rb +2 -2
- data/lib/rubocop/cop/rails/delegate_allow_blank.rb +10 -19
- data/lib/rubocop/cop/rails/enum_uniqueness.rb +12 -40
- data/lib/rubocop/cop/rails/file_path.rb +80 -0
- data/lib/rubocop/cop/rails/find_each.rb +5 -14
- data/lib/rubocop/cop/rails/http_positional_arguments.rb +30 -24
- data/lib/rubocop/cop/rails/not_null_column.rb +23 -0
- data/lib/rubocop/cop/rails/reversible_migration.rb +217 -0
- data/lib/rubocop/cop/rails/safe_navigation.rb +4 -2
- data/lib/rubocop/cop/rails/skips_model_validations.rb +46 -0
- data/lib/rubocop/cop/rails/time_zone.rb +1 -1
- data/lib/rubocop/cop/rails/uniq_before_pluck.rb +7 -5
- data/lib/rubocop/cop/registry.rb +170 -0
- data/lib/rubocop/cop/{lint → security}/eval.rb +7 -1
- data/lib/rubocop/cop/security/marshal_load.rb +33 -0
- data/lib/rubocop/cop/security/yaml_load.rb +37 -0
- data/lib/rubocop/cop/style/align_hash.rb +138 -169
- data/lib/rubocop/cop/style/and_or.rb +1 -1
- data/lib/rubocop/cop/style/braces_around_hash_parameters.rb +10 -15
- data/lib/rubocop/cop/style/case_indentation.rb +36 -27
- data/lib/rubocop/cop/style/conditional_assignment.rb +64 -47
- data/lib/rubocop/cop/style/each_with_object.rb +4 -1
- data/lib/rubocop/cop/style/else_alignment.rb +14 -20
- data/lib/rubocop/cop/style/empty_case_condition.rb +16 -25
- data/lib/rubocop/cop/style/empty_else.rb +20 -22
- data/lib/rubocop/cop/style/empty_literal.rb +4 -4
- data/lib/rubocop/cop/style/empty_method.rb +12 -6
- data/lib/rubocop/cop/style/encoding.rb +1 -1
- data/lib/rubocop/cop/style/file_name.rb +24 -4
- data/lib/rubocop/cop/style/first_method_argument_line_break.rb +1 -1
- data/lib/rubocop/cop/style/format_string.rb +17 -48
- data/lib/rubocop/cop/style/frozen_string_literal_comment.rb +40 -11
- data/lib/rubocop/cop/style/guard_clause.rb +11 -17
- data/lib/rubocop/cop/style/hash_syntax.rb +24 -42
- data/lib/rubocop/cop/style/identical_conditional_branches.rb +40 -28
- data/lib/rubocop/cop/style/if_inside_else.rb +6 -9
- data/lib/rubocop/cop/style/if_unless_modifier.rb +16 -25
- data/lib/rubocop/cop/style/if_unless_modifier_of_if_unless.rb +3 -9
- data/lib/rubocop/cop/style/indent_array.rb +1 -1
- data/lib/rubocop/cop/style/indentation_width.rb +29 -60
- data/lib/rubocop/cop/style/infinite_loop.rb +21 -22
- data/lib/rubocop/cop/style/method_call_with_args_parentheses.rb +86 -0
- data/lib/rubocop/cop/style/{method_call_parentheses.rb → method_call_without_args_parentheses.rb} +8 -1
- data/lib/rubocop/cop/style/missing_else.rb +40 -14
- data/lib/rubocop/cop/style/multiline_if_modifier.rb +5 -15
- data/lib/rubocop/cop/style/multiline_if_then.rb +14 -8
- data/lib/rubocop/cop/style/multiline_method_call_indentation.rb +3 -3
- data/lib/rubocop/cop/style/multiline_ternary_operator.rb +1 -5
- data/lib/rubocop/cop/style/mutable_constant.rb +3 -2
- data/lib/rubocop/cop/style/negated_if.rb +3 -19
- data/lib/rubocop/cop/style/negated_while.rb +2 -17
- data/lib/rubocop/cop/style/nested_modifier.rb +16 -43
- data/lib/rubocop/cop/style/nested_ternary_operator.rb +3 -5
- data/lib/rubocop/cop/style/next.rb +23 -21
- data/lib/rubocop/cop/style/non_nil_check.rb +2 -3
- data/lib/rubocop/cop/style/not.rb +1 -3
- data/lib/rubocop/cop/style/numeric_literals.rb +2 -2
- data/lib/rubocop/cop/style/one_line_conditional.rb +12 -22
- data/lib/rubocop/cop/style/option_hash.rb +4 -15
- data/lib/rubocop/cop/style/parallel_assignment.rb +1 -3
- data/lib/rubocop/cop/style/parentheses_around_condition.rb +8 -12
- data/lib/rubocop/cop/style/percent_q_literals.rb +15 -12
- data/lib/rubocop/cop/style/redundant_freeze.rb +3 -2
- data/lib/rubocop/cop/style/redundant_parentheses.rb +27 -4
- data/lib/rubocop/cop/style/redundant_return.rb +4 -8
- data/lib/rubocop/cop/style/safe_navigation.rb +13 -6
- data/lib/rubocop/cop/style/space_after_colon.rb +2 -4
- data/lib/rubocop/cop/style/space_around_block_parameters.rb +1 -1
- data/lib/rubocop/cop/style/space_around_operators.rb +15 -13
- data/lib/rubocop/cop/style/string_methods.rb +1 -3
- data/lib/rubocop/cop/style/symbol_array.rb +1 -5
- data/lib/rubocop/cop/style/ternary_parentheses.rb +5 -6
- data/lib/rubocop/cop/style/trailing_comma_in_arguments.rb +2 -5
- data/lib/rubocop/cop/style/trailing_comma_in_literal.rb +1 -1
- data/lib/rubocop/cop/style/unless_else.rb +1 -5
- data/lib/rubocop/cop/style/when_then.rb +4 -2
- data/lib/rubocop/cop/style/while_until_do.rb +9 -13
- data/lib/rubocop/cop/style/while_until_modifier.rb +12 -11
- data/lib/rubocop/cop/style/word_array.rb +5 -9
- data/lib/rubocop/cop/team.rb +16 -15
- data/lib/rubocop/cop/util.rb +13 -3
- data/lib/rubocop/formatter/clang_style_formatter.rb +2 -2
- data/lib/rubocop/formatter/disabled_config_formatter.rb +2 -1
- data/lib/rubocop/magic_comment.rb +196 -0
- data/lib/rubocop/options.rb +5 -4
- data/lib/rubocop/processed_source.rb +1 -1
- data/lib/rubocop/rspec/cop_helper.rb +9 -0
- data/lib/rubocop/rspec/shared_examples.rb +1 -1
- data/lib/rubocop/runner.rb +7 -2
- data/lib/rubocop/version.rb +1 -1
- metadata +41 -14
- data/lib/rubocop/ast_node.rb +0 -624
- data/lib/rubocop/ast_node/builder.rb +0 -30
- data/lib/rubocop/ast_node/sexp.rb +0 -13
- data/lib/rubocop/cop/mixin/hash_node.rb +0 -14
- data/lib/rubocop/cop/mixin/if_node.rb +0 -42
data/config/disabled.yml
CHANGED
@@ -69,6 +69,11 @@ Style/InlineComment:
|
|
69
69
|
Description: 'Avoid trailing inline comments.'
|
70
70
|
Enabled: false
|
71
71
|
|
72
|
+
Style/MethodCallWithArgsParentheses:
|
73
|
+
Description: 'Use parentheses for method calls with arguments.'
|
74
|
+
StyleGuide: '#method-invocation-parens'
|
75
|
+
Enabled: false
|
76
|
+
|
72
77
|
Style/MethodCalledOnDoEndBlock:
|
73
78
|
Description: 'Avoid chaining a method call on a do...end block.'
|
74
79
|
StyleGuide: '#single-line-blocks'
|
@@ -113,3 +118,7 @@ Style/SymbolArray:
|
|
113
118
|
Description: 'Use %i or %I for arrays of symbols.'
|
114
119
|
StyleGuide: '#percent-i'
|
115
120
|
Enabled: false
|
121
|
+
|
122
|
+
Style/SingleLineBlockParams:
|
123
|
+
Description: 'Enforces the names of some block params.'
|
124
|
+
Enabled: false
|
data/config/enabled.yml
CHANGED
@@ -404,7 +404,7 @@ Style/LineEndConcatenation:
|
|
404
404
|
line end.
|
405
405
|
Enabled: true
|
406
406
|
|
407
|
-
Style/
|
407
|
+
Style/MethodCallWithoutArgsParentheses:
|
408
408
|
Description: 'Do not use parentheses for method calls with no arguments.'
|
409
409
|
StyleGuide: '#method-invocation-parens'
|
410
410
|
Enabled: true
|
@@ -710,10 +710,6 @@ Style/SignalException:
|
|
710
710
|
StyleGuide: '#prefer-raise-over-fail'
|
711
711
|
Enabled: true
|
712
712
|
|
713
|
-
Style/SingleLineBlockParams:
|
714
|
-
Description: 'Enforces the names of some block params.'
|
715
|
-
Enabled: true
|
716
|
-
|
717
713
|
Style/SingleLineMethods:
|
718
714
|
Description: 'Avoid single-line methods.'
|
719
715
|
StyleGuide: '#no-single-line-methods'
|
@@ -1120,10 +1116,6 @@ Lint/EnsureReturn:
|
|
1120
1116
|
StyleGuide: '#no-return-ensure'
|
1121
1117
|
Enabled: true
|
1122
1118
|
|
1123
|
-
Lint/Eval:
|
1124
|
-
Description: 'The use of eval represents a serious security risk.'
|
1125
|
-
Enabled: true
|
1126
|
-
|
1127
1119
|
Lint/FloatOutOfRange:
|
1128
1120
|
Description: >-
|
1129
1121
|
Catches floating-point literals too large or small for Ruby to
|
@@ -1176,6 +1168,10 @@ Lint/Loop:
|
|
1176
1168
|
StyleGuide: '#loop-with-break'
|
1177
1169
|
Enabled: true
|
1178
1170
|
|
1171
|
+
Lint/MultipleCompare:
|
1172
|
+
Description: "Use `&&` operator to compare multiple value."
|
1173
|
+
Enabled: true
|
1174
|
+
|
1179
1175
|
Lint/NestedMethodDefinition:
|
1180
1176
|
Description: 'Do not use nested method definitions.'
|
1181
1177
|
StyleGuide: '#no-nested-methods'
|
@@ -1225,6 +1221,10 @@ Lint/RescueException:
|
|
1225
1221
|
StyleGuide: '#no-blind-rescues'
|
1226
1222
|
Enabled: true
|
1227
1223
|
|
1224
|
+
Lint/SafeNavigationChain:
|
1225
|
+
Description: 'Do not chain ordinary method call after safe navigation operator.'
|
1226
|
+
Enabled: true
|
1227
|
+
|
1228
1228
|
Lint/ShadowedException:
|
1229
1229
|
Description: >-
|
1230
1230
|
Avoid rescuing a higher level exception
|
@@ -1279,6 +1279,7 @@ Lint/UselessAccessModifier:
|
|
1279
1279
|
Description: 'Checks for useless access modifiers.'
|
1280
1280
|
Enabled: true
|
1281
1281
|
ContextCreatingMethods: []
|
1282
|
+
MethodCreatingMethods: []
|
1282
1283
|
|
1283
1284
|
Lint/UselessAssignment:
|
1284
1285
|
Description: 'Checks for useless assignment to a local variable.'
|
@@ -1408,6 +1409,10 @@ Performance/RedundantSortBy:
|
|
1408
1409
|
Description: 'Use `sort` instead of `sort_by { |x| x }`.'
|
1409
1410
|
Enabled: true
|
1410
1411
|
|
1412
|
+
Performance/RegexpMatch:
|
1413
|
+
Description: 'Use `match?` instead of `Regexp#match`, `String#match`, `Symbol#match`, `Regexp#===` or `=~` when `MatchData` is not used.'
|
1414
|
+
Enabled: true
|
1415
|
+
|
1411
1416
|
Performance/ReverseEach:
|
1412
1417
|
Description: 'Use `reverse_each` instead of `reverse.each`.'
|
1413
1418
|
Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code'
|
@@ -1488,6 +1493,10 @@ Rails/Exit:
|
|
1488
1493
|
exits during unit testing or running in production.
|
1489
1494
|
Enabled: true
|
1490
1495
|
|
1496
|
+
Rails/FilePath:
|
1497
|
+
Description: 'Use `Rails.root.join` for file path joining.'
|
1498
|
+
Enabled: true
|
1499
|
+
|
1491
1500
|
Rails/FindBy:
|
1492
1501
|
Description: 'Prefer find_by over where.first.'
|
1493
1502
|
StyleGuide: 'https://github.com/bbatsov/rails-style-guide#find_by'
|
@@ -1537,6 +1546,12 @@ Rails/RequestReferer:
|
|
1537
1546
|
Description: 'Use consistent syntax for request.referer.'
|
1538
1547
|
Enabled: true
|
1539
1548
|
|
1549
|
+
Rails/ReversibleMigration:
|
1550
|
+
Description: 'Checks whether the change method of the migration file is reversible.'
|
1551
|
+
StyleGuide: 'https://github.com/bbatsov/rails-style-guide#reversible-migration'
|
1552
|
+
Reference: 'http://api.rubyonrails.org/classes/ActiveRecord/Migration/CommandRecorder.html'
|
1553
|
+
Enabled: true
|
1554
|
+
|
1540
1555
|
Rails/SafeNavigation:
|
1541
1556
|
Description: "Use Ruby's safe navigation operator (`&.`) instead of `try!`"
|
1542
1557
|
Enabled: true
|
@@ -1555,10 +1570,21 @@ Rails/UniqBeforePluck:
|
|
1555
1570
|
Description: 'Prefer the use of uniq or distinct before pluck.'
|
1556
1571
|
Enabled: true
|
1557
1572
|
|
1573
|
+
Rails/SkipsModelValidations:
|
1574
|
+
Description: >-
|
1575
|
+
Use methods that skips model validations with caution.
|
1576
|
+
See reference for more information.
|
1577
|
+
Reference: 'http://guides.rubyonrails.org/active_record_validations.html#skipping-validations'
|
1578
|
+
Enabled: true
|
1579
|
+
|
1558
1580
|
Rails/Validation:
|
1559
1581
|
Description: 'Use validates :attribute, hash of validations.'
|
1560
1582
|
Enabled: true
|
1561
1583
|
|
1584
|
+
Security/Eval:
|
1585
|
+
Description: 'The use of eval represents a serious security risk.'
|
1586
|
+
Enabled: true
|
1587
|
+
|
1562
1588
|
Security/JSONLoad:
|
1563
1589
|
Description: >-
|
1564
1590
|
Prefer usage of `JSON.parse` over `JSON.load` due to potential
|
@@ -1569,6 +1595,20 @@ Security/JSONLoad:
|
|
1569
1595
|
# on the value of the argument.
|
1570
1596
|
AutoCorrect: false
|
1571
1597
|
|
1598
|
+
Security/MarshalLoad:
|
1599
|
+
Description: >-
|
1600
|
+
Avoid using of `Marshal.load` or `Marshal.restore` due to potential
|
1601
|
+
security issues. See reference for more information.
|
1602
|
+
Reference: 'http://ruby-doc.org/core-2.3.3/Marshal.html#module-Marshal-label-Security+considerations'
|
1603
|
+
Enabled: true
|
1604
|
+
|
1605
|
+
Security/YAMLLoad:
|
1606
|
+
Description: >-
|
1607
|
+
Prefer usage of `YAML.safe_load` over `YAML.load` due to potential
|
1608
|
+
security issues. See reference for more information.
|
1609
|
+
Reference: 'https://ruby-doc.org/stdlib-2.3.3/libdoc/yaml/rdoc/YAML.html#module-YAML-label-Security'
|
1610
|
+
Enabled: true
|
1611
|
+
|
1572
1612
|
##################### Bundler #############################
|
1573
1613
|
|
1574
1614
|
Bundler/DuplicatedGem:
|
data/lib/rubocop.rb
CHANGED
@@ -19,17 +19,33 @@ require 'rubocop/string_util'
|
|
19
19
|
require 'rubocop/name_similarity'
|
20
20
|
require 'rubocop/node_pattern'
|
21
21
|
require 'rubocop/string_interpreter'
|
22
|
-
require 'rubocop/
|
23
|
-
require 'rubocop/
|
24
|
-
require 'rubocop/
|
25
|
-
require 'rubocop/
|
22
|
+
require 'rubocop/ast/sexp'
|
23
|
+
require 'rubocop/ast/node'
|
24
|
+
require 'rubocop/ast/node/mixin/conditional_node'
|
25
|
+
require 'rubocop/ast/node/mixin/hash_element_node'
|
26
|
+
require 'rubocop/ast/node/mixin/modifier_node'
|
27
|
+
require 'rubocop/ast/node/array_node'
|
28
|
+
require 'rubocop/ast/node/case_node'
|
29
|
+
require 'rubocop/ast/node/for_node'
|
30
|
+
require 'rubocop/ast/node/hash_node'
|
31
|
+
require 'rubocop/ast/node/if_node'
|
32
|
+
require 'rubocop/ast/node/keyword_splat_node'
|
33
|
+
require 'rubocop/ast/node/pair_node'
|
34
|
+
require 'rubocop/ast/node/until_node'
|
35
|
+
require 'rubocop/ast/node/when_node'
|
36
|
+
require 'rubocop/ast/node/while_node'
|
37
|
+
require 'rubocop/ast/builder'
|
38
|
+
require 'rubocop/ast/traversal'
|
26
39
|
require 'rubocop/error'
|
27
40
|
require 'rubocop/warning'
|
28
41
|
|
29
42
|
require 'rubocop/cop/util'
|
30
43
|
require 'rubocop/cop/offense'
|
44
|
+
require 'rubocop/cop/message_annotator'
|
31
45
|
require 'rubocop/cop/ignored_node'
|
32
46
|
require 'rubocop/cop/autocorrect_logic'
|
47
|
+
require 'rubocop/cop/badge'
|
48
|
+
require 'rubocop/cop/registry'
|
33
49
|
require 'rubocop/cop/cop'
|
34
50
|
require 'rubocop/cop/commissioner'
|
35
51
|
require 'rubocop/cop/corrector'
|
@@ -54,16 +70,17 @@ require 'rubocop/cop/mixin/configurable_max'
|
|
54
70
|
require 'rubocop/cop/mixin/code_length' # relies on configurable_max
|
55
71
|
require 'rubocop/cop/mixin/classish_length' # relies on code_length
|
56
72
|
require 'rubocop/cop/mixin/configurable_enforced_style'
|
73
|
+
require 'rubocop/cop/mixin/configurable_formatting'
|
57
74
|
require 'rubocop/cop/mixin/configurable_naming'
|
58
75
|
require 'rubocop/cop/mixin/configurable_numbering'
|
59
76
|
require 'rubocop/cop/mixin/def_node'
|
60
77
|
require 'rubocop/cop/mixin/documentation_comment'
|
78
|
+
require 'rubocop/cop/mixin/duplication'
|
61
79
|
require 'rubocop/cop/mixin/empty_lines_around_body'
|
62
80
|
require 'rubocop/cop/mixin/end_keyword_alignment'
|
63
81
|
require 'rubocop/cop/mixin/first_element_line_break'
|
64
82
|
require 'rubocop/cop/mixin/frozen_string_literal'
|
65
|
-
require 'rubocop/cop/mixin/
|
66
|
-
require 'rubocop/cop/mixin/if_node'
|
83
|
+
require 'rubocop/cop/mixin/hash_alignment'
|
67
84
|
require 'rubocop/cop/mixin/integer_node'
|
68
85
|
require 'rubocop/cop/mixin/on_method_def'
|
69
86
|
require 'rubocop/cop/mixin/match_range'
|
@@ -87,6 +104,7 @@ require 'rubocop/cop/mixin/space_inside' # relies on surrounding_space
|
|
87
104
|
require 'rubocop/cop/mixin/statement_modifier'
|
88
105
|
require 'rubocop/cop/mixin/string_help'
|
89
106
|
require 'rubocop/cop/mixin/string_literals_help'
|
107
|
+
require 'rubocop/cop/mixin/target_ruby_version'
|
90
108
|
require 'rubocop/cop/mixin/too_many_lines'
|
91
109
|
require 'rubocop/cop/mixin/trailing_comma'
|
92
110
|
require 'rubocop/cop/mixin/unused_argument'
|
@@ -115,7 +133,6 @@ require 'rubocop/cop/lint/empty_when'
|
|
115
133
|
require 'rubocop/cop/lint/end_alignment'
|
116
134
|
require 'rubocop/cop/lint/end_in_method'
|
117
135
|
require 'rubocop/cop/lint/ensure_return'
|
118
|
-
require 'rubocop/cop/lint/eval'
|
119
136
|
require 'rubocop/cop/lint/float_out_of_range'
|
120
137
|
require 'rubocop/cop/lint/format_parameter_mismatch'
|
121
138
|
require 'rubocop/cop/lint/handle_exceptions'
|
@@ -126,6 +143,7 @@ require 'rubocop/cop/lint/invalid_character_literal'
|
|
126
143
|
require 'rubocop/cop/lint/literal_in_condition'
|
127
144
|
require 'rubocop/cop/lint/literal_in_interpolation'
|
128
145
|
require 'rubocop/cop/lint/loop'
|
146
|
+
require 'rubocop/cop/lint/multiple_compare'
|
129
147
|
require 'rubocop/cop/lint/nested_method_definition'
|
130
148
|
require 'rubocop/cop/lint/next_without_accumulator'
|
131
149
|
require 'rubocop/cop/lint/non_local_exit_from_iterator'
|
@@ -135,6 +153,7 @@ require 'rubocop/cop/lint/percent_symbol_array'
|
|
135
153
|
require 'rubocop/cop/lint/rand_one'
|
136
154
|
require 'rubocop/cop/lint/require_parentheses'
|
137
155
|
require 'rubocop/cop/lint/rescue_exception'
|
156
|
+
require 'rubocop/cop/lint/safe_navigation_chain'
|
138
157
|
require 'rubocop/cop/lint/shadowed_exception'
|
139
158
|
require 'rubocop/cop/lint/shadowing_outer_local_variable'
|
140
159
|
require 'rubocop/cop/lint/string_conversion_in_interpolation'
|
@@ -179,6 +198,7 @@ require 'rubocop/cop/performance/redundant_block_call'
|
|
179
198
|
require 'rubocop/cop/performance/redundant_match'
|
180
199
|
require 'rubocop/cop/performance/redundant_merge'
|
181
200
|
require 'rubocop/cop/performance/redundant_sort_by'
|
201
|
+
require 'rubocop/cop/performance/regexp_match'
|
182
202
|
require 'rubocop/cop/performance/reverse_each'
|
183
203
|
require 'rubocop/cop/performance/sample'
|
184
204
|
require 'rubocop/cop/performance/size'
|
@@ -278,7 +298,8 @@ require 'rubocop/cop/style/lambda'
|
|
278
298
|
require 'rubocop/cop/style/lambda_call'
|
279
299
|
require 'rubocop/cop/style/leading_comment_space'
|
280
300
|
require 'rubocop/cop/style/line_end_concatenation'
|
281
|
-
require 'rubocop/cop/style/
|
301
|
+
require 'rubocop/cop/style/method_call_without_args_parentheses'
|
302
|
+
require 'rubocop/cop/style/method_call_with_args_parentheses'
|
282
303
|
require 'rubocop/cop/style/method_called_on_do_end_block'
|
283
304
|
require 'rubocop/cop/style/method_def_parentheses'
|
284
305
|
require 'rubocop/cop/style/method_name'
|
@@ -399,6 +420,7 @@ require 'rubocop/cop/rails/delegate'
|
|
399
420
|
require 'rubocop/cop/rails/delegate_allow_blank'
|
400
421
|
require 'rubocop/cop/rails/enum_uniqueness'
|
401
422
|
require 'rubocop/cop/rails/exit'
|
423
|
+
require 'rubocop/cop/rails/file_path'
|
402
424
|
require 'rubocop/cop/rails/find_by'
|
403
425
|
require 'rubocop/cop/rails/find_each'
|
404
426
|
require 'rubocop/cop/rails/has_and_belongs_to_many'
|
@@ -409,14 +431,19 @@ require 'rubocop/cop/rails/output'
|
|
409
431
|
require 'rubocop/cop/rails/pluralization_grammar'
|
410
432
|
require 'rubocop/cop/rails/read_write_attribute'
|
411
433
|
require 'rubocop/cop/rails/request_referer'
|
434
|
+
require 'rubocop/cop/rails/reversible_migration'
|
412
435
|
require 'rubocop/cop/rails/safe_navigation'
|
413
436
|
require 'rubocop/cop/rails/save_bang'
|
414
437
|
require 'rubocop/cop/rails/scope_args'
|
438
|
+
require 'rubocop/cop/rails/skips_model_validations'
|
415
439
|
require 'rubocop/cop/rails/time_zone'
|
416
440
|
require 'rubocop/cop/rails/uniq_before_pluck'
|
417
441
|
require 'rubocop/cop/rails/validation'
|
418
442
|
|
443
|
+
require 'rubocop/cop/security/eval'
|
419
444
|
require 'rubocop/cop/security/json_load'
|
445
|
+
require 'rubocop/cop/security/marshal_load'
|
446
|
+
require 'rubocop/cop/security/yaml_load'
|
420
447
|
|
421
448
|
require 'rubocop/cop/team'
|
422
449
|
|
@@ -444,6 +471,7 @@ require 'rubocop/config_store'
|
|
444
471
|
require 'rubocop/target_finder'
|
445
472
|
require 'rubocop/token'
|
446
473
|
require 'rubocop/comment_config'
|
474
|
+
require 'rubocop/magic_comment'
|
447
475
|
require 'rubocop/processed_source'
|
448
476
|
require 'rubocop/result_cache'
|
449
477
|
require 'rubocop/runner'
|
@@ -0,0 +1,59 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module AST
|
5
|
+
# `RuboCop::Builder` is an AST builder that is utilized to let `Parser`
|
6
|
+
# generate ASTs with {RuboCop::AST::Node}.
|
7
|
+
#
|
8
|
+
# @example
|
9
|
+
# buffer = Parser::Source::Buffer.new('(string)')
|
10
|
+
# buffer.source = 'puts :foo'
|
11
|
+
#
|
12
|
+
# builder = RuboCop::Builder.new
|
13
|
+
# parser = Parser::CurrentRuby.new(builder)
|
14
|
+
# root_node = parser.parse(buffer)
|
15
|
+
class Builder < Parser::Builders::Default
|
16
|
+
NODE_MAP = {
|
17
|
+
ArrayNode => [:array],
|
18
|
+
CaseNode => [:case],
|
19
|
+
ForNode => [:for],
|
20
|
+
HashNode => [:hash],
|
21
|
+
IfNode => [:if],
|
22
|
+
KeywordSplatNode => [:kwsplat],
|
23
|
+
PairNode => [:pair],
|
24
|
+
UntilNode => [:until, :until_post],
|
25
|
+
WhenNode => [:when],
|
26
|
+
WhileNode => [:while, :while_post]
|
27
|
+
}.freeze
|
28
|
+
|
29
|
+
# Generates {Node} from the given information.
|
30
|
+
#
|
31
|
+
# @return [Node] the generated node
|
32
|
+
def n(type, children, source_map)
|
33
|
+
node_klass(type).new(type, children, location: source_map)
|
34
|
+
end
|
35
|
+
|
36
|
+
# TODO: Figure out what to do about literal encoding handling...
|
37
|
+
# More details here https://github.com/whitequark/parser/issues/283
|
38
|
+
def string_value(token)
|
39
|
+
value(token)
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def node_klass(type)
|
45
|
+
node_map[type] || Node
|
46
|
+
end
|
47
|
+
|
48
|
+
# Take the human readable constant and generate a hash map where each
|
49
|
+
# (mapped) node type is a key with its constant as the value.
|
50
|
+
def node_map
|
51
|
+
@node_map ||= begin
|
52
|
+
NODE_MAP.each_pair.each_with_object({}) do |(klass, types), map|
|
53
|
+
types.each { |type| map[type] = klass }
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,607 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RuboCop
|
4
|
+
module AST
|
5
|
+
# `RuboCop::AST::Node` is a subclass of `Parser::AST::Node`. It provides
|
6
|
+
# access to parent nodes and an object-oriented way to traverse an AST with
|
7
|
+
# the power of `Enumerable`.
|
8
|
+
#
|
9
|
+
# It has predicate methods for every node type, like this:
|
10
|
+
#
|
11
|
+
# @example
|
12
|
+
# node.send_type? # Equivalent to: `node.type == :send`
|
13
|
+
# node.op_asgn_type? # Equivalent to: `node.type == :op_asgn`
|
14
|
+
#
|
15
|
+
# # Non-word characters (other than a-zA-Z0-9_) in type names are omitted.
|
16
|
+
# node.defined_type? # Equivalent to: `node.type == :defined?`
|
17
|
+
#
|
18
|
+
# # Find the first lvar node under the receiver node.
|
19
|
+
# lvar_node = node.each_descendant.find(&:lvar_type?)
|
20
|
+
#
|
21
|
+
class Node < Parser::AST::Node # rubocop:disable Metrics/ClassLength
|
22
|
+
include RuboCop::AST::Sexp
|
23
|
+
|
24
|
+
COMPARISON_OPERATORS = [:!, :==, :===, :!=, :<=, :>=, :>, :<, :<=>].freeze
|
25
|
+
|
26
|
+
TRUTHY_LITERALS = [:str, :dstr, :xstr, :int, :float, :sym, :dsym, :array,
|
27
|
+
:hash, :regexp, :true, :irange, :erange, :complex,
|
28
|
+
:rational, :regopt].freeze
|
29
|
+
FALSEY_LITERALS = [:false, :nil].freeze
|
30
|
+
LITERALS = (TRUTHY_LITERALS + FALSEY_LITERALS).freeze
|
31
|
+
COMPOSITE_LITERALS = [:dstr, :xstr, :dsym, :array, :hash, :irange,
|
32
|
+
:erange, :regexp].freeze
|
33
|
+
BASIC_LITERALS = (LITERALS - COMPOSITE_LITERALS).freeze
|
34
|
+
MUTABLE_LITERALS = [:str, :dstr, :xstr, :array, :hash].freeze
|
35
|
+
IMMUTABLE_LITERALS = (LITERALS - MUTABLE_LITERALS).freeze
|
36
|
+
|
37
|
+
VARIABLES = [:ivar, :gvar, :cvar, :lvar].freeze
|
38
|
+
REFERENCES = [:nth_ref, :back_ref].freeze
|
39
|
+
KEYWORDS = [:alias, :and, :break, :case, :class, :def, :defs, :defined?,
|
40
|
+
:kwbegin, :do, :else, :ensure, :for, :if, :module, :next,
|
41
|
+
:not, :or, :postexe, :redo, :rescue, :retry, :return, :self,
|
42
|
+
:super, :zsuper, :then, :undef, :until, :when, :while,
|
43
|
+
:yield].freeze
|
44
|
+
OPERATOR_KEYWORDS = [:and, :or].freeze
|
45
|
+
SPECIAL_KEYWORDS = %w(__FILE__ __LINE__ __ENCODING__).freeze
|
46
|
+
|
47
|
+
# def_matcher can be used to define a pattern-matching method on Node
|
48
|
+
class << self
|
49
|
+
def def_matcher(method_name, pattern_str)
|
50
|
+
compiler = RuboCop::NodePattern::Compiler.new(pattern_str, 'self')
|
51
|
+
src = "def #{method_name}(" \
|
52
|
+
"#{compiler.emit_param_list});" \
|
53
|
+
"#{compiler.emit_method_code};end"
|
54
|
+
|
55
|
+
file, lineno = *caller.first.split(':')
|
56
|
+
class_eval(src, file, lineno.to_i)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# @see http://rubydoc.info/gems/ast/AST/Node:initialize
|
61
|
+
def initialize(type, children = [], properties = {})
|
62
|
+
@mutable_attributes = {}
|
63
|
+
|
64
|
+
# ::AST::Node#initialize freezes itself.
|
65
|
+
super
|
66
|
+
|
67
|
+
# #parent= may be invoked multiple times for a node because there are
|
68
|
+
# pending nodes while constructing AST and they are replaced later.
|
69
|
+
# For example, `lvar` and `send` type nodes are initially created as an
|
70
|
+
# `ident` type node and fixed to the appropriate type later.
|
71
|
+
# So, the #parent attribute needs to be mutable.
|
72
|
+
each_child_node do |child_node|
|
73
|
+
child_node.parent = self unless child_node.complete?
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
Parser::Meta::NODE_TYPES.each do |node_type|
|
78
|
+
method_name = "#{node_type.to_s.gsub(/\W/, '')}_type?"
|
79
|
+
define_method(method_name) do
|
80
|
+
type == node_type
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns the parent node, or `nil` if the receiver is a root node.
|
85
|
+
#
|
86
|
+
# @return [Node, nil] the parent node or `nil`
|
87
|
+
def parent
|
88
|
+
@mutable_attributes[:parent]
|
89
|
+
end
|
90
|
+
|
91
|
+
def parent=(node)
|
92
|
+
@mutable_attributes[:parent] = node
|
93
|
+
end
|
94
|
+
|
95
|
+
def complete!
|
96
|
+
@mutable_attributes.freeze
|
97
|
+
each_child_node(&:complete!)
|
98
|
+
end
|
99
|
+
|
100
|
+
def complete?
|
101
|
+
@mutable_attributes.frozen?
|
102
|
+
end
|
103
|
+
|
104
|
+
protected :parent=
|
105
|
+
|
106
|
+
# Override `AST::Node#updated` so that `AST::Processor` does not try to
|
107
|
+
# mutate our ASTs. Since we keep references from children to parents and
|
108
|
+
# not just the other way around, we cannot update an AST and share
|
109
|
+
# identical subtrees. Rather, the entire AST must be copied any time any
|
110
|
+
# part of it is changed.
|
111
|
+
def updated(type = nil, children = nil, properties = {})
|
112
|
+
properties[:location] ||= @location
|
113
|
+
Node.new(type || @type, children || @children, properties)
|
114
|
+
end
|
115
|
+
|
116
|
+
# Returns the index of the receiver node in its siblings. (Sibling index
|
117
|
+
# uses zero based numbering.)
|
118
|
+
#
|
119
|
+
# @return [Integer] the index of the receiver node in its siblings
|
120
|
+
def sibling_index
|
121
|
+
parent.children.index { |sibling| sibling.equal?(self) }
|
122
|
+
end
|
123
|
+
|
124
|
+
# Calls the given block for each ancestor node from parent to root.
|
125
|
+
# If no block is given, an `Enumerator` is returned.
|
126
|
+
#
|
127
|
+
# @overload each_ancestor
|
128
|
+
# Yield all nodes.
|
129
|
+
# @overload each_ancestor(type)
|
130
|
+
# Yield only nodes matching the type.
|
131
|
+
# @param [Symbol] type a node type
|
132
|
+
# @overload each_ancestor(type_a, type_b, ...)
|
133
|
+
# Yield only nodes matching any of the types.
|
134
|
+
# @param [Symbol] type_a a node type
|
135
|
+
# @param [Symbol] type_b a node type
|
136
|
+
# @overload each_ancestor(types)
|
137
|
+
# Yield only nodes matching any of types in the array.
|
138
|
+
# @param [Array<Symbol>] types an array containing node types
|
139
|
+
# @yieldparam [Node] node each ancestor node
|
140
|
+
# @return [self] if a block is given
|
141
|
+
# @return [Enumerator] if no block is given
|
142
|
+
def each_ancestor(*types, &block)
|
143
|
+
return to_enum(__method__, *types) unless block_given?
|
144
|
+
|
145
|
+
visit_ancestors(types, &block)
|
146
|
+
|
147
|
+
self
|
148
|
+
end
|
149
|
+
|
150
|
+
# Returns an array of ancestor nodes.
|
151
|
+
# This is a shorthand for `node.each_ancestor.to_a`.
|
152
|
+
#
|
153
|
+
# @return [Array<Node>] an array of ancestor nodes
|
154
|
+
def ancestors
|
155
|
+
each_ancestor.to_a
|
156
|
+
end
|
157
|
+
|
158
|
+
# Calls the given block for each child node.
|
159
|
+
# If no block is given, an `Enumerator` is returned.
|
160
|
+
#
|
161
|
+
# Note that this is different from `node.children.each { |child| ... }`
|
162
|
+
# which yields all children including non-node elements.
|
163
|
+
#
|
164
|
+
# @overload each_child_node
|
165
|
+
# Yield all nodes.
|
166
|
+
# @overload each_child_node(type)
|
167
|
+
# Yield only nodes matching the type.
|
168
|
+
# @param [Symbol] type a node type
|
169
|
+
# @overload each_child_node(type_a, type_b, ...)
|
170
|
+
# Yield only nodes matching any of the types.
|
171
|
+
# @param [Symbol] type_a a node type
|
172
|
+
# @param [Symbol] type_b a node type
|
173
|
+
# @overload each_child_node(types)
|
174
|
+
# Yield only nodes matching any of types in the array.
|
175
|
+
# @param [Array<Symbol>] types an array containing node types
|
176
|
+
# @yieldparam [Node] node each child node
|
177
|
+
# @return [self] if a block is given
|
178
|
+
# @return [Enumerator] if no block is given
|
179
|
+
def each_child_node(*types)
|
180
|
+
return to_enum(__method__, *types) unless block_given?
|
181
|
+
|
182
|
+
children.each do |child|
|
183
|
+
next unless child.is_a?(Node)
|
184
|
+
yield child if types.empty? || types.include?(child.type)
|
185
|
+
end
|
186
|
+
|
187
|
+
self
|
188
|
+
end
|
189
|
+
|
190
|
+
# Returns an array of child nodes.
|
191
|
+
# This is a shorthand for `node.each_child_node.to_a`.
|
192
|
+
#
|
193
|
+
# @return [Array<Node>] an array of child nodes
|
194
|
+
def child_nodes
|
195
|
+
each_child_node.to_a
|
196
|
+
end
|
197
|
+
|
198
|
+
# Calls the given block for each descendant node with depth first order.
|
199
|
+
# If no block is given, an `Enumerator` is returned.
|
200
|
+
#
|
201
|
+
# @overload each_descendant
|
202
|
+
# Yield all nodes.
|
203
|
+
# @overload each_descendant(type)
|
204
|
+
# Yield only nodes matching the type.
|
205
|
+
# @param [Symbol] type a node type
|
206
|
+
# @overload each_descendant(type_a, type_b, ...)
|
207
|
+
# Yield only nodes matching any of the types.
|
208
|
+
# @param [Symbol] type_a a node type
|
209
|
+
# @param [Symbol] type_b a node type
|
210
|
+
# @overload each_descendant(types)
|
211
|
+
# Yield only nodes matching any of types in the array.
|
212
|
+
# @param [Array<Symbol>] types an array containing node types
|
213
|
+
# @yieldparam [Node] node each descendant node
|
214
|
+
# @return [self] if a block is given
|
215
|
+
# @return [Enumerator] if no block is given
|
216
|
+
def each_descendant(*types, &block)
|
217
|
+
return to_enum(__method__, *types) unless block_given?
|
218
|
+
|
219
|
+
visit_descendants(types, &block)
|
220
|
+
|
221
|
+
self
|
222
|
+
end
|
223
|
+
|
224
|
+
# Returns an array of descendant nodes.
|
225
|
+
# This is a shorthand for `node.each_descendant.to_a`.
|
226
|
+
#
|
227
|
+
# @return [Array<Node>] an array of descendant nodes
|
228
|
+
def descendants
|
229
|
+
each_descendant.to_a
|
230
|
+
end
|
231
|
+
|
232
|
+
# Calls the given block for the receiver and each descendant node in
|
233
|
+
# depth-first order.
|
234
|
+
# If no block is given, an `Enumerator` is returned.
|
235
|
+
#
|
236
|
+
# This method would be useful when you treat the receiver node as the root
|
237
|
+
# of a tree and want to iterate over all nodes in the tree.
|
238
|
+
#
|
239
|
+
# @overload each_node
|
240
|
+
# Yield all nodes.
|
241
|
+
# @overload each_node(type)
|
242
|
+
# Yield only nodes matching the type.
|
243
|
+
# @param [Symbol] type a node type
|
244
|
+
# @overload each_node(type_a, type_b, ...)
|
245
|
+
# Yield only nodes matching any of the types.
|
246
|
+
# @param [Symbol] type_a a node type
|
247
|
+
# @param [Symbol] type_b a node type
|
248
|
+
# @overload each_node(types)
|
249
|
+
# Yield only nodes matching any of types in the array.
|
250
|
+
# @param [Array<Symbol>] types an array containing node types
|
251
|
+
# @yieldparam [Node] node each node
|
252
|
+
# @return [self] if a block is given
|
253
|
+
# @return [Enumerator] if no block is given
|
254
|
+
def each_node(*types, &block)
|
255
|
+
return to_enum(__method__, *types) unless block_given?
|
256
|
+
|
257
|
+
yield self if types.empty? || types.include?(type)
|
258
|
+
|
259
|
+
visit_descendants(types, &block)
|
260
|
+
|
261
|
+
self
|
262
|
+
end
|
263
|
+
|
264
|
+
def source
|
265
|
+
loc.expression.source
|
266
|
+
end
|
267
|
+
|
268
|
+
def source_range
|
269
|
+
loc.expression
|
270
|
+
end
|
271
|
+
|
272
|
+
## Destructuring
|
273
|
+
|
274
|
+
def_matcher :receiver, '{(send $_ ...) (block (send $_ ...) ...)}'
|
275
|
+
def_matcher :method_name, '{(send _ $_ ...) (block (send _ $_ ...) ...)}'
|
276
|
+
def_matcher :method_args, '{(send _ _ $...) (block (send _ _ $...) ...)}'
|
277
|
+
# Note: for masgn, #asgn_rhs will be an array node
|
278
|
+
def_matcher :asgn_rhs, '[assignment? (... $_)]'
|
279
|
+
def_matcher :str_content, '(str $_)'
|
280
|
+
|
281
|
+
def const_name
|
282
|
+
return unless const_type?
|
283
|
+
namespace, name = *self
|
284
|
+
if namespace && !namespace.cbase_type?
|
285
|
+
"#{namespace.const_name}::#{name}"
|
286
|
+
else
|
287
|
+
name.to_s
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
def_matcher :defined_module0, <<-PATTERN
|
292
|
+
{(class (const $_ $_) ...)
|
293
|
+
(module (const $_ $_) ...)
|
294
|
+
(casgn $_ $_ (send (const nil {:Class :Module}) :new ...))
|
295
|
+
(casgn $_ $_ (block (send (const nil {:Class :Module}) :new ...) ...))}
|
296
|
+
PATTERN
|
297
|
+
private :defined_module0
|
298
|
+
|
299
|
+
def defined_module
|
300
|
+
namespace, name = *defined_module0
|
301
|
+
s(:const, namespace, name) if name
|
302
|
+
end
|
303
|
+
|
304
|
+
def defined_module_name
|
305
|
+
(const = defined_module) && const.const_name
|
306
|
+
end
|
307
|
+
|
308
|
+
## Searching the AST
|
309
|
+
|
310
|
+
def parent_module_name
|
311
|
+
# what class or module is this method/constant/etc definition in?
|
312
|
+
# returns nil if answer cannot be determined
|
313
|
+
ancestors = each_ancestor(:class, :module, :sclass, :casgn, :block)
|
314
|
+
result = ancestors.map do |ancestor|
|
315
|
+
parent_module_name_part(ancestor) { |full_name| return full_name }
|
316
|
+
end.compact.reverse.join('::')
|
317
|
+
result.empty? ? 'Object' : result
|
318
|
+
end
|
319
|
+
|
320
|
+
## Predicates
|
321
|
+
|
322
|
+
def multiline?
|
323
|
+
source_range && (source_range.first_line != source_range.last_line)
|
324
|
+
end
|
325
|
+
|
326
|
+
def single_line?
|
327
|
+
!multiline?
|
328
|
+
end
|
329
|
+
|
330
|
+
def asgn_method_call?
|
331
|
+
!COMPARISON_OPERATORS.include?(method_name) &&
|
332
|
+
method_name.to_s.end_with?('='.freeze)
|
333
|
+
end
|
334
|
+
|
335
|
+
def_matcher :equals_asgn?, '{lvasgn ivasgn cvasgn gvasgn casgn masgn}'
|
336
|
+
def_matcher :shorthand_asgn?, '{op_asgn or_asgn and_asgn}'
|
337
|
+
def_matcher :assignment?,
|
338
|
+
'{equals_asgn? shorthand_asgn? asgn_method_call?}'
|
339
|
+
|
340
|
+
def literal?
|
341
|
+
LITERALS.include?(type)
|
342
|
+
end
|
343
|
+
|
344
|
+
def basic_literal?
|
345
|
+
BASIC_LITERALS.include?(type)
|
346
|
+
end
|
347
|
+
|
348
|
+
def truthy_literal?
|
349
|
+
TRUTHY_LITERALS.include?(type)
|
350
|
+
end
|
351
|
+
|
352
|
+
def falsey_literal?
|
353
|
+
FALSEY_LITERALS.include?(type)
|
354
|
+
end
|
355
|
+
|
356
|
+
def mutable_literal?
|
357
|
+
MUTABLE_LITERALS.include?(type)
|
358
|
+
end
|
359
|
+
|
360
|
+
def immutable_literal?
|
361
|
+
IMMUTABLE_LITERALS.include?(type)
|
362
|
+
end
|
363
|
+
|
364
|
+
[:literal, :basic_literal].each do |kind|
|
365
|
+
recursive_kind = :"recursive_#{kind}?"
|
366
|
+
kind_filter = :"#{kind}?"
|
367
|
+
define_method(recursive_kind) do
|
368
|
+
case type
|
369
|
+
when :send
|
370
|
+
receiver, method_name, *args = *self
|
371
|
+
COMPARISON_OPERATORS.include?(method_name) &&
|
372
|
+
receiver.send(recursive_kind) &&
|
373
|
+
args.all?(&recursive_kind)
|
374
|
+
when :begin, :pair, *OPERATOR_KEYWORDS, *COMPOSITE_LITERALS
|
375
|
+
children.all?(&recursive_kind)
|
376
|
+
else
|
377
|
+
send(kind_filter)
|
378
|
+
end
|
379
|
+
end
|
380
|
+
end
|
381
|
+
|
382
|
+
def variable?
|
383
|
+
VARIABLES.include?(type)
|
384
|
+
end
|
385
|
+
|
386
|
+
def reference?
|
387
|
+
REFERENCES.include?(type)
|
388
|
+
end
|
389
|
+
|
390
|
+
def keyword?
|
391
|
+
return true if special_keyword? || keyword_not?
|
392
|
+
return false unless KEYWORDS.include?(type)
|
393
|
+
|
394
|
+
!OPERATOR_KEYWORDS.include?(type) || loc.operator.is?(type.to_s)
|
395
|
+
end
|
396
|
+
|
397
|
+
def special_keyword?
|
398
|
+
SPECIAL_KEYWORDS.include?(source)
|
399
|
+
end
|
400
|
+
|
401
|
+
def keyword_not?
|
402
|
+
_receiver, method_name, *args = *self
|
403
|
+
args.empty? && method_name == :! && loc.selector.is?('not'.freeze)
|
404
|
+
end
|
405
|
+
|
406
|
+
def keyword_bang?
|
407
|
+
_receiver, method_name, *args = *self
|
408
|
+
args.empty? && method_name == :! && loc.selector.is?('!'.freeze)
|
409
|
+
end
|
410
|
+
|
411
|
+
def unary_operation?
|
412
|
+
return false unless loc.respond_to?(:selector) && loc.selector
|
413
|
+
Cop::Util.operator?(loc.selector.source.to_sym) &&
|
414
|
+
source_range.begin_pos == loc.selector.begin_pos
|
415
|
+
end
|
416
|
+
|
417
|
+
def binary_operation?
|
418
|
+
return false unless loc.respond_to?(:selector) && loc.selector
|
419
|
+
Cop::Util.operator?(method_name) &&
|
420
|
+
source_range.begin_pos != loc.selector.begin_pos
|
421
|
+
end
|
422
|
+
|
423
|
+
def chained?
|
424
|
+
return false unless argument?
|
425
|
+
|
426
|
+
receiver, _method_name, *_args = *parent
|
427
|
+
equal?(receiver)
|
428
|
+
end
|
429
|
+
|
430
|
+
def argument?
|
431
|
+
parent && parent.send_type?
|
432
|
+
end
|
433
|
+
|
434
|
+
def numeric_type?
|
435
|
+
int_type? || float_type?
|
436
|
+
end
|
437
|
+
|
438
|
+
def_matcher :guard_clause?, <<-PATTERN
|
439
|
+
[{(send nil {:raise :fail} ...) return break next} single_line?]
|
440
|
+
PATTERN
|
441
|
+
|
442
|
+
def_matcher :command?, '(send nil %1 ...)'
|
443
|
+
def_matcher :lambda?, '(block (send nil :lambda) ...)'
|
444
|
+
def_matcher :proc?, <<-PATTERN
|
445
|
+
{(block (send nil :proc) ...)
|
446
|
+
(block (send (const nil :Proc) :new) ...)
|
447
|
+
(send (const nil :Proc) :new)}
|
448
|
+
PATTERN
|
449
|
+
def_matcher :lambda_or_proc?, '{lambda? proc?}'
|
450
|
+
|
451
|
+
def_matcher :class_constructor?, <<-PATTERN
|
452
|
+
{ (send (const nil {:Class :Module}) :new ...)
|
453
|
+
(block (send (const nil {:Class :Module}) :new ...) ...)}
|
454
|
+
PATTERN
|
455
|
+
|
456
|
+
def_matcher :module_definition?, <<-PATTERN
|
457
|
+
{class module (casgn _ _ class_constructor?)}
|
458
|
+
PATTERN
|
459
|
+
|
460
|
+
# Some expressions are evaluated for their value, some for their side
|
461
|
+
# effects, and some for both
|
462
|
+
# If we know that an expression is useful only for its side effects, that
|
463
|
+
# means we can transform it in ways which preserve the side effects, but
|
464
|
+
# change the return value
|
465
|
+
# So, does the return value of this node matter? If we changed it to
|
466
|
+
# `(...; nil)`, might that affect anything?
|
467
|
+
#
|
468
|
+
# rubocop:disable Metrics/MethodLength
|
469
|
+
def value_used?
|
470
|
+
# Be conservative and return true if we're not sure.
|
471
|
+
return false if parent.nil?
|
472
|
+
|
473
|
+
case parent.type
|
474
|
+
when :array, :defined?, :dstr, :dsym, :eflipflop, :erange, :float,
|
475
|
+
:hash, :iflipflop, :irange, :not, :pair, :regexp, :str, :sym,
|
476
|
+
:when, :xstr
|
477
|
+
parent.value_used?
|
478
|
+
when :begin, :kwbegin
|
479
|
+
begin_value_used?
|
480
|
+
when :for
|
481
|
+
for_value_used?
|
482
|
+
when :case, :if
|
483
|
+
case_if_value_used?
|
484
|
+
when :while, :until, :while_post, :until_post
|
485
|
+
while_until_value_used?
|
486
|
+
else
|
487
|
+
true
|
488
|
+
end
|
489
|
+
end
|
490
|
+
# rubocop:enable Metrics/MethodLength
|
491
|
+
|
492
|
+
# Some expressions are evaluated for their value, some for their side
|
493
|
+
# effects, and some for both.
|
494
|
+
# If we know that expressions are useful only for their return values,
|
495
|
+
# and have no side effects, that means we can reorder them, change the
|
496
|
+
# number of times they are evaluated, or replace them with other
|
497
|
+
# expressions which are equivalent in value.
|
498
|
+
# So, is evaluation of this node free of side effects?
|
499
|
+
#
|
500
|
+
def pure?
|
501
|
+
# Be conservative and return false if we're not sure
|
502
|
+
case type
|
503
|
+
when :__FILE__, :__LINE__, :const, :cvar, :defined?, :false, :float,
|
504
|
+
:gvar, :int, :ivar, :lvar, :nil, :str, :sym, :true, :regopt
|
505
|
+
true
|
506
|
+
when :and, :array, :begin, :case, :dstr, :dsym, :eflipflop, :ensure,
|
507
|
+
:erange, :for, :hash, :if, :iflipflop, :irange, :kwbegin, :not,
|
508
|
+
:or, :pair, :regexp, :until, :until_post, :when, :while,
|
509
|
+
:while_post
|
510
|
+
child_nodes.all?(&:pure?)
|
511
|
+
else
|
512
|
+
false
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
protected
|
517
|
+
|
518
|
+
def visit_descendants(types, &block)
|
519
|
+
each_child_node do |child|
|
520
|
+
yield child if types.empty? || types.include?(child.type)
|
521
|
+
child.visit_descendants(types, &block)
|
522
|
+
end
|
523
|
+
end
|
524
|
+
|
525
|
+
private
|
526
|
+
|
527
|
+
def visit_ancestors(types)
|
528
|
+
last_node = self
|
529
|
+
|
530
|
+
while (current_node = last_node.parent)
|
531
|
+
yield current_node if types.empty? ||
|
532
|
+
types.include?(current_node.type)
|
533
|
+
last_node = current_node
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
def begin_value_used?
|
538
|
+
# the last child node determines the value of the parent
|
539
|
+
sibling_index == parent.children.size - 1 ? parent.value_used? : false
|
540
|
+
end
|
541
|
+
|
542
|
+
def for_value_used?
|
543
|
+
# `for var in enum; body; end`
|
544
|
+
# (for <var> <enum> <body>)
|
545
|
+
sibling_index == 2 ? parent.value_used? : true
|
546
|
+
end
|
547
|
+
|
548
|
+
def case_if_value_used?
|
549
|
+
# (case <condition> <when...>)
|
550
|
+
# (if <condition> <truebranch> <falsebranch>)
|
551
|
+
sibling_index.zero? ? true : parent.value_used?
|
552
|
+
end
|
553
|
+
|
554
|
+
def while_until_value_used?
|
555
|
+
# (while <condition> <body>) -> always evaluates to `nil`
|
556
|
+
sibling_index.zero?
|
557
|
+
end
|
558
|
+
|
559
|
+
def parent_module_name_part(node)
|
560
|
+
case node.type
|
561
|
+
when :class, :module, :casgn
|
562
|
+
# TODO: if constant name has cbase (leading ::), then we don't need
|
563
|
+
# to keep traversing up through nested classes/modules
|
564
|
+
node.defined_module_name
|
565
|
+
when :sclass
|
566
|
+
yield parent_module_name_for_sclass(node)
|
567
|
+
else # block
|
568
|
+
parent_module_name_for_block(node) { yield nil }
|
569
|
+
end
|
570
|
+
end
|
571
|
+
|
572
|
+
def parent_module_name_for_sclass(sclass_node)
|
573
|
+
# TODO: look for constant definition and see if it is nested
|
574
|
+
# inside a class or module
|
575
|
+
subject = sclass_node.children[0]
|
576
|
+
|
577
|
+
if subject.const_type?
|
578
|
+
"#<Class:#{subject.const_name}>"
|
579
|
+
elsif subject.self_type?
|
580
|
+
"#<Class:#{sclass_node.parent_module_name}>"
|
581
|
+
end
|
582
|
+
end
|
583
|
+
|
584
|
+
def parent_module_name_for_block(ancestor)
|
585
|
+
if ancestor.method_name == :class_eval
|
586
|
+
# `class_eval` with no receiver applies to whatever module or class
|
587
|
+
# we are currently in
|
588
|
+
return unless (receiver = ancestor.receiver)
|
589
|
+
yield unless receiver.const_type?
|
590
|
+
receiver.const_name
|
591
|
+
elsif !new_class_or_module_block?(ancestor)
|
592
|
+
yield
|
593
|
+
end
|
594
|
+
end
|
595
|
+
|
596
|
+
def new_class_or_module_block?(block_node)
|
597
|
+
receiver = block_node.receiver
|
598
|
+
|
599
|
+
block_node.method_name == :new &&
|
600
|
+
receiver && receiver.const_type? &&
|
601
|
+
(receiver.const_name == 'Class' || receiver.const_name == 'Module') &&
|
602
|
+
block_node.parent &&
|
603
|
+
block_node.parent.casgn_type?
|
604
|
+
end
|
605
|
+
end
|
606
|
+
end
|
607
|
+
end
|