Skip to content

Commit

Permalink
Merge branch '__rultor'
Browse files Browse the repository at this point in the history
  • Loading branch information
rultor committed Jul 16, 2020
2 parents aeb208e + 7bf903b commit e18df97
Show file tree
Hide file tree
Showing 13 changed files with 201 additions and 101 deletions.
7 changes: 6 additions & 1 deletion aibolit/ast_framework/ast.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@

from javalang.tree import Node
from typing import Union, Any, Set, List, Iterator, Tuple, Dict, cast
from networkx import DiGraph, dfs_labeled_edges # type: ignore
from networkx import DiGraph, dfs_labeled_edges, dfs_preorder_nodes # type: ignore
from deprecated import deprecated # type: ignore

from aibolit.ast_framework.ast_node_type import ASTNodeType
Expand Down Expand Up @@ -101,6 +101,11 @@ def get_subtrees(self, root_type: ASTNodeType) -> Iterator['AST']:
subtree = []
current_subtree_root = -1

def get_subtree(self, node: ASTNode) -> 'AST':
subtree_nodes_indexes = dfs_preorder_nodes(self.tree, node.node_index)
subtree = self.tree.subgraph(subtree_nodes_indexes)
return AST(subtree, node.node_index)

@deprecated(reason='Use ASTNode functionality instead.')
def children_with_type(self, node: int, child_type: ASTNodeType) -> Iterator[int]:
'''
Expand Down
12 changes: 8 additions & 4 deletions aibolit/ast_framework/ast_node.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,6 @@ def __init__(self, graph: DiGraph, node_index: int):
self._graph = graph
self._node_index = node_index

def __dir__(self) -> List[str]:
node_type = self._graph.nodes[self._node_index]['node_type']
return ['children'] + list(common_attributes) + list(attributes_by_node_type[node_type])

@property
def children(self) -> Iterator['ASTNode']:
for child_index in self._graph.succ[self._node_index]:
Expand All @@ -59,6 +55,11 @@ def __getattr__(self, attribute_name: str):
attribute = [ASTNode(self._graph, item.node_index) for item in attribute]
return attribute

def __dir__(self) -> List[str]:
node_type = self._graph.nodes[self._node_index]['node_type']
return ASTNode._public_fixed_interface + \
list(common_attributes) + list(attributes_by_node_type[node_type])

def __str__(self) -> str:
text_representation = f'node index: {self._node_index}'
node_type = self.__getattr__('node_type')
Expand All @@ -69,3 +70,6 @@ def __str__(self) -> str:

def __repr__(self) -> str:
return f'<ASTNode node_type: {self.__getattr__("node_type")}, node_index: {self._node_index}>'

# names of methods and properties, which is not generated dynamically
_public_fixed_interface = ['children', 'node_index', 'subtree']
2 changes: 2 additions & 0 deletions aibolit/ast_framework/java_class.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@


from cached_property import cached_property # type: ignore
from deprecated import deprecated # type: ignore

from typing import Dict, Set, TYPE_CHECKING
from networkx import DiGraph # type: ignore
Expand All @@ -34,6 +35,7 @@
from aibolit.ast_framework.java_package import JavaPackage


@deprecated("This functionality must be transmitted to ASTNode")
class JavaClass(AST):
def __init__(self, tree: DiGraph, root: int, java_package: 'JavaPackage'):
self.tree = tree
Expand Down
2 changes: 2 additions & 0 deletions aibolit/ast_framework/java_class_field.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# SOFTWARE.

from cached_property import cached_property # type: ignore
from deprecated import deprecated # type: ignore

from typing import TYPE_CHECKING
from networkx import DiGraph # type: ignore
Expand All @@ -31,6 +32,7 @@
from aibolit.ast_framework.java_class import JavaClass


@deprecated("This functionality must be transmitted to ASTNode")
class JavaClassField(AST):
def __init__(self, tree: DiGraph, root: int, java_class: 'JavaClass'):
self.tree = tree
Expand Down
2 changes: 2 additions & 0 deletions aibolit/ast_framework/java_class_method.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
from cached_property import cached_property # type: ignore
from typing import Dict, Set, TYPE_CHECKING
from networkx import DiGraph, dfs_tree # type: ignore
from deprecated import deprecated # type: ignore

from aibolit.utils.cfg_builder import build_cfg
from aibolit.ast_framework import AST, ASTNodeType
Expand All @@ -32,6 +33,7 @@
from aibolit.ast_framework.java_class import JavaClass


@deprecated("This functionality must be transmitted to ASTNode")
class JavaClassMethod(AST):
def __init__(self, tree: DiGraph, root: int, java_class: 'JavaClass'):
self.tree = tree
Expand Down
2 changes: 2 additions & 0 deletions aibolit/ast_framework/java_package.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
# SOFTWARE.

from cached_property import cached_property # type: ignore
from deprecated import deprecated # type: ignore

from typing import Dict

Expand All @@ -29,6 +30,7 @@
from aibolit.ast_framework.java_class import JavaClass


@deprecated("This functionality must be transmitted to ASTNode")
class JavaPackage(AST):
def __init__(self, filename: str):
ast = AST.build_from_javalang(build_ast(filename))
Expand Down
35 changes: 30 additions & 5 deletions aibolit/patterns/assert_in_code/assert_in_code.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,40 @@
# The MIT License (MIT)
#
# Copyright (c) 2020 Aibolit
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from typing import List

from aibolit.utils.ast_builder import build_ast
from aibolit.ast_framework import AST, ASTNodeType
from aibolit.utils.ast_builder import build_ast


class AssertInCode:
'''
Finds use of all asserts.
'''

def value(self, filename: str) -> List[int]:
tree = AST.build_from_javalang(build_ast(filename))
ast = AST.build_from_javalang(build_ast(filename))
lines: List[int] = []
nodes = tree.get_nodes(ASTNodeType.ASSERT_STATEMENT)
for node in nodes:
lines.append(tree.get_attr(node, 'line'))
for assert_node in ast.get_proxy_nodes(ASTNodeType.ASSERT_STATEMENT):
lines.append(assert_node.line)

return lines
Original file line number Diff line number Diff line change
Expand Up @@ -27,13 +27,6 @@


class ForceTypeCastingFinder:

def value(self, filename: str) -> List[int]:
tree = AST.build_from_javalang(build_ast(filename))
lines: List[int] = []

nodes = tree.get_nodes(ASTNodeType.CAST)
for node in nodes:
lines.append(tree.get_line_number_from_children(node))

return lines
ast = AST.build_from_javalang(build_ast(filename))
return [cast.expression.line for cast in ast.get_proxy_nodes(ASTNodeType.CAST)]
61 changes: 44 additions & 17 deletions aibolit/patterns/if_return_if_detection/if_detection.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,52 @@
from aibolit.ast_framework import ASTNodeType
from aibolit.ast_framework.java_package import JavaPackage
# The MIT License (MIT)
#
# Copyright (c) 2020 Aibolit
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from typing import List

from aibolit.ast_framework import AST, ASTNode, ASTNodeType
from aibolit.utils.ast_builder import build_ast


class CountIfReturn:
'''
Returns lines with if statement which has also return statement
and other conditions with else.
Finds "if" statements with else branches and return statement in then branch.
'''
def __init__(self):
pass

def value(self, filename: str) -> List[int]:
detected_lines = []
ast = JavaPackage(filename).java_classes
for class_name in ast:
java_class = ast[class_name]
for index, if_node in enumerate(java_class.get_nodes(ASTNodeType.IF_STATEMENT)):
all_childs = [i for i in java_class.tree.succ[if_node]]
if len(all_childs) == 3:
for i in java_class.tree.succ[all_childs[1]]:
if java_class.get_type(i) == ASTNodeType.RETURN_STATEMENT:
detected_lines += [java_class.get_attr(if_node, 'line')]
return detected_lines
ast = AST.build_from_javalang(build_ast(filename))
lines: List[int] = []
for if_statement in ast.get_proxy_nodes(ASTNodeType.IF_STATEMENT):
if if_statement.else_statement is not None and \
self._is_then_branch_return(if_statement):
lines.append(if_statement.line)

return lines

def _is_then_branch_return(self, if_statement: ASTNode) -> bool:
if if_statement.then_statement.node_type == ASTNodeType.RETURN_STATEMENT:
return True

if if_statement.then_statement.node_type == ASTNodeType.BLOCK_STATEMENT:
return any(statement.node_type == ASTNodeType.RETURN_STATEMENT
for statement in if_statement.then_statement.statements)

return False
50 changes: 33 additions & 17 deletions aibolit/patterns/instanceof/instance_of.py
Original file line number Diff line number Diff line change
@@ -1,29 +1,45 @@
# The MIT License (MIT)
#
# Copyright (c) 2020 Aibolit
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from typing import List

from aibolit.utils.ast_builder import build_ast
from aibolit.ast_framework import AST, ASTNodeType
from aibolit.utils.ast_builder import build_ast


class InstanceOf:
def __init__(self):
pass
"""
Finds instance_of operator and .isInstance() method call.
"""

def value(self, filename: str):
"""
Traverse over AST tree finds instance_of and .isInstance().
:param filename:
:return:
List of code lines
"""
tree = AST.build_from_javalang(build_ast(filename))
ast = AST.build_from_javalang(build_ast(filename))
lines: List[int] = []
for node in tree.get_nodes(ASTNodeType.BINARY_OPERATION):
if tree.get_binary_operation_name(node) == 'instanceof':
lines.append(tree.get_line_number_from_children(node))
for binary_operator in ast.get_proxy_nodes(ASTNodeType.BINARY_OPERATION):
if binary_operator.operator == 'instanceof':
lines.append(binary_operator.line)

for node in tree.get_nodes(ASTNodeType.METHOD_INVOCATION):
method_name = tree.get_method_invocation_params(node).method_name
if method_name == 'isInstance':
lines.append(tree.get_attr(node, 'line'))
for method_invocation in ast.get_proxy_nodes(ASTNodeType.METHOD_INVOCATION):
if method_invocation.member == 'isInstance':
lines.append(method_invocation.line)

return lines
56 changes: 32 additions & 24 deletions aibolit/patterns/multiple_while/multiple_while.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,42 @@
# The MIT License (MIT)
#
# Copyright (c) 2020 Aibolit
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.

from typing import List

from aibolit.utils.ast_builder import build_ast
from aibolit.ast_framework import AST, ASTNodeType
from typing import List, Set


class MultipleWhile:

def __init__(self):
pass

def get_top_level_while_qty(self, tree: AST, node: int) -> int:
list_while_nodes: List[int] = []
set_child_while_nodes: Set[int] = set()
for child in tree.all_children_with_type(node, ASTNodeType.WHILE_STATEMENT):
list_while_nodes.append(child)
set_internal_while = set(tree.list_all_children_with_type(child, ASTNodeType.WHILE_STATEMENT))
set_child_while_nodes |= set_internal_while
return len(list_while_nodes) - len(set_child_while_nodes)
'''
Finds methods, with more than one "while" cycle, exceluding nested ones.
'''

def value(self, filename: str) -> List[int]:
"""
Travers over AST tree and finds function with sequential while statement
:param filename:
:return:
List of LineNumber of methods which have sequential while statements
"""

tree = AST.build_from_javalang(build_ast(filename))
ast = AST.build_from_javalang(build_ast(filename))
lines: List[int] = []
for node in tree.get_nodes(ASTNodeType.METHOD_DECLARATION):
if self.get_top_level_while_qty(tree, node) > 1:
lines.append(tree.get_attr(node, 'line'))
for method_declaration in ast.get_subtrees(ASTNodeType.METHOD_DECLARATION):
top_level_while_loops = method_declaration.get_subtrees(ASTNodeType.WHILE_STATEMENT)
if len(list(top_level_while_loops)) > 1:
lines.append(method_declaration.get_root().line)

return lines
Loading

0 comments on commit e18df97

Please sign in to comment.