mirror of
				https://gitea.invidious.io/iv-org/shard-ameba.git
				synced 2024-08-15 00:53:29 +00:00 
			
		
		
		
	Merge pull request #327 from crystal-ameba/Sija/specs-cleanup
This commit is contained in:
		
						commit
						b9bc5aaab2
					
				
					 66 changed files with 354 additions and 841 deletions
				
			
		|  | @ -10,29 +10,29 @@ module Ameba::AST | |||
|     describe ".of" do | ||||
|       context "Crystal::If" do | ||||
|         it "constructs a branch in If.cond" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method | ||||
|               if a = get_something # --> Crystal::Assign | ||||
|                 puts a | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = get_something" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in If.then" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method | ||||
|               if true | ||||
|                 a = 2 # --> Crystal::Assign | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 2" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in If.else" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method | ||||
|               if true | ||||
|                 nil | ||||
|  | @ -40,45 +40,45 @@ module Ameba::AST | |||
|                 a = 2 # --> Crystal::Assign | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 2" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in inline If" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               a = 0 if a == 2 # --> Crystal::Assign | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 0" | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context "Crystal::Unless" do | ||||
|         it "constructs a branch in Unless.cond" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method | ||||
|               unless a = get_something # --> Crystal::Assign | ||||
|                 puts a | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = get_something" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in Unless.then" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method | ||||
|               unless true | ||||
|                 a = 2 # --> Crystal::Assign | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 2" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a new branch in Unless.else" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method | ||||
|               unless true | ||||
|                 nil | ||||
|  | @ -86,188 +86,188 @@ module Ameba::AST | |||
|                 a = 2 # --> Crystal::Assign | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 2" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in inline Unless" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               (a = 0; b = 3) unless a == 2 # --> Crystal::Expressions | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "(a = 0\nb = 3)" | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context "Crystal::BinaryOp" do | ||||
|         it "constructs a branch in left node" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               (a = 2) && do_something | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "(a = 2)" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in right node" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               do_something || (a = 0) | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "(a = 0)" | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context "Crystal::Case" do | ||||
|         it "constructs a branch in cond" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               case (a = 2) | ||||
|               when true then nil | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "(a = 2)" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in when" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               case a | ||||
|               when a = 3 then nil | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "when a = 3\n  nil\n" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in else" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               case a | ||||
|               when true then nil | ||||
|               else a = 4 | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 4" | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context "Crystal::While" do | ||||
|         it "constructs a branch in cond" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               while a = 1 | ||||
|                 nil | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 1" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in body" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               while true | ||||
|                 b = (a = 1) | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "b = (a = 1)" | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context "Crystal::Until" do | ||||
|         it "constructs a branch in cond" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               until a = 1 | ||||
|                 nil | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 1" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in body" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               until false | ||||
|                 b = (a = 1) | ||||
|               end | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "b = (a = 1)" | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context "Crystal::ExceptionHandler" do | ||||
|         it "constructs a branch in body" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               a = 1 | ||||
|             rescue | ||||
|               nil | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 1" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in a rescue" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|             rescue | ||||
|               a = 1 | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 1" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in else" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|             rescue | ||||
|             else | ||||
|               a = 1 | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 1" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in ensure" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|             rescue | ||||
|             ensure | ||||
|               a = 1 | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 1" | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context "Crystal::MacroIf" do | ||||
|         it "constructs a branch in cond" do | ||||
|           branch = branch_of_assign_in_def %( | ||||
|           branch = branch_of_assign_in_def <<-CRYSTAL | ||||
|             def method(a) | ||||
|               {% if a = 2 %} | ||||
|               {% end %} | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch.to_s.should eq "a = 2" | ||||
|         end | ||||
| 
 | ||||
|         it "constructs a branch in then" do | ||||
|           nodes = as_nodes %( | ||||
|           nodes = as_nodes <<-CRYSTAL | ||||
|             def method(a) | ||||
|               {% if true %} | ||||
|                 a = 2 | ||||
|               {% end %} | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first) | ||||
|           branch.to_s.strip.should eq "a = 2" | ||||
|         end | ||||
|  | @ -275,24 +275,24 @@ module Ameba::AST | |||
| 
 | ||||
|       context "Crystal::MacroFor" do | ||||
|         it "constructs a branch in body" do | ||||
|           nodes = as_nodes %( | ||||
|           nodes = as_nodes <<-CRYSTAL | ||||
|             def method(a) | ||||
|               {% for x in [1, 2, 3] %} | ||||
|                 a = 2 | ||||
|               {% end %} | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           branch = Branch.of(nodes.macro_literal_nodes.first, nodes.def_nodes.first) | ||||
|           branch.to_s.strip.should eq "a = 2" | ||||
|         end | ||||
|       end | ||||
| 
 | ||||
|       it "returns nil if branch does not exist" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           def method | ||||
|             a = 2 | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         branch = Branch.of(nodes.assign_nodes.first, nodes.def_nodes.first) | ||||
|         branch.should be_nil | ||||
|       end | ||||
|  | @ -300,11 +300,11 @@ module Ameba::AST | |||
| 
 | ||||
|     describe "#initialize" do | ||||
|       it "creates new branch" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           if true | ||||
|             a = 2 | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         branchable = Branchable.new nodes.if_nodes.first | ||||
|         branch = Branch.new nodes.assign_nodes.first, branchable | ||||
|         branch.node.should_not be_nil | ||||
|  | @ -313,22 +313,22 @@ module Ameba::AST | |||
| 
 | ||||
|     describe "delegation" do | ||||
|       it "delegates to_s to node" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           if true | ||||
|             a = 2 | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         branchable = Branchable.new nodes.if_nodes.first | ||||
|         branch = Branch.new nodes.assign_nodes.first, branchable | ||||
|         branch.to_s.should eq branch.node.to_s | ||||
|       end | ||||
| 
 | ||||
|       it "delegates locations to node" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           if true | ||||
|             a = 2 | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         branchable = Branchable.new nodes.if_nodes.first | ||||
|         branch = Branch.new nodes.assign_nodes.first, branchable | ||||
|         branch.location.should eq branch.node.location | ||||
|  | @ -338,22 +338,22 @@ module Ameba::AST | |||
| 
 | ||||
|     describe "#in_loop?" do | ||||
|       it "returns true if branch is in a loop" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           while true | ||||
|             a = 1 | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         branchable = Branchable.new nodes.while_nodes.first | ||||
|         branch = Branch.new nodes.assign_nodes.first, branchable | ||||
|         branch.in_loop?.should be_true | ||||
|       end | ||||
| 
 | ||||
|       it "returns false if branch is not in a loop" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           if a > 2 | ||||
|             a = 1 | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         branchable = Branchable.new nodes.if_nodes.first | ||||
|         branch = Branch.new nodes.assign_nodes.first, branchable | ||||
|         branch.in_loop?.should be_false | ||||
|  |  | |||
|  | @ -4,20 +4,20 @@ module Ameba::AST | |||
|   describe Branchable do | ||||
|     describe "#initialize" do | ||||
|       it "creates a new branchable" do | ||||
|         branchable = Branchable.new as_node %(a = 2 if true) | ||||
|         branchable = Branchable.new as_node "a = 2 if true" | ||||
|         branchable.node.should_not be_nil | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     describe "delegation" do | ||||
|       it "delegates to_s to @node" do | ||||
|         node = as_node %(a = 2 if true) | ||||
|         node = as_node "a = 2 if true" | ||||
|         branchable = Branchable.new node | ||||
|         branchable.to_s.should eq node.to_s | ||||
|       end | ||||
| 
 | ||||
|       it "delegates locations to @node" do | ||||
|         node = as_node %(a = 2 if true) | ||||
|         node = as_node "a = 2 if true" | ||||
|         branchable = Branchable.new node | ||||
|         branchable.location.should eq node.location | ||||
|         branchable.end_location.should eq node.end_location | ||||
|  | @ -26,22 +26,22 @@ module Ameba::AST | |||
| 
 | ||||
|     describe "#loop?" do | ||||
|       it "returns true if it is a while loop" do | ||||
|         branchable = Branchable.new as_node %(while true; a = 2; end) | ||||
|         branchable = Branchable.new as_node "while true; a = 2; end" | ||||
|         branchable.loop?.should be_true | ||||
|       end | ||||
| 
 | ||||
|       it "returns true if it is the until loop" do | ||||
|         branchable = Branchable.new as_node %(until false; a = 2; end) | ||||
|         branchable = Branchable.new as_node "until false; a = 2; end" | ||||
|         branchable.loop?.should be_true | ||||
|       end | ||||
| 
 | ||||
|       it "returns true if it is loop" do | ||||
|         branchable = Branchable.new as_node %(loop {}) | ||||
|         branchable = Branchable.new as_node "loop {}" | ||||
|         branchable.loop?.should be_true | ||||
|       end | ||||
| 
 | ||||
|       it "returns false otherwise" do | ||||
|         branchable = Branchable.new as_node %(a = 2 if true) | ||||
|         branchable = Branchable.new as_node "a = 2 if true" | ||||
|         branchable.loop?.should be_false | ||||
|       end | ||||
|     end | ||||
|  |  | |||
|  | @ -18,7 +18,7 @@ module Ameba::AST | |||
|         end | ||||
| 
 | ||||
|         it "delegates locations to @node" do | ||||
|           node = as_node %(break if true) | ||||
|           node = as_node("break if true") | ||||
|           flow_expression = FlowExpression.new node, false | ||||
|           flow_expression.location.should eq node.location | ||||
|           flow_expression.end_location.should eq node.end_location | ||||
|  | @ -27,20 +27,20 @@ module Ameba::AST | |||
| 
 | ||||
|       describe "#unreachable_nodes" do | ||||
|         it "returns unreachable nodes" do | ||||
|           nodes = as_nodes %( | ||||
|           nodes = as_nodes <<-CRYSTAL | ||||
|             def foobar | ||||
|               return | ||||
|               a = 1 | ||||
|               a = 2 | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           node = nodes.expressions_nodes.first | ||||
|           flow_expression = FlowExpression.new node, false | ||||
|           flow_expression.unreachable_nodes.should eq nodes.assign_nodes | ||||
|         end | ||||
| 
 | ||||
|         it "returns nil if there is no unreachable node after loop" do | ||||
|           nodes = as_nodes %( | ||||
|           nodes = as_nodes <<-CRYSTAL | ||||
|             def run | ||||
|               idx = items.size - 1 | ||||
|               while 0 <= idx | ||||
|  | @ -49,19 +49,19 @@ module Ameba::AST | |||
| 
 | ||||
|               puts "foo" | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           node = nodes.expressions_nodes.first | ||||
|           flow_expression = FlowExpression.new node, false | ||||
|           flow_expression.unreachable_nodes.empty?.should eq true | ||||
|         end | ||||
| 
 | ||||
|         it "returns nil if there is no unreachable node" do | ||||
|           nodes = as_nodes %( | ||||
|           nodes = as_nodes <<-CRYSTAL | ||||
|             def foobar | ||||
|               a = 1 | ||||
|               return a | ||||
|             end | ||||
|           ) | ||||
|             CRYSTAL | ||||
|           node = nodes.expressions_nodes.first | ||||
|           flow_expression = FlowExpression.new node, false | ||||
|           flow_expression.unreachable_nodes.empty?.should eq true | ||||
|  |  | |||
|  | @ -49,14 +49,14 @@ module Ameba::AST | |||
| 
 | ||||
|   describe "#references?" do | ||||
|     it "returns true if current scope references variable" do | ||||
|       nodes = as_nodes %( | ||||
|       nodes = as_nodes <<-CRYSTAL | ||||
|         def method | ||||
|           a = 2 | ||||
|           block do | ||||
|             3.times { |i| a = a + i } | ||||
|           end | ||||
|         end | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       scope = Scope.new nodes.def_nodes.first | ||||
|       var_node = nodes.var_nodes.first | ||||
|       scope.add_variable var_node | ||||
|  | @ -69,14 +69,14 @@ module Ameba::AST | |||
|     end | ||||
| 
 | ||||
|     it "returns false if inner scopes are not checked" do | ||||
|       nodes = as_nodes %( | ||||
|       nodes = as_nodes <<-CRYSTAL | ||||
|         def method | ||||
|           a = 2 | ||||
|           block do | ||||
|             3.times { |i| a = a + i } | ||||
|           end | ||||
|         end | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       scope = Scope.new nodes.def_nodes.first | ||||
|       var_node = nodes.var_nodes.first | ||||
|       scope.add_variable var_node | ||||
|  | @ -89,7 +89,7 @@ module Ameba::AST | |||
|     end | ||||
| 
 | ||||
|     it "returns false if current scope does not reference variable" do | ||||
|       nodes = as_nodes %( | ||||
|       nodes = as_nodes <<-CRYSTAL | ||||
|         def method | ||||
|           a = 2 | ||||
|           block do | ||||
|  | @ -97,7 +97,7 @@ module Ameba::AST | |||
|             3.times { |i| b = b + i } | ||||
|           end | ||||
|         end | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       scope = Scope.new nodes.def_nodes.first | ||||
|       var_node = nodes.var_nodes.first | ||||
|       scope.add_variable var_node | ||||
|  | @ -150,57 +150,53 @@ module Ameba::AST | |||
| 
 | ||||
|   describe "#block?" do | ||||
|     it "returns true if Crystal::Block" do | ||||
|       nodes = as_nodes %( | ||||
|         3.times {} | ||||
|       ) | ||||
|       nodes = as_nodes("3.times {}") | ||||
|       scope = Scope.new nodes.block_nodes.first | ||||
|       scope.block?.should be_true | ||||
|     end | ||||
| 
 | ||||
|     it "returns false otherwise" do | ||||
|       scope = Scope.new as_node "a = 1" | ||||
|       scope = Scope.new as_node("a = 1") | ||||
|       scope.block?.should be_false | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe "#spawn_block?" do | ||||
|     it "returns true if a node is a spawn block" do | ||||
|       nodes = as_nodes %( | ||||
|         spawn {} | ||||
|       ) | ||||
|       nodes = as_nodes("spawn {}") | ||||
|       scope = Scope.new nodes.block_nodes.first | ||||
|       scope.spawn_block?.should be_true | ||||
|     end | ||||
| 
 | ||||
|     it "returns false otherwise" do | ||||
|       scope = Scope.new as_node "a = 1" | ||||
|       scope = Scope.new as_node("a = 1") | ||||
|       scope.spawn_block?.should be_false | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe "#in_macro?" do | ||||
|     it "returns true if Crystal::Macro" do | ||||
|       nodes = as_nodes %( | ||||
|       nodes = as_nodes <<-CRYSTAL | ||||
|         macro included | ||||
|         end | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       scope = Scope.new nodes.macro_nodes.first | ||||
|       scope.in_macro?.should be_true | ||||
|     end | ||||
| 
 | ||||
|     it "returns true if node is nested to Crystal::Macro" do | ||||
|       nodes = as_nodes %( | ||||
|       nodes = as_nodes <<-CRYSTAL | ||||
|         macro included | ||||
|           {{ @type.each do |type| a = type end }} | ||||
|         end | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       outer_scope = Scope.new nodes.macro_nodes.first | ||||
|       scope = Scope.new nodes.block_nodes.first, outer_scope | ||||
|       scope.in_macro?.should be_true | ||||
|     end | ||||
| 
 | ||||
|     it "returns false otherwise" do | ||||
|       scope = Scope.new as_node "a = 1" | ||||
|       scope = Scope.new as_node("a = 1") | ||||
|       scope.in_macro?.should be_false | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -41,15 +41,14 @@ module Ameba::AST | |||
| 
 | ||||
|     describe "#branch" do | ||||
|       it "returns the branch of the assignment" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           def method(a) | ||||
|             if a | ||||
|               a = 3  # --> Crystal::Expressions | ||||
|               puts a | ||||
|             end | ||||
|           end | ||||
|         ) | ||||
| 
 | ||||
|           CRYSTAL | ||||
|         scope = Scope.new nodes.def_nodes.first | ||||
|         variable = Variable.new(nodes.var_nodes.first, scope) | ||||
|         assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) | ||||
|  | @ -58,7 +57,7 @@ module Ameba::AST | |||
|       end | ||||
| 
 | ||||
|       it "returns inner branch" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           def method(a, b) | ||||
|             if a | ||||
|               if b | ||||
|  | @ -66,7 +65,7 @@ module Ameba::AST | |||
|               end | ||||
|             end | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         scope = Scope.new nodes.def_nodes.first | ||||
|         variable = Variable.new(nodes.var_nodes.first, scope) | ||||
|         assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) | ||||
|  | @ -75,12 +74,11 @@ module Ameba::AST | |||
|       end | ||||
| 
 | ||||
|       it "returns nil if assignment does not have a branch" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           def method(a) | ||||
|             a = 2 | ||||
|           end | ||||
|         ) | ||||
| 
 | ||||
|           CRYSTAL | ||||
|         scope = Scope.new nodes.def_nodes.first | ||||
|         variable = Variable.new(nodes.var_nodes.first, scope) | ||||
|         assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) | ||||
|  | @ -90,12 +88,11 @@ module Ameba::AST | |||
| 
 | ||||
|     describe "#transformed?" do | ||||
|       it "returns false if the assignment is not transformed by the compiler" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           def method(a) | ||||
|             a = 2 | ||||
|           end | ||||
|         ) | ||||
| 
 | ||||
|           CRYSTAL | ||||
|         scope = Scope.new nodes.def_nodes.first | ||||
|         variable = Variable.new(nodes.var_nodes.first, scope) | ||||
|         assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) | ||||
|  | @ -103,11 +100,10 @@ module Ameba::AST | |||
|       end | ||||
| 
 | ||||
|       it "returns true if the assignment is transformed by the compiler" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           array.each do |(a, b)| | ||||
|           end | ||||
|         ) | ||||
| 
 | ||||
|           CRYSTAL | ||||
|         scope = Scope.new nodes.block_nodes.first | ||||
|         variable = Variable.new(nodes.var_nodes.first, scope) | ||||
|         assignment = Assignment.new(nodes.assign_nodes.first, variable, scope) | ||||
|  |  | |||
|  | @ -79,12 +79,12 @@ module Ameba::AST | |||
| 
 | ||||
|     describe "#captured_by_block?" do | ||||
|       it "returns truthy if the variable is captured by block" do | ||||
|         nodes = as_nodes %( | ||||
|         nodes = as_nodes <<-CRYSTAL | ||||
|           def method | ||||
|             a = 2 | ||||
|             3.times { |i| a = a + i } | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         scope = Scope.new nodes.def_nodes.first | ||||
|         var_node = nodes.var_nodes.first | ||||
|         scope.add_variable var_node | ||||
|  | @ -95,12 +95,12 @@ module Ameba::AST | |||
|         variable.captured_by_block?.should be_truthy | ||||
|       end | ||||
| 
 | ||||
|       it "returns falsey if the variable is not captured by the block" do | ||||
|         scope = Scope.new as_node %( | ||||
|       it "returns falsy if the variable is not captured by the block" do | ||||
|         scope = Scope.new as_node <<-CRYSTAL | ||||
|           def method | ||||
|             a = 1 | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         scope.add_variable Crystal::Var.new "a" | ||||
|         variable = scope.variables.first | ||||
|         variable.captured_by_block?.should be_falsey | ||||
|  |  | |||
|  | @ -19,33 +19,33 @@ module Ameba::AST | |||
|       end | ||||
| 
 | ||||
|       it "is 1 if there is Macro::For" do | ||||
|         code = %( | ||||
|           def initialize() | ||||
|         code = <<-CRYSTAL | ||||
|           def initialize | ||||
|             {% for c in ALL_NODES %} | ||||
|               true || false | ||||
|             {% end %} | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         node = Crystal::Parser.new(code).parse | ||||
|         visitor = CountingVisitor.new node | ||||
|         visitor.count.should eq 1 | ||||
|       end | ||||
| 
 | ||||
|       it "is 1 if there is Macro::If" do | ||||
|         code = %( | ||||
|           def initialize() | ||||
|         code = <<-CRYSTAL | ||||
|           def initialize | ||||
|             {% if foo.bar? %} | ||||
|               true || false | ||||
|             {% end %} | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         node = Crystal::Parser.new(code).parse | ||||
|         visitor = CountingVisitor.new node | ||||
|         visitor.count.should eq 1 | ||||
|       end | ||||
| 
 | ||||
|       it "increases count for every exhaustive case" do | ||||
|         code = %( | ||||
|         code = <<-CRYSTAL | ||||
|           def hello(a : Int32 | Int64 | Float32 | Float64) | ||||
|             case a | ||||
|             in Int32   then "int32" | ||||
|  | @ -54,7 +54,7 @@ module Ameba::AST | |||
|             in Float64 then "float64" | ||||
|             end | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         node = Crystal::Parser.new(code).parse | ||||
|         visitor = CountingVisitor.new node | ||||
|         visitor.count.should eq 2 | ||||
|  |  | |||
|  | @ -6,17 +6,17 @@ module Ameba::AST | |||
|   describe FlowExpressionVisitor do | ||||
|     it "creates an expression for return" do | ||||
|       rule = FlowExpressionRule.new | ||||
|       FlowExpressionVisitor.new rule, Source.new %( | ||||
|       FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL | ||||
|         def foo | ||||
|           return :bar | ||||
|         end | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       rule.expressions.size.should eq 1 | ||||
|     end | ||||
| 
 | ||||
|     it "can create multiple expressions" do | ||||
|       rule = FlowExpressionRule.new | ||||
|       FlowExpressionVisitor.new rule, Source.new %( | ||||
|       FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL | ||||
|         def foo | ||||
|           if bar | ||||
|             return :baz | ||||
|  | @ -24,13 +24,13 @@ module Ameba::AST | |||
|             return :foobar | ||||
|           end | ||||
|         end | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       rule.expressions.size.should eq 3 | ||||
|     end | ||||
| 
 | ||||
|     it "properly creates nested flow expressions" do | ||||
|       rule = FlowExpressionRule.new | ||||
|       FlowExpressionVisitor.new rule, Source.new %( | ||||
|       FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL | ||||
|         def foo | ||||
|           return ( | ||||
|             loop do | ||||
|  | @ -39,27 +39,27 @@ module Ameba::AST | |||
|             end | ||||
|           ) | ||||
|         end | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       rule.expressions.size.should eq 4 | ||||
|     end | ||||
| 
 | ||||
|     it "creates an expression for break" do | ||||
|       rule = FlowExpressionRule.new | ||||
|       FlowExpressionVisitor.new rule, Source.new %( | ||||
|       FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL | ||||
|         while true | ||||
|           break | ||||
|         end | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       rule.expressions.size.should eq 1 | ||||
|     end | ||||
| 
 | ||||
|     it "creates an expression for next" do | ||||
|       rule = FlowExpressionRule.new | ||||
|       FlowExpressionVisitor.new rule, Source.new %( | ||||
|       FlowExpressionVisitor.new rule, Source.new <<-CRYSTAL | ||||
|         while true | ||||
|           next if something | ||||
|         end | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       rule.expressions.size.should eq 1 | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -5,11 +5,11 @@ module Ameba::AST | |||
|   rule = RedundantControlExpressionRule.new | ||||
| 
 | ||||
|   describe RedundantControlExpressionVisitor do | ||||
|     node = as_node %( | ||||
|     node = as_node <<-CRYSTAL | ||||
|       a = 1 | ||||
|       b = 2 | ||||
|       return a + b | ||||
|     ) | ||||
|       CRYSTAL | ||||
|     subject = RedundantControlExpressionVisitor.new(rule, source, node) | ||||
| 
 | ||||
|     it "assigns valid attributes" do | ||||
|  |  | |||
|  | @ -4,37 +4,37 @@ module Ameba::AST | |||
|   describe ScopeVisitor do | ||||
|     it "creates a scope for the def" do | ||||
|       rule = ScopeRule.new | ||||
|       ScopeVisitor.new rule, Source.new %( | ||||
|       ScopeVisitor.new rule, Source.new <<-CRYSTAL | ||||
|         def method | ||||
|         end | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       rule.scopes.size.should eq 1 | ||||
|     end | ||||
| 
 | ||||
|     it "creates a scope for the proc" do | ||||
|       rule = ScopeRule.new | ||||
|       ScopeVisitor.new rule, Source.new %( | ||||
|       ScopeVisitor.new rule, Source.new <<-CRYSTAL | ||||
|         -> {} | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       rule.scopes.size.should eq 1 | ||||
|     end | ||||
| 
 | ||||
|     it "creates a scope for the block" do | ||||
|       rule = ScopeRule.new | ||||
|       ScopeVisitor.new rule, Source.new %( | ||||
|       ScopeVisitor.new rule, Source.new <<-CRYSTAL | ||||
|         3.times {} | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       rule.scopes.size.should eq 2 | ||||
|     end | ||||
| 
 | ||||
|     context "inner scopes" do | ||||
|       it "creates scope for block inside def" do | ||||
|         rule = ScopeRule.new | ||||
|         ScopeVisitor.new rule, Source.new %( | ||||
|         ScopeVisitor.new rule, Source.new <<-CRYSTAL | ||||
|           def method | ||||
|             3.times {} | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         rule.scopes.size.should eq 2 | ||||
|         rule.scopes.last.outer_scope.should_not be_nil | ||||
|         rule.scopes.first.outer_scope.should eq rule.scopes.last | ||||
|  | @ -42,11 +42,11 @@ module Ameba::AST | |||
| 
 | ||||
|       it "creates scope for block inside block" do | ||||
|         rule = ScopeRule.new | ||||
|         ScopeVisitor.new rule, Source.new %( | ||||
|         ScopeVisitor.new rule, Source.new <<-CRYSTAL | ||||
|           3.times do | ||||
|             2.times {} | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         rule.scopes.size.should eq 3 | ||||
|         inner_block = rule.scopes.first | ||||
|         outer_block = rule.scopes.last | ||||
|  |  | |||
|  | @ -4,10 +4,12 @@ module Ameba::AST | |||
|   describe TopLevelNodesVisitor do | ||||
|     describe "#require_nodes" do | ||||
|       it "returns require node" do | ||||
|         source = Source.new %( | ||||
|         source = Source.new <<-CRYSTAL | ||||
|           require "foo" | ||||
|           def bar; end | ||||
|         ) | ||||
| 
 | ||||
|           def bar | ||||
|           end | ||||
|           CRYSTAL | ||||
|         visitor = TopLevelNodesVisitor.new(source.ast) | ||||
|         visitor.require_nodes.size.should eq 1 | ||||
|         visitor.require_nodes.first.to_s.should eq %q(require "foo") | ||||
|  |  | |||
|  | @ -26,135 +26,135 @@ module Ameba | |||
|     end | ||||
| 
 | ||||
|     it "disables a rule with a comment directive" do | ||||
|       s = Source.new %Q( | ||||
|       source = Source.new <<-CRYSTAL | ||||
|         # ameba:disable #{NamedRule.name} | ||||
|         Time.epoch(1483859302) | ||||
|       ) | ||||
|       s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!") | ||||
|       s.should be_valid | ||||
|         CRYSTAL | ||||
|       source.add_issue(NamedRule.new, location: {1, 12}, message: "Error!") | ||||
|       source.should be_valid | ||||
|     end | ||||
| 
 | ||||
|     it "disables a rule with a line that ends with a comment directive" do | ||||
|       s = Source.new %Q( | ||||
|       source = Source.new <<-CRYSTAL | ||||
|         Time.epoch(1483859302) # ameba:disable #{NamedRule.name} | ||||
|       ) | ||||
|       s.add_issue(NamedRule.new, location: {1, 12}, message: "Error!") | ||||
|       s.should be_valid | ||||
|         CRYSTAL | ||||
|       source.add_issue(NamedRule.new, location: {1, 12}, message: "Error!") | ||||
|       source.should be_valid | ||||
|     end | ||||
| 
 | ||||
|     it "does not disable a rule of a different name" do | ||||
|       s = Source.new %Q( | ||||
|       source = Source.new <<-CRYSTAL | ||||
|         # ameba:disable WrongName | ||||
|         Time.epoch(1483859302) | ||||
|       ) | ||||
|       s.add_issue(NamedRule.new, location: {2, 12}, message: "Error!") | ||||
|       s.should_not be_valid | ||||
|         CRYSTAL | ||||
|       source.add_issue(NamedRule.new, location: {2, 12}, message: "Error!") | ||||
|       source.should_not be_valid | ||||
|     end | ||||
| 
 | ||||
|     it "disables a rule if multiple rule names provided" do | ||||
|       s = Source.new %Q( | ||||
|       source = Source.new <<-CRYSTAL | ||||
|         # ameba:disable SomeRule LargeNumbers #{NamedRule.name} SomeOtherRule | ||||
|         Time.epoch(1483859302) | ||||
|       ) | ||||
|       s.add_issue(NamedRule.new, location: {2, 12}, message: "") | ||||
|       s.should be_valid | ||||
|         CRYSTAL | ||||
|       source.add_issue(NamedRule.new, location: {2, 12}, message: "") | ||||
|       source.should be_valid | ||||
|     end | ||||
| 
 | ||||
|     it "disables a rule if multiple rule names are separated by comma" do | ||||
|       s = Source.new %Q( | ||||
|       source = Source.new <<-CRYSTAL | ||||
|         # ameba:disable SomeRule, LargeNumbers, #{NamedRule.name}, SomeOtherRule | ||||
|         Time.epoch(1483859302) | ||||
|       ) | ||||
|       s.add_issue(NamedRule.new, location: {2, 12}, message: "") | ||||
|       s.should be_valid | ||||
|         CRYSTAL | ||||
|       source.add_issue(NamedRule.new, location: {2, 12}, message: "") | ||||
|       source.should be_valid | ||||
|     end | ||||
| 
 | ||||
|     it "does not disable if multiple rule names used without required one" do | ||||
|       s = Source.new %( | ||||
|       source = Source.new <<-CRYSTAL | ||||
|         # ameba:disable SomeRule, SomeOtherRule LargeNumbers | ||||
|         Time.epoch(1483859302) | ||||
|       ) | ||||
|       s.add_issue(NamedRule.new, location: {2, 12}, message: "") | ||||
|       s.should_not be_valid | ||||
|         CRYSTAL | ||||
|       source.add_issue(NamedRule.new, location: {2, 12}, message: "") | ||||
|       source.should_not be_valid | ||||
|     end | ||||
| 
 | ||||
|     it "does not disable if comment directive has wrong place" do | ||||
|       s = Source.new %Q( | ||||
|       source = Source.new <<-CRYSTAL | ||||
|         # ameba:disable #{NamedRule.name} | ||||
|         # | ||||
|         Time.epoch(1483859302) | ||||
|       ) | ||||
|       s.add_issue(NamedRule.new, location: {3, 12}, message: "") | ||||
|       s.should_not be_valid | ||||
|         CRYSTAL | ||||
|       source.add_issue(NamedRule.new, location: {3, 12}, message: "") | ||||
|       source.should_not be_valid | ||||
|     end | ||||
| 
 | ||||
|     it "does not disable if comment directive added to the wrong line" do | ||||
|       s = Source.new %Q( | ||||
|       source = Source.new <<-CRYSTAL | ||||
|         if use_epoch? # ameba:disable #{NamedRule.name} | ||||
|           Time.epoch(1483859302) | ||||
|         end | ||||
|       ) | ||||
|       s.add_issue(NamedRule.new, location: {3, 12}, message: "") | ||||
|       s.should_not be_valid | ||||
|         CRYSTAL | ||||
|       source.add_issue(NamedRule.new, location: {3, 12}, message: "") | ||||
|       source.should_not be_valid | ||||
|     end | ||||
| 
 | ||||
|     it "does not disable if that is not a comment directive" do | ||||
|       s = Source.new %Q( | ||||
|       source = Source.new <<-CRYSTAL | ||||
|         "ameba:disable #{NamedRule.name}" | ||||
|         Time.epoch(1483859302) | ||||
|       ) | ||||
|       s.add_issue(NamedRule.new, location: {3, 12}, message: "") | ||||
|       s.should_not be_valid | ||||
|         CRYSTAL | ||||
|       source.add_issue(NamedRule.new, location: {3, 12}, message: "") | ||||
|       source.should_not be_valid | ||||
|     end | ||||
| 
 | ||||
|     it "does not disable if that is a commented out directive" do | ||||
|       s = Source.new %Q( | ||||
|       source = Source.new <<-CRYSTAL | ||||
|         # # ameba:disable #{NamedRule.name} | ||||
|         Time.epoch(1483859302) | ||||
|       ) | ||||
|       s.add_issue(NamedRule.new, location: {3, 12}, message: "") | ||||
|       s.should_not be_valid | ||||
|         CRYSTAL | ||||
|       source.add_issue(NamedRule.new, location: {3, 12}, message: "") | ||||
|       source.should_not be_valid | ||||
|     end | ||||
| 
 | ||||
|     it "does not disable if that is an inline commented out directive" do | ||||
|       s = Source.new %Q( | ||||
|       source = Source.new <<-CRYSTAL | ||||
|         a = 1 # Disable it: # ameba:disable #{NamedRule.name} | ||||
|       ) | ||||
|       s.add_issue(NamedRule.new, location: {2, 12}, message: "") | ||||
|       s.should_not be_valid | ||||
|         CRYSTAL | ||||
|       source.add_issue(NamedRule.new, location: {2, 12}, message: "") | ||||
|       source.should_not be_valid | ||||
|     end | ||||
| 
 | ||||
|     context "with group name" do | ||||
|       it "disables one rule with a group" do | ||||
|         s = Source.new %Q( | ||||
|         source = Source.new <<-CRYSTAL | ||||
|           a = 1 # ameba:disable #{DummyRule.rule_name} | ||||
|         ) | ||||
|         s.add_issue(DummyRule.new, location: {1, 12}, message: "") | ||||
|         s.should be_valid | ||||
|           CRYSTAL | ||||
|         source.add_issue(DummyRule.new, location: {1, 12}, message: "") | ||||
|         source.should be_valid | ||||
|       end | ||||
| 
 | ||||
|       it "doesn't disable others rules" do | ||||
|         s = Source.new %Q( | ||||
|         source = Source.new <<-CRYSTAL | ||||
|           a = 1 # ameba:disable #{DummyRule.rule_name} | ||||
|         ) | ||||
|         s.add_issue(NamedRule.new, location: {2, 12}, message: "") | ||||
|         s.should_not be_valid | ||||
|           CRYSTAL | ||||
|         source.add_issue(NamedRule.new, location: {2, 12}, message: "") | ||||
|         source.should_not be_valid | ||||
|       end | ||||
| 
 | ||||
|       it "disables a hole group of rules" do | ||||
|         s = Source.new %Q( | ||||
|         source = Source.new <<-CRYSTAL | ||||
|           a = 1 # ameba:disable #{DummyRule.group_name} | ||||
|         ) | ||||
|         s.add_issue(DummyRule.new, location: {1, 12}, message: "") | ||||
|         s.should be_valid | ||||
|           CRYSTAL | ||||
|         source.add_issue(DummyRule.new, location: {1, 12}, message: "") | ||||
|         source.should be_valid | ||||
|       end | ||||
| 
 | ||||
|       it "does not disable rules which do not belong to the group" do | ||||
|         s = Source.new %Q( | ||||
|         source = Source.new <<-CRYSTAL | ||||
|           a = 1 # ameba:disable Lint | ||||
|         ) | ||||
|         s.add_issue(DummyRule.new, location: {2, 12}, message: "") | ||||
|         s.should_not be_valid | ||||
|           CRYSTAL | ||||
|         source.add_issue(DummyRule.new, location: {2, 12}, message: "") | ||||
|         source.should_not be_valid | ||||
|       end | ||||
|     end | ||||
|   end | ||||
|  |  | |||
|  | @ -34,7 +34,7 @@ module Ameba::Rule::Layout | |||
|     end | ||||
| 
 | ||||
|     context "properties" do | ||||
|       it "allows to configure max length of the line" do | ||||
|       it "#max_length" do | ||||
|         rule = LineLength.new | ||||
|         rule.max_length = long_line.size | ||||
| 
 | ||||
|  |  | |||
|  | @ -5,7 +5,7 @@ module Ameba::Rule::Layout | |||
| 
 | ||||
|   describe TrailingWhitespace do | ||||
|     it "passes if all lines do not have trailing whitespace" do | ||||
|       expect_no_issues subject, "no-whispace" | ||||
|       expect_no_issues subject, "no-whitespace" | ||||
|     end | ||||
| 
 | ||||
|     it "fails if there is a line with trailing whitespace" do | ||||
|  | @ -15,16 +15,5 @@ module Ameba::Rule::Layout | |||
| 
 | ||||
|       expect_correction source, "whitespace at the end" | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       source = Source.new "a = 1\n b = 2 ", "source.cr" | ||||
|       subject.catch(source).should_not be_valid | ||||
| 
 | ||||
|       issue = source.issues.first | ||||
|       issue.rule.should_not be_nil | ||||
|       issue.location.to_s.should eq "source.cr:2:7" | ||||
|       issue.end_location.to_s.should eq "source.cr:2:7" | ||||
|       issue.message.should eq "Trailing whitespace detected" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -94,16 +94,6 @@ module Ameba::Rule::Lint | |||
|           a | ||||
|           CRYSTAL | ||||
|       end | ||||
| 
 | ||||
|       it "reports rule, pos and message" do | ||||
|         source = Source.new "a != true", "source.cr" | ||||
|         subject.catch(source) | ||||
| 
 | ||||
|         issue = source.issues.first | ||||
|         issue.rule.should_not be_nil | ||||
|         issue.location.to_s.should eq "source.cr:1:1" | ||||
|         issue.message.should eq "Comparison to a boolean is pointless" | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     context "boolean on the left" do | ||||
|  | @ -165,17 +155,6 @@ module Ameba::Rule::Lint | |||
|           a | ||||
|           CRYSTAL | ||||
|       end | ||||
| 
 | ||||
|       it "reports rule, pos and message" do | ||||
|         source = Source.new "true != a", "source.cr" | ||||
|         subject.catch(source).should_not be_valid | ||||
| 
 | ||||
|         issue = source.issues.first | ||||
|         issue.rule.should_not be_nil | ||||
|         issue.location.to_s.should eq "source.cr:1:1" | ||||
|         issue.end_location.to_s.should eq "source.cr:1:9" | ||||
|         issue.message.should eq "Comparison to a boolean is pointless" | ||||
|       end | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -28,16 +28,5 @@ module Ameba::Rule::Lint | |||
|           CRYSTAL | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new "pp! :foo", "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:1:1" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:8" | ||||
|       issue.message.should eq "Possibly forgotten debug-related `pp!` call detected" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -31,16 +31,5 @@ module Ameba::Rule::Lint | |||
| 
 | ||||
|       expect_no_corrections source | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new "debugger", "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:1:1" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:8" | ||||
|       issue.message.should eq "Possible forgotten debugger statement detected" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -22,28 +22,5 @@ module Ameba::Rule::Lint | |||
| 
 | ||||
|       expect_no_corrections source | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       source = Source.new %( | ||||
|         require "./thing" | ||||
|         require "./thing" | ||||
|         require "./another_thing" | ||||
|         require "./another_thing" | ||||
|       ), "source.cr" | ||||
| 
 | ||||
|       subject.catch(source).should_not be_valid | ||||
| 
 | ||||
|       issue = source.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 "" | ||||
|       issue.message.should eq "Duplicated require of `./thing`" | ||||
| 
 | ||||
|       issue = source.issues.last | ||||
|       issue.rule.should_not be_nil | ||||
|       issue.location.to_s.should eq "source.cr:4:1" | ||||
|       issue.end_location.to_s.should eq "" | ||||
|       issue.message.should eq "Duplicated require of `./another_thing`" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -3,17 +3,17 @@ require "../../../spec_helper" | |||
| module Ameba | ||||
|   subject = Rule::Lint::EmptyExpression.new | ||||
| 
 | ||||
|   def it_detects_empty_expression(code) | ||||
|     it "detects empty expression" do | ||||
|   private def it_detects_empty_expression(code, *, file = __FILE__, line = __LINE__) | ||||
|     it %(detects empty expression "#{code}"), file, line do | ||||
|       s = Source.new code | ||||
|       rule = Rule::Lint::EmptyExpression.new | ||||
|       rule.catch(s).should_not be_valid | ||||
|       rule.catch(s).should_not be_valid, file: file, line: line | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe Rule::Lint::EmptyExpression do | ||||
|     it "passes if there is no empty expression" do | ||||
|       s = Source.new %( | ||||
|       s = Source.new <<-CRYSTAL | ||||
|         def method() | ||||
|         end | ||||
| 
 | ||||
|  | @ -30,7 +30,7 @@ module Ameba | |||
| 
 | ||||
|         begin "" end | ||||
|         [nil] << nil | ||||
|       ) | ||||
|         CRYSTAL | ||||
|       subject.catch(s).should be_valid | ||||
|     end | ||||
| 
 | ||||
|  | @ -110,18 +110,5 @@ module Ameba | |||
|       ) | ||||
|       subject.catch(s).should be_valid | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, location and message" do | ||||
|       s = Source.new %( | ||||
|         if () | ||||
|         end | ||||
|       ), "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:1:4" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:5" | ||||
|       issue.message.should eq "Avoid empty expressions" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -64,21 +64,5 @@ module Ameba::Rule::Lint | |||
|         end | ||||
|         CRYSTAL | ||||
|     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 | ||||
|  |  | |||
|  | @ -32,17 +32,5 @@ module Ameba::Rule::Lint | |||
|           # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Duplicated keys in hash literal: "key1", "key2" | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, location and message" do | ||||
|       s = Source.new %q( | ||||
|         h = {"a" => 1, "a" => 2} | ||||
|       ), "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:1:5" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:24" | ||||
|       issue.message.should eq %(Duplicated keys in hash literal: "a") | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -58,18 +58,5 @@ module Ameba::Rule::Lint | |||
|           CRYSTAL | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         "foo" == "foo" | ||||
|       ), "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:1:1" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:14" | ||||
|       issue.message.should eq "Comparison always evaluates to true" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -58,18 +58,5 @@ module Ameba::Rule::Lint | |||
|           CRYSTAL | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         (1..3).index(1).not_nil! | ||||
|       ), "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:1:8" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:24" | ||||
|       issue.message.should eq "Use `index! {...}` instead of `index {...}.not_nil!`" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -32,18 +32,5 @@ module Ameba::Rule::Lint | |||
|           CRYSTAL | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         (1..3).first?.not_nil! | ||||
|       ), "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:1:15" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:22" | ||||
|       issue.message.should eq "Avoid using `not_nil!`" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -68,14 +68,14 @@ module Ameba::Rule::Lint | |||
|     end | ||||
| 
 | ||||
|     context "properties" do | ||||
|       it "allows to configure string_array_unwanted_symbols" do | ||||
|       it "#string_array_unwanted_symbols" do | ||||
|         rule = PercentArrays.new | ||||
|         rule.string_array_unwanted_symbols = "," | ||||
|         s = Source.new %( %w("one") ) | ||||
|         rule.catch(s).should be_valid | ||||
|       end | ||||
| 
 | ||||
|       it "allows to configure symbol_array_unwanted_symbols" do | ||||
|       it "#symbol_array_unwanted_symbols" do | ||||
|         rule = PercentArrays.new | ||||
|         rule.symbol_array_unwanted_symbols = "," | ||||
|         s = Source.new %( %i(:one) ) | ||||
|  |  | |||
|  | @ -25,16 +25,5 @@ module Ameba::Rule::Lint | |||
|         # ^^^^^ error: rand(1) always returns 0 | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, location and a message" do | ||||
|       s = Source.new "rand(1)", "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:1:1" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:7" | ||||
|       issue.message.should eq "rand(1) always returns 0" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -157,20 +157,6 @@ module Ameba::Rule::Lint | |||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, location and message" do | ||||
|       source = Source.new %( | ||||
|         foo = 1 | ||||
|         3.times { |foo| foo + 1 } | ||||
|       ), "source.cr" | ||||
|       subject.catch(source).should_not be_valid | ||||
| 
 | ||||
|       issue = source.issues.first | ||||
|       issue.rule.should_not be_nil | ||||
|       issue.location.to_s.should eq "source.cr:2:12" | ||||
|       issue.end_location.should be_nil | ||||
|       issue.message.should eq "Shadowing outer local variable `foo`" | ||||
|     end | ||||
| 
 | ||||
|     context "macro" do | ||||
|       it "does not report shadowed vars in outer scope" do | ||||
|         expect_no_issues subject, <<-CRYSTAL | ||||
|  |  | |||
|  | @ -194,24 +194,5 @@ module Ameba::Rule::Lint | |||
|         end | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, location and message" do | ||||
|       s = Source.new %( | ||||
|         i = 0 | ||||
|         while true | ||||
|           i += 1 | ||||
|           spawn { i } | ||||
|         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:4:11" | ||||
|       issue.end_location.to_s.should eq "source.cr:4:11" | ||||
|       issue.message.should eq "Shared variable `i` is used in fiber" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -23,16 +23,6 @@ module Ameba::Rule::Lint | |||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, location and message" do | ||||
|       s = Source.new "def hello end", "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:1:11" | ||||
|       issue.message.should match /unexpected token: "?end"? \(expected ["'];["'] or newline\)/ | ||||
|     end | ||||
| 
 | ||||
|     it "has highest severity" do | ||||
|       subject.severity.should eq Severity::Error | ||||
|     end | ||||
|  |  | |||
|  | @ -24,22 +24,5 @@ module Ameba::Rule::Lint | |||
|         end | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, location and message" do | ||||
|       s = Source.new %( | ||||
|         case | ||||
|         when String | ||||
|           puts "hello" | ||||
|         when can_generate? | ||||
|           generate if can_generate? | ||||
|         end | ||||
|       ), "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:5:15" | ||||
|       issue.end_location.to_s.should eq "source.cr:5:27" | ||||
|       issue.message.should eq "Useless condition in when detected" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -46,8 +46,8 @@ module Ameba::Rule::Performance | |||
|     end | ||||
| 
 | ||||
|     context "properties" do | ||||
|       it "allows to configure object_call_names" do | ||||
|         rule = Rule::Performance::AnyAfterFilter.new | ||||
|       it "#filter_names" do | ||||
|         rule = AnyAfterFilter.new | ||||
|         rule.filter_names = %w(select) | ||||
| 
 | ||||
|         expect_no_issues rule, <<-CRYSTAL | ||||
|  | @ -66,14 +66,5 @@ module Ameba::Rule::Performance | |||
|         expect_no_corrections source | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       source = expect_issue subject, <<-CRYSTAL | ||||
|         [1, 2, 3].reject { |e| e > 2 }.any? | ||||
|                 # ^^^^^^^^^^^^^^^^^^^^^^^^^^ error: Use `any? {...}` instead of `reject {...}.any?` | ||||
|         CRYSTAL | ||||
| 
 | ||||
|       expect_no_corrections source | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -42,18 +42,5 @@ module Ameba::Rule::Performance | |||
|           CRYSTAL | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       source = Source.new path: "source.cr", code: %( | ||||
|         [1, 2, 3].any? | ||||
|       ) | ||||
|       subject.catch(source).should_not be_valid | ||||
|       issue = source.issues.first | ||||
| 
 | ||||
|       issue.rule.should_not be_nil | ||||
|       issue.location.to_s.should eq "source.cr:1:11" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:14" | ||||
|       issue.message.should eq "Use `!{...}.empty?` instead of `{...}.any?`" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -44,7 +44,7 @@ module Ameba::Rule::Performance | |||
|     end | ||||
| 
 | ||||
|     context "properties" do | ||||
|       it "allows to configure `call_names`" do | ||||
|       it "#call_names" do | ||||
|         rule = ChainedCallWithNoBang.new | ||||
|         rule.call_names = %w(uniq) | ||||
| 
 | ||||
|  | @ -54,22 +54,6 @@ module Ameba::Rule::Performance | |||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       source = Source.new path: "source.cr", code: <<-CODE | ||||
|         [1, 2, 3].select { |e| e > 1 }.reverse | ||||
|         CODE | ||||
| 
 | ||||
|       subject.catch(source).should_not be_valid | ||||
|       source.issues.size.should eq 1 | ||||
| 
 | ||||
|       issue = source.issues.first | ||||
|       issue.rule.should_not be_nil | ||||
|       issue.location.to_s.should eq "source.cr:1:32" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:38" | ||||
| 
 | ||||
|       issue.message.should eq "Use bang method variant `reverse!` after chained `select` call" | ||||
|     end | ||||
| 
 | ||||
|     context "macro" do | ||||
|       it "doesn't report in macro scope" do | ||||
|         expect_no_issues subject, <<-CRYSTAL | ||||
|  |  | |||
|  | @ -36,18 +36,5 @@ module Ameba::Rule::Performance | |||
|           CRYSTAL | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         (1..3).map(&.itself).compact | ||||
|       ), "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:1:8" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:29" | ||||
|       issue.message.should eq "Use `compact_map {...}` instead of `map {...}.compact`" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -62,8 +62,8 @@ module Ameba::Rule::Performance | |||
|     end | ||||
| 
 | ||||
|     context "properties" do | ||||
|       it "allows to configure object_call_names" do | ||||
|         rule = Rule::Performance::FirstLastAfterFilter.new | ||||
|       it "#filter_names" do | ||||
|         rule = FirstLastAfterFilter.new | ||||
|         rule.filter_names = %w(reject) | ||||
| 
 | ||||
|         expect_no_issues rule, <<-CRYSTAL | ||||
|  | @ -72,21 +72,6 @@ module Ameba::Rule::Performance | |||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         [1, 2, 3].select { |e| e > 2 }.first | ||||
|       ), "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:1:11" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:37" | ||||
| 
 | ||||
|       issue.message.should eq "Use `find {...}` instead of `select {...}.first`" | ||||
|     end | ||||
| 
 | ||||
|     context "macro" do | ||||
|       it "doesn't report in macro scope" do | ||||
|         expect_no_issues subject, <<-CRYSTAL | ||||
|  |  | |||
|  | @ -30,18 +30,5 @@ module Ameba::Rule::Performance | |||
|           CRYSTAL | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         %w[Alice Bob].map(&.chars).flatten | ||||
|       ), "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:1:15" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:35" | ||||
|       issue.message.should eq "Use `flat_map {...}` instead of `map {...}.flatten`" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -45,18 +45,5 @@ module Ameba::Rule::Performance | |||
|           CRYSTAL | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         (1..3).map(&.to_u64).sum | ||||
|       ), "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:1:8" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:25" | ||||
|       issue.message.should eq "Use `sum {...}` instead of `map {...}.sum`" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -44,8 +44,8 @@ module Ameba::Rule::Performance | |||
|     end | ||||
| 
 | ||||
|     context "properties" do | ||||
|       it "allows to configure object caller names" do | ||||
|         rule = Rule::Performance::SizeAfterFilter.new | ||||
|       it "#filter_names" do | ||||
|         rule = SizeAfterFilter.new | ||||
|         rule.filter_names = %w(select) | ||||
| 
 | ||||
|         expect_no_issues rule, <<-CRYSTAL | ||||
|  | @ -61,18 +61,5 @@ module Ameba::Rule::Performance | |||
|           CRYSTAL | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         lines.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:4" | ||||
|       issue.end_location.to_s.should eq "source.cr:2:25" | ||||
|       issue.message.should eq "Use `count {...}` instead of `reject {...}.size`." | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -3,10 +3,10 @@ require "../../../spec_helper" | |||
| module Ameba | ||||
|   subject = Rule::Style::ConstantNames.new | ||||
| 
 | ||||
|   private def it_reports_constant(name, value, expected) | ||||
|     it "reports constant name #{expected}" do | ||||
|   private def it_reports_constant(name, value, expected, *, file = __FILE__, line = __LINE__) | ||||
|     it "reports constant name #{expected}", file, line do | ||||
|       rule = Rule::Style::ConstantNames.new | ||||
|       expect_issue rule, <<-CRYSTAL, name: name | ||||
|       expect_issue rule, <<-CRYSTAL, name: name, file: file, line: line | ||||
|           %{name} = #{value} | ||||
|         # ^{name} error: Constant name should be screaming-cased: #{expected}, not #{name} | ||||
|         CRYSTAL | ||||
|  | @ -17,7 +17,7 @@ module Ameba | |||
|     it "passes if type names are screaming-cased" do | ||||
|       expect_no_issues subject, <<-CRYSTAL | ||||
|         LUCKY_NUMBERS     = [3, 7, 11] | ||||
|         DOCUMENTATION_URL = "http://crystal-lang.org/docs" | ||||
|         DOCUMENTATION_URL = "https://crystal-lang.org/docs" | ||||
| 
 | ||||
|         Int32 | ||||
| 
 | ||||
|  | @ -37,19 +37,5 @@ module Ameba | |||
|     # it_reports_constant "MyBadConstant", "1", "MYBADCONSTANT" | ||||
|     it_reports_constant "Wrong_NAME", "2", "WRONG_NAME" | ||||
|     it_reports_constant "Wrong_Name", "3", "WRONG_NAME" | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         Const_Name = 1 | ||||
|       ), "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:1:1" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:10" | ||||
|       issue.message.should eq( | ||||
|         "Constant name should be screaming-cased: CONST_NAME, not Const_Name" | ||||
|       ) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -3,11 +3,11 @@ require "../../../spec_helper" | |||
| module Ameba | ||||
|   subject = Rule::Style::GuardClause.new | ||||
| 
 | ||||
|   def it_reports_body(body, *, line = __LINE__) | ||||
|   private def it_reports_body(body, *, file = __FILE__, line = __LINE__) | ||||
|     rule = Rule::Style::GuardClause.new | ||||
| 
 | ||||
|     it "reports an issue if method body is if / unless without else" do | ||||
|       source = expect_issue rule, <<-CRYSTAL, line: line | ||||
|     it "reports an issue if method body is if / unless without else", file, line do | ||||
|       source = expect_issue rule, <<-CRYSTAL, file: file, line: line | ||||
|         def func | ||||
|           if something | ||||
|         # ^^ error: Use a guard clause (`return unless something`) instead of wrapping the code inside a conditional expression. | ||||
|  | @ -23,7 +23,7 @@ module Ameba | |||
|         end | ||||
|         CRYSTAL | ||||
| 
 | ||||
|       expect_correction source, <<-CRYSTAL, line: line | ||||
|       expect_correction source, <<-CRYSTAL, file: file, line: line | ||||
|         def func | ||||
|           return unless something | ||||
|             #{body} | ||||
|  | @ -38,8 +38,8 @@ module Ameba | |||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports an issue if method body ends with if / unless without else" do | ||||
|       source = expect_issue rule, <<-CRYSTAL, line: line | ||||
|     it "reports an issue if method body ends with if / unless without else", file, line do | ||||
|       source = expect_issue rule, <<-CRYSTAL, file: file, line: line | ||||
|         def func | ||||
|           test | ||||
|           if something | ||||
|  | @ -57,7 +57,7 @@ module Ameba | |||
|         end | ||||
|         CRYSTAL | ||||
| 
 | ||||
|       expect_correction source, <<-CRYSTAL, line: line | ||||
|       expect_correction source, <<-CRYSTAL, file: file, line: line | ||||
|         def func | ||||
|           test | ||||
|           return unless something | ||||
|  | @ -75,11 +75,11 @@ module Ameba | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def it_reports_control_expression(kw, *, line = __LINE__) | ||||
|   private def it_reports_control_expression(kw, *, file = __FILE__, line = __LINE__) | ||||
|     rule = Rule::Style::GuardClause.new | ||||
| 
 | ||||
|     it "reports an issue with #{kw} in the if branch" do | ||||
|       source = expect_issue rule, <<-CRYSTAL, line: line | ||||
|     it "reports an issue with #{kw} in the if branch", file, line do | ||||
|       source = expect_issue rule, <<-CRYSTAL, file: file, line: line | ||||
|         def func | ||||
|           if something | ||||
|         # ^^ error: Use a guard clause (`#{kw} if something`) instead of wrapping the code inside a conditional expression. | ||||
|  | @ -90,11 +90,11 @@ module Ameba | |||
|         end | ||||
|         CRYSTAL | ||||
| 
 | ||||
|       expect_no_corrections source, line: line | ||||
|       expect_no_corrections source, file: file, line: line | ||||
|     end | ||||
| 
 | ||||
|     it "reports an issue with #{kw} in the else branch" do | ||||
|       source = expect_issue rule, <<-CRYSTAL, line: line | ||||
|     it "reports an issue with #{kw} in the else branch", file, line do | ||||
|       source = expect_issue rule, <<-CRYSTAL, file: file, line: line | ||||
|         def func | ||||
|           if something | ||||
|         # ^^ error: Use a guard clause (`#{kw} unless something`) instead of wrapping the code inside a conditional expression. | ||||
|  | @ -105,11 +105,11 @@ module Ameba | |||
|         end | ||||
|         CRYSTAL | ||||
| 
 | ||||
|       expect_no_corrections source, line: line | ||||
|       expect_no_corrections source, file: file, line: line | ||||
|     end | ||||
| 
 | ||||
|     it "doesn't report an issue if condition has multiple lines" do | ||||
|       expect_no_issues rule, <<-CRYSTAL, line: line | ||||
|     it "doesn't report an issue if condition has multiple lines", file, line do | ||||
|       expect_no_issues rule, <<-CRYSTAL, file: file, line: line | ||||
|         def func | ||||
|           if something && | ||||
|                something_else | ||||
|  | @ -121,8 +121,8 @@ module Ameba | |||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "does not report an issue if #{kw} is inside elsif" do | ||||
|       expect_no_issues rule, <<-CRYSTAL, line: line | ||||
|     it "does not report an issue if #{kw} is inside elsif", file, line do | ||||
|       expect_no_issues rule, <<-CRYSTAL, file: file, line: line | ||||
|         def func | ||||
|           if something | ||||
|             a | ||||
|  | @ -133,8 +133,8 @@ module Ameba | |||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "does not report an issue if #{kw} is inside if..elsif..else..end" do | ||||
|       expect_no_issues rule, <<-CRYSTAL, line: line | ||||
|     it "does not report an issue if #{kw} is inside if..elsif..else..end", file, line do | ||||
|       expect_no_issues rule, <<-CRYSTAL, file: file, line: line | ||||
|         def func | ||||
|           if something | ||||
|             a | ||||
|  | @ -147,8 +147,8 @@ module Ameba | |||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "doesn't report an issue if control flow expr has multiple lines" do | ||||
|       expect_no_issues rule, <<-CRYSTAL, line: line | ||||
|     it "doesn't report an issue if control flow expr has multiple lines", file, line do | ||||
|       expect_no_issues rule, <<-CRYSTAL, file: file, line: line | ||||
|         def func | ||||
|           if something | ||||
|             #{kw} \\ | ||||
|  | @ -161,8 +161,8 @@ module Ameba | |||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports an issue if non-control-flow branch has multiple lines" do | ||||
|       source = expect_issue rule, <<-CRYSTAL, line: line | ||||
|     it "reports an issue if non-control-flow branch has multiple lines", file, line do | ||||
|       source = expect_issue rule, <<-CRYSTAL, file: file, line: line | ||||
|         def func | ||||
|           if something | ||||
|         # ^^ error: Use a guard clause (`#{kw} if something`) instead of wrapping the code inside a conditional expression. | ||||
|  | @ -174,7 +174,7 @@ module Ameba | |||
|         end | ||||
|         CRYSTAL | ||||
| 
 | ||||
|       expect_no_corrections source, line: line | ||||
|       expect_no_corrections source, file: file, line: line | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -42,9 +42,10 @@ module Ameba::Rule::Style | |||
|     end | ||||
| 
 | ||||
|     context "properties" do | ||||
|       it "allows to configure filter_names" do | ||||
|       it "#filter_names" do | ||||
|         rule = IsAFilter.new | ||||
|         rule.filter_names = %w(select) | ||||
| 
 | ||||
|         expect_no_issues rule, <<-CRYSTAL | ||||
|           [1, 2, nil].reject(&.nil?) | ||||
|           CRYSTAL | ||||
|  | @ -58,20 +59,5 @@ module Ameba::Rule::Style | |||
|           CRYSTAL | ||||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       source = Source.new path: "source.cr", code: %( | ||||
|         [1, 2, nil].reject(&.nil?) | ||||
|       ) | ||||
|       subject.catch(source).should_not be_valid | ||||
|       source.issues.size.should eq 1 | ||||
| 
 | ||||
|       issue = source.issues.first | ||||
|       issue.rule.should_not be_nil | ||||
|       issue.location.to_s.should eq "source.cr:1:13" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:26" | ||||
| 
 | ||||
|       issue.message.should eq "Use `reject(Nil)` instead of `reject {...}`" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -34,19 +34,5 @@ module Ameba::Rule::Style | |||
|         a.nil? | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, location and message" do | ||||
|       s = Source.new %( | ||||
|         nil.is_a? Nil | ||||
|       ), "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:1:11" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:13" | ||||
|       issue.message.should eq IsANil::MSG | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -3,12 +3,12 @@ require "../../../spec_helper" | |||
| module Ameba | ||||
|   subject = Rule::Style::LargeNumbers.new | ||||
| 
 | ||||
|   private def it_transforms(number, expected) | ||||
|     it "transforms large number #{number}" do | ||||
|   private def it_transforms(number, expected, *, file = __FILE__, line = __LINE__) | ||||
|     it "transforms large number #{number}", file, line do | ||||
|       rule = Rule::Style::LargeNumbers.new | ||||
|       rule.int_min_digits = 5 | ||||
| 
 | ||||
|       source = expect_issue rule, <<-CRYSTAL, number: number | ||||
|       source = expect_issue rule, <<-CRYSTAL, number: number, file: file, line: line | ||||
|         number = %{number} | ||||
|                # ^{number} error: Large numbers should be written with underscores: #{expected} | ||||
|         CRYSTAL | ||||
|  | @ -97,10 +97,12 @@ module Ameba | |||
|     it_transforms "10000_i16", "10_000_i16" | ||||
|     it_transforms "10000_i32", "10_000_i32" | ||||
|     it_transforms "10000_i64", "10_000_i64" | ||||
|     it_transforms "10000_i128", "10_000_i128" | ||||
| 
 | ||||
|     it_transforms "10000_u16", "10_000_u16" | ||||
|     it_transforms "10000_u32", "10_000_u32" | ||||
|     it_transforms "10000_u64", "10_000_u64" | ||||
|     it_transforms "10000_u128", "10_000_u128" | ||||
| 
 | ||||
|     it_transforms "123456_f32", "123_456_f32" | ||||
|     it_transforms "123456_f64", "123_456_f64" | ||||
|  | @ -117,20 +119,8 @@ module Ameba | |||
|     it_transforms "3.001234", "3.001_234" | ||||
|     it_transforms "3.0012345", "3.001_234_5" | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %q( | ||||
|         1200000 | ||||
|       ), "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:1:1" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:7" | ||||
|       issue.message.should match /1_200_000/ | ||||
|     end | ||||
| 
 | ||||
|     context "properties" do | ||||
|       it "allows to configure integer min digits" do | ||||
|       it "#int_min_digits" do | ||||
|         rule = Rule::Style::LargeNumbers.new | ||||
|         rule.int_min_digits = 10 | ||||
|         expect_no_issues rule, %q(1200000) | ||||
|  |  | |||
|  | @ -3,10 +3,10 @@ require "../../../spec_helper" | |||
| module Ameba | ||||
|   subject = Rule::Style::MethodNames.new | ||||
| 
 | ||||
|   private def it_reports_method_name(name, expected) | ||||
|     it "reports method name #{expected}" do | ||||
|   private def it_reports_method_name(name, expected, *, file = __FILE__, line = __LINE__) | ||||
|     it "reports method name #{expected}", file, line do | ||||
|       rule = Rule::Style::MethodNames.new | ||||
|       expect_issue rule, <<-CRYSTAL, name: name | ||||
|       expect_issue rule, <<-CRYSTAL, name: name, file: file, line: line | ||||
|         def %{name}; end | ||||
|           # ^{name} error: Method name should be underscore-cased: #{expected}, not %{name} | ||||
|         CRYSTAL | ||||
|  | @ -38,20 +38,5 @@ module Ameba | |||
|     it_reports_method_name "firstName", "first_name" | ||||
|     it_reports_method_name "date_of_Birth", "date_of_birth" | ||||
|     it_reports_method_name "homepageURL", "homepage_url" | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         def bad_Name(a) | ||||
|         end | ||||
|       ), "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:1:5" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:12" | ||||
|       issue.message.should eq( | ||||
|         "Method name should be underscore-cased: bad_name, not bad_Name" | ||||
|       ) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -53,16 +53,5 @@ module Ameba::Rule::Style | |||
|         end | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new ":nok unless !s.empty?", "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:1:1" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:21" | ||||
|       issue.message.should eq "Avoid negated conditions in unless blocks" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -43,7 +43,7 @@ module Ameba::Rule::Style | |||
|     end | ||||
| 
 | ||||
|     context "properties" do | ||||
|       context "#exclude_ternary=" do | ||||
|       context "#exclude_ternary" do | ||||
|         it "skips ternary control expressions by default" do | ||||
|           expect_no_issues subject, <<-CRYSTAL | ||||
|             (foo > bar) ? true : false | ||||
|  | @ -51,7 +51,7 @@ module Ameba::Rule::Style | |||
|         end | ||||
| 
 | ||||
|         it "allows to configure assignments" do | ||||
|           rule = Rule::Style::ParenthesesAroundCondition.new | ||||
|           rule = ParenthesesAroundCondition.new | ||||
|           rule.exclude_ternary = false | ||||
| 
 | ||||
|           expect_issue rule, <<-CRYSTAL | ||||
|  | @ -75,7 +75,7 @@ module Ameba::Rule::Style | |||
|         end | ||||
|       end | ||||
| 
 | ||||
|       context "#allow_safe_assignment=" do | ||||
|       context "#allow_safe_assignment" do | ||||
|         it "reports assignments by default" do | ||||
|           expect_issue subject, <<-CRYSTAL | ||||
|             if (foo = @foo) | ||||
|  | @ -98,7 +98,7 @@ module Ameba::Rule::Style | |||
|         end | ||||
| 
 | ||||
|         it "allows to configure assignments" do | ||||
|           rule = Rule::Style::ParenthesesAroundCondition.new | ||||
|           rule = ParenthesesAroundCondition.new | ||||
|           rule.allow_safe_assignment = true | ||||
| 
 | ||||
|           expect_issue rule, <<-CRYSTAL | ||||
|  |  | |||
|  | @ -27,24 +27,6 @@ module Ameba::Rule::Style | |||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %q( | ||||
|         class Image | ||||
|           def is_valid?(x) | ||||
|             true | ||||
|           end | ||||
|         end | ||||
|       ), "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:3" | ||||
|       issue.end_location.to_s.should eq "source.cr:4:5" | ||||
|       issue.message.should eq( | ||||
|         "Favour method name 'valid?' over 'is_valid?'") | ||||
|     end | ||||
| 
 | ||||
|     it "ignores if alternative name isn't valid syntax" do | ||||
|       expect_no_issues subject, <<-CRYSTAL | ||||
|         class Image | ||||
|  |  | |||
|  | @ -294,24 +294,5 @@ module Ameba::Rule::Style | |||
|         } | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %q( | ||||
|         def method | ||||
|           begin | ||||
|             open_connection | ||||
|           ensure | ||||
|             close_connection | ||||
|           end | ||||
|         end | ||||
|       ), "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:3" | ||||
|       issue.end_location.to_s.should eq "source.cr:2:7" | ||||
|       issue.message.should eq "Redundant `begin` block detected" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -201,7 +201,7 @@ module Ameba::Rule::Style | |||
|     end | ||||
| 
 | ||||
|     context "properties" do | ||||
|       context "#allow_multi_next=" do | ||||
|       context "#allow_multi_next" do | ||||
|         it "allows multi next statements by default" do | ||||
|           expect_no_issues subject, <<-CRYSTAL | ||||
|             block do |a, b| | ||||
|  | @ -211,7 +211,7 @@ module Ameba::Rule::Style | |||
|         end | ||||
| 
 | ||||
|         it "allows to configure multi next statements" do | ||||
|           rule = Rule::Style::RedundantNext.new | ||||
|           rule = RedundantNext.new | ||||
|           rule.allow_multi_next = false | ||||
|           source = expect_issue rule, <<-CRYSTAL | ||||
|             block do |a, b| | ||||
|  | @ -238,7 +238,7 @@ module Ameba::Rule::Style | |||
|         end | ||||
| 
 | ||||
|         it "allows to configure empty next statements" do | ||||
|           rule = Rule::Style::RedundantNext.new | ||||
|           rule = RedundantNext.new | ||||
|           rule.allow_empty_next = false | ||||
|           source = expect_issue rule, <<-CRYSTAL | ||||
|             block do | ||||
|  |  | |||
|  | @ -284,7 +284,7 @@ module Ameba::Rule::Style | |||
|     end | ||||
| 
 | ||||
|     context "properties" do | ||||
|       context "#allow_multi_return=" do | ||||
|       context "#allow_multi_return" do | ||||
|         it "allows multi returns by default" do | ||||
|           expect_no_issues subject, <<-CRYSTAL | ||||
|             def method(a, b) | ||||
|  | @ -294,7 +294,7 @@ module Ameba::Rule::Style | |||
|         end | ||||
| 
 | ||||
|         it "allows to configure multi returns" do | ||||
|           rule = Rule::Style::RedundantReturn.new | ||||
|           rule = RedundantReturn.new | ||||
|           rule.allow_multi_return = false | ||||
|           source = expect_issue rule, <<-CRYSTAL | ||||
|             def method(a, b) | ||||
|  | @ -321,7 +321,7 @@ module Ameba::Rule::Style | |||
|         end | ||||
| 
 | ||||
|         it "allows to configure empty returns" do | ||||
|           rule = Rule::Style::RedundantReturn.new | ||||
|           rule = RedundantReturn.new | ||||
|           rule.allow_empty_return = false | ||||
|           source = expect_issue rule, <<-CRYSTAL | ||||
|             def method | ||||
|  |  | |||
|  | @ -3,10 +3,10 @@ require "../../../spec_helper" | |||
| module Ameba | ||||
|   subject = Rule::Style::TypeNames.new | ||||
| 
 | ||||
|   private def it_reports_name(type, name, expected) | ||||
|     it "reports type name #{expected}" do | ||||
|   private def it_reports_name(type, name, expected, *, file = __FILE__, line = __LINE__) | ||||
|     it "reports type name #{expected}", file, line do | ||||
|       rule = Rule::Style::TypeNames.new | ||||
|       expect_issue rule, <<-CRYSTAL, type: type, name: name | ||||
|       expect_issue rule, <<-CRYSTAL, type: type, name: name, file: file, line: line | ||||
|         %{type} %{name}; end | ||||
|         # ^{type}^{name}^^^^ error: Type name should be camelcased: #{expected}, but it was %{name} | ||||
|         CRYSTAL | ||||
|  | @ -49,20 +49,5 @@ module Ameba | |||
|         # ^{} error: Type name should be camelcased: NumericValue, but it was Numeric_value | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         class My_class | ||||
|         end | ||||
|       ), "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:1:1" | ||||
|       issue.end_location.to_s.should eq "source.cr:2:3" | ||||
|       issue.message.should eq( | ||||
|         "Type name should be camelcased: MyClass, but it was My_class" | ||||
|       ) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -22,23 +22,5 @@ module Ameba::Rule::Style | |||
|         end | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         unless something | ||||
|           :one | ||||
|         else | ||||
|           :two | ||||
|         end | ||||
|       ), "source.cr" | ||||
|       subject.catch(s).should_not be_valid | ||||
| 
 | ||||
|       issue = s.issues.first | ||||
|       issue.should_not be_nil | ||||
|       issue.rule.should_not be_nil | ||||
|       issue.location.to_s.should eq "source.cr:1:1" | ||||
|       issue.end_location.to_s.should eq "source.cr:5:3" | ||||
|       issue.message.should eq "Favour if over unless with else" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -3,10 +3,10 @@ require "../../../spec_helper" | |||
| module Ameba | ||||
|   subject = Rule::Style::VariableNames.new | ||||
| 
 | ||||
|   private def it_reports_var_name(name, value, expected) | ||||
|     it "reports variable name #{expected}" do | ||||
|   private def it_reports_var_name(name, value, expected, *, file = __FILE__, line = __LINE__) | ||||
|     it "reports variable name #{expected}", file, line do | ||||
|       rule = Rule::Style::VariableNames.new | ||||
|       expect_issue rule, <<-CRYSTAL, name: name | ||||
|       expect_issue rule, <<-CRYSTAL, name: name, file: file, line: line | ||||
|           %{name} = #{value} | ||||
|         # ^{name} error: Var name should be underscore-cased: #{expected}, not %{name} | ||||
|         CRYSTAL | ||||
|  | @ -62,19 +62,5 @@ module Ameba | |||
|         end | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       s = Source.new %( | ||||
|         badName = "Yeah" | ||||
|       ), "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:1:1" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:7" | ||||
|       issue.message.should eq( | ||||
|         "Var name should be underscore-cased: bad_name, not badName" | ||||
|       ) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -64,10 +64,12 @@ module Ameba::Rule::Style | |||
|     context "properties" do | ||||
|       it "#exclude_calls_with_block" do | ||||
|         rule = VerboseBlock.new | ||||
| 
 | ||||
|         rule.exclude_calls_with_block = true | ||||
|         expect_no_issues rule, <<-CRYSTAL | ||||
|           (1..3).in_groups_of(1) { |i| i.map(&.to_s) } | ||||
|           CRYSTAL | ||||
| 
 | ||||
|         rule.exclude_calls_with_block = false | ||||
|         source = expect_issue rule, <<-CRYSTAL | ||||
|           (1..3).in_groups_of(1) { |i| i.map(&.to_s) } | ||||
|  | @ -81,12 +83,14 @@ module Ameba::Rule::Style | |||
| 
 | ||||
|       it "#exclude_multiple_line_blocks" do | ||||
|         rule = VerboseBlock.new | ||||
| 
 | ||||
|         rule.exclude_multiple_line_blocks = true | ||||
|         expect_no_issues rule, <<-CRYSTAL | ||||
|           (1..3).any? do |i| | ||||
|             i.odd? | ||||
|           end | ||||
|           CRYSTAL | ||||
| 
 | ||||
|         rule.exclude_multiple_line_blocks = false | ||||
|         source = expect_issue rule, <<-CRYSTAL | ||||
|           (1..3).any? do |i| | ||||
|  | @ -102,12 +106,14 @@ module Ameba::Rule::Style | |||
| 
 | ||||
|       it "#exclude_prefix_operators" do | ||||
|         rule = VerboseBlock.new | ||||
| 
 | ||||
|         rule.exclude_prefix_operators = true | ||||
|         expect_no_issues rule, <<-CRYSTAL | ||||
|           (1..3).sum { |i| +i } | ||||
|           (1..3).sum { |i| -i } | ||||
|           (1..3).sum { |i| ~i } | ||||
|           CRYSTAL | ||||
| 
 | ||||
|         rule.exclude_prefix_operators = false | ||||
|         rule.exclude_operators = false | ||||
|         source = expect_issue rule, <<-CRYSTAL | ||||
|  | @ -128,10 +134,12 @@ module Ameba::Rule::Style | |||
| 
 | ||||
|       it "#exclude_operators" do | ||||
|         rule = VerboseBlock.new | ||||
| 
 | ||||
|         rule.exclude_operators = true | ||||
|         expect_no_issues rule, <<-CRYSTAL | ||||
|           (1..3).sum { |i| i * 2 } | ||||
|           CRYSTAL | ||||
| 
 | ||||
|         rule.exclude_operators = false | ||||
|         source = expect_issue rule, <<-CRYSTAL | ||||
|           (1..3).sum { |i| i * 2 } | ||||
|  | @ -145,10 +153,12 @@ module Ameba::Rule::Style | |||
| 
 | ||||
|       it "#exclude_setters" do | ||||
|         rule = VerboseBlock.new | ||||
| 
 | ||||
|         rule.exclude_setters = true | ||||
|         expect_no_issues rule, <<-CRYSTAL | ||||
|           Char::Reader.new("abc").tap { |reader| reader.pos = 0 } | ||||
|           CRYSTAL | ||||
| 
 | ||||
|         rule.exclude_setters = false | ||||
|         source = expect_issue rule, <<-CRYSTAL | ||||
|           Char::Reader.new("abc").tap { |reader| reader.pos = 0 } | ||||
|  | @ -163,12 +173,14 @@ module Ameba::Rule::Style | |||
|       it "#max_line_length" do | ||||
|         rule = VerboseBlock.new | ||||
|         rule.exclude_multiple_line_blocks = false | ||||
| 
 | ||||
|         rule.max_line_length = 60 | ||||
|         expect_no_issues rule, <<-CRYSTAL | ||||
|           (1..3).tap &.tap &.tap &.tap &.tap &.tap &.tap do |i| | ||||
|             i.to_s.reverse.strip.blank? | ||||
|           end | ||||
|           CRYSTAL | ||||
| 
 | ||||
|         rule.max_line_length = nil | ||||
|         source = expect_issue rule, <<-CRYSTAL | ||||
|           (1..3).tap &.tap &.tap &.tap &.tap &.tap &.tap do |i| | ||||
|  | @ -184,10 +196,12 @@ module Ameba::Rule::Style | |||
| 
 | ||||
|       it "#max_length" do | ||||
|         rule = VerboseBlock.new | ||||
| 
 | ||||
|         rule.max_length = 30 | ||||
|         expect_no_issues rule, <<-CRYSTAL | ||||
|           (1..3).tap { |i| i.to_s.split.reverse.join.strip.blank? } | ||||
|           CRYSTAL | ||||
| 
 | ||||
|         rule.max_length = nil | ||||
|         source = expect_issue rule, <<-CRYSTAL | ||||
|           (1..3).tap { |i| i.to_s.split.reverse.join.strip.blank? } | ||||
|  | @ -216,6 +230,7 @@ module Ameba::Rule::Style | |||
|     it "reports call args and named_args" do | ||||
|       rule = VerboseBlock.new | ||||
|       rule.exclude_operators = false | ||||
| 
 | ||||
|       source = expect_issue rule, <<-CRYSTAL | ||||
|         (1..3).map { |i| i.to_s[start: 0.to_i64, count: 3]? } | ||||
|              # ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: [...] `map(&.to_s.[start: 0.to_i64, count: 3]?)` | ||||
|  | @ -258,20 +273,5 @@ module Ameba::Rule::Style | |||
|         (1..3).join(separator: '.', &.to_s) | ||||
|         CRYSTAL | ||||
|     end | ||||
| 
 | ||||
|     it "reports rule, pos and message" do | ||||
|       source = Source.new path: "source.cr", code: %( | ||||
|         (1..3).any? { |i| i.odd? } | ||||
|       ) | ||||
|       subject.catch(source).should_not be_valid | ||||
|       source.issues.size.should eq 1 | ||||
| 
 | ||||
|       issue = source.issues.first | ||||
|       issue.rule.should_not be_nil | ||||
|       issue.location.to_s.should eq "source.cr:1:8" | ||||
|       issue.end_location.to_s.should eq "source.cr:1:26" | ||||
| 
 | ||||
|       issue.message.should eq "Use short block notation instead: `any?(&.odd?)`" | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -65,6 +65,7 @@ module Ameba | |||
|         rules = [AtoAA.new] of Rule::Base | ||||
|         source = Source.new "class A; end", "source.cr" | ||||
|         message = "Infinite loop in source.cr caused by Ameba/AtoAA" | ||||
| 
 | ||||
|         expect_raises(Runner::InfiniteCorrectionLoopError, message) do | ||||
|           Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run | ||||
|         end | ||||
|  | @ -95,11 +96,11 @@ module Ameba | |||
| 
 | ||||
|         it "does not run other rules" do | ||||
|           rules = [Rule::Lint::Syntax.new, Rule::Style::ConstantNames.new] of Rule::Base | ||||
|           source = Source.new %q( | ||||
|           source = Source.new <<-CRYSTAL | ||||
|             MyBadConstant = 1 | ||||
| 
 | ||||
|             when my_bad_syntax | ||||
|           ) | ||||
|             CRYSTAL | ||||
| 
 | ||||
|           Runner.new(rules, [source], formatter, default_severity).run | ||||
|           source.should_not be_valid | ||||
|  | @ -110,9 +111,9 @@ module Ameba | |||
|       context "unneeded disables" do | ||||
|         it "reports an issue if such disable exists" do | ||||
|           rules = [Rule::Lint::UnneededDisableDirective.new] of Rule::Base | ||||
|           source = Source.new %( | ||||
|           source = Source.new <<-CRYSTAL | ||||
|             a = 1 # ameba:disable LineLength | ||||
|           ) | ||||
|             CRYSTAL | ||||
| 
 | ||||
|           Runner.new(rules, [source], formatter, default_severity).run | ||||
|           source.should_not be_valid | ||||
|  | @ -134,9 +135,7 @@ module Ameba | |||
|       it "writes the explanation if sources are not valid and location found" do | ||||
|         io.clear | ||||
|         rules = [ErrorRule.new] of Rule::Base | ||||
|         source = Source.new %( | ||||
|             a = 1 | ||||
|           ), "source.cr" | ||||
|         source = Source.new "a = 1", "source.cr" | ||||
| 
 | ||||
|         runner = Runner.new(rules, [source], formatter, default_severity).run | ||||
|         runner.explain({file: "source.cr", line: 1, column: 1}, io) | ||||
|  | @ -146,9 +145,7 @@ module Ameba | |||
|       it "writes nothing if sources are not valid and location is not found" do | ||||
|         io.clear | ||||
|         rules = [ErrorRule.new] of Rule::Base | ||||
|         source = Source.new %( | ||||
|             a = 1 | ||||
|           ), "source.cr" | ||||
|         source = Source.new "a = 1", "source.cr" | ||||
| 
 | ||||
|         runner = Runner.new(rules, [source], formatter, default_severity).run | ||||
|         runner.explain({file: "source.cr", line: 1, column: 2}, io) | ||||
|  | @ -167,10 +164,9 @@ module Ameba | |||
| 
 | ||||
|       it "returns false if there are invalid sources" do | ||||
|         rules = Rule.rules.map &.new.as(Rule::Base) | ||||
|         s = Source.new %q( | ||||
|           WrongConstant = 5 | ||||
|         ) | ||||
|         Runner.new(rules, [s], formatter, default_severity).run.success?.should be_false | ||||
|         source = Source.new "WrongConstant = 5" | ||||
| 
 | ||||
|         Runner.new(rules, [source], formatter, default_severity).run.success?.should be_false | ||||
|       end | ||||
| 
 | ||||
|       it "depends on the level of severity" do | ||||
|  | @ -184,11 +180,11 @@ module Ameba | |||
| 
 | ||||
|       it "returns false if issue is disabled" do | ||||
|         rules = [NamedRule.new] of Rule::Base | ||||
|         source = Source.new %( | ||||
|         source = Source.new <<-CRYSTAL | ||||
|           def foo | ||||
|             bar = 1 # ameba:disable #{NamedRule.name} | ||||
|           end | ||||
|         ) | ||||
|           CRYSTAL | ||||
|         source.add_issue NamedRule.new, location: {2, 1}, | ||||
|           message: "Useless assignment" | ||||
| 
 | ||||
|  | @ -205,6 +201,7 @@ module Ameba | |||
|             rules = [AtoB.new, BtoA.new] | ||||
|             source = Source.new "class A; end", "source.cr" | ||||
|             message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoA" | ||||
| 
 | ||||
|             expect_raises(Runner::InfiniteCorrectionLoopError, message) do | ||||
|               Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run | ||||
|             end | ||||
|  | @ -214,11 +211,12 @@ module Ameba | |||
|         context "if there are multiple offenses in an inspected file" do | ||||
|           it "aborts because of an infinite loop" do | ||||
|             rules = [AtoB.new, BtoA.new] | ||||
|             source = Source.new %( | ||||
|             source = Source.new <<-CRYSTAL, "source.cr" | ||||
|               class A; end | ||||
|               class A_A; end | ||||
|             ), "source.cr" | ||||
|               CRYSTAL | ||||
|             message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoA" | ||||
| 
 | ||||
|             expect_raises(Runner::InfiniteCorrectionLoopError, message) do | ||||
|               Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run | ||||
|             end | ||||
|  | @ -231,6 +229,7 @@ module Ameba | |||
|           rules = [ClassToModule.new, ModuleToClass.new, AtoB.new, BtoA.new] | ||||
|           source = Source.new "class A_A; end", "source.cr" | ||||
|           message = "Infinite loop in source.cr caused by Ameba/ClassToModule, Ameba/AtoB -> Ameba/ModuleToClass, Ameba/BtoA" | ||||
| 
 | ||||
|           expect_raises(Runner::InfiniteCorrectionLoopError, message) do | ||||
|             Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run | ||||
|           end | ||||
|  | @ -242,6 +241,7 @@ module Ameba | |||
|           rules = [AtoB.new, BtoC.new, CtoA.new] | ||||
|           source = Source.new "class A; end", "source.cr" | ||||
|           message = "Infinite loop in source.cr caused by Ameba/AtoB -> Ameba/BtoC -> Ameba/CtoA" | ||||
| 
 | ||||
|           expect_raises(Runner::InfiniteCorrectionLoopError, message) do | ||||
|             Runner.new(rules, [source], formatter, default_severity, autocorrect: true).run | ||||
|           end | ||||
|  |  | |||
|  | @ -1,13 +1,13 @@ | |||
| require "../spec_helper" | ||||
| 
 | ||||
| module Ameba | ||||
|   private def it_tokenizes(str, expected) | ||||
|     it "tokenizes #{str}" do | ||||
|       ([] of String).tap do |token_types| | ||||
|   private def it_tokenizes(str, expected, *, file = __FILE__, line = __LINE__) | ||||
|     it "tokenizes #{str}", file, line do | ||||
|       %w[].tap do |token_types| | ||||
|         Tokenizer.new(Source.new str, normalize: false) | ||||
|           .run { |token| token_types << token.type.to_s } | ||||
|           .should be_true | ||||
|       end.should eq expected | ||||
|       end.should eq(expected), file: file, line: line | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|  |  | |||
|  | @ -286,3 +286,7 @@ end | |||
| def as_nodes(source) | ||||
|   Ameba::TestNodeVisitor.new(as_node source) | ||||
| end | ||||
| 
 | ||||
| def trailing_whitespace | ||||
|   ' ' | ||||
| end | ||||
|  |  | |||
|  | @ -1,15 +1,21 @@ | |||
| module Ameba::Rule::Lint | ||||
|   # This rule checks for mistyped shorthand assignments. | ||||
|   # | ||||
|   #     # bad | ||||
|   # This is considered invalid: | ||||
|   # | ||||
|   # ``` | ||||
|   # x = -y | ||||
|   # x = +y | ||||
|   # x = !y | ||||
|   # ``` | ||||
|   # | ||||
|   #     # good | ||||
|   # And this is valid: | ||||
|   # | ||||
|   # ``` | ||||
|   # x -= y # or x = -y | ||||
|   # x += y # or x = +y | ||||
|   # x != y # or x = !y | ||||
|   # ``` | ||||
|   # | ||||
|   # YAML configuration example: | ||||
|   # | ||||
|  | @ -27,9 +33,9 @@ module Ameba::Rule::Lint | |||
|     MSG = "Suspicious assignment detected. Did you mean `%s`?" | ||||
| 
 | ||||
|     MISTAKES = { | ||||
|       "=-" => "-=", | ||||
|       "=+" => "+=", | ||||
|       "=!" => "!=", | ||||
|       "=-": "-=", | ||||
|       "=+": "+=", | ||||
|       "=!": "!=", | ||||
|     } | ||||
| 
 | ||||
|     def test(source, node : Crystal::Assign) | ||||
|  | @ -43,9 +49,9 @@ module Ameba::Rule::Lint | |||
|       op_text = source_between(op_location, op_end_location, source.lines) | ||||
| 
 | ||||
|       return unless op_text | ||||
|       return unless MISTAKES.has_key?(op_text) | ||||
|       return unless suggestion = MISTAKES[op_text]? | ||||
| 
 | ||||
|       issue_for op_location, op_end_location, MSG % MISTAKES[op_text] | ||||
|       issue_for op_location, op_end_location, MSG % suggestion | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -57,8 +57,7 @@ module Ameba::Rule::Style | |||
| 
 | ||||
|       if cond.is_a?(Crystal::Assign) && allow_safe_assignment? | ||||
|         issue_for cond, MSG_MISSING do |corrector| | ||||
|           corrector.insert_before(cond, '(') | ||||
|           corrector.insert_after(cond, ')') | ||||
|           corrector.wrap(cond, '(', ')') | ||||
|         end | ||||
|         return | ||||
|       end | ||||
|  |  | |||
|  | @ -130,7 +130,7 @@ module Ameba | |||
|           rule.test(source) | ||||
|         end | ||||
|         check_unneeded_directives(source) | ||||
|         break unless autocorrect? && source.correct | ||||
|         break unless autocorrect? && source.correct? | ||||
| 
 | ||||
|         # The issues that couldn't be corrected will be found again so we | ||||
|         # only keep the corrected ones in order to avoid duplicate reporting. | ||||
|  |  | |||
|  | @ -24,9 +24,10 @@ module Ameba | |||
| 
 | ||||
|     # Corrects any correctable issues and updates `code`. | ||||
|     # Returns `false` if no issues were corrected. | ||||
|     def correct | ||||
|     def correct? | ||||
|       corrector = Corrector.new(code) | ||||
|       issues.each(&.correct(corrector)) | ||||
| 
 | ||||
|       corrected_code = corrector.process | ||||
|       return false if code == corrected_code | ||||
| 
 | ||||
|  |  | |||
|  | @ -125,7 +125,9 @@ class Ameba::Source | |||
| 
 | ||||
|     private def check_range_validity(begin_pos, end_pos) | ||||
|       return unless begin_pos < 0 || end_pos > code.size | ||||
|       raise IndexError.new("The range #{begin_pos}...#{end_pos} is outside the bounds of the source") | ||||
|       raise IndexError.new( | ||||
|         "The range #{begin_pos}...#{end_pos} is outside the bounds of the source" | ||||
|       ) | ||||
|     end | ||||
|   end | ||||
| end | ||||
|  |  | |||
|  | @ -2,6 +2,7 @@ | |||
| class Ameba::Spec::AnnotatedSource | ||||
|   ANNOTATION_PATTERN_1 = /\A\s*(# )?(\^+|\^{})( error:)? / | ||||
|   ANNOTATION_PATTERN_2 = " # error: " | ||||
| 
 | ||||
|   ABBREV = "[...]" | ||||
| 
 | ||||
|   getter lines : Array(String) | ||||
|  | @ -15,6 +16,7 @@ class Ameba::Spec::AnnotatedSource | |||
|   def self.parse(annotated_code) | ||||
|     lines = [] of String | ||||
|     annotations = [] of {Int32, String, String} | ||||
| 
 | ||||
|     code_lines = annotated_code.split('\n') # must preserve trailing newline | ||||
|     code_lines.each do |code_line| | ||||
|       case | ||||
|  | @ -39,7 +41,9 @@ class Ameba::Spec::AnnotatedSource | |||
|   # NOTE: Annotations are sorted so that reconstructing the annotation | ||||
|   #       text via `#to_s` is deterministic. | ||||
|   def initialize(@lines, annotations : Enumerable({Int32, String, String})) | ||||
|     @annotations = annotations.to_a.sort_by { |line, _, message| {line, message} } | ||||
|     @annotations = annotations.to_a.sort_by do |line, _, message| | ||||
|       {line, message} | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   # Annotates the source code with the Ameba issues provided. | ||||
|  | @ -47,7 +51,9 @@ class Ameba::Spec::AnnotatedSource | |||
|   # NOTE: Annotations are sorted so that reconstructing the annotation | ||||
|   #       text via `#to_s` is deterministic. | ||||
|   def initialize(@lines, issues : Enumerable(Issue)) | ||||
|     @annotations = issues_to_annotations(issues).sort_by { |line, _, message| {line, message} } | ||||
|     @annotations = issues_to_annotations(issues).sort_by do |line, _, message| | ||||
|       {line, message} | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   def ==(other) | ||||
|  |  | |||
|  | @ -13,8 +13,8 @@ module Ameba::Spec | |||
|     def failure_message(source) | ||||
|       String.build do |str| | ||||
|         str << "Source expected to be valid, but there are issues: \n\n" | ||||
|         source.issues.reject(&.disabled?).each do |e| | ||||
|           str << "  * #{e.rule.name}: #{e.message}\n" | ||||
|         source.issues.reject(&.disabled?).each do |issue| | ||||
|           str << "  * #{issue.rule.name}: #{issue.message}\n" | ||||
|         end | ||||
|       end | ||||
|     end | ||||
|  |  | |||
|  | @ -129,7 +129,7 @@ module Ameba::Spec::ExpectIssue | |||
|   end | ||||
| 
 | ||||
|   def expect_correction(source, correction, *, file = __FILE__, line = __LINE__) | ||||
|     raise "Use `expect_no_corrections` if the code will not change" unless source.correct | ||||
|     raise "Use `expect_no_corrections` if the code will not change" unless source.correct? | ||||
|     return if correction == source.code | ||||
| 
 | ||||
|     fail <<-MSG, file, line | ||||
|  | @ -144,7 +144,7 @@ module Ameba::Spec::ExpectIssue | |||
|   end | ||||
| 
 | ||||
|   def expect_no_corrections(source, *, file = __FILE__, line = __LINE__) | ||||
|     return unless source.correct | ||||
|     return unless source.correct? | ||||
| 
 | ||||
|     fail <<-MSG, file, line | ||||
|       Expected no corrections, but got: | ||||
|  |  | |||
|  | @ -14,9 +14,5 @@ module Ameba | |||
|   end | ||||
| end | ||||
| 
 | ||||
| def trailing_whitespace | ||||
|   ' ' | ||||
| end | ||||
| 
 | ||||
| include Ameba::Spec::BeValid | ||||
| include Ameba::Spec::ExpectIssue | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue