mirror of
				https://gitea.invidious.io/iv-org/shard-ameba.git
				synced 2024-08-15 00:53:29 +00:00 
			
		
		
		
	Merge pull request #136 from crystal-ameba/feat/empty-loop
New rule: EmptyLoop
This commit is contained in:
		
						commit
						81631af631
					
				
					 3 changed files with 157 additions and 0 deletions
				
			
		
							
								
								
									
										88
									
								
								spec/ameba/rule/lint/empty_loop_spec.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										88
									
								
								spec/ameba/rule/lint/empty_loop_spec.cr
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,88 @@
 | 
				
			||||||
 | 
					require "../../../spec_helper"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					module Ameba::Rule::Lint
 | 
				
			||||||
 | 
					  describe EmptyLoop do
 | 
				
			||||||
 | 
					    subject = EmptyLoop.new
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it "does not report if there are not empty loops" do
 | 
				
			||||||
 | 
					      s = Source.new %(
 | 
				
			||||||
 | 
					        a = 1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        while a < 10
 | 
				
			||||||
 | 
					          a += 1
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        until a == 10
 | 
				
			||||||
 | 
					         a += 1
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        loop do
 | 
				
			||||||
 | 
					          a += 1
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      subject.catch(s).should be_valid
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it "reports if there is an empty while loop" do
 | 
				
			||||||
 | 
					      s = Source.new %(
 | 
				
			||||||
 | 
					        a = 1
 | 
				
			||||||
 | 
					        while true
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      subject.catch(s).should_not be_valid
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it "doesn't report if while loop has non-literals in cond block" do
 | 
				
			||||||
 | 
					      s = Source.new %(
 | 
				
			||||||
 | 
					        a = 1
 | 
				
			||||||
 | 
					        while a = gets.to_s
 | 
				
			||||||
 | 
					          # nothing here
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      subject.catch(s).should be_valid
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it "reports if there is an empty until loop" do
 | 
				
			||||||
 | 
					      s = Source.new %(
 | 
				
			||||||
 | 
					        do_something
 | 
				
			||||||
 | 
					        until false
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      subject.catch(s).should_not be_valid
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it "doesn't report if until loop has non-literals in cond block" do
 | 
				
			||||||
 | 
					      s = Source.new %(
 | 
				
			||||||
 | 
					        until socket_open?
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      subject.catch(s).should be_valid
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it "reports if there an empty loop" do
 | 
				
			||||||
 | 
					      s = Source.new %(
 | 
				
			||||||
 | 
					        a = 1
 | 
				
			||||||
 | 
					        loop do
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      )
 | 
				
			||||||
 | 
					      subject.catch(s).should_not be_valid
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    it "reports rule, message and location" do
 | 
				
			||||||
 | 
					      s = Source.new %(
 | 
				
			||||||
 | 
					        a = 1
 | 
				
			||||||
 | 
					        loop do
 | 
				
			||||||
 | 
					          # comment goes here
 | 
				
			||||||
 | 
					        end
 | 
				
			||||||
 | 
					      ), "source.cr"
 | 
				
			||||||
 | 
					      subject.catch(s).should_not be_valid
 | 
				
			||||||
 | 
					      s.issues.size.should eq 1
 | 
				
			||||||
 | 
					      issue = s.issues.first
 | 
				
			||||||
 | 
					      issue.rule.should_not be_nil
 | 
				
			||||||
 | 
					      issue.location.to_s.should eq "source.cr:2:1"
 | 
				
			||||||
 | 
					      issue.end_location.to_s.should eq "source.cr:4:3"
 | 
				
			||||||
 | 
					      issue.message.should eq EmptyLoop::MSG
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
| 
						 | 
					@ -25,6 +25,7 @@ module Ameba::AST
 | 
				
			||||||
    Var,
 | 
					    Var,
 | 
				
			||||||
    When,
 | 
					    When,
 | 
				
			||||||
    While,
 | 
					    While,
 | 
				
			||||||
 | 
					    Until,
 | 
				
			||||||
  ]
 | 
					  ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  # An AST Visitor that traverses the source and allows all nodes
 | 
					  # An AST Visitor that traverses the source and allows all nodes
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										68
									
								
								src/ameba/rule/lint/empty_loop.cr
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										68
									
								
								src/ameba/rule/lint/empty_loop.cr
									
										
									
									
									
										Normal file
									
								
							| 
						 | 
					@ -0,0 +1,68 @@
 | 
				
			||||||
 | 
					module Ameba::Rule::Lint
 | 
				
			||||||
 | 
					  # A rule that disallows empty loops.
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # This is considered invalid:
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # ```
 | 
				
			||||||
 | 
					  # while false
 | 
				
			||||||
 | 
					  # end
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # until 10
 | 
				
			||||||
 | 
					  # end
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # loop do
 | 
				
			||||||
 | 
					  #   # nothing here
 | 
				
			||||||
 | 
					  # end
 | 
				
			||||||
 | 
					  # ```
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # And this is valid:
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # ```
 | 
				
			||||||
 | 
					  # a = 1
 | 
				
			||||||
 | 
					  # while a < 10
 | 
				
			||||||
 | 
					  #   a += 1
 | 
				
			||||||
 | 
					  # end
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # until socket_opened?
 | 
				
			||||||
 | 
					  # end
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # loop do
 | 
				
			||||||
 | 
					  #   do_something_here
 | 
				
			||||||
 | 
					  # end
 | 
				
			||||||
 | 
					  # ```
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # YAML configuration example:
 | 
				
			||||||
 | 
					  #
 | 
				
			||||||
 | 
					  # ```
 | 
				
			||||||
 | 
					  # Lint/EmptyLoop:
 | 
				
			||||||
 | 
					  #   Enabled: true
 | 
				
			||||||
 | 
					  # ```
 | 
				
			||||||
 | 
					  struct EmptyLoop < Base
 | 
				
			||||||
 | 
					    include AST::Util
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    properties do
 | 
				
			||||||
 | 
					      description "Disallows empty loops"
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    MSG = "Empty loop detected"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test(source, node : Crystal::Call)
 | 
				
			||||||
 | 
					      return unless loop?(node)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      check_node(source, node, node.block)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test(source, node : Crystal::While)
 | 
				
			||||||
 | 
					      check_node(source, node, node.body) if literal?(node.cond)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def test(source, node : Crystal::Until)
 | 
				
			||||||
 | 
					      check_node(source, node, node.body) if literal?(node.cond)
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    private def check_node(source, node, loop_body)
 | 
				
			||||||
 | 
					      body = loop_body.is_a?(Crystal::Block) ? loop_body.body : loop_body
 | 
				
			||||||
 | 
					      issue_for node, MSG if body.nil? || body.nop?
 | 
				
			||||||
 | 
					    end
 | 
				
			||||||
 | 
					  end
 | 
				
			||||||
 | 
					end
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue