# encoding: UTF-8moduleRuboCopmoduleCopmodulePerformance# This cop is used to identify usages of `shuffle.first`, `shuffle.last`# and `shuffle[]` and change them to use `sample` instead.## @example# # bad# [1, 2, 3].shuffle.first# [1, 2, 3].shuffle.first(2)# [1, 2, 3].shuffle.last# [1, 2, 3].shuffle[2]# [1, 2, 3].shuffle[0, 2] # sample(2) will do the same# [1, 2, 3].shuffle[0..2] # sample(3) will do the same# [1, 2, 3].shuffle(random: Random.new).first## # good# [1, 2, 3].shuffle# [1, 2, 3].sample# [1, 2, 3].sample(3)# [1, 2, 3].shuffle[1, 3] # sample(3) might return a longer Array# [1, 2, 3].shuffle[1..3] # sample(3) might return a longer Array# [1, 2, 3].shuffle[foo, bar]# [1, 2, 3].shuffle(random: Random.new)classSample<CopMSG='Use `%<correct>s` instead of `%<incorrect>s`.'defon_send(node)analyzer=ShuffleAnalyzer.new(node)returnunlessanalyzer.offensive?add_offense(node,analyzer.source_range,analyzer.message)enddefautocorrect(node)ShuffleAnalyzer.new(node).autocorrectend# An internal class for representing a shuffle + method node analyzer.classShuffleAnalyzerdefinitialize(shuffle_node)@shuffle_node=shuffle_node@method_node=shuffle_node.parentenddefautocorrect->(corrector){corrector.replace(source_range,correct)}enddefmessageformat(MSG,correct: correct,incorrect: source_range.source)enddefoffensive?shuffle_node.to_a[1]==:shuffle&&corrigible?enddefsource_rangeParser::Source::Range.new(shuffle_node.loc.expression.source_buffer,shuffle_node.loc.selector.begin_pos,method_node.loc.expression.end_pos)endprivateattr_reader:method_node,:shuffle_nodedefcorrectargs=[sample_arg,shuffle_arg].compact.join(', ')args.empty??'sample':"sample(#{args})"enddefcorrigible?casemethodwhen:first,:lastthentruewhen:[]thensample_size!=:unknownelsefalseendenddefmethodmethod_node.to_a[1]enddefmethod_arg_,_,arg=*method_nodearg.loc.expression.sourceifargend# FIXME: use Range#size once Ruby 1.9 support is droppeddefrange_size(range_node)vals=*range_nodereturn:unknownunlessvals.all?(&:int_type?)low,high=*vals.map(&:to_a).map(&:first)return:unknownunlesslow.zero?&&high>=0caserange_node.typewhen:erangethenhigh-lowwhen:irangethenhigh-low+1endenddefsample_argcasemethodwhen:first,:lastthenmethod_argwhen:[]thensample_sizeendenddefsample_size_,_,*args=*method_nodecaseargs.sizewhen1arg=args.firstcasearg.typewhen:erange,:irangethenrange_size(arg)when:intthenarg.to_a.first.zero??nil::unknownelse:unknownendwhen2first,second=*argsreturn:unknownunlessfirst.int_type?&&first.to_a.first.zero?second.int_type??second.to_a.first::unknownendenddefshuffle_arg_,_,arg=*shuffle_nodearg.loc.expression.sourceifargendendendendendend