def test_reference_reachability_with_referenced_all_params(self): ast = self.create_ast(Fixtures.REFERENCED_ALL_PARAMS) scope_plugin = ScopePlugin() scope_plugin.process(ast) expected_variables_unused = { 'a:': False, 'l:': False, 'a:0': False, 'a:000': False, 'a:param': False, 'a:param1': False, 'a:param2': False, 'a:param_var': False, 'a:param_with_range': False, 'a:firstline': False, 'a:lastline': False, } self.assertVariablesUndeclared(expected_variables_unused, scope_plugin, ast)
def test_reference_reachability_with_referenced_all(self): ast = self.create_ast(Fixtures.REFERENCED_ALL) scope_plugin = ScopePlugin() scope_plugin.process(ast) expected_variables_undeclared = { 'g:explicit_global_var': False, 'b:buffer_local_var': False, 'w:window_local_var': False, 't:tab_local_var': False, 's:script_local_var': False, 'implicit_global_var': False, 'g:implicit_global_var': False, '$ENV_VAR': False, '@"': False, '&opt_var': False, 'v:count': False, 'count': False, 'g:': False, 'b:': False, 'w:': False, 't:': False, 'v:': False, 'v:key': False, 'v:val': False, 'filter': False, 'g:dict': True, } self.assertVariablesUndeclared(expected_variables_undeclared, scope_plugin, ast)
def assertProcessing(self, file_path, expected_scope_tree): parser = Parser() ast = parser.parse_file(file_path) plugin = ScopePlugin() plugin.process(ast) self.assertScopeTree(ast['vint_scope_tree'], expected_scope_tree)
def test_reference_reachability_with_unreferenced_var(self): ast = self.create_ast(Fixtures.UNREFERENCED_VAR) scope_plugin = ScopePlugin() scope_plugin.process(ast) expected_variables_undeclared = { 's:implicit_global_var': True, } self.assertVariablesUndeclared(expected_variables_undeclared, scope_plugin, ast)
def test_reference_reachability_with_unanalyzable(self): ast = self.create_ast(Fixtures.UNANALYZABLE) scope_plugin = ScopePlugin() scope_plugin.process(ast) expected_variables_undeclared = { 'list_slice': True, 'dict': True, } self.assertVariablesUndeclared(expected_variables_undeclared, scope_plugin, ast)
def test_reference_reachability_with_referenced_all_funcs_in_func(self): ast = self.create_ast(Fixtures.REFERENCED_ALL_FUNC_IN_FUNC) scope_plugin = ScopePlugin() scope_plugin.process(ast) expected_variables_undeclared = { 'g:ExplicitGlobalFunc': False, 'ImplicitGlobalFunc': False, 's:ExplicitScriptLocalFunc': False, } self.assertVariablesUndeclared(expected_variables_undeclared, scope_plugin, ast)
def test_declarative_identifiers_referenced_with_referenced_all_func(self): ast = self.create_ast(Fixtures.REFERENCED_ALL_FUNC) scope_plugin = ScopePlugin() scope_plugin.process(ast) expected_variables_unused = { 'g:ExplicitGlobalFunc': False, 's:ScriptLocalFunc': False, 'ImplicitGlobalFunc': False, } self.assertVariablesUnused(expected_variables_unused, scope_plugin, ast)
def test_declarative_identifiers_referenced_with_unreferenced_func_in_func( self): ast = self.create_ast(Fixtures.UNREFERENCED_FUNC_IN_FUNC) scope_plugin = ScopePlugin() scope_plugin.process(ast) expected_variables_unused = { 'FuncContext': True, 'g:ExplicitGlobalFunc': True, 'ImplicitGlobalFunc': True, 's:ExplicitScriptLocalFunc': True, } self.assertVariablesUnused(expected_variables_unused, scope_plugin, ast)
def _is_declared_identifier(self, node): identifier_name = node['value'] # Ignore definition identifiers. # See ScopePlugin documents to understand what is definition identifier. is_definition_identifier = node[ScopePlugin.DEFINITION_IDENTIFIER_FLAG_KEY] if is_definition_identifier: return True # No prefix identifier is already declared if the identifier is built-in. is_builtin_identifier = node[ScopePlugin.BUILTIN_IDENTIFIER_FLAG_KEY] if is_builtin_identifier: return True # Ignore special identifiers such as '...' and 'a:000' if self._is_ignored_identifier(identifier_name): return True scope = node[ScopePlugin.SCOPE_KEY] declaration_scope = ScopePlugin.detect_scope(identifier_name, scope) traceability_map = ProhibitUsingUndeclaredVariable.identifier_tracability_map is_traceable_identifier = traceability_map[declaration_scope] if not is_traceable_identifier: # Optimistic decision. Expect the identifier is declared by other # file. return True while scope is not None: if identifier_name in scope['variables']: return True scope = scope['parent_scope'] return False
def test_declarative_identifiers_referenced_with_unreferenced_func_in_func(self): ast = self.create_ast(Fixtures.UNREFERENCED_FUNC_IN_FUNC) scope_plugin = ScopePlugin() scope_plugin.process(ast) expected_variables_unused = { 'FuncContext': True, 'g:ExplicitGlobalFunc': True, 'ImplicitGlobalFunc': True, 's:ExplicitScriptLocalFunc': True, } self.assertVariablesUnused(expected_variables_unused, scope_plugin, ast)
def test_reference_reachability_with_unreferenced_params(self): ast = self.create_ast(Fixtures.UNREFERENCED_PARAMS) scope_plugin = ScopePlugin() scope_plugin.process(ast) expected_variables_undeclared = { 'param': True, 'a:1': True, 'a:firstline': True, 'a:lastline': True, 'a:18': True, 'a:19': True, 'a:20': True, } self.assertVariablesUndeclared(expected_variables_undeclared, scope_plugin, ast)
def test_process_with_builtin(self): parser = Parser() ast = parser.parse_file(Fixtures['BUILTIN']) plugin = ScopePlugin() plugin.process(ast) expected_builtin_flags = { 'abs': True, 'sin': True, 'strlen': True, 'g:MyFunction': False, } # Keep identifier name that traverser visited identifiers_checking_map = { 'abs': False, 'sin': False, 'strlen': False, 'g:MyFunction': False, } def test_identifier(node): if NodeType(node['type']) is not NodeType.IDENTIFIER: return identifier = node # We focus to non-definition identifier if identifier[ScopePlugin.DEFINITION_IDENTIFIER_FLAG_KEY]: return identifier_name = identifier['value'] identifiers_checking_map[identifier_name] = True is_builtin_identifier = identifier[ ScopePlugin.BUILTIN_IDENTIFIER_FLAG_KEY] expected_builtin_flag = expected_builtin_flags[identifier_name] self.assertEqual(is_builtin_identifier, expected_builtin_flag) traverse(ast, on_enter=test_identifier) self.assertTrue(all(identifiers_checking_map.values()))
def test_process_with_builtin(self): parser = Parser() ast = parser.parse_file(Fixtures['BUILTIN']) plugin = ScopePlugin() plugin.process(ast) expected_builtin_flags = { 'abs': True, 'sin': True, 'strlen': True, 'g:MyFunction': False, } # Keep identifier name that traverser visited identifiers_checking_map = { 'abs': False, 'sin': False, 'strlen': False, 'g:MyFunction': False, } def test_identifier(node): if NodeType(node['type']) is not NodeType.IDENTIFIER: return identifier = node # We focus to non-definition identifier if identifier[ScopePlugin.DEFINITION_IDENTIFIER_FLAG_KEY]: return identifier_name = identifier['value'] identifiers_checking_map[identifier_name] = True is_builtin_identifier = identifier[ScopePlugin.BUILTIN_IDENTIFIER_FLAG_KEY] expected_builtin_flag = expected_builtin_flags[identifier_name] self.assertEqual(is_builtin_identifier, expected_builtin_flag) traverse(ast, on_enter=test_identifier) self.assertTrue(all(identifiers_checking_map.values()))
def test_declarative_identifiers_referenced_with_referenced_all(self): ast = self.create_ast(Fixtures.REFERENCED_ALL) scope_plugin = ScopePlugin() scope_plugin.process(ast) expected_variables_unused = { 'g:explicit_global_var': False, 'b:buffer_local_var': False, 'w:window_local_var': False, 't:tab_local_var': False, 's:script_local_var': False, 'implicit_global_var': False, '$ENV_VAR': False, '@"': False, '&opt_var': False, 'v:count': False, } self.assertVariablesUnused(expected_variables_unused, scope_plugin, ast)
def __init__(self, policy_set, config_dict_global): self._plugins = { 'scope': ScopePlugin(), } self._policy_set = policy_set self._config_comment_source = ConfigCommentSource() self._config = self._decorate_config(config_dict_global, self._config_comment_source) self._parser = self.build_parser() self._listeners_map = {}
def _is_declared_identifier(self, node): identifier_name = node['value'] # Ignore definition identifiers. # See ScopePlugin documents to understand what is definition identifier. is_definition_identifier = node[ ScopePlugin.DEFINITION_IDENTIFIER_FLAG_KEY] if is_definition_identifier: return True # No prefix identifier is already declared if the identifier is built-in. is_builtin_identifier = node[ScopePlugin.BUILTIN_IDENTIFIER_FLAG_KEY] if is_builtin_identifier: return True # Ignore special identifiers such as '...' and 'a:000' if self._is_ignored_identifier(identifier_name): return True scope = node[ScopePlugin.SCOPE_KEY] declaration_scope = ScopePlugin.detect_scope(identifier_name, scope) traceability_map = ProhibitUsingUndeclaredVariable.identifier_tracability_map is_traceable_identifier = traceability_map[declaration_scope] if not is_traceable_identifier: # Optimistic decision. Expect the identifier is declared by other # file. return True while scope is not None: if identifier_name in scope['variables']: return True scope = scope['parent_scope'] return False
def prettify_node_type(node): node['type'] = NodeType(node['type']) if __name__ == '__main__': arg_parser = ArgumentParser(prog='show_ast', description='Show AST') arg_parser.add_argument('--enable-neovim', action='store_true', help='Enable Neovim syntax') arg_parser.add_argument('files', nargs='*', help='File to parse') namespace = vars(arg_parser.parse_args(sys.argv[1:])) filepaths = map(Path, namespace['files']) enable_neovim = namespace['enable_neovim'] scope_plugin = ScopePlugin() parser = Parser(plugins={'scope': scope_plugin}, enable_neovim=enable_neovim) for filepath in filepaths: ast = parser.parse_file(filepath) traverse(ast, on_enter=prettify_node_type) print("////////// AST //////////\n") pprint(ast) print("\n\n") print("////////// SCOPE TREE //////////\n") pprint(scope_plugin._ref_tester._scope_tree)