def test_built_scope_tree_by_process_with_func_call(self): ast = self.create_ast(Fixtures.CALLING_FUNC) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, # no declarative variables variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, ) ] ) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_declaring_var(self): ast = self.create_ast(Fixtures.DECLARING_VAR) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], 'g:explicit_global_var': [self.create_variable()], 'b:buffer_local_var': [self.create_variable()], 'w:window_local_var': [self.create_variable()], 't:tab_local_var': [self.create_variable()], 'g:implicit_global_var': [self.create_variable(is_implicit=True)], '$ENV_VAR': [self.create_variable()], '@"': [self.create_variable()], '&opt_var': [self.create_variable()], 'v:count': [self.create_variable(is_builtin=True)], }, child_scopes=[ self.create_scope(ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], 's:script_local_var': [self.create_variable()], }) ]) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_loop_var(self): ast = self.create_ast(Fixtures.LOOP_VAR) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], 'g:implicit_global_loop_var': [self.create_variable(is_implicit=True)] }, child_scopes=[ self.create_scope(ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }) ]) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_destructuring_assignment(self): ast = self.create_ast(Fixtures.DESTRUCTURING_ASSIGNMENT) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], 'g:for_var1': [self.create_variable()], 'g:for_var2': [self.create_variable()], 'g:let_var1': [self.create_variable()], 'g:let_var2': [self.create_variable()], 'g:let_var3': [self.create_variable()], 'g:rest': [self.create_variable()], # g:list members are not analyzable }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, ) ] ) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_loop_var(self): ast = self.create_ast(Fixtures.LOOP_VAR) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], 'g:implicit_global_loop_var': [self.create_variable(is_implicit=True)] }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], } ) ] ) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_redir(self): ast = self.create_ast(Fixtures.REDIR) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], 'g:var': [self.create_variable()] }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], } ) ] ) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_destructuring_assignment(self): ast = self.create_ast(Fixtures.DESTRUCTURING_ASSIGNMENT) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], 'g:for_var1': [self.create_variable()], 'g:for_var2': [self.create_variable()], 'g:let_var1': [self.create_variable()], 'g:let_var2': [self.create_variable()], 'g:let_var3': [self.create_variable()], 'g:rest': [self.create_variable()], # g:list members are not analyzable }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, ) ]) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_func_call(self): ast = self.create_ast(Fixtures.CALLING_FUNC) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, # no declarative variables variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, ) ]) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_lambda(self): ast = self.create_ast(Fixtures.LAMBDA) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.LAMBDA, variables={ 'i': [ self.create_variable( explicity=ExplicityOfScopeVisibility. IMPLICIT_BUT_CONSTRAINED, is_explicit_lambda_argument=True, ) ], 'a:000': [self.create_variable()], 'a:0': [self.create_variable()], 'a:1': [self.create_variable()], 'a:2': [self.create_variable()], 'a:3': [self.create_variable()], 'a:4': [self.create_variable()], 'a:5': [self.create_variable()], 'a:6': [self.create_variable()], 'a:7': [self.create_variable()], 'a:8': [self.create_variable()], 'a:9': [self.create_variable()], 'a:10': [self.create_variable()], 'a:11': [self.create_variable()], 'a:12': [self.create_variable()], 'a:13': [self.create_variable()], 'a:14': [self.create_variable()], 'a:15': [self.create_variable()], 'a:16': [self.create_variable()], 'a:17': [self.create_variable()], 'a:18': [self.create_variable()], 'a:19': [self.create_variable()], }, ), ]) ]) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_declaring_func(self): ast = self.create_ast(Fixtures.DECLARING_FUNC) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, functions={ 'g:ExplicitGlobalFunc': [self.create_variable()], 'g:ImplicitGlobalFunc': [self.create_variable(is_implicit=True)], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, functions={'s:ScriptLocalFunc': [self.create_variable()]}, child_scopes=[ self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }, ), self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }, ), self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }, ), ]) ]) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_lambda(self): ast = self.create_ast(Fixtures.LAMBDA) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.LAMBDA, variables={ 'i': [self.create_variable( explicity=ExplicityOfScopeVisibility.IMPLICIT_BUT_CONSTRAINED, is_explicit_lambda_argument=True, )], 'a:000': [self.create_variable()], 'a:0': [self.create_variable()], 'a:1': [self.create_variable()], 'a:2': [self.create_variable()], 'a:3': [self.create_variable()], 'a:4': [self.create_variable()], 'a:5': [self.create_variable()], 'a:6': [self.create_variable()], 'a:7': [self.create_variable()], 'a:8': [self.create_variable()], 'a:9': [self.create_variable()], 'a:10': [self.create_variable()], 'a:11': [self.create_variable()], 'a:12': [self.create_variable()], 'a:13': [self.create_variable()], 'a:14': [self.create_variable()], 'a:15': [self.create_variable()], 'a:16': [self.create_variable()], 'a:17': [self.create_variable()], 'a:18': [self.create_variable()], 'a:19': [self.create_variable()], }, ), ] ) ] ) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_declaring_func_in_func(self): ast = self.create_ast(Fixtures.DECLARING_FUNC_IN_FUNC) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, functions={ 'FuncContext': [ self.create_variable(explicity=ExplicityOfScopeVisibility. IMPLICIT_BUT_CONSTRAINED) ], 'ImplicitGlobalFunc': [ self.create_variable(explicity=ExplicityOfScopeVisibility. IMPLICIT_BUT_CONSTRAINED) ], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }), ]), ]) ]) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_declaring_func_in_func(self): ast = self.create_ast(Fixtures.DECLARING_FUNC_IN_FUNC) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, functions={ 'g:FuncContext': [self.create_variable(is_implicit=True)], 'g:ImplicitGlobalFunc': [self.create_variable(is_implicit=True)], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], } ), ] ), ] ) ] ) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_declaring_with_dict_key(self): ast = self.create_ast(Fixtures.DICT_KEY) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], # Member functions are not analyzable }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], # Member functions are not analyzable }, child_scopes=[ self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'l:self': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], } ), self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'l:self': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], } ), ] ) ] ) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_declaring_with_dict_key(self): ast = self.create_ast(Fixtures.DICT_KEY) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], # Member functions are not analyzable }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], # Member functions are not analyzable }, child_scopes=[ self.create_scope(ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'l:self': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }), self.create_scope(ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'l:self': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }), ]) ]) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_declarative_identifier_links_by_process(self): ast = self.create_ast(Fixtures.DECLARING_AND_REFERENCING) # Function name identifier node dec_id_node = ast['body'][0]['left'] linker = ScopeLinker() linker.process(ast) scope_tree = linker.scope_tree # Expect a script local scope expected_scope = scope_tree['child_scopes'][0] link_registry = linker.link_registry actual_scope = link_registry.get_context_scope_by_identifier(dec_id_node) self.assertScopeTreeEqual(expected_scope, actual_scope)
def test_built_reference_variable_links_by_process(self): ast = self.create_ast(Fixtures.DECLARING_AND_REFERENCING) # Function name identifier node expected_dec_id = ast['body'][0]['left'] linker = ScopeLinker() linker.process(ast) scope_tree = linker.scope_tree # Function local scope scope = scope_tree['child_scopes'][0] variable_func = scope['functions']['s:Function'][0] link_registry = linker.link_registry actual_dec_id = link_registry.get_declarative_identifier_by_variable(variable_func) self.assertScopeTreeEqual(expected_dec_id, actual_dec_id)
def test_built_declarative_identifier_links_by_process(self): ast = self.create_ast(Fixtures.DECLARING_AND_REFERENCING) # Function name identifier node dec_id_node = ast['body'][0]['left'] linker = ScopeLinker() linker.process(ast) scope_tree = linker.scope_tree # Expect a script local scope expected_scope = scope_tree['child_scopes'][0] link_registry = linker.link_registry actual_scope = link_registry.get_context_scope_by_identifier( dec_id_node) self.assertScopeTreeEqual(expected_scope, actual_scope)
def test_built_reference_variable_links_by_process(self): ast = self.create_ast(Fixtures.DECLARING_AND_REFERENCING) # Function name identifier node expected_dec_id = ast['body'][0]['left'] linker = ScopeLinker() linker.process(ast) scope_tree = linker.scope_tree # Function local scope scope = scope_tree['child_scopes'][0] variable_func = scope['functions']['s:Function'][0] link_registry = linker.link_registry actual_dec_id = link_registry.get_declarative_identifier_by_variable( variable_func) self.assertScopeTreeEqual(expected_dec_id, actual_dec_id)
def test_built_scope_tree_by_process_with_declaring_var_in_func(self): ast = self.create_ast(Fixtures.DECLARING_VAR_IN_FUNC) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, functions={ 'FuncContext': [self.create_variable(explicity=ExplicityOfScopeVisibility.IMPLICIT_BUT_CONSTRAINED)], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], 'explicit_func_local_var': [self.create_variable()], 'implicit_func_local_var': [self.create_variable(explicity=ExplicityOfScopeVisibility.IMPLICIT)], } ) ] ) ] ) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_declaring_var_in_func(self): ast = self.create_ast(Fixtures.DECLARING_VAR_IN_FUNC) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, functions={ 'g:FuncContext': [self.create_variable(is_implicit=True)], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], 'l:explicit_func_local_var': [self.create_variable()], 'l:implicit_func_local_var': [self.create_variable(is_implicit=True)], }) ]) ]) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def process(self, ast): scope_linker = ScopeLinker() scope_linker.process(ast) id_collector = IdentifierClassifier.IdentifierCollector() classified_id_group = id_collector.collect_identifiers(ast) dec_id_nodes = classified_id_group['static_declaring_identifiers'] ref_id_nodes = classified_id_group['static_referencing_identifiers'] self._scope_tree = scope_linker.scope_tree self._link_registry = scope_linker.link_registry # Attach a parent_scope accessor to the scope tree ReferenceReachabilityTester.TwoWayScopeReferenceAttacher.attach( self._scope_tree) # Reset REFERECED_FLAG to False for dec_id_node in dec_id_nodes: dec_id_node[REFERECED_FLAG] = False for ref_id_node in ref_id_nodes: is_reachable = self.check_reachability(ref_id_node) ref_id_node[REACHABILITY_FLAG] = is_reachable
def test_built_scope_tree_by_process_with_declaring_var(self): ast = self.create_ast(Fixtures.DECLARING_VAR) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], 'g:explicit_global_var': [self.create_variable()], 'b:buffer_local_var': [self.create_variable()], 'w:window_local_var': [self.create_variable()], 't:tab_local_var': [self.create_variable()], 'g:implicit_global_var': [self.create_variable(is_implicit=True)], '$ENV_VAR': [self.create_variable()], '@"': [self.create_variable()], '&opt_var': [self.create_variable()], 'v:count': [self.create_variable(is_builtin=True)], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], 's:script_local_var': [self.create_variable()], } ) ] ) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_redir(self): ast = self.create_ast(Fixtures.REDIR) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], 'g:var': [self.create_variable()] }, child_scopes=[ self.create_scope(ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }) ]) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_declaring_func(self): ast = self.create_ast(Fixtures.DECLARING_FUNC) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, functions={ 'ExplicitGlobalFunc': [self.create_variable(explicity=ExplicityOfScopeVisibility.UNRECOMMENDED_EXPLICIT)], 'ImplicitGlobalFunc': [self.create_variable(explicity=ExplicityOfScopeVisibility.IMPLICIT_BUT_CONSTRAINED)], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, functions={ 's:ScriptLocalFunc': [self.create_variable()] }, child_scopes=[ self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }, ), self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }, ), self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }, ), ] ) ] ) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def test_built_scope_tree_by_process_with_func_param(self): ast = self.create_ast(Fixtures.FUNC_PARAM) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, functions={ 'g:FunctionWithNoParams': [self.create_variable()], 'g:FunctionWithOneParam': [self.create_variable()], 'g:FunctionWithTwoParams': [self.create_variable()], 'g:FunctionWithVarParams': [self.create_variable()], 'g:FunctionWithParamsAndVarParams': [self.create_variable()], 'g:FunctionWithRange': [self.create_variable()], 'g:FunctionWithDict': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], } ), self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], 'a:param': [self.create_variable()], } ), self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], 'a:param1': [self.create_variable()], 'a:param2': [self.create_variable()], } ), self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:1': [self.create_variable()], 'a:2': [self.create_variable()], 'a:3': [self.create_variable()], 'a:4': [self.create_variable()], 'a:5': [self.create_variable()], 'a:6': [self.create_variable()], 'a:7': [self.create_variable()], 'a:8': [self.create_variable()], 'a:9': [self.create_variable()], 'a:10': [self.create_variable()], 'a:11': [self.create_variable()], 'a:12': [self.create_variable()], 'a:13': [self.create_variable()], 'a:14': [self.create_variable()], 'a:15': [self.create_variable()], 'a:16': [self.create_variable()], 'a:17': [self.create_variable()], 'a:18': [self.create_variable()], 'a:19': [self.create_variable()], 'a:20': [self.create_variable()], 'a:000': [self.create_variable()], } ), self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:param_var1': [self.create_variable()], 'a:0': [self.create_variable()], 'a:1': [self.create_variable()], 'a:2': [self.create_variable()], 'a:3': [self.create_variable()], 'a:4': [self.create_variable()], 'a:5': [self.create_variable()], 'a:6': [self.create_variable()], 'a:7': [self.create_variable()], 'a:8': [self.create_variable()], 'a:9': [self.create_variable()], 'a:10': [self.create_variable()], 'a:11': [self.create_variable()], 'a:12': [self.create_variable()], 'a:13': [self.create_variable()], 'a:14': [self.create_variable()], 'a:15': [self.create_variable()], 'a:16': [self.create_variable()], 'a:17': [self.create_variable()], 'a:18': [self.create_variable()], 'a:19': [self.create_variable()], 'a:000': [self.create_variable()], } ), self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:param': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], 'a:firstline': [self.create_variable()], 'a:lastline': [self.create_variable()], } ), self.create_scope( ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'l:self': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], } ), ] ) ] ) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
class ReferenceReachabilityTester(object): """ A class for tester for reference reachability. """ class TwoWayScopeReferenceAttacher(object): """ A class for AST processor to do attach to way reference between a parent scope and the child scopes. """ @classmethod def attach(cls, root_scope_tree): # type: (Scope) -> Scope root_scope_tree.parent = None return cls._attach_recursively(root_scope_tree) @classmethod def _attach_recursively(cls, scope_tree): # type: (Scope) -> Scope for child_scope in scope_tree.child_scopes: child_scope.parent = scope_tree cls._attach_recursively(child_scope) return scope_tree def __init__(self): self._scope_linker = ScopeLinker() # type: ScopeLinker def process(self, ast): self._scope_linker.process(ast) id_collector = IdentifierClassifier.IdentifierCollector() classified_id_group = id_collector.collect_identifiers(ast) dec_id_nodes = classified_id_group.statically_declared_identifiers ref_id_nodes = classified_id_group.statically_referencing_identifiers # Attach a parent_scope accessor to the scope tree ReferenceReachabilityTester.TwoWayScopeReferenceAttacher.attach( self._scope_linker.scope_tree) # Reset REFERENCED_FLAG to False for dec_id_node in dec_id_nodes: dec_id_node[REFERENCED_FLAG] = False for ref_id_node in ref_id_nodes: is_reachable = self.check_reachability(ref_id_node) ref_id_node[REACHABILITY_FLAG] = is_reachable def get_objective_scope_visibility( self, decl_or_ref_id_node ): # type: (Dict[str, Any]) -> ScopeVisibilityHint """ Returns a objective scope visibility by a declarative identifier node or a reference identifier node. """ context_scope = self._scope_linker.link_registry.get_context_scope_by_identifier( decl_or_ref_id_node) possible_visibility_hint = detect_possible_scope_visibility( decl_or_ref_id_node, context_scope) possible_explicity = possible_visibility_hint.explicity # Return the visibility soon if the node already have a determined visibility. if possible_explicity is not ExplicityOfScopeVisibility.IMPLICIT_OR_LAMBDA: determined_visibility_hint = possible_visibility_hint return determined_visibility_hint # The node have IMPLICIT_OR_LAMBDA can be only reference identifier nodes. # SEE: detect_possible_scope_visibility ref_id_node = decl_or_ref_id_node # It is constrained implicit if it is a lambda argument. if self._is_reference_to_lambda_argument(ref_id_node): return ScopeVisibilityHint( scope_visibility=possible_visibility_hint.scope_visibility, explicity=ExplicityOfScopeVisibility.IMPLICIT_BUT_CONSTRAINED, ) # Otherwise it is implicit. return ScopeVisibilityHint( scope_visibility=possible_visibility_hint.scope_visibility, explicity=ExplicityOfScopeVisibility.IMPLICIT) def _reset_referenced_flag(self, scope_tree): # type: (Scope) -> None for child_scope in scope_tree.child_scopes: for functions in child_scope.functions.values(): for func in functions: decl_id_node = self._scope_linker.link_registry.get_declarative_identifier_by_variable( func) decl_id_node[REFERENCED_FLAG] = False for variables in child_scope.variables.values(): for variable in variables: decl_id_node = self._scope_linker.link_registry.get_declarative_identifier_by_variable( variable) decl_id_node[REFERENCED_FLAG] = False self._reset_referenced_flag(child_scope) def get_referenced_variable_declarations(self, ref_id_node): # type: (Dict[str, Any]) -> Union[List[VariableDeclaration], GlobalVariableDeclaration] scope = self._scope_linker.link_registry.get_context_scope_by_identifier( ref_id_node) var_name = remove_optional_scope_prefix(ref_id_node['value']) is_func_id = is_function_identifier(ref_id_node) while scope is not None: if is_func_id: functions_list = scope.functions if var_name in functions_list: # The function is found in the symbol table for functions. funcs = functions_list[var_name] for func in funcs: declaring_id_node = self._scope_linker.link_registry \ .get_declarative_identifier_by_variable(func) declaring_id_node[REFERENCED_FLAG] = True return funcs else: # We can access the function via a variable function # reference if the function not found from the symbol table # for functions. So we should check the symbol table for # variables to search the function reference. pass variables_list = scope.variables if var_name in variables_list: # The variable or function reference found in the symbol table # for variables. variables = variables_list[var_name] for variable in variables: declaring_id_node = self._scope_linker.link_registry \ .get_declarative_identifier_by_variable(variable) declaring_id_node[REFERENCED_FLAG] = True return variables scope = scope.parent # If it is builtin, it will be used by Vim. if is_builtin_variable(ref_id_node): return GLOBAL_VARIABLE_DECLARATION return [] def check_reachability(self, ref_id_node): # type: (Dict[str, Any]) -> bool # NOTE: We can only assume dynamic identifiers can reach. if is_dynamic_identifier(ref_id_node): return True declarative_variables_or_global = self.get_referenced_variable_declarations( ref_id_node) if isinstance(declarative_variables_or_global, GlobalVariableDeclaration): # NOTE: It is a global like variable, so we can only assume it can reach. return True # NOTE: It is reachable if at least one declarative identifier can reach to found. return len(declarative_variables_or_global) > 0 def _is_reference_to_lambda_argument( self, ref_id_node): # type: (Dict[str, Any]) -> bool # NOTE: We can only assume dynamic identifiers can reach. if is_dynamic_identifier(ref_id_node): return True declarative_variables_or_global = self.get_referenced_variable_declarations( ref_id_node) if isinstance(declarative_variables_or_global, GlobalVariableDeclaration): return False declarative_variables = declarative_variables_or_global for declarative_variable in declarative_variables: if declarative_variable.is_explicit_lambda_argument: return True return False
def test_built_scope_tree_by_process_with_func_param(self): ast = self.create_ast(Fixtures.FUNC_PARAM) linker = ScopeLinker() linker.process(ast) expected_scope_tree = self.create_scope( ScopeVisibility.GLOBAL_LIKE, variables={ 'g:': [self.create_variable()], 'b:': [self.create_variable()], 'w:': [self.create_variable()], 't:': [self.create_variable()], 'v:': [self.create_variable()], }, functions={ 'g:FunctionWithNoParams': [self.create_variable()], 'g:FunctionWithOneParam': [self.create_variable()], 'g:FunctionWithTwoParams': [self.create_variable()], 'g:FunctionWithVarParams': [self.create_variable()], 'g:FunctionWithParamsAndVarParams': [self.create_variable()], 'g:FunctionWithRange': [self.create_variable()], 'g:FunctionWithDict': [self.create_variable()], }, child_scopes=[ self.create_scope( ScopeVisibility.SCRIPT_LOCAL, variables={ 's:': [self.create_variable()], }, child_scopes=[ self.create_scope(ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }), self.create_scope(ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], 'a:param': [self.create_variable()], }), self.create_scope(ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], 'a:param1': [self.create_variable()], 'a:param2': [self.create_variable()], }), self.create_scope(ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:1': [self.create_variable()], 'a:2': [self.create_variable()], 'a:3': [self.create_variable()], 'a:4': [self.create_variable()], 'a:5': [self.create_variable()], 'a:6': [self.create_variable()], 'a:7': [self.create_variable()], 'a:8': [self.create_variable()], 'a:9': [self.create_variable()], 'a:10': [self.create_variable()], 'a:11': [self.create_variable()], 'a:12': [self.create_variable()], 'a:13': [self.create_variable()], 'a:14': [self.create_variable()], 'a:15': [self.create_variable()], 'a:16': [self.create_variable()], 'a:17': [self.create_variable()], 'a:18': [self.create_variable()], 'a:19': [self.create_variable()], 'a:20': [self.create_variable()], 'a:000': [self.create_variable()], }), self.create_scope(ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:param_var1': [self.create_variable()], 'a:0': [self.create_variable()], 'a:1': [self.create_variable()], 'a:2': [self.create_variable()], 'a:3': [self.create_variable()], 'a:4': [self.create_variable()], 'a:5': [self.create_variable()], 'a:6': [self.create_variable()], 'a:7': [self.create_variable()], 'a:8': [self.create_variable()], 'a:9': [self.create_variable()], 'a:10': [self.create_variable()], 'a:11': [self.create_variable()], 'a:12': [self.create_variable()], 'a:13': [self.create_variable()], 'a:14': [self.create_variable()], 'a:15': [self.create_variable()], 'a:16': [self.create_variable()], 'a:17': [self.create_variable()], 'a:18': [self.create_variable()], 'a:19': [self.create_variable()], 'a:000': [self.create_variable()], }), self.create_scope(ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'a:': [self.create_variable()], 'a:param': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], 'a:firstline': [self.create_variable()], 'a:lastline': [self.create_variable()], }), self.create_scope(ScopeVisibility.FUNCTION_LOCAL, variables={ 'l:': [self.create_variable()], 'l:self': [self.create_variable()], 'a:': [self.create_variable()], 'a:0': [self.create_variable()], 'a:000': [self.create_variable()], }), ]) ]) self.assertScopeTreeEqual(expected_scope_tree, linker.scope_tree)
def __init__(self): self._scope_linker = ScopeLinker() # type: ScopeLinker
class ReferenceReachabilityTester(object): """ A class for tester for reference reachability. """ class TwoWayScopeReferenceAttacher(object): """ A class for AST processor to do attach to way reference between a parent scope and the child scopes. """ @classmethod def attach(cls, root_scope_tree): # type: (Scope) -> Scope root_scope_tree.parent = None return cls._attach_recursively(root_scope_tree) @classmethod def _attach_recursively(cls, scope_tree): # type: (Scope) -> Scope for child_scope in scope_tree.child_scopes: child_scope.parent = scope_tree cls._attach_recursively(child_scope) return scope_tree def __init__(self): self._scope_linker = ScopeLinker() # type: ScopeLinker def process(self, ast): self._scope_linker.process(ast) id_collector = IdentifierClassifier.IdentifierCollector() classified_id_group = id_collector.collect_identifiers(ast) dec_id_nodes = classified_id_group.statically_declared_identifiers ref_id_nodes = classified_id_group.statically_referencing_identifiers # Attach a parent_scope accessor to the scope tree ReferenceReachabilityTester.TwoWayScopeReferenceAttacher.attach(self._scope_linker.scope_tree) # Reset REFERENCED_FLAG to False for dec_id_node in dec_id_nodes: dec_id_node[REFERENCED_FLAG] = False for ref_id_node in ref_id_nodes: is_reachable = self.check_reachability(ref_id_node) ref_id_node[REACHABILITY_FLAG] = is_reachable def get_objective_scope_visibility(self, decl_or_ref_id_node): # type: (Dict[str, Any]) -> ScopeVisibilityHint """ Returns a objective scope visibility by a declarative identifier node or a reference identifier node. """ context_scope = self._scope_linker.link_registry.get_context_scope_by_identifier(decl_or_ref_id_node) possible_visibility_hint = detect_possible_scope_visibility(decl_or_ref_id_node, context_scope) possible_explicity = possible_visibility_hint.explicity # Return the visibility soon if the node already have a determined visibility. if possible_explicity is not ExplicityOfScopeVisibility.IMPLICIT_OR_LAMBDA: determined_visibility_hint = possible_visibility_hint return determined_visibility_hint # The node have IMPLICIT_OR_LAMBDA can be only reference identifier nodes. # SEE: detect_possible_scope_visibility ref_id_node = decl_or_ref_id_node # It is constrained implicit if it is a lambda argument. if self._is_reference_to_lambda_argument(ref_id_node): return ScopeVisibilityHint( scope_visibility=possible_visibility_hint.scope_visibility, explicity=ExplicityOfScopeVisibility.IMPLICIT_BUT_CONSTRAINED, ) # Otherwise it is implicit. return ScopeVisibilityHint( scope_visibility=possible_visibility_hint.scope_visibility, explicity=ExplicityOfScopeVisibility.IMPLICIT ) def _reset_referenced_flag(self, scope_tree): # type: (Scope) -> None for child_scope in scope_tree.child_scopes: for functions in child_scope.functions.values(): for func in functions: decl_id_node = self._scope_linker.link_registry.get_declarative_identifier_by_variable(func) decl_id_node[REFERENCED_FLAG] = False for variables in child_scope.variables.values(): for variable in variables: decl_id_node = self._scope_linker.link_registry.get_declarative_identifier_by_variable(variable) decl_id_node[REFERENCED_FLAG] = False self._reset_referenced_flag(child_scope) def get_referenced_variable_declarations(self, ref_id_node): # type: (Dict[str, Any]) -> Union[List[VariableDeclaration], GlobalVariableDeclaration] scope = self._scope_linker.link_registry.get_context_scope_by_identifier(ref_id_node) var_name = remove_optional_scope_prefix(ref_id_node['value']) is_func_id = is_function_identifier(ref_id_node) while scope is not None: if is_func_id: functions_list = scope.functions if var_name in functions_list: # The function is found in the symbol table for functions. funcs = functions_list[var_name] for func in funcs: declaring_id_node = self._scope_linker.link_registry \ .get_declarative_identifier_by_variable(func) declaring_id_node[REFERENCED_FLAG] = True return funcs else: # We can access the function via a variable function # reference if the function not found from the symbol table # for functions. So we should check the symbol table for # variables to search the function reference. pass variables_list = scope.variables if var_name in variables_list: # The variable or function reference found in the symbol table # for variables. variables = variables_list[var_name] for variable in variables: declaring_id_node = self._scope_linker.link_registry \ .get_declarative_identifier_by_variable(variable) declaring_id_node[REFERENCED_FLAG] = True return variables scope = scope.parent # If it is builtin, it will be used by Vim. if is_builtin_variable(ref_id_node): return GLOBAL_VARIABLE_DECLARATION return [] def check_reachability(self, ref_id_node): # type: (Dict[str, Any]) -> bool # NOTE: We can only assume dynamic identifiers can reach. if is_dynamic_identifier(ref_id_node): return True declarative_variables_or_global = self.get_referenced_variable_declarations(ref_id_node) if isinstance(declarative_variables_or_global, GlobalVariableDeclaration): # NOTE: It is a global like variable, so we can only assume it can reach. return True # NOTE: It is reachable if at least one declarative identifier can reach to found. return len(declarative_variables_or_global) > 0 def _is_reference_to_lambda_argument(self, ref_id_node): # type: (Dict[str, Any]) -> bool # NOTE: We can only assume dynamic identifiers can reach. if is_dynamic_identifier(ref_id_node): return True declarative_variables_or_global = self.get_referenced_variable_declarations(ref_id_node) if isinstance(declarative_variables_or_global, GlobalVariableDeclaration): return False declarative_variables = declarative_variables_or_global for declarative_variable in declarative_variables: if declarative_variable.is_explicit_lambda_argument: return True return False