summaryrefslogtreecommitdiff
path: root/rubocop/cop
diff options
context:
space:
mode:
authorGitLab Bot <gitlab-bot@gitlab.com>2020-02-13 15:08:52 +0000
committerGitLab Bot <gitlab-bot@gitlab.com>2020-02-13 15:08:52 +0000
commit0ab47b994caa80c5587f33dc818626b66cfdafe2 (patch)
tree5ef3976d2f84e3368903a67ba2dbd87a74b9a43c /rubocop/cop
parent1308dc5eb484ab0f8064989fc551ebdb4b1a7976 (diff)
downloadgitlab-ce-0ab47b994caa80c5587f33dc818626b66cfdafe2.tar.gz
Add latest changes from gitlab-org/gitlab@master
Diffstat (limited to 'rubocop/cop')
-rw-r--r--rubocop/cop/gitlab/keys-first-and-values-first.rb55
1 files changed, 55 insertions, 0 deletions
diff --git a/rubocop/cop/gitlab/keys-first-and-values-first.rb b/rubocop/cop/gitlab/keys-first-and-values-first.rb
new file mode 100644
index 00000000000..9b68957cba2
--- /dev/null
+++ b/rubocop/cop/gitlab/keys-first-and-values-first.rb
@@ -0,0 +1,55 @@
+module RuboCop
+ module Cop
+ module Gitlab
+ class KeysFirstAndValuesFirst < RuboCop::Cop::Cop
+ FIRST_PATTERN = /\Afirst\z/.freeze
+
+ def message(used_method)
+ <<~MSG
+ Don't use `.keys.first` and `.values.first`.
+ Instead use `.each_key.first` and `.each_value.first` (or `.first.first` and `first.second`)
+
+ This will reduce memory usage and execution time.
+ MSG
+ end
+
+ def on_send(node)
+ if find_on_keys_or_values?(node)
+ add_offense(node, location: :selector, message: message(node.method_name))
+ end
+ end
+
+ def autocorrect(node)
+ lambda do |corrector|
+ replace_with = if node.descendants.first.method_name == :values
+ '.each_value'
+ elsif node.descendants.first.method_name == :keys
+ '.each_key'
+ else
+ throw("Expect '.values.first' or '.keys.first', but get #{node.descendants.first.method_name}.first")
+ end
+
+ upto_including_keys_or_values = node.descendants.first.source_range
+ before_keys_or_values = node.descendants[1].source_range
+ range_to_replace = node.source_range
+ .with(begin_pos: before_keys_or_values.end_pos,
+ end_pos: upto_including_keys_or_values.end_pos)
+ corrector.replace(range_to_replace, replace_with)
+ end
+ end
+
+ def find_on_keys_or_values?(node)
+ chained_on_node = node.descendants.first
+ node.method_name.to_s =~ FIRST_PATTERN &&
+ chained_on_node.is_a?(RuboCop::AST::SendNode) &&
+ [:keys, :values].include?(chained_on_node.method_name) &&
+ node.descendants[1]
+ end
+
+ def method_name_for_node(node)
+ children[1].to_s
+ end
+ end
+ end
+ end
+end