mirror of
				https://gitea.invidious.io/iv-org/shard-ameba.git
				synced 2024-08-15 00:53:29 +00:00 
			
		
		
		
	New rule: Performance/Count
This commit is contained in:
		
							parent
							
								
									f4680a75f4
								
							
						
					
					
						commit
						446f557c23
					
				
					 2 changed files with 115 additions and 0 deletions
				
			
		
							
								
								
									
										67
									
								
								spec/ameba/rule/performance/count_spec.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										67
									
								
								spec/ameba/rule/performance/count_spec.cr
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,67 @@
 | 
				
			||||||
 | 
					require "../../../spec_helper"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module Ameba::Rule::Performance
 | 
				
			||||||
 | 
					  subject = Count.new
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  describe Count do
 | 
				
			||||||
 | 
					    it "passes if there is no potential performance improvements" do
 | 
				
			||||||
 | 
					      source = Source.new %(
 | 
				
			||||||
 | 
					        [1, 2, 3].select { |e| e > 2 }
 | 
				
			||||||
 | 
					        [1, 2, 3].reject { |e| e < 2 }
 | 
				
			||||||
 | 
					        [1, 2, 3].count { |e| e > 2 && e.odd? }
 | 
				
			||||||
 | 
					        [1, 2, 3].count { |e| e < 2 && e.even? }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        User.select("field AS name").count
 | 
				
			||||||
 | 
					        Company.select(:value).count
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      subject.catch(source).should be_valid
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it "reports if there is a select followed by size" do
 | 
				
			||||||
 | 
					      source = Source.new %(
 | 
				
			||||||
 | 
					        [1, 2, 3].select { |e| e > 2 }.size
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      subject.catch(source).should_not be_valid
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it "reports if there is a reject followed by size" do
 | 
				
			||||||
 | 
					      source = Source.new %(
 | 
				
			||||||
 | 
					        [1, 2, 3].reject { |e| e < 2 }.size
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      subject.catch(source).should_not be_valid
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it "reports if a block shorthand used" do
 | 
				
			||||||
 | 
					      source = Source.new %(
 | 
				
			||||||
 | 
					        [1, 2, 3].reject(&.empty?).size
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      subject.catch(source).should_not be_valid
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    context "properties" do
 | 
				
			||||||
 | 
					      it "allows to configure object caller names" do
 | 
				
			||||||
 | 
					        source = Source.new %(
 | 
				
			||||||
 | 
					          [1, 2, 3].reject(&.empty?).size
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        rule = Rule::Performance::Count.new
 | 
				
			||||||
 | 
					        rule.object_call_names = %w(select)
 | 
				
			||||||
 | 
					        rule.catch(source).should be_valid
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it "reports rule, pos and message" do
 | 
				
			||||||
 | 
					      s = Source.new %(
 | 
				
			||||||
 | 
					        File.read(path)
 | 
				
			||||||
 | 
					          .split("\n")
 | 
				
			||||||
 | 
					          .reject(&.empty?)
 | 
				
			||||||
 | 
					          .size
 | 
				
			||||||
 | 
					      ), "source.cr"
 | 
				
			||||||
 | 
					      subject.catch(s).should_not be_valid
 | 
				
			||||||
 | 
					      issue = s.issues.first
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      issue.rule.should_not be_nil
 | 
				
			||||||
 | 
					      issue.location.to_s.should eq "source.cr:2:9"
 | 
				
			||||||
 | 
					      issue.message.should eq "Use `count {...}` instead of `reject {...}.size`."
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
							
								
								
									
										48
									
								
								src/ameba/rule/performance/count.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/ameba/rule/performance/count.cr
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,48 @@
 | 
				
			||||||
 | 
					module Ameba::Rule::Performance
 | 
				
			||||||
 | 
					  # This rule is used to identify usage of `size` calls that follow to object
 | 
				
			||||||
 | 
					  # caller names `select` and `reject`.
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # For example, this is considered invalid:
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # ```
 | 
				
			||||||
 | 
					  # [1, 2, 3].select { |e| e > 2 }.size
 | 
				
			||||||
 | 
					  # [1, 2, 3].reject { |e| e < 2 }.size
 | 
				
			||||||
 | 
					  # [1, 2, 3].select(&.< 2).size
 | 
				
			||||||
 | 
					  # [0, 1, 2].reject(&.zero?).size
 | 
				
			||||||
 | 
					  # ```
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # And it should be written as this:
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # ```
 | 
				
			||||||
 | 
					  # [1, 2, 3].count { |e| e > 2 }
 | 
				
			||||||
 | 
					  # [1, 2, 3].count { |e| e < 2 }
 | 
				
			||||||
 | 
					  # [1, 2, 3].count(&.< 2)
 | 
				
			||||||
 | 
					  # [0, 1, 2].count(&.zero?)
 | 
				
			||||||
 | 
					  # ```
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  struct Count < Base
 | 
				
			||||||
 | 
					    SIZE_CALL_NAME = "size"
 | 
				
			||||||
 | 
					    MSG = "Use `count {...}` instead of `%s {...}.#{SIZE_CALL_NAME}`."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    properties do
 | 
				
			||||||
 | 
					      object_call_names : Array(String) = %w(select reject)
 | 
				
			||||||
 | 
					      description "Identifies usage of `size` calls that follow to object \
 | 
				
			||||||
 | 
					                   caller names (`select`/`reject` by default)."
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test(source)
 | 
				
			||||||
 | 
					      AST::NodeVisitor.new self, source
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test(source, node : Crystal::Call)
 | 
				
			||||||
 | 
					      return unless node.name == SIZE_CALL_NAME && (obj = node.obj)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if obj.is_a?(Crystal::Call) &&
 | 
				
			||||||
 | 
					         object_call_names.includes?(obj.name) && !obj.block.nil?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        issue_for obj, MSG % obj.name
 | 
				
			||||||
 | 
					      end
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue