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