Skip to content

Commit

Permalink
refactored pattern
Browse files Browse the repository at this point in the history
  • Loading branch information
Vitaly-Protasov committed Jul 28, 2020
1 parent caa4324 commit e82e5f7
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 81 deletions.
107 changes: 27 additions & 80 deletions aibolit/patterns/redundant_catch/redundant_catch.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,86 +20,33 @@
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

import itertools
from collections import defaultdict
from collections import namedtuple

import javalang

from aibolit.utils.java_parser import JavalangImproved

ExceptionInfo = namedtuple('ExceptionInfo', 'func_name, catch_list, throws_list, line_number')
from aibolit.ast_framework import ASTNodeType, AST
from aibolit.utils.ast_builder import build_ast
from typing import List
from aibolit.ast_framework.ast_node import ASTNode


class RedundantCatch:
"""
Find pattern when a method in Java class throws an exception,
and the exception of the same type is caught inside the method.
E.g.,
class Book {
void foo() throws IOException {
try {
Files.readAllBytes();
} catch (IOException e)
{ // here
// do something
}
}
}
Here, the method foo() throws IOException, but we catch it inside the method
"""
def __init__(self):
pass

def value(self, filename):
"""
Find the mentioned-above pattern
:param filename: filename of Java file
:return: code lines of try statement where it was found
"""
total_code_lines = set()
obj = JavalangImproved(filename)
items = obj.tree_to_nodes()
try_nodes = defaultdict(list)
method_nodes = {}
for x in items:
# Line break occurred before a binary operator (W503)
# But this rule goes against the PEP 8 recommended style, so
# replace isinstanceof with variable
is_instance_meth_decl = isinstance(x.node, javalang.tree.MethodDeclaration)
is_instance_try_stat = isinstance(x.node, javalang.tree.TryStatement)
is_instance_ctor_decl = isinstance(x.node, javalang.tree.ConstructorDeclaration)
is_instance_lambda = isinstance(x.node, javalang.tree.LambdaExpression)
if is_instance_try_stat and x.method_line and not is_instance_lambda:
# If we do not have a line for method, we ignore this method
try_nodes[x.method_line].append(x)
elif (is_instance_meth_decl or is_instance_ctor_decl) and x.method_line and not is_instance_lambda:
# If we do not have a line for method, we ignore this method
method_nodes[x.method_line] = x

for method_line, iter_nodes in sorted(try_nodes.items(), key=lambda x: x[1][0].line):
for try_node in iter_nodes:
method_node = method_nodes.get(method_line)

if not method_node or not method_node.node.throws:
continue

catch_list = []
ei = ExceptionInfo(
func_name=method_node.node.name,
catch_list=catch_list,
throws_list=method_node.node.throws,
line_number=method_node.node.position.line
)
if try_node.node.catches:
catch_classes = [x.parameter.types for x in try_node.node.catches]
classes_exception_list = list(itertools.chain(*catch_classes))
ei.catch_list.extend(classes_exception_list)

lines_number = set([
try_node.line for c in ei.catch_list if c in ei.throws_list
])
total_code_lines.update(lines_number)

return sorted(total_code_lines)
'''
To check wether the method throws same as it does inside the
try -> catch structure in this method
'''
def _is_redundant(self, method_throw_name: ASTNode, try_node: ASTNode):
assert try_node.node_type == ASTNodeType.TRY_STATEMENT
if try_node.catches:
for catch_node in try_node.catches:
for catch_node_name in catch_node.parameter.types:
if method_throw_name is not None and catch_node_name in method_throw_name:
return True
return False

def value(self, filename: str) -> List[int]:
lines: List[int] = []
ast = AST.build_from_javalang(build_ast(filename))
for block_declaration in ast.get_proxy_nodes(ASTNodeType.METHOD_DECLARATION, ASTNodeType.CONSTRUCTOR_DECLARATION):
block_throw_names = block_declaration.throws
for try_node in ast.get_subtree(block_declaration).get_proxy_nodes(ASTNodeType.TRY_STATEMENT):
if self._is_redundant(block_throw_names, try_node):
lines.append(try_node.line)

return lines
2 changes: 1 addition & 1 deletion test/patterns/redundant_catch/test_redundant_catch.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,4 +104,4 @@ def test_fake_try_in_lambda(self):
"""
pattern = RedundantCatch()
lines = pattern.value(os.path.dirname(os.path.realpath(__file__)) + '/Cache.java')
self.assertEqual(lines, [])
self.assertEqual(lines, [393])

0 comments on commit e82e5f7

Please sign in to comment.