diff --git a/aibolit/patterns/method_chaining/method_chaining.py b/aibolit/patterns/method_chaining/method_chaining.py index e79eb2cc..07cbc7a2 100644 --- a/aibolit/patterns/method_chaining/method_chaining.py +++ b/aibolit/patterns/method_chaining/method_chaining.py @@ -5,43 +5,32 @@ class MethodChainFind: - ''' - In this pattern we check whether - more than one method chaining - invocation is used or not. - ''' - def _is_invocation_inside(self, node: ASTNode) -> bool: - # to process structures such as: - # obj.methodInv1(list).methodInv2("blabla") - for i in node.children: - if i.node_type == ASTNodeType.METHOD_INVOCATION: - return True - - return False - - def _check_chained_method(self, ast: AST, method_invocation: ASTNode) -> bool: - childs = 0 - for node in method_invocation.children: - if node.node_type == ASTNodeType.METHOD_INVOCATION: - # we check structures such as: - # new MyObject().Start() - # .SpecifySomeParameter(list) - # .SpecifySomeOtherParameter("list") - if self._is_invocation_inside(node) or node.qualifier is None: - childs += 1 - return childs > 0 + """ + Finds chained methods, i.e. foo().bar() + """ def value(self, filename: str) -> List[int]: lines: List[int] = [] ast = AST.build_from_javalang(build_ast(filename)) - for stetement_expression in ast.get_proxy_nodes(ASTNodeType.STATEMENT_EXPRESSION): - expression_child = stetement_expression.expression - if expression_child.node_type == ASTNodeType.METHOD_INVOCATION and \ - self._check_chained_method(ast, expression_child): - lines.append(stetement_expression.line) - - elif expression_child.node_type == ASTNodeType.CLASS_CREATOR and \ - len(expression_child.selectors) > 1: - lines.append(stetement_expression.line) + for node in ast.get_proxy_nodes(ASTNodeType.CLASS_CREATOR, + ASTNodeType.METHOD_INVOCATION, + ASTNodeType.THIS): + selectors_qty = self._get_selectors_qty(node) + if selectors_qty > MethodChainFind._allowed_number_of_selectord[node.node_type]: + lines.append(node.line) return lines + + def _get_selectors_qty(self, node: ASTNode) -> int: + if not hasattr(node, "selectors") or node.selectors is None: + return 0 + + return len(node.selectors) + + _allowed_number_of_selectord = { + # found node already is a method invocation, so no further invocations are allowed + ASTNodeType.METHOD_INVOCATION: 0, + ASTNodeType.THIS: 1, + # code example: new Object().foo().bar() + ASTNodeType.CLASS_CREATOR: 1, + } diff --git a/test/patterns/method_chaining/NestedChainWIthThis.java b/test/patterns/method_chaining/NestedChainWIthThis.java index 796e9bc0..30602b38 100644 --- a/test/patterns/method_chaining/NestedChainWIthThis.java +++ b/test/patterns/method_chaining/NestedChainWIthThis.java @@ -11,7 +11,9 @@ public void start() { ); obj = new Object(); - obj.ohhhh().set(this.govno("Zh").zhopa(whaaat).concat(whaaat)).opopo(whaaat); + obj.ohhhh().set( + this.govno("Zh").zhopa(whaaat).concat(whaaat) + ).opopo(whaaat); System.out.println("asdasd" + aaa + "34234" + bbb); list = new ArrayList<>(); for (int i = 0; i < 10; i++) @@ -19,7 +21,7 @@ public void start() { list = new ArrayList<>(); for (int i = 0; i < 10; i++) list.add(Boolean.FALSE); - + } diff --git a/test/patterns/method_chaining/NestedChainWithSimpleMethodInvocation.java b/test/patterns/method_chaining/NestedChainWithSimpleMethodInvocation.java index 122c4cb7..c8e0c3a9 100644 --- a/test/patterns/method_chaining/NestedChainWithSimpleMethodInvocation.java +++ b/test/patterns/method_chaining/NestedChainWithSimpleMethodInvocation.java @@ -12,7 +12,9 @@ public void start() { obj = new Object(); obj1 = new Object(); - obj.dummy1().dummy2(obj1.foo1("Zh").foo2(whaaat).foo3(whaaat)).dummy3(whaaat); + obj.dummy1().dummy2( + obj1.foo1("Zh").foo2(whaaat).foo3(whaaat) + ).dummy3(whaaat); System.out.println("asdasd" + aaa + "34234" + bbb); list = new ArrayList<>(); for (int i = 0; i < 10; i++) @@ -20,7 +22,7 @@ public void start() { list = new ArrayList<>(); for (int i = 0; i < 10; i++) list.add(Boolean.FALSE); - + } diff --git a/test/patterns/method_chaining/test_method_chaining.py b/test/patterns/method_chaining/test_method_chaining.py index 50757afd..72cca346 100644 --- a/test/patterns/method_chaining/test_method_chaining.py +++ b/test/patterns/method_chaining/test_method_chaining.py @@ -40,7 +40,7 @@ def test_empty_method_chain(self): def test_chain_with_new_object(self): lines = self.method_chain_finder.value(Path(self.dir_path, 'MethodChainNewObjectMethods.java')) - self.assertEqual(lines, [34]) + self.assertEqual(lines, [23, 34]) def test_method_chain_in_different_methods(self): lines = self.method_chain_finder.value(Path(self.dir_path, 'MethodChainInDifferentMethods.java')) @@ -68,11 +68,11 @@ def test_chain_without_object_creating(self): def test_nested_chain_with_this(self): lines = self.method_chain_finder.value(Path(self.dir_path, 'NestedChainWIthThis.java')) - self.assertEqual(lines, [14]) + self.assertEqual(lines, [14, 15]) def test_nested_chain_with_simple_method_invocation(self): lines = self.method_chain_finder.value(Path(self.dir_path, 'NestedChainWithSimpleMethodInvocation.java')) - self.assertEqual(lines, [15]) + self.assertEqual(lines, [15, 16]) def test_nested_chain_complicated_structure(self): """ @@ -84,7 +84,7 @@ def test_nested_chain_complicated_structure(self): def test_smallest_chain(self): lines = self.method_chain_finder.value(Path(self.dir_path, 'SmallestChain.java')) - self.assertEqual(lines, [31]) + self.assertEqual(lines, [31, 83, 84]) def test_fake_chain(self): lines = self.method_chain_finder.value(Path(self.dir_path, 'FakeChain.java'))