mirror of
				https://gitea.invidious.io/iv-org/shard-ameba.git
				synced 2024-08-15 00:53:29 +00:00 
			
		
		
		
	Avoid exponential recursion while finding variable references in scopes (#203)
* Avoid exponential recursion while finding variable references in scopes * Adjust source example in test
This commit is contained in:
		
							parent
							
								
									d28f9f756e
								
							
						
					
					
						commit
						51b0a07e81
					
				
					 5 changed files with 109 additions and 5 deletions
				
			
		|  | @ -47,6 +47,68 @@ module Ameba::AST | |||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe "#references?" do | ||||
|     it "returns true if current scope references variable" do | ||||
|       nodes = as_nodes %( | ||||
|         def method | ||||
|           a = 2 | ||||
|           block do | ||||
|             3.times { |i| a = a + i } | ||||
|           end | ||||
|         end | ||||
|       ) | ||||
|       scope = Scope.new nodes.def_nodes.first | ||||
|       var_node = nodes.var_nodes.first | ||||
|       scope.add_variable var_node | ||||
|       scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope) | ||||
| 
 | ||||
|       variable = Variable.new(var_node, scope) | ||||
|       variable.reference nodes.var_nodes.first, scope.inner_scopes.first | ||||
| 
 | ||||
|       scope.references?(variable).should be_true | ||||
|     end | ||||
| 
 | ||||
|     it "returns false if inner scopes are not checked" do | ||||
|       nodes = as_nodes %( | ||||
|         def method | ||||
|           a = 2 | ||||
|           block do | ||||
|             3.times { |i| a = a + i } | ||||
|           end | ||||
|         end | ||||
|       ) | ||||
|       scope = Scope.new nodes.def_nodes.first | ||||
|       var_node = nodes.var_nodes.first | ||||
|       scope.add_variable var_node | ||||
|       scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope) | ||||
| 
 | ||||
|       variable = Variable.new(var_node, scope) | ||||
|       variable.reference nodes.var_nodes.first, scope.inner_scopes.first | ||||
| 
 | ||||
|       scope.references?(variable, check_inner_scopes: false).should be_false | ||||
|     end | ||||
| 
 | ||||
|     it "returns false if current scope does not reference variable" do | ||||
|       nodes = as_nodes %( | ||||
|         def method | ||||
|           a = 2 | ||||
|           block do | ||||
|             b = 3 | ||||
|             3.times { |i| b = b + i } | ||||
|           end | ||||
|         end | ||||
|       ) | ||||
|       scope = Scope.new nodes.def_nodes.first | ||||
|       var_node = nodes.var_nodes.first | ||||
|       scope.add_variable var_node | ||||
|       scope.inner_scopes << Scope.new(nodes.block_nodes.first, scope) | ||||
| 
 | ||||
|       variable = Variable.new(var_node, scope) | ||||
| 
 | ||||
|       scope.inner_scopes.first.references?(variable).should be_false | ||||
|     end | ||||
|   end | ||||
| 
 | ||||
|   describe "#add_variable" do | ||||
|     it "adds a new variable to the scope" do | ||||
|       scope = Scope.new as_node("") | ||||
|  |  | |||
|  | @ -1006,6 +1006,48 @@ module Ameba::Rule::Lint | |||
|       end | ||||
|     end | ||||
| 
 | ||||
|     it "does not report if variable is referenced and there is a deep level scope" do | ||||
|       s = Source.new %( | ||||
|         response = JSON.build do |json| | ||||
|           json.object do | ||||
|             json.object do | ||||
|               json.object do | ||||
|                 json.object do | ||||
|                   json.object do | ||||
|                     json.object do | ||||
|                       json.object do | ||||
|                         json.object do | ||||
|                           json.object do | ||||
|                             json.object do | ||||
|                               json.object do | ||||
|                                 json.object do | ||||
|                                   json.object do | ||||
|                                     json.object do | ||||
|                                       json.object do | ||||
|                                         anything | ||||
|                                       end | ||||
|                                     end | ||||
|                                   end | ||||
|                                 end | ||||
|                               end | ||||
|                             end | ||||
|                           end | ||||
|                         end | ||||
|                       end | ||||
|                     end | ||||
|                   end | ||||
|                 end | ||||
|               end | ||||
|             end | ||||
|           end | ||||
|         end | ||||
| 
 | ||||
|         response = JSON.parse(response) | ||||
|         response | ||||
|        ) | ||||
|       subject.catch(s).should be_valid | ||||
|     end | ||||
| 
 | ||||
|     context "uninitialized" do | ||||
|       it "reports if uninitialized assignment is not referenced at a top level" do | ||||
|         s = Source.new %( | ||||
|  |  | |||
|  | @ -134,10 +134,10 @@ module Ameba::AST | |||
| 
 | ||||
|     # Returns true if current scope (or any of inner scopes) references variable, | ||||
|     # false if not. | ||||
|     def references?(variable : Variable) | ||||
|     def references?(variable : Variable, check_inner_scopes = true) | ||||
|       variable.references.any? do |reference| | ||||
|         reference.scope == self || | ||||
|           inner_scopes.any?(&.references? variable) | ||||
|         return true if reference.scope == self | ||||
|         check_inner_scopes && inner_scopes.any?(&.references?(variable)) | ||||
|       end || variable.used_in_macro? | ||||
|     end | ||||
| 
 | ||||
|  |  | |||
|  | @ -113,7 +113,7 @@ module Ameba::AST | |||
|     # ``` | ||||
|     def captured_by_block?(scope = @scope) | ||||
|       scope.inner_scopes.each do |inner_scope| | ||||
|         return true if inner_scope.block? && inner_scope.references?(self) | ||||
|         return true if inner_scope.block? && inner_scope.references?(self, check_inner_scopes: false) | ||||
|         return true if captured_by_block?(inner_scope) | ||||
|       end | ||||
| 
 | ||||
|  |  | |||
|  | @ -39,7 +39,7 @@ module Ameba::Rule::Lint | |||
| 
 | ||||
|     def test(source, node, scope : AST::Scope) | ||||
|       scope.variables.each do |var| | ||||
|         next if var.captured_by_block? || var.used_in_macro? || var.ignored? | ||||
|         next if var.ignored? || var.used_in_macro? || var.captured_by_block? | ||||
| 
 | ||||
|         var.assignments.each do |assign| | ||||
|           next if assign.referenced? || assign.transformed? | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue