diff --git a/README.md b/README.md index 9134180..383c38a 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ - - + + Logo @@ -23,6 +23,12 @@ + + + + + +

Codellm-Devkit (CLDK) is a multilingual program analysis framework that bridges the gap between traditional static analysis tools and Large Language Models (LLMs) specialized for code (CodeLLMs). Codellm-Devkit allows developers to streamline the process of transforming raw code into actionable insights by providing a unified interface for integrating outputs from various analysis tools and preparing them for effective use by CodeLLMs. diff --git a/cldk/analysis/c/c_analysis.py b/cldk/analysis/c/c_analysis.py index 8461913..12cf9d9 100644 --- a/cldk/analysis/c/c_analysis.py +++ b/cldk/analysis/c/c_analysis.py @@ -142,7 +142,7 @@ def get_functions(self) -> Dict[str, CFunction]: """Should return all functions in the project. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, Dict[str, JCallable]]: Dictionary of dictionaries of all methods in the C code with qualified class name as key and dictionary of methods in that class. @@ -169,7 +169,7 @@ def get_C_file(self, file_name: str) -> str: file_name (str): The name of the file. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: str: C file name containing the given qualified class. @@ -183,7 +183,7 @@ def get_C_compilation_unit(self, file_path: str) -> CTranslationUnit: file_path (str): Absolute path to C source file Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: CTranslationUnit: Compilation unit object for C source file @@ -197,7 +197,7 @@ def get_functions_in_file(self, file_name: str) -> List[CFunction]: file_name (str): The name of the file. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, JCallable]: A dictionary of all constructors of the given class. @@ -208,7 +208,7 @@ def get_macros(self) -> List[CMacro]: """Should return a list of all macros in the C code. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[CMacro]: A list of all macros in the C code. @@ -222,7 +222,7 @@ def get_macros_in_file(self, file_name: str) -> List[CMacro] | None: file_name (str): The name of the file. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[CMacro]: A list of all macros in the given file. Returns None if no macros are found. diff --git a/cldk/analysis/common/__init__.py b/cldk/analysis/commons/__init__.py similarity index 100% rename from cldk/analysis/common/__init__.py rename to cldk/analysis/commons/__init__.py diff --git a/cldk/analysis/common/lsp/__init__.py b/cldk/analysis/commons/lsp/__init__.py similarity index 100% rename from cldk/analysis/common/lsp/__init__.py rename to cldk/analysis/commons/lsp/__init__.py diff --git a/cldk/analysis/common/lsp/lsp.py b/cldk/analysis/commons/lsp/lsp.py similarity index 100% rename from cldk/analysis/common/lsp/lsp.py rename to cldk/analysis/commons/lsp/lsp.py diff --git a/cldk/analysis/python/treesitter/__init__.py b/cldk/analysis/commons/treesitter/__init__.py similarity index 84% rename from cldk/analysis/python/treesitter/__init__.py rename to cldk/analysis/commons/treesitter/__init__.py index 7774f00..58c211a 100644 --- a/cldk/analysis/python/treesitter/__init__.py +++ b/cldk/analysis/commons/treesitter/__init__.py @@ -18,6 +18,7 @@ Treesitter package """ -from cldk.analysis.python.treesitter.python_sitter import PythonSitter +from .treesitter_java import TreesitterJava +from .treesitter_python import TreesitterPython -__all__ = ["PythonSitter"] +__all__ = ["TreesitterJava", "TreesitterPython"] diff --git a/cldk/models/treesitter/models.py b/cldk/analysis/commons/treesitter/models.py similarity index 100% rename from cldk/models/treesitter/models.py rename to cldk/analysis/commons/treesitter/models.py diff --git a/cldk/analysis/java/treesitter/java_sitter.py b/cldk/analysis/commons/treesitter/treesitter_java.py similarity index 99% rename from cldk/analysis/java/treesitter/java_sitter.py rename to cldk/analysis/commons/treesitter/treesitter_java.py index 3eeb761..3c0033d 100644 --- a/cldk/analysis/java/treesitter/java_sitter.py +++ b/cldk/analysis/commons/treesitter/treesitter_java.py @@ -15,20 +15,20 @@ ################################################################################ """ -JavaSitter module +TreesitterJava module """ import logging from itertools import groupby from typing import List, Set, Dict from tree_sitter import Language, Node, Parser, Query, Tree import tree_sitter_java as tsjava -from cldk.models.treesitter import Captures +from cldk.analysis.commons.treesitter.models import Captures logger = logging.getLogger(__name__) # pylint: disable=too-many-public-methods -class JavaSitter: +class TreesitterJava: """ Treesitter for Java usecases. """ diff --git a/cldk/analysis/python/treesitter/python_sitter.py b/cldk/analysis/commons/treesitter/treesitter_python.py similarity index 98% rename from cldk/analysis/python/treesitter/python_sitter.py rename to cldk/analysis/commons/treesitter/treesitter_python.py index dad49f0..d423ab9 100644 --- a/cldk/analysis/python/treesitter/python_sitter.py +++ b/cldk/analysis/commons/treesitter/treesitter_python.py @@ -15,22 +15,21 @@ ################################################################################ """ -PythonSitter module +TreesitterPython module """ -import glob import os from pathlib import Path from typing import List -from tree_sitter import Language, Parser, Query, Node, Tree +from tree_sitter import Language, Parser, Node, Tree import tree_sitter_python as tspython from cldk.models.python.models import PyMethod, PyClass, PyArg, PyImport, PyModule, PyCallSite -from cldk.models.treesitter import Captures -from cldk.utils.treesitter.tree_sitter_utils import TreeSitterUtils +from cldk.analysis.commons.treesitter.models import Captures +from cldk.analysis.commons.treesitter.utils.treesitter_utils import TreeSitterUtils -class PythonSitter: +class TreesitterPython: """ Tree sitter for Python use cases. """ @@ -49,6 +48,7 @@ def is_parsable(self, code: str) -> bool: Returns: True if the code is parsable, False otherwise """ + def syntax_error(node): if node.type == "ERROR": return True diff --git a/cldk/utils/treesitter/__init__.py b/cldk/analysis/commons/treesitter/utils/__init__.py similarity index 90% rename from cldk/utils/treesitter/__init__.py rename to cldk/analysis/commons/treesitter/utils/__init__.py index 7644d5b..2eb30b9 100644 --- a/cldk/utils/treesitter/__init__.py +++ b/cldk/analysis/commons/treesitter/utils/__init__.py @@ -17,3 +17,6 @@ """ Treesitter package """ +from .treesitter_utils import TreeSitterUtils + +__all__ = ["TreeSitterUtils"] diff --git a/cldk/utils/treesitter/tree_sitter_utils.py b/cldk/analysis/commons/treesitter/utils/treesitter_utils.py similarity index 100% rename from cldk/utils/treesitter/tree_sitter_utils.py rename to cldk/analysis/commons/treesitter/utils/treesitter_utils.py diff --git a/cldk/analysis/java/codeanalyzer/codeanalyzer.py b/cldk/analysis/java/codeanalyzer/codeanalyzer.py index bafafb5..61d0fd2 100644 --- a/cldk/analysis/java/codeanalyzer/codeanalyzer.py +++ b/cldk/analysis/java/codeanalyzer/codeanalyzer.py @@ -28,7 +28,7 @@ import networkx as nx from cldk.analysis import AnalysisLevel -from cldk.analysis.java.treesitter import JavaSitter +from cldk.analysis.commons.treesitter import TreesitterJava from cldk.models.java import JGraphEdges from cldk.models.java.enums import CRUDOperationType from cldk.models.java.models import JApplication, JCRUDOperation, JCallable, JField, JMethodDetail, JType, JCompilationUnit, JGraphEdgesST @@ -303,7 +303,7 @@ def _generate_call_graph(self, using_symbol_table) -> nx.DiGraph: NotImplementedError("Call graph generation using symbol table is not implemented yet.") else: sdg = self.get_system_dependency_graph() - tsu = JavaSitter() + tsu = TreesitterJava() edge_list = [ ( (jge.source.method.signature, jge.source.klass), @@ -511,6 +511,16 @@ def get_java_file(self, qualified_class_name) -> str: if (qualified_class_name) in v.type_declarations.keys(): return k + def get_compilation_units(self) -> List[JCompilationUnit]: + """Get all the compilation units in the symbol table. + + Returns: + List[JCompilationUnit]: A list of compilation units. + """ + if self.application is None: + self.application = self._init_codeanalyzer() + return self.get_symbol_table().values() + def get_java_compilation_unit(self, file_path: str) -> JCompilationUnit: """Given the path of a Java source file, returns the compilation unit object from the symbol table. @@ -672,7 +682,7 @@ def __call_graph_using_symbol_table(self, qualified_class_name: str, method_sign sdg = self.__raw_call_graph_using_symbol_table_target_method(target_class_name=qualified_class_name, target_method_signature=method_signature) else: sdg = self.__raw_call_graph_using_symbol_table(qualified_class_name=qualified_class_name, method_signature=method_signature) - tsu = JavaSitter() + tsu = TreesitterJava() edge_list = [ ( (jge.source.method.signature, jge.source.klass), @@ -895,7 +905,7 @@ def get_all_crud_operations(self) -> List[Dict[str, Union[JType, JCallable, List """Should return a dictionary of all CRUD operations in the source code. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, List[str]]: A dictionary of all CRUD operations in the source code. @@ -912,7 +922,7 @@ def get_all_read_operations(self) -> List[Dict[str, Union[JType, JCallable, List """Should return a list of all read operations in the source code. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[Dict[str, Union[str, JCallable, List[CRUDOperation]]]]:: A list of all read operations in the source code. @@ -934,7 +944,7 @@ def get_all_create_operations(self) -> List[Dict[str, Union[JType, JCallable, Li """Should return a list of all create operations in the source code. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[Dict[str, Union[str, JCallable, List[CRUDOperation]]]]: A list of all create operations in the source code. @@ -956,7 +966,7 @@ def get_all_update_operations(self) -> List[Dict[str, Union[JType, JCallable, Li """Should return a list of all update operations in the source code. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[Dict[str, Union[str, JCallable, List[CRUDOperation]]]]: A list of all update operations in the source code. @@ -979,7 +989,7 @@ def get_all_delete_operations(self) -> List[Dict[str, Union[JType, JCallable, Li """Should return a list of all delete operations in the source code. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[Dict[str, Union[str, JCallable, List[CRUDOperation]]]]: A list of all delete operations in the source code. diff --git a/cldk/analysis/java/codeql/__init__.py b/cldk/analysis/java/codeql/__init__.py deleted file mode 100644 index 0c1a3f7..0000000 --- a/cldk/analysis/java/codeql/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -################################################################################ -# Copyright IBM Corporation 2024 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -################################################################################ - -""" -CodeQL package -""" - -from .codeql import JCodeQL - -__all__ = ["JCodeQL"] diff --git a/cldk/analysis/java/codeql/backend.py b/cldk/analysis/java/codeql/backend.py deleted file mode 100644 index ce287de..0000000 --- a/cldk/analysis/java/codeql/backend.py +++ /dev/null @@ -1,168 +0,0 @@ -################################################################################ -# Copyright IBM Corporation 2024 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -################################################################################ - -""" -Backend module -""" - -import subprocess -import tempfile -from pathlib import Path -import shlex -from typing import List -import pandas as pd -from pandas import DataFrame - -from cldk.utils.exceptions import CodeQLQueryExecutionException - - -class CodeQLQueryRunner: - """ - A class for executing CodeQL queries against a CodeQL database. - - Parameters - ---------- - database_path : str - The path to the CodeQL database. - - Attributes - ---------- - database_path : Path - The path to the CodeQL database. - temp_file_path : Path - The path to the temporary query file. - csv_output_file : Path - The path to the CSV output file. - temp_bqrs_file_path : Path - The path to the temporary bqrs file. - temp_qlpack_file : Path - The path to the temporary qlpack file. - - Methods - ------- - __enter__() - Context entry that creates temporary files to execute a CodeQL query. - execute(query_string, column_names) - Writes the query to the temporary file and executes it against the specified CodeQL database. - __exit__(exc_type, exc_val, exc_tb) - Clean up resources used by the CodeQL analysis. - - Raises - ------ - CodeQLQueryExecutionException - If there is an error executing the query. - """ - - def __init__(self, database_path: str): - self.database_path: Path = Path(database_path) - self.temp_file_path: Path = None - - def __enter__(self): - """ - Context entry that creates temporary files to execute a CodeQL query. - - Returns - ------- - instance : object - The instance of the class. - - Notes - ----- - This method creates temporary files to hold the query and store their paths. - """ - - # Create a temporary file to hold the query and store its path - temp_file = tempfile.NamedTemporaryFile("w", delete=False, suffix=".ql") - csv_file = tempfile.NamedTemporaryFile("w", delete=False, suffix=".csv") - bqrs_file = tempfile.NamedTemporaryFile("w", delete=False, suffix=".bqrs") - self.temp_file_path = Path(temp_file.name) - self.csv_output_file = Path(csv_file.name) - self.temp_bqrs_file_path = Path(bqrs_file.name) - - # Let's close the files, we'll reopen them by path when needed. - temp_file.close() - bqrs_file.close() - csv_file.close() - - # Create a temporary qlpack.yml file - self.temp_qlpack_file = self.temp_file_path.parent / "qlpack.yml" - with self.temp_qlpack_file.open("w") as f: - f.write("name: temp\n") - f.write("version: 1.0.0\n") - f.write("libraryPathDependencies: codeql/java-all\n") - - return self - - def execute(self, query_string: str, column_names: List[str]) -> DataFrame: - """Writes the query to the temporary file and executes it against the specified CodeQL database. - - Args: - query_string (str): The CodeQL query string to be executed. - column_names (List[str]): The list of column names for the CSV the CodeQL produces when we execute the query. - - Returns: - dict: A dictionary containing the resulting DataFrame. - - Raises: - RuntimeError: If the context manager is not entered using the 'with' statement. - CodeQLQueryExecutionException: If there is an error executing the query. - """ - if not self.temp_file_path: - raise RuntimeError("Context manager not entered. Use 'with' statement.") - - # Write the query to the temp file so we can execute it. - self.temp_file_path.write_text(query_string) - - # Construct and execute the CodeQL CLI command asking for a JSON output. - codeql_query_cmd = shlex.split(f"codeql query run {self.temp_file_path} --database={self.database_path} --output={self.temp_bqrs_file_path}", posix=False) - - call = subprocess.Popen(codeql_query_cmd, stdout=None, stderr=None) - _, err = call.communicate() - if call.returncode != 0: - raise CodeQLQueryExecutionException(f"Error executing query: {err.stderr}") - - # Convert the bqrs file to a CSV file - bqrs2csv_command = shlex.split(f"codeql bqrs decode --format=csv --output={self.csv_output_file} {self.temp_bqrs_file_path}", posix=False) - - # Read the CSV file content and cast it to a DataFrame - - call = subprocess.Popen(bqrs2csv_command, stdout=None, stderr=None) - _, err = call.communicate() - if call.returncode != 0: - raise CodeQLQueryExecutionException(f"Error executing query: {err.stderr}") - else: - return pd.read_csv( - self.csv_output_file, - header=None, - names=column_names, - skiprows=[0], - ) - - def __exit__(self, exc_type, exc_val, exc_tb): - """ - Clean up resources used by the CodeQL analysis. - - Deletes the temporary files created during the analysis, including the temporary file path, - the CSV output file, and the temporary QL pack file. - """ - if self.temp_file_path and self.temp_file_path.exists(): - self.temp_file_path.unlink() - - if self.csv_output_file and self.csv_output_file.exists(): - self.csv_output_file.unlink() - - if self.temp_qlpack_file and self.temp_qlpack_file.exists(): - self.temp_qlpack_file.unlink() diff --git a/cldk/analysis/java/codeql/codeql.py b/cldk/analysis/java/codeql/codeql.py deleted file mode 100644 index 352df2e..0000000 --- a/cldk/analysis/java/codeql/codeql.py +++ /dev/null @@ -1,258 +0,0 @@ -################################################################################ -# Copyright IBM Corporation 2024 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -################################################################################ - -""" -CodeQL module -""" - -from pathlib import Path -import shlex -import subprocess - -from pandas import DataFrame -from cldk.models.java import JApplication -from cldk.analysis.java.codeql.backend import CodeQLQueryRunner -from tempfile import TemporaryDirectory -import atexit -import signal - -from cldk.utils.exceptions import CodeQLDatabaseBuildException -import networkx as nx -from typing import Union - - -class JCodeQL: - """A class for building the application view of a Java application using CodeQL. - - Parameters - ---------- - project_dir : str or Path - The path to the root of the Java project. - codeql_db : str or Path or None - The path to the CodeQL database. If None, a temporary directory is created to store the database. - - Attributes - ---------- - db_path : Path - The path to the CodeQL database. - - Methods - ------- - _init_codeql_db(project_dir, codeql_db) - Initializes the CodeQL database. - _build_application_view() - Builds the application view of the java application. - _build_call_graph() - Builds the call graph of the application. - get_application_view() - Returns the application view of the java application. - get_class_hierarchy() - Returns the class hierarchy of the java application. - get_call_graph() - Returns the call graph of the java application. - get_all_methods() - Returns all the methods of the java application. - get_all_classes() - Returns all the classes of the java application. - """ - - def __init__(self, project_dir: Union[str, Path], codeql_db: Union[str, Path, None]) -> None: - self.db_path = self._init_codeql_db(project_dir, codeql_db) - - @staticmethod - def _init_codeql_db(project_dir: Union[str, Path], codeql_db: Union[str, Path, None]) -> Path: - """Should initialize the CodeQL database. - - Parameters - ---------- - project_dir : str or Path - The path to the root of the Java project. - codeql_db : str or Path or None - The path to the CodeQL database. If None, a temporary directory is created to store the database. - - Returns - ------- - Path - The path to the CodeQL database. - - Raises - ------ - CodeQLDatabaseBuildException - If there is an error building the CodeQL database. - """ - - # Cast to Path if the project_dir is a string. - project_dir = Path(project_dir) if isinstance(project_dir, str) else project_dir - - # Create a codeql database. Use a temporary directory if the user doesn't specify - if codeql_db is None: - db_path: TemporaryDirectory = TemporaryDirectory(delete=False, ignore_cleanup_errors=True) - codeql_db = db_path.name - # Since the user is not providing the codeql database path, we'll destroy the database at exit. - # TODO: this may be a potential gotcha. Is there a better solution here? - # TODO (BACKWARD COMPATIBILITY ISSUE): Only works on 3.12. - # If necessary, use shutil to handle this differently in 3.11 and below. - atexit.register(lambda: db_path.cleanup()) - # Also register the cleanup function for SIGINT and SIGTERM - signal.signal(signal.SIGINT, lambda *args, **kwargs: db_path.cleanup()) - signal.signal(signal.SIGTERM, lambda *args, **kwargs: db_path.cleanup()) - - codeql_db_create_cmd = shlex.split(f"codeql database create {codeql_db} --source-root={project_dir} --language=java --overwrite", posix=False) - call = subprocess.Popen( - codeql_db_create_cmd, - stdout=subprocess.DEVNULL, - stderr=subprocess.PIPE, - ) - _, error = call.communicate() - if call.returncode != 0: - raise CodeQLDatabaseBuildException(f"Error building CodeQL database: {error.decode()}") - return Path(codeql_db) - - def _build_application_view(self) -> JApplication: - """ - Builds the application view of the java application. - - Returns - ------- - JApplication - The JApplication object representing the application view. - """ - application: JApplication = JApplication() - - # Lets build the class hierarchy tree first and store that information in the application object. - query = [] - - # Add import - query += ["import java"] - - # List classes and their superclasses (ignoring non-application classes and anonymous classes) - query += [ - "from Class cls", - "where cls.fromSource() and not cls.isAnonymous()", - "select cls, cls.getASupertype().getQualifiedName()", - ] - - # Execute the query using the CodeQLQueryRunner context manager - with CodeQLQueryRunner(self.db_path) as codeql_query: - class_superclass_pairs: DataFrame = codeql_query.execute( - query_string="\n".join(query), - column_names=["class", "superclass"], - ) - - application.cha = self.__process_class_hierarchy_pairs_to_tree(class_superclass_pairs) - return application - - @staticmethod - def __process_class_hierarchy_pairs_to_tree( - query_result: DataFrame, - ) -> nx.DiGraph: - """ - Processes the query result into a directed graph representing the class hierarchy of the application. - - Parameters - ---------- - query_result : DataFrame - The result of the class hierarchy query. - - Returns - ------- - nx.DiGraph - A directed graph representing the class hierarchy of the application. - """ - return nx.from_pandas_edgelist(query_result, "class", "superclass", create_using=nx.DiGraph()) - - def _build_call_graph(self) -> nx.DiGraph: - """Builds the call graph of the application. - - Returns - ------- - nx.DiGraph - A directed graph representing the call graph of the application. - """ - query = [] - - # Add import - query += ["import java"] - - # Add Call edges between caller and callee and filter to only capture application methods. - query += [ - "from Method caller, Method callee", - "where", - "caller.fromSource() and", - "callee.fromSource() and", - "caller.calls(callee)", - "select", - ] - - # Caller metadata - query += [ - "caller.getFile().getAbsolutePath(),", - '"[" + caller.getBody().getLocation().getStartLine() + ", " + caller.getBody().getLocation().getEndLine() + "]", //Caller body slice indices', - "caller.getQualifiedName(), // Caller's fullsignature", - "caller.getAModifier(), // caller's method modifier", - "caller.paramsString(), // caller's method parameter types", - "caller.getReturnType().toString(), // Caller's return type", - "caller.getDeclaringType().getQualifiedName(), // Caller's class", - "caller.getDeclaringType().getAModifier(), // Caller's class modifier", - ] - - # Callee metadata - query += [ - "callee.getFile().getAbsolutePath(),", - '"[" + callee.getBody().getLocation().getStartLine() + ", " + callee.getBody().getLocation().getEndLine() + "]", //Caller body slice indices', - "callee.getQualifiedName(), // Caller's fullsignature", - "callee.getAModifier(), // callee's method modifier", - "callee.paramsString(), // callee's method parameter types", - "callee.getReturnType().toString(), // Caller's return type", - "callee.getDeclaringType().getQualifiedName(), // Caller's class", - "callee.getDeclaringType().getAModifier() // Caller's class modifier", - ] - - query_string = "\n".join(query) - - # Execute the query using the CodeQLQueryRunner context manager - with CodeQLQueryRunner(self.db_path) as query: - query_result: DataFrame = query.execute( - query_string, - column_names=[ - # Caller Columns - "caller_file", - "caller_body_slice_index", - "caller_signature", - "caller_modifier", - "caller_params", - "caller_return_type", - "caller_class_signature", - "caller_class_modifier", - # Callee Columns - "callee_file", - "callee_body_slice_index", - "callee_signature", - "callee_modifier", - "callee_params", - "callee_return_type", - "callee_class_signature", - "callee_class_modifier", - ], - ) - - # Process the query results into JMethod instances - callgraph: nx.DiGraph = self.__process_call_edges_to_callgraph(query_result) - return callable - - @staticmethod - def __process_call_edges_to_callgraph(query_result: DataFrame) -> nx.DiGraph: - pass diff --git a/cldk/analysis/java/java_analysis.py b/cldk/analysis/java/java_analysis.py index a95557f..54b9679 100644 --- a/cldk/analysis/java/java_analysis.py +++ b/cldk/analysis/java/java_analysis.py @@ -19,19 +19,17 @@ """ from pathlib import Path -from typing import Any, Dict, List, Tuple, Set, Union +from typing import Dict, List, Tuple, Set, Union import networkx as nx from tree_sitter import Tree -from cldk.analysis import SymbolTable, CallGraph, AnalysisLevel -from cldk.analysis.java.treesitter import JavaSitter +from cldk.analysis import SymbolTable, CallGraph +from cldk.analysis.commons.treesitter import TreesitterJava from cldk.models.java import JCallable from cldk.models.java import JApplication from cldk.models.java.models import JCRUDOperation, JCompilationUnit, JMethodDetail, JType, JField from cldk.analysis.java.codeanalyzer import JCodeanalyzer -from cldk.analysis.java.codeql import JCodeQL -from cldk.utils.analysis_engine import AnalysisEngine class JavaAnalysis(SymbolTable, CallGraph): @@ -40,7 +38,6 @@ def __init__( self, project_dir: str | Path | None, source_code: str | None, - analysis_backend: str, analysis_backend_path: str | None, analysis_json_path: str | Path | None, analysis_level: str, @@ -53,7 +50,6 @@ def __init__( Args: project_dir (str | Path | None): The directory path of the project. source_code (str | None): Java file for single source file analysis. - analysis_backend (str): The analysis_backend used for analysis. Currently 'codeql' and 'codeanalyzer' are supported. analysis_backend_path (str | None): The path to the analysis_backend, defaults to None and in the case of codeql, it is assumed that the cli is installed and available in the PATH. In the case of codeanalyzer the codeanalyzer.jar is downloaded from the lastest release. analysis_json_path (str | Path | None): The path save the to the analysis database (analysis.json), defaults to None. If None, the analysis database is not persisted. analysis_level (str): Analysis level (symbol-table, call-graph) @@ -75,24 +71,19 @@ def __init__( self.analysis_backend_path = analysis_backend_path self.eager_analysis = eager_analysis self.use_graalvm_binary = use_graalvm_binary - self.analysis_backend = analysis_backend self.target_files = target_files + self.treesitter_java: TreesitterJava = TreesitterJava() # Initialize the analysis analysis_backend - if analysis_backend.lower() == "codeql": - self.analysis_backend: JCodeQL = JCodeQL(self.project_dir, self.analysis_json_path) - elif analysis_backend.lower() == "codeanalyzer": - self.backend: JCodeanalyzer = JCodeanalyzer( - project_dir=self.project_dir, - source_code=self.source_code, - eager_analysis=self.eager_analysis, - analysis_level=self.analysis_level, - analysis_json_path=self.analysis_json_path, - use_graalvm_binary=self.use_graalvm_binary, - analysis_backend_path=self.analysis_backend_path, - target_files=self.target_files, - ) - else: - raise NotImplementedError(f"Support for {analysis_backend} has not been implemented yet.") + self.backend: JCodeanalyzer = JCodeanalyzer( + project_dir=self.project_dir, + source_code=self.source_code, + eager_analysis=self.eager_analysis, + analysis_level=self.analysis_level, + analysis_json_path=self.analysis_json_path, + use_graalvm_binary=self.use_graalvm_binary, + analysis_backend_path=self.analysis_backend_path, + target_files=self.target_files, + ) def get_imports(self) -> List[str]: """Should return all the imports in the source code. @@ -159,15 +150,7 @@ def get_compilation_units(self) -> List[JCompilationUnit]: Returns: List[JCompilationUnit]: Compilation units of the Java code. """ - - # TODO: This code is broken: - # JCodeanalyzer does not have a get_compilation_units() method - # Commenting out until implemented - - # if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - # raise NotImplementedError("Support for this functionality has not been implemented yet.") - # return self.backend.get_compilation_units() - raise NotImplementedError("Support for this functionality has not been implemented yet.") + return self.backend.get_compilation_units() def get_class_hierarchy(self) -> nx.DiGraph: """Should return class hierarchy of the java code. @@ -179,8 +162,6 @@ def get_class_hierarchy(self) -> nx.DiGraph: nx.DiGraph: The class hierarchy of the Java code. """ - if self.backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") raise NotImplementedError("Class hierarchy is not implemented yet.") def is_parsable(self, source_code: str) -> bool: @@ -192,7 +173,7 @@ def is_parsable(self, source_code: str) -> bool: Returns: True if the code is parsable, False otherwise """ - return JavaSitter().is_parsable(source_code) + return self.treesitter_java.is_parsable(source_code) def get_raw_ast(self, source_code: str) -> Tree: """ @@ -203,7 +184,7 @@ def get_raw_ast(self, source_code: str) -> Tree: Returns: Tree: the raw AST """ - return JavaSitter().get_raw_ast(source_code) + return self.treesitter_java.get_raw_ast(source_code) def get_call_graph(self) -> nx.DiGraph: """Should return the call graph of the Java code. @@ -267,26 +248,22 @@ def get_methods(self) -> Dict[str, Dict[str, JCallable]]: """Should return all methods in the Java code. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, Dict[str, JCallable]]: Dictionary of dictionaries of all methods in the Java code with qualified class name as key and dictionary of methods in that class. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_all_methods_in_application() def get_classes(self) -> Dict[str, JType]: """Should return all classes in the Java code. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, JType]: A dictionary of all classes in the Java code, with qualified class names as keys. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_all_classes() def get_classes_by_criteria(self, inclusions=None, exclusions=None) -> Dict[str, JType]: @@ -297,15 +274,11 @@ def get_classes_by_criteria(self, inclusions=None, exclusions=None) -> Dict[str, exclusions (List, optional): exclusion criteria for the classes. Defaults to None. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, JType]: A dict of all classes in the Java code, with qualified class names as keys """ - - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") - if exclusions is None: exclusions = [] if inclusions is None: @@ -332,14 +305,12 @@ def get_class(self, qualified_class_name: str) -> JType: qualified_class_name (str): The qualified name of the class. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: JType: Class object for the given qualified class name. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_class(qualified_class_name) def get_method(self, qualified_class_name: str, qualified_method_name: str) -> JCallable: @@ -350,13 +321,11 @@ def get_method(self, qualified_class_name: str, qualified_method_name: str) -> J qualified_method_name (str): The qualified name of the method. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: JCallable: A method for the given qualified method name. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_method(qualified_class_name, qualified_method_name) def get_java_file(self, qualified_class_name: str) -> str: @@ -366,13 +335,11 @@ def get_java_file(self, qualified_class_name: str) -> str: qualified_class_name (str): The qualified name of the class. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: str: Java file name containing the given qualified class. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_java_file(qualified_class_name) def get_java_compilation_unit(self, file_path: str) -> JCompilationUnit: @@ -382,13 +349,11 @@ def get_java_compilation_unit(self, file_path: str) -> JCompilationUnit: file_path (str): Absolute path to Java source file Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: JCompilationUnit: Compilation unit object for Java source file """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_java_compilation_unit(file_path) def get_methods_in_class(self, qualified_class_name) -> Dict[str, JCallable]: @@ -398,13 +363,11 @@ def get_methods_in_class(self, qualified_class_name) -> Dict[str, JCallable]: qualified_class_name (str): qualified class name Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, JCallable]: A dictionary of all constructors of the given class. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_all_methods_in_class(qualified_class_name) def get_constructors(self, qualified_class_name) -> Dict[str, JCallable]: @@ -414,13 +377,11 @@ def get_constructors(self, qualified_class_name) -> Dict[str, JCallable]: qualified_class_name (str): qualified class name Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, JCallable]: A dictionary of all constructors of the given class. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_all_constructors(qualified_class_name) def get_fields(self, qualified_class_name) -> List[JField]: @@ -430,13 +391,11 @@ def get_fields(self, qualified_class_name) -> List[JField]: qualified_class_name (str): qualified class name Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[JField]: A list of all fields of the given class. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_all_fields(qualified_class_name) def get_nested_classes(self, qualified_class_name) -> List[JType]: @@ -446,13 +405,11 @@ def get_nested_classes(self, qualified_class_name) -> List[JType]: qualified_class_name (str): qualified class name Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[JType]: A list of nested classes for the given class. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_all_nested_classes(qualified_class_name) def get_sub_classes(self, qualified_class_name) -> Dict[str, JType]: @@ -472,13 +429,11 @@ def get_extended_classes(self, qualified_class_name) -> List[str]: qualified_class_name (str): The qualified name of the class. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[str]: A list of extended classes for the given class. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_extended_classes(qualified_class_name) def get_implemented_interfaces(self, qualified_class_name: str) -> List[str]: @@ -488,13 +443,11 @@ def get_implemented_interfaces(self, qualified_class_name: str) -> List[str]: qualified_class_name (str): The qualified name of the class. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[str]: A list of implemented interfaces for the given class. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_implemented_interfaces(qualified_class_name) def __get_class_call_graph_using_symbol_table(self, qualified_class_name: str, method_signature: str | None = None) -> (List)[Tuple[JMethodDetail, JMethodDetail]]: @@ -505,13 +458,11 @@ def __get_class_call_graph_using_symbol_table(self, qualified_class_name: str, m method_signature (str | None, optional): The signature of the method in the class.. Defaults to None. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[Tuple[JMethodDetail, JMethodDetail]]: An edge list of the call graph for the given class and method. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_class_call_graph_using_symbol_table(qualified_class_name, method_signature) def get_class_call_graph(self, qualified_class_name: str, method_signature: str | None = None, using_symbol_table: bool = False) -> List[Tuple[JMethodDetail, JMethodDetail]]: @@ -523,48 +474,42 @@ def get_class_call_graph(self, qualified_class_name: str, method_signature: str using_symbol_table (bool, optional): Generate call graph using symbol table. Defaults to False. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[Tuple[JMethodDetail, JMethodDetail]]: An edge list of the call graph for the given class and method. """ if using_symbol_table: return self.__get_class_call_graph_using_symbol_table(qualified_class_name=qualified_class_name, method_signature=method_signature) - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_class_call_graph(qualified_class_name, method_signature) def get_entry_point_classes(self) -> Dict[str, JType]: """Should return a dictionary of all entry point classes in the Java code. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, JType]: A dict of all entry point classes in the Java code, with qualified class names as keys """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_all_entry_point_classes() def get_entry_point_methods(self) -> Dict[str, Dict[str, JCallable]]: """Should return a dictionary of all entry point methods in the Java code with qualified class name as key and dictionary of methods in that class as value Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, Dict[str, JCallable]]: A dictionary of dictionaries of entry point methods in the Java code. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") return self.backend.get_all_entry_point_methods() def remove_all_comments(self) -> str: """Remove all comments from the source code. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: str: The source code with all comments removed. @@ -578,7 +523,7 @@ def get_methods_with_annotations(self, annotations: List[str]) -> Dict[str, List annotations (List[str]): List of annotation strings. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, List[Dict]]: Dictionary with annotations as keys and a list of dictionaries containing method names and bodies, as values. @@ -587,21 +532,16 @@ def get_methods_with_annotations(self, annotations: List[str]) -> Dict[str, List raise NotImplementedError("Support for this functionality has not been implemented yet.") def get_test_methods(self) -> Dict[str, str]: - """Should return a dictionary of method names and method bodies. - - Args: - source_class_code (str): String containing code for a java class. + """Should return a dictionary of method names and method bodies Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: Dict[str, str]: Dictionary of method names and method bodies. """ - if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.CODEANALYZER]: - raise NotImplementedError("Support for this functionality has not been implemented yet.") - return self.backend.get_test_methods(self.source_code) + return self.treesitter_java.get_test_methods(source_class_code=self.source_code) def get_calling_lines(self, target_method_name: str) -> List[int]: """Should return a list of line numbers in source method block where target method is called. @@ -610,7 +550,7 @@ def get_calling_lines(self, target_method_name: str) -> List[int]: target_method_name (str): target method name. Raises: - NotImplementedError: Raised when current AnalysisEngine does not support this function. + NotImplementedError: Raised when we do not support this function. Returns: List[int]: List of line numbers within in source method code block. diff --git a/cldk/analysis/java/treesitter/__init__.py b/cldk/analysis/java/treesitter/__init__.py deleted file mode 100644 index 8026f75..0000000 --- a/cldk/analysis/java/treesitter/__init__.py +++ /dev/null @@ -1,23 +0,0 @@ -################################################################################ -# Copyright IBM Corporation 2024 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -################################################################################ - -""" -Treesitter package -""" - -from cldk.analysis.java.treesitter.java_sitter import JavaSitter - -__all__ = ["JavaSitter"] diff --git a/cldk/analysis/python/python_analysis.py b/cldk/analysis/python/python_analysis.py index 2c9f460..7a0697f 100644 --- a/cldk/analysis/python/python_analysis.py +++ b/cldk/analysis/python/python_analysis.py @@ -22,7 +22,7 @@ from typing import List from cldk.analysis import SymbolTable -from cldk.analysis.python.treesitter import PythonSitter +from cldk.analysis.commons.treesitter import TreesitterPython from cldk.models.python.models import PyMethod, PyImport, PyModule, PyClass @@ -31,7 +31,6 @@ class PythonAnalysis(SymbolTable): def __init__( self, - analysis_backend: str, eager_analysis: bool, project_dir: str | Path | None, source_code: str | None, @@ -45,16 +44,7 @@ def __init__( self.analysis_backend_path = analysis_backend_path self.eager_analysis = eager_analysis self.use_graalvm_binary = use_graalvm_binary - - # Initialize the analysis analysis_backend - if analysis_backend.lower() == "codeql": - raise NotImplementedError("Support for {analysis_backend} has not been implemented yet.") - elif analysis_backend.lower() == "codeanalyzer": - raise NotImplementedError("Support for {analysis_backend} has not been implemented yet.") - elif analysis_backend.lower() == "treesitter": - self.analysis_backend: PythonSitter = PythonSitter() - else: - raise NotImplementedError("Support for {analysis_backend} has not been implemented yet.") + self.analysis_backend: TreesitterPython = TreesitterPython() def get_methods(self) -> List[PyMethod]: """ @@ -96,7 +86,7 @@ def is_parsable(self, source_code: str) -> bool: Returns: True if the code is parsable, False otherwise """ - return PythonSitter().is_parsable(source_code) + return TreesitterPython().is_parsable(source_code) def get_raw_ast(self, source_code: str) -> str: """ @@ -107,7 +97,7 @@ def get_raw_ast(self, source_code: str) -> str: Returns: Tree: the raw AST """ - return PythonSitter().get_raw_ast(source_code) + return TreesitterPython().get_raw_ast(source_code) def get_imports(self) -> List[PyImport]: """ diff --git a/cldk/core.py b/cldk/core.py index b3c7f81..a97fd6d 100644 --- a/cldk/core.py +++ b/cldk/core.py @@ -26,7 +26,7 @@ from cldk.analysis import AnalysisLevel from cldk.analysis.c import CAnalysis from cldk.analysis.java import JavaAnalysis -from cldk.analysis.java.treesitter import JavaSitter +from cldk.analysis.commons.treesitter import TreesitterJava from cldk.utils.exceptions import CldkInitializationException from cldk.utils.sanitization.java import TreesitterSanitizer @@ -56,7 +56,6 @@ def analysis( project_path: str | Path | None = None, source_code: str | None = None, eager: bool = False, - analysis_backend: str | None = "codeanalyzer", analysis_level: str = AnalysisLevel.symbol_table, target_files: List[str] | None = None, analysis_backend_path: str | None = None, @@ -64,7 +63,7 @@ def analysis( use_graalvm_binary: bool = False, ) -> JavaAnalysis: """ - Initialize the preprocessor based on the specified language and analysis_backend. + Initialize the preprocessor based on the specified language. Parameters ---------- @@ -73,18 +72,11 @@ def analysis( source_code : str, optional The source code of the project, defaults to None. If None, it is assumed that the whole project is being analyzed. - analysis_backend : str, optional - The analysis_backend used for analysis, defaults to "codeql". analysis_backend_path : str, optional - The path to the analysis_backend, defaults to None and in the case of codeql, it is assumed that the cli is - installed and available in the PATH. In the case of codeanalyzer the codeanalyzer.jar is downloaded from the - lastest release. + The path to the analysis backend, defaults to None where it assumes the default backend path. analysis_json_path : str or Path, optional The path save the to the analysis database (analysis.json), defaults to None. If None, the analysis database is not persisted. - use_graalvm_binary : bool, optional - A flag indicating whether to use the GraalVM binary for SDG analysis, defaults to False. If False, - the default Java binary is used and one needs to have Java 17 or higher installed. eager : bool, optional A flag indicating whether to perform eager analysis, defaults to False. If True, the analysis is performed eagerly. That is, the analysis.json file is created during analysis every time even if it already exists. @@ -121,7 +113,6 @@ def analysis( return JavaAnalysis( project_dir=project_path, source_code=source_code, - analysis_backend=analysis_backend, analysis_level=analysis_level, analysis_backend_path=analysis_backend_path, analysis_json_path=analysis_json_path, @@ -145,7 +136,7 @@ def treesitter_parser(self): """ if self.language == "java": - return JavaSitter() + return TreesitterJava() else: raise NotImplementedError(f"Treesitter parser for {self.language} is not implemented yet.") diff --git a/cldk/models/treesitter/__init__.py b/cldk/models/treesitter/__init__.py index c4abf4f..1c23178 100644 --- a/cldk/models/treesitter/__init__.py +++ b/cldk/models/treesitter/__init__.py @@ -18,6 +18,6 @@ Treesitter package """ -from .models import Captures +from ...analysis.commons.treesitter.models import Captures __all__ = ["Captures"] diff --git a/cldk/utils/analysis_engine.py b/cldk/utils/analysis_engine.py deleted file mode 100644 index 8f93f52..0000000 --- a/cldk/utils/analysis_engine.py +++ /dev/null @@ -1,25 +0,0 @@ -################################################################################ -# Copyright IBM Corporation 2024 -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -################################################################################ - -""" -Analysis Engine module -""" - - -class AnalysisEngine: - TREESITTER: str = "treesitter" - CODEQL: str = "codeql" - CODEANALYZER: str = "codeanalyzer" diff --git a/cldk/utils/sanitization/java/treesitter_sanitizer.py b/cldk/utils/sanitization/java/treesitter_sanitizer.py index dfca9af..85de9b3 100644 --- a/cldk/utils/sanitization/java/treesitter_sanitizer.py +++ b/cldk/utils/sanitization/java/treesitter_sanitizer.py @@ -22,8 +22,8 @@ from copy import deepcopy from typing import Dict, List, Set -from cldk.analysis.java.treesitter import JavaSitter -from cldk.models.treesitter import Captures +from cldk.analysis.commons.treesitter import TreesitterJava +from cldk.analysis.commons.treesitter.models import Captures log = logging.getLogger(__name__) @@ -33,7 +33,7 @@ class TreesitterSanitizer: def __init__(self, source_code): self.source_code = source_code self.sanitized_code = deepcopy(self.source_code) - self.__javasitter = JavaSitter() + self.__javasitter = TreesitterJava() def keep_only_focal_method_and_its_callees(self, focal_method: str) -> str: """Remove all methods except the focal method and its callees. diff --git a/cldk/utils/sanitization/java/treesitter_utils.py b/cldk/utils/sanitization/java/treesitter_utils.py index 4156004..570e1a4 100644 --- a/cldk/utils/sanitization/java/treesitter_utils.py +++ b/cldk/utils/sanitization/java/treesitter_utils.py @@ -22,10 +22,10 @@ from copy import deepcopy from typing import Dict, List, Any, LiteralString -from cldk.analysis.java.treesitter import JavaSitter -from cldk.models.treesitter import Captures +from cldk.analysis.commons.treesitter.treesitter_java import TreesitterJava +from cldk.analysis.commons.treesitter.models import Captures -java_sitter = JavaSitter() +java_sitter = TreesitterJava() def _replace_in_source( diff --git a/docs/images/cldk-dark.png b/docs/images/cldk-dark.png new file mode 100644 index 0000000..fa4492a Binary files /dev/null and b/docs/images/cldk-dark.png differ diff --git a/docs/images/cldk-light.png b/docs/images/cldk-light.png new file mode 100644 index 0000000..acf9131 Binary files /dev/null and b/docs/images/cldk-light.png differ diff --git a/tests/analysis/java/test_java_analysis.py b/tests/analysis/java/test_java_analysis.py index 21765f6..1b5d54e 100644 --- a/tests/analysis/java/test_java_analysis.py +++ b/tests/analysis/java/test_java_analysis.py @@ -20,7 +20,7 @@ import os import json -from typing import Dict, List, Tuple +from typing import Dict, List, Set, Tuple from unittest.mock import patch, MagicMock from tree_sitter import Tree @@ -31,7 +31,6 @@ from cldk.analysis import AnalysisLevel from cldk.analysis.java import JavaAnalysis from cldk.models.java.models import JCallable, JCompilationUnit, JField, JMethodDetail, JApplication, JType -from cldk.utils.analysis_engine import AnalysisEngine def test_get_symbol_table_is_not_null(test_fixture, analysis_json): @@ -45,7 +44,6 @@ def test_get_symbol_table_is_not_null(test_fixture, analysis_json): cldk = CLDK(language="java") analysis = cldk.analysis( project_path=test_fixture, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, eager=True, analysis_level=AnalysisLevel.call_graph, @@ -53,28 +51,6 @@ def test_get_symbol_table_is_not_null(test_fixture, analysis_json): assert analysis.get_symbol_table() is not None -# def test_get_class_call_graph(test_fixture, analysis_json): -# """Should return the class call graph""" -# # Patch subprocess so that it does not run codeanalyzer -# with patch("cldk.analysis.java.codeanalyzer.codeanalyzer.subprocess.run") as run_mock: -# run_mock.return_value = MagicMock(stdout=analysis_json, returncode=0) - -# # Initialize the CLDK object with the project directory, language, and analysis_backend. -# cldk = CLDK(language="java") - -# analysis = cldk.analysis( -# project_path=test_fixture, -# analysis_backend=AnalysisEngine.CODEANALYZER, -# analysis_backend_path=None, -# eager=True, -# analysis_level=AnalysisLevel.call_graph, -# ) -# class_call_graph: List[Tuple[JMethodDetail, JMethodDetail]] = analysis.get_class_call_graph( -# qualified_class_name="com.ibm.websphere.samples.daytrader.impl.direct.TradeDirectDBUtils" -# ) -# assert class_call_graph is not None - - def test_get_imports(test_fixture, analysis_json): """Should return NotImplemented for get_imports()""" @@ -84,7 +60,6 @@ def test_get_imports(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -108,7 +83,6 @@ def test_get_variables(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -132,7 +106,6 @@ def test_get_service_entry_point_classes(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -156,7 +129,6 @@ def test_get_service_entry_point_methods(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -180,7 +152,6 @@ def test_get_application_view(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -212,7 +183,6 @@ def test_get_symbol_table(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -237,7 +207,6 @@ def test_get_compilation_units(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -247,9 +216,7 @@ def test_get_compilation_units(test_fixture, analysis_json): ) # When this is implemented please add a real test case - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_compilation_units() - assert except_info.type == NotImplementedError + assert java_analysis.get_compilation_units() != None def test_get_class_hierarchy(test_fixture, analysis_json): @@ -261,7 +228,6 @@ def test_get_class_hierarchy(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -285,7 +251,6 @@ def test_is_parsable(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -311,7 +276,6 @@ def test_get_raw_ast(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -340,7 +304,6 @@ def test_get_call_graph(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -366,7 +329,6 @@ def test_get_call_graph_json(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.call_graph, @@ -394,7 +356,6 @@ def test_get_callers(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.call_graph, @@ -441,7 +402,6 @@ def test_get_callees(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.call_graph, @@ -493,7 +453,6 @@ def test_get_methods(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -509,12 +468,6 @@ def test_get_methods(test_fixture, analysis_json): for _, method in methods.items(): assert isinstance(method, Dict) - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_methods() - assert except_info.type == NotImplementedError - def test_get_classes(test_fixture, analysis_json): """Should return the classes""" @@ -525,7 +478,6 @@ def test_get_classes(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -541,12 +493,6 @@ def test_get_classes(test_fixture, analysis_json): for _, a_class in classes.items(): assert isinstance(a_class, JType) - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_classes() - assert except_info.type == NotImplementedError - def test_get_classes_by_criteria(test_fixture, analysis_json): """Should return the classes by criteria""" @@ -557,7 +503,6 @@ def test_get_classes_by_criteria(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -588,12 +533,6 @@ def test_get_classes_by_criteria(test_fixture, analysis_json): assert isinstance(classes, Dict) assert len(classes) == 1 - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_classes_by_criteria() - assert except_info.type == NotImplementedError - def test_get_class(test_fixture, analysis_json): """Should return a single class""" @@ -604,7 +543,6 @@ def test_get_class(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -617,12 +555,6 @@ def test_get_class(test_fixture, analysis_json): assert the_class is not None assert isinstance(the_class, JType) - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_class("com.ibm.websphere.samples.daytrader.util.Log") - assert except_info.type == NotImplementedError - def test_get_method(test_fixture, analysis_json): """Should return a single method""" @@ -633,7 +565,6 @@ def test_get_method(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -647,12 +578,6 @@ def test_get_method(test_fixture, analysis_json): assert isinstance(the_method, JCallable) assert the_method.declaration == "public static void trace(String message)" - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_method("com.ibm.websphere.samples.daytrader.util.Log", "trace(String)") - assert except_info.type == NotImplementedError - def test_get_java_file(test_fixture, analysis_json): """Should return the java file and compilation unit""" @@ -663,7 +588,6 @@ def test_get_java_file(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -684,12 +608,6 @@ def test_get_java_file(test_fixture, analysis_json): assert comp_unit is not None assert isinstance(comp_unit, JCompilationUnit) - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_java_file("com.ibm.websphere.samples.daytrader.util.Log") - assert except_info.type == NotImplementedError - def test_get_methods_in_class(test_fixture, analysis_json): """Should return the methods in a class""" @@ -700,7 +618,6 @@ def test_get_methods_in_class(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -717,12 +634,6 @@ def test_get_methods_in_class(test_fixture, analysis_json): for method in methods: assert isinstance(methods[method], JCallable) - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_methods_in_class("com.ibm.websphere.samples.daytrader.util.Log") - assert except_info.type == NotImplementedError - def test_get_fields(test_fixture, analysis_json): """Should return the fields for a class""" @@ -733,7 +644,6 @@ def test_get_fields(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -750,12 +660,6 @@ def test_get_fields(test_fixture, analysis_json): for field in fields: assert isinstance(field, JField) - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_fields("com.ibm.websphere.samples.daytrader.beans.MarketSummaryDataBean") - assert except_info.type == NotImplementedError - def test_get_nested_classes(test_fixture, analysis_json): """Should return the nested classes for a class""" @@ -766,7 +670,6 @@ def test_get_nested_classes(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -792,7 +695,6 @@ def test_get_sub_classes(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -825,7 +727,6 @@ def test_get_extended_classes(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -848,12 +749,6 @@ def test_get_extended_classes(test_fixture, analysis_json): for extend in extended: assert isinstance(extend, str) - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_extended_classes("com.ibm.websphere.samples.daytrader.beans.MarketSummaryDataBean") - assert except_info.type == NotImplementedError - def test_get_implemented_interfaces(test_fixture, analysis_json): """Should return the implemented interfaces classes for a class""" @@ -864,7 +759,6 @@ def test_get_implemented_interfaces(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.symbol_table, @@ -887,12 +781,6 @@ def test_get_implemented_interfaces(test_fixture, analysis_json): for extend in extended: assert isinstance(extend, str) - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_implemented_interfaces("com.ibm.websphere.samples.daytrader.beans.MarketSummaryDataBean") - assert except_info.type == NotImplementedError - def test_get_class_call_graph(test_fixture, analysis_json): """Should return the class call graph""" @@ -903,7 +791,6 @@ def test_get_class_call_graph(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.call_graph, @@ -928,12 +815,6 @@ def test_get_class_call_graph(test_fixture, analysis_json): for graph in call_graph: assert isinstance(graph, Tuple) - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_class_call_graph("com.ibm.websphere.samples.daytrader.beans.MarketSummaryDataBean") - assert except_info.type == NotImplementedError - def test_get_entry_point_classes(test_fixture, analysis_json): """Should return the entry point classes""" @@ -944,7 +825,6 @@ def test_get_entry_point_classes(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.call_graph, @@ -960,12 +840,6 @@ def test_get_entry_point_classes(test_fixture, analysis_json): for _, entry_point in entry_point_classes.items(): assert isinstance(entry_point, JType) - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_entry_point_classes() - assert except_info.type == NotImplementedError - def test_get_entry_point_methods(test_fixture, analysis_json): """Should return the entry point methods""" @@ -976,7 +850,6 @@ def test_get_entry_point_methods(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.call_graph, @@ -994,12 +867,6 @@ def test_get_entry_point_methods(test_fixture, analysis_json): for _, method in entry_point.items(): assert isinstance(method, JCallable) - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_entry_point_methods() - assert except_info.type == NotImplementedError - def test_remove_all_comments(test_fixture, analysis_json): """remove all comments""" @@ -1010,7 +877,6 @@ def test_remove_all_comments(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.call_graph, @@ -1039,7 +905,6 @@ def test_get_methods_with_annotations(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.call_graph, @@ -1062,14 +927,23 @@ def test_get_methods_with_annotations(test_fixture, analysis_json): def test_get_test_methods(test_fixture, analysis_json): """Should return test methods""" + java_code_with_test_annotations = """package com.ibm.websphere.samples.daytrader.web.prims.ejb3; +import org.junit.jupiter.api.Test; +import static org.junit.jupiter.api.Assertions.assertEquals; +public class TradeDirectDBUtilsTest { + @Test + public void testBuildDB() { + assertEquals(1, 1); + } +} +""" # Patch subprocess so that it does not run codeanalyzer with patch("cldk.analysis.java.codeanalyzer.codeanalyzer.subprocess.run") as run_mock: run_mock.return_value = MagicMock(stdout=analysis_json, returncode=0) java_analysis = JavaAnalysis( project_dir=test_fixture, - source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, + source_code=java_code_with_test_annotations, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.call_graph, @@ -1078,24 +952,9 @@ def test_get_test_methods(test_fixture, analysis_json): eager_analysis=False, ) - # TODO: The code is broken. It requires Treesitter but JCodeanalyzer does not! - - try: - test_methods = java_analysis.get_test_methods() - assert test_methods is not None - assert isinstance(test_methods, Dict) - assert len(test_methods) > 0 - - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_test_methods() - assert except_info.type == NotImplementedError - except NotImplementedError: - assert True - return - - assert False, "Did not raise NotImplementedError" + test_methods = java_analysis.get_test_methods() + assert test_methods is not None + assert isinstance(test_methods, Dict) def test_get_calling_lines(test_fixture, analysis_json): @@ -1107,7 +966,6 @@ def test_get_calling_lines(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.call_graph, @@ -1123,12 +981,6 @@ def test_get_calling_lines(test_fixture, analysis_json): assert calling_lines is not None assert isinstance(calling_lines, List) assert len(calling_lines) > 0 - - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_calling_lines("trace(String)") - assert except_info.type == NotImplementedError except NotImplementedError: assert True return @@ -1145,7 +997,6 @@ def test_get_call_targets(test_fixture, analysis_json): java_analysis = JavaAnalysis( project_dir=test_fixture, source_code=None, - analysis_backend=AnalysisEngine.CODEANALYZER, analysis_backend_path=None, analysis_json_path=None, analysis_level=AnalysisLevel.call_graph, @@ -1160,12 +1011,6 @@ def test_get_call_targets(test_fixture, analysis_json): assert call_targets is not None assert isinstance(call_targets, Set) assert len(call_targets) > 0 - - # Test with unsupported backend - java_analysis.analysis_backend = AnalysisEngine.CODEQL - with pytest.raises(NotImplementedError) as except_info: - java_analysis.get_calling_lines("trace(String)") - assert except_info.type == NotImplementedError except NotImplementedError: assert True return diff --git a/tests/analysis/java/test_java_sitter.py b/tests/analysis/java/test_java_sitter.py index 3dc6278..73ae639 100644 --- a/tests/analysis/java/test_java_sitter.py +++ b/tests/analysis/java/test_java_sitter.py @@ -19,15 +19,15 @@ """ import os from typing import List, Set, Dict -from tree_sitter import Node, Tree +from tree_sitter import Tree import pytest -from cldk.analysis.java.treesitter import JavaSitter +from cldk.analysis.commons.treesitter import TreesitterJava def test_method_is_not_in_class(test_fixture): """not find the method in the class""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file and send its contents filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/beans/MarketSummaryDataBean.java") @@ -45,7 +45,7 @@ def test_method_is_not_in_class(test_fixture): def test_is_parsable(test_fixture): """Should be able to parse the file""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file and send its contents filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/beans/MarketSummaryDataBean.java") @@ -64,7 +64,7 @@ def test_is_parsable(test_fixture): def test_get_raw_ast(test_fixture): """Should return the raw AST""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file and send its contents filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/util/Log.java") @@ -79,7 +79,7 @@ def test_get_raw_ast(test_fixture): def test_get_all_imports(test_fixture): """Should return all of the imports""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file and send its contents filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/util/Log.java") @@ -98,7 +98,7 @@ def test_get_all_imports(test_fixture): def test_get_package_name(test_fixture): """Should return the package name""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file and send its contents filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/util/Log.java") @@ -113,7 +113,7 @@ def test_get_package_name(test_fixture): def test_get_class_name(test_fixture): """Should return the class name""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file and send its contents filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/util/Log.java") @@ -128,7 +128,7 @@ def test_get_class_name(test_fixture): def test_get_superclass(test_fixture): """Should return the superclass name""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file with no supper class filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/util/Log.java") @@ -158,7 +158,7 @@ def test_get_superclass(test_fixture): def test_get_all_interfaces(test_fixture): """Should return all interfaces""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file with interfaces filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/impl/direct/TradeDirect.java") @@ -185,7 +185,7 @@ def test_get_all_interfaces(test_fixture): def test_get_method_name_from_declaration(): """Should return the method name from a declarations""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() declaration = "public Future submitOrder(Integer orderID, boolean twoPhase)" method_name = java_sitter.get_method_name_from_declaration(declaration) @@ -196,7 +196,7 @@ def test_get_method_name_from_declaration(): def test_get_method_name_from_invocation(): """Should return the method name from an invocation""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() invocation = "asyncOrder.setProperties(orderID,twoPhase);" method_name = java_sitter.get_method_name_from_invocation(invocation) @@ -207,7 +207,7 @@ def test_get_method_name_from_invocation(): def test_get_identifier_from_arbitrary_statement(): """Should return the method name from an arbitrary statement""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() arbitrary_statement = "asyncOrder.setProperties(orderID,twoPhase);" identifier = java_sitter.get_identifier_from_arbitrary_statement(arbitrary_statement) @@ -218,7 +218,7 @@ def test_get_identifier_from_arbitrary_statement(): def test_safe_ascend(test_fixture): """safely ascend the node tree""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Test is catches if the node is None with pytest.raises(ValueError) as except_info: @@ -256,14 +256,14 @@ def test_safe_ascend(test_fixture): def test_get_call_targets(): """get the call targets""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # TODO: This test case needs to be written def test_get_calling_lines(): """get the calling lines""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() source_method_code = """ public static BigDecimal computeHoldingsTotal(Collection holdingDataBeans) { @@ -296,7 +296,7 @@ def test_get_calling_lines(): def test_get_test_methods(test_fixture): """Should return the test methods""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # TODO: Need to find an example with test methods @@ -313,7 +313,7 @@ def test_get_test_methods(test_fixture): def test_get_methods_with_annotations(test_fixture): """Should return methods with annotations""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file with annotations filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/web/prims/PingJDBCRead2JSP.java") @@ -334,7 +334,7 @@ def test_get_methods_with_annotations(test_fixture): def test_get_all_type_invocations(test_fixture): """Should return all of the type invocations""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/web/prims/PingJDBCRead2JSP.java") @@ -352,7 +352,7 @@ def test_get_all_type_invocations(test_fixture): def test_get_method_return_type(): """get the methods return type""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() source_method_code = """ public static BigDecimal computeHoldingsTotal(Collection holdingDataBeans) { @@ -377,7 +377,7 @@ def test_get_method_return_type(): def test_get_lexical_tokens(test_fixture): """Should return the lexical tokens""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/web/prims/PingJDBCRead2JSP.java") @@ -393,7 +393,7 @@ def test_get_lexical_tokens(test_fixture): def test_remove_all_comments(test_fixture): """remove all comments""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/web/prims/PingJDBCRead2JSP.java") @@ -410,7 +410,7 @@ def test_remove_all_comments(test_fixture): def test_make_pruned_code_prettier(test_fixture): """make pruned code prettier""" - java_sitter = JavaSitter() + java_sitter = TreesitterJava() # Get a test source file filename = os.path.join(test_fixture, "src/main/java/com/ibm/websphere/samples/daytrader/web/prims/PingJDBCRead2JSP.java") diff --git a/tests/analysis/java/test_jcodeanalyzer.py b/tests/analysis/java/test_jcodeanalyzer.py index c0395d9..035ba49 100644 --- a/tests/analysis/java/test_jcodeanalyzer.py +++ b/tests/analysis/java/test_jcodeanalyzer.py @@ -372,7 +372,7 @@ def test_get_all_callers(test_fixture, analysis_json): # Call using symbol table # TODO: This currently doesn't work. Code has bad call as seen in this error message: - # TypeError: JavaSitter.get_calling_lines() missing 1 required positional argument: 'is_target_method_a_constructor' + # TypeError: TreesitterJava.get_calling_lines() missing 1 required positional argument: 'is_target_method_a_constructor' all_callers = code_analyzer.get_all_callers("com.ibm.websphere.samples.daytrader.util.Log", "log(String)", True) assert all_callers is not None assert isinstance(all_callers, Dict) @@ -406,7 +406,7 @@ def test_get_all_callees(test_fixture, analysis_json): # Call using the symbol table # TODO: Throws the following exception - # TypeError: JavaSitter.get_calling_lines() missing 1 required positional argument: 'is_target_method_a_constructor' + # TypeError: TreesitterJava.get_calling_lines() missing 1 required positional argument: 'is_target_method_a_constructor' all_callees = code_analyzer.get_all_callees("com.ibm.websphere.samples.daytrader.util.Log", "printCollection(String, Collection)", True) assert all_callees is not None assert isinstance(all_callees, Dict) diff --git a/tests/analysis/python/test_python_analysis.py b/tests/analysis/python/test_python_analysis.py index 8329bfc..1997131 100644 --- a/tests/analysis/python/test_python_analysis.py +++ b/tests/analysis/python/test_python_analysis.py @@ -23,7 +23,6 @@ import pytest from cldk.analysis.python import PythonAnalysis -from cldk.utils.analysis_engine import AnalysisEngine from cldk.models.python.models import PyClass, PyImport, PyMethod, PyModule PYTHON_CODE = """ @@ -66,33 +65,9 @@ def divide(self, a, b): """ -def test_not_implemented(): - """Should return raise a not implemented exception""" - # test with CodeQL - with pytest.raises(NotImplementedError) as except_info: - _ = PythonAnalysis( - analysis_backend=AnalysisEngine.CODEQL, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) - assert except_info.type == NotImplementedError - - # test with CodeAnalyzer - with pytest.raises(NotImplementedError) as except_info: - _ = PythonAnalysis( - analysis_backend=AnalysisEngine.CODEANALYZER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) - assert except_info.type == NotImplementedError - - # Test with unknown backend - with pytest.raises(NotImplementedError) as except_info: - _ = PythonAnalysis(analysis_backend="unknown", eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) - assert except_info.type == NotImplementedError - - def test_get_methods(): """Should return all of the methods""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) all_methods = python_analysis.get_methods() assert all_methods is not None @@ -104,9 +79,7 @@ def test_get_methods(): def test_get_functions(): """Should return all of the functions""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) all_functions = python_analysis.get_functions() assert all_functions is not None @@ -118,9 +91,7 @@ def test_get_functions(): def test_get_all_modules(tmp_path): """Should return all of the modules""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=tmp_path, source_code=None, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=tmp_path, source_code=None, analysis_backend_path=None, analysis_json_path=None) # set up some temporary modules temp_file_path = os.path.join(tmp_path, "hello.py") @@ -140,9 +111,7 @@ def test_get_all_modules(tmp_path): def test_get_method_details(): """Should return the method details""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) method_details = python_analysis.get_method_details("add(self, a, b)") assert method_details is not None @@ -152,9 +121,7 @@ def test_get_method_details(): def test_is_parsable(): """Should be able to parse the code""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) code = "def is_parsable(self, code: str) -> bool: return True" is_parsable = python_analysis.is_parsable(code) @@ -167,9 +134,7 @@ def test_is_parsable(): def test_get_raw_ast(): """Should return the raw AST""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) raw_ast = python_analysis.get_raw_ast(PYTHON_CODE) assert raw_ast is not None @@ -179,9 +144,7 @@ def test_get_raw_ast(): def test_get_imports(): """Should return all of the imports""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) all_imports = python_analysis.get_imports() assert all_imports is not None @@ -193,9 +156,7 @@ def test_get_imports(): def test_get_variables(): """Should return all of the variables""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) with pytest.raises(NotImplementedError) as except_info: python_analysis.get_variables() @@ -204,9 +165,7 @@ def test_get_variables(): def test_get_classes(): """Should return all of the classes""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) all_classes = python_analysis.get_classes() assert all_classes is not None @@ -219,9 +178,7 @@ def test_get_classes(): def test_get_classes_by_criteria(): """Should return all of the classes that match the criteria""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) with pytest.raises(NotImplementedError) as except_info: python_analysis.get_classes_by_criteria() @@ -230,9 +187,7 @@ def test_get_classes_by_criteria(): def test_get_sub_classes(): """Should return all of the subclasses""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) with pytest.raises(NotImplementedError) as except_info: python_analysis.get_sub_classes() @@ -241,9 +196,7 @@ def test_get_sub_classes(): def test_get_nested_classes(): """Should return all of the nested classes""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) with pytest.raises(NotImplementedError) as except_info: python_analysis.get_nested_classes() @@ -252,9 +205,7 @@ def test_get_nested_classes(): def test_get_constructors(): """Should return all of the constructors""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) with pytest.raises(NotImplementedError) as except_info: python_analysis.get_constructors() @@ -263,9 +214,7 @@ def test_get_constructors(): def test_get_methods_in_class(): """Should return all of the methods in the class""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) with pytest.raises(NotImplementedError) as except_info: python_analysis.get_methods_in_class() @@ -274,9 +223,7 @@ def test_get_methods_in_class(): def test_get_fields(): """Should return all of the fields in the class""" - python_analysis = PythonAnalysis( - analysis_backend=AnalysisEngine.TREESITTER, eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None - ) + python_analysis = PythonAnalysis(eager_analysis=True, project_dir=None, source_code=PYTHON_CODE, analysis_backend_path=None, analysis_json_path=None) with pytest.raises(NotImplementedError) as except_info: python_analysis.get_fields() diff --git a/tests/analysis/python/test_python_sitter.py b/tests/analysis/python/test_treesitter_python.py similarity index 88% rename from tests/analysis/python/test_python_sitter.py rename to tests/analysis/python/test_treesitter_python.py index 1f837c9..311ed80 100644 --- a/tests/analysis/python/test_python_sitter.py +++ b/tests/analysis/python/test_treesitter_python.py @@ -22,7 +22,7 @@ from typing import List from tree_sitter import Tree -from cldk.analysis.python.treesitter import PythonSitter +from cldk.analysis.commons.treesitter import TreesitterPython from cldk.models.python.models import PyClass, PyImport, PyMethod, PyModule PYTHON_CODE = """ @@ -67,7 +67,7 @@ def divide(self, a, b): def test_is_parsable(): """Should be able to parse the code""" - python_sitter = PythonSitter() + python_sitter = TreesitterPython() code = "def is_parsable(self, code: str) -> bool: return True" is_parsable = python_sitter.is_parsable(code) @@ -78,14 +78,14 @@ def test_is_parsable(): assert is_parsable is False # Test when parse returns None - with patch("cldk.analysis.python.treesitter.python_sitter.Parser.parse") as parse_mock: + with patch("cldk.analysis.commons.treesitter.treesitter_python.Parser.parse") as parse_mock: parse_mock.return_value = None code = "def is_parsable(self, code: str) -> bool: return True" is_parsable = python_sitter.is_parsable(code) assert is_parsable is False # Test exception conditions <- Not sure why this doesn't work - # with patch("cldk.analysis.python.treesitter.python_sitter.Node.children") as recursion_mock: + # with patch("cldk.analysis.commons.treesitter.python_sitter.Node.children") as recursion_mock: # recursion_mock.side_effect = RecursionError() # code = "def is_parsable(self, code: str) -> bool: return True" # is_parsable = python_sitter.is_parsable(code) @@ -94,7 +94,7 @@ def test_is_parsable(): def test_get_raw_ast(): """Should return the raw AST""" - python_sitter = PythonSitter() + python_sitter = TreesitterPython() raw_ast = python_sitter.get_raw_ast(PYTHON_CODE) assert raw_ast is not None @@ -104,7 +104,7 @@ def test_get_raw_ast(): def test_get_all_methods(): """Should return all of the methods""" - python_sitter = PythonSitter() + python_sitter = TreesitterPython() all_methods = python_sitter.get_all_methods(PYTHON_CODE) assert all_methods is not None @@ -116,7 +116,7 @@ def test_get_all_methods(): def test_get_all_functions(): """Should return all of the functions""" - python_sitter = PythonSitter() + python_sitter = TreesitterPython() all_functions = python_sitter.get_all_functions(PYTHON_CODE) assert all_functions is not None @@ -128,7 +128,7 @@ def test_get_all_functions(): def test_get_method_details(): """Should return the method details""" - python_sitter = PythonSitter() + python_sitter = TreesitterPython() method_details = python_sitter.get_method_details(PYTHON_CODE, "add(self, a, b)") assert method_details is not None @@ -136,7 +136,7 @@ def test_get_method_details(): assert method_details.full_signature == "add(self, a, b)" # Test when get_all_methods returns empty list - with patch("cldk.analysis.python.treesitter.python_sitter.PythonSitter.get_all_methods") as method_mock: + with patch("cldk.analysis.commons.treesitter.treesitter_python.TreesitterPython.get_all_methods") as method_mock: method_mock.return_value = [] method_details = python_sitter.get_method_details(PYTHON_CODE, "add(self, a, b)") assert method_details is None @@ -144,7 +144,7 @@ def test_get_method_details(): def test_get_all_imports(): """Should return all of the imports""" - python_sitter = PythonSitter() + python_sitter = TreesitterPython() all_imports = python_sitter.get_all_imports(PYTHON_CODE) assert all_imports is not None @@ -157,7 +157,7 @@ def test_get_all_imports(): def test_get_module_details(): """Should return the module details""" - python_sitter = PythonSitter() + python_sitter = TreesitterPython() module_details = python_sitter.get_module_details(PYTHON_CODE) assert module_details is not None @@ -169,7 +169,7 @@ def test_get_module_details(): def test_get_all_import_details(): """Should return all of the import details""" - python_sitter = PythonSitter() + python_sitter = TreesitterPython() all_import_details = python_sitter.get_all_imports_details(PYTHON_CODE) assert all_import_details is not None @@ -181,7 +181,7 @@ def test_get_all_import_details(): def test_get_all_classes(): """Should return all of the classes""" - python_sitter = PythonSitter() + python_sitter = TreesitterPython() all_classes = python_sitter.get_all_classes(PYTHON_CODE) assert all_classes is not None @@ -194,7 +194,7 @@ def test_get_all_classes(): def test_get_all_modules(tmp_path): """Should return all of the modules""" - python_sitter = PythonSitter() + python_sitter = TreesitterPython() # set up some temporary modules temp_file_path = os.path.join(tmp_path, "hello.py") diff --git a/tests/models/java/test_java_models.py b/tests/models/java/test_java_models.py index cfabe05..9e32d04 100644 --- a/tests/models/java/test_java_models.py +++ b/tests/models/java/test_java_models.py @@ -3,9 +3,7 @@ def test_get_class_call_graph(analysis_json_fixture): - # Initialize the CLDK object with the project directory, language, and analysis_backend. + """Initialize the CLDK object with the project directory, language, and analysis_backend.""" cldk = CLDK(language="java") - - analysis = cldk.analysis( - project_path=analysis_json_fixture, analysis_backend="codeanalyzer", analysis_json_path=analysis_json_fixture, eager=False, analysis_level="call-graph" - ) + analysis = cldk.analysis(project_path=analysis_json_fixture, analysis_json_path=analysis_json_fixture, eager=False, analysis_level="call-graph") + assert analysis is not None