Skip to content

Commit 7d70520

Browse files
committed
Initialize C/C++ support feature branch.
Signed-off-by: Rahul Krishna <[email protected]>
1 parent 54f6644 commit 7d70520

File tree

12 files changed

+1156
-3
lines changed

12 files changed

+1156
-3
lines changed

cldk/analysis/c/__init__.py

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
################################################################################
2+
# Copyright IBM Corporation 2024
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
################################################################################
16+
17+
"""
18+
C Analysis
19+
"""
20+
from .c_analysis import CAnalysis

cldk/analysis/c/c_analysis.py

Lines changed: 376 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,376 @@
1+
################################################################################
2+
# Copyright IBM Corporation 2024
3+
#
4+
# Licensed under the Apache License, Version 2.0 (the "License");
5+
# you may not use this file except in compliance with the License.
6+
# You may obtain a copy of the License at
7+
#
8+
# http://www.apache.org/licenses/LICENSE-2.0
9+
#
10+
# Unless required by applicable law or agreed to in writing, software
11+
# distributed under the License is distributed on an "AS IS" BASIS,
12+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
# See the License for the specific language governing permissions and
14+
# limitations under the License.
15+
################################################################################
16+
17+
"""
18+
Analysis model for C projects
19+
"""
20+
21+
from pathlib import Path
22+
from typing import Dict, List, Optional
23+
24+
from networkx import DiGraph
25+
26+
from cldk.analysis.c.clang import ClangAnalyzer
27+
from cldk.models.c import CApplication, CFunction, CTranslationUnit, CMacro, CTypedef, CStruct, CEnum, CVariable
28+
29+
30+
class CAnalysis:
31+
32+
def __init__(self, project_dir: Path) -> None:
33+
"""Initialization method for C Analysis backend."""
34+
if not isinstance(project_dir, Path):
35+
project_dir = Path(project_dir)
36+
self.c_application = self._init_application(project_dir)
37+
38+
def _init_application(self, project_dir: Path) -> CApplication:
39+
"""Initializes the C application object.
40+
41+
Args:
42+
project_dir (Path): Path to the project directory.
43+
44+
Returns:
45+
CApplication: C application object.
46+
"""
47+
analyzer = ClangAnalyzer()
48+
49+
# Analyze each file
50+
translation_units = {}
51+
for source_file in project_dir.rglob("*.c"):
52+
tu = analyzer.analyze_file(source_file)
53+
translation_units[str(source_file)] = tu
54+
55+
# Create application model
56+
return CApplication(translation_units=translation_units)
57+
58+
def get_c_application(self) -> CApplication:
59+
"""Returns the C application object.
60+
61+
Returns:
62+
CApplication: C application object.
63+
"""
64+
return self.c_application
65+
66+
def get_imports(self) -> List[str]:
67+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
68+
69+
def get_variables(self, **kwargs):
70+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
71+
72+
def get_application_view(self) -> CApplication:
73+
return self.c_application
74+
75+
def get_symbol_table(self) -> Dict[str, CTranslationUnit]:
76+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
77+
78+
def get_compilation_units(self) -> List[CTranslationUnit]:
79+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
80+
81+
def is_parsable(self, source_code: str) -> bool:
82+
"""
83+
Check if the code is parsable using clang parser.
84+
Args:
85+
source_code: source code
86+
87+
Returns:
88+
True if the code is parsable, False otherwise
89+
"""
90+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
91+
92+
def get_call_graph(self) -> DiGraph:
93+
"""Returns the call graph of the C code.
94+
95+
Returns:
96+
DiGraph: The call graph of the C code.
97+
"""
98+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
99+
100+
def get_call_graph_json(self) -> str:
101+
"""Returns a serialized call graph in json.
102+
103+
Raises:
104+
NotImplementedError: Raised when this functionality is not suported.
105+
106+
Returns:
107+
str: Call graph in json.
108+
"""
109+
110+
raise NotImplementedError("Producing a call graph over a single file is not implemented yet.")
111+
112+
def get_callers(self, function: CFunction) -> Dict:
113+
"""Returns a dictionary of callers of the target method.
114+
115+
Args:
116+
function (CFunction): A CFunction object.
117+
118+
Raises:
119+
NotImplementedError: Raised when this functionality is not suported.
120+
121+
Returns:
122+
Dict: A dictionary of callers of target function.
123+
"""
124+
125+
raise NotImplementedError("Generating all callers over a single file is not implemented yet.")
126+
127+
def get_callees(self, function: CFunction) -> Dict:
128+
"""Returns a dictionary of callees in a fuction.
129+
130+
Args:
131+
function (CFunction): A CFunction object.
132+
133+
Raises:
134+
NotImplementedError: Raised when this functionality is not suported.
135+
136+
Returns:
137+
Dict: Dictionary with callee details.
138+
"""
139+
raise NotImplementedError("Generating all callees over a single file is not implemented yet.")
140+
141+
def get_functions(self) -> Dict[str, CFunction]:
142+
"""Returns all functions in the project.
143+
144+
Raises:
145+
NotImplementedError: Raised when current AnalysisEngine does not support this function.
146+
147+
Returns:
148+
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.
149+
"""
150+
for _, translation_unit in self.c_application.translation_units.items():
151+
return translation_unit.functions
152+
153+
def get_function(self, function_name: str, file_name: Optional[str]) -> CFunction | List[CFunction]:
154+
"""Returns a function object given the function name.
155+
156+
Args:
157+
function_name (str): The name of the function.
158+
file_name (str): The name of the file containing the function.
159+
160+
Returns:
161+
CFunction: A method for the given qualified method name. If multiple functions with the same name exist, a list of functions is returned.
162+
"""
163+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
164+
165+
def get_C_file(self, file_name: str) -> str:
166+
"""Returns a class given qualified class name.
167+
168+
Args:
169+
file_name (str): The name of the file.
170+
171+
Raises:
172+
NotImplementedError: Raised when current AnalysisEngine does not support this function.
173+
174+
Returns:
175+
str: C file name containing the given qualified class.
176+
"""
177+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
178+
179+
def get_C_compilation_unit(self, file_path: str) -> CTranslationUnit:
180+
"""Given the path of a C source file, returns the compilation unit object from the symbol table.
181+
182+
Args:
183+
file_path (str): Absolute path to C source file
184+
185+
Raises:
186+
NotImplementedError: Raised when current AnalysisEngine does not support this function.
187+
188+
Returns:
189+
CTranslationUnit: Compilation unit object for C source file
190+
"""
191+
if self.analysis_backend in [AnalysisEngine.CODEQL, AnalysisEngine.TREESITTER]:
192+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
193+
return self.backend.get_C_compilation_unit(file_path)
194+
195+
def get_functions_in_file(self, file_name: str) -> List[CFunction]:
196+
"""Returns a dictionary of all methods of the given class.
197+
198+
Args:
199+
file_name (str): The name of the file.
200+
201+
Raises:
202+
NotImplementedError: Raised when current AnalysisEngine does not support this function.
203+
204+
Returns:
205+
Dict[str, JCallable]: A dictionary of all constructors of the given class.
206+
"""
207+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
208+
209+
def get_macros(self) -> List[CMacro]:
210+
"""Returns a list of all macros in the C code.
211+
212+
Raises:
213+
NotImplementedError: Raised when current AnalysisEngine does not support this function.
214+
215+
Returns:
216+
List[CMacro]: A list of all macros in the C code.
217+
"""
218+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
219+
220+
def get_macros_in_file(self, file_name: str) -> List[CMacro] | None:
221+
"""Returns a list of all macros in the given file.
222+
223+
Args:
224+
file_name (str): The name of the file.
225+
226+
Raises:
227+
NotImplementedError: Raised when current AnalysisEngine does not support this function.
228+
229+
Returns:
230+
List[CMacro]: A list of all macros in the given file. Returns None if no macros are found.
231+
"""
232+
raise NotImplementedError("Support for this functionality has not been implemented yet.")
233+
234+
235+
def get_includes(self) -> List[str]:
236+
"""Returns a list of all include statements across all files in the C code.
237+
238+
Returns:
239+
List[str]: A list of all include statements. Returns empty list if none found.
240+
"""
241+
all_includes = []
242+
for translation_unit in self.translation_units.values():
243+
all_includes.extend(translation_unit.includes)
244+
return all_includes
245+
246+
247+
def get_includes_in_file(self, file_name: str) -> List[str] | None:
248+
"""Returns a list of all include statements in the given file.
249+
250+
Args:
251+
file_name (str): The name of the file to search in.
252+
253+
Returns:
254+
List[str] | None: List of includes in the file, or None if file not found.
255+
"""
256+
if file_name in self.translation_units:
257+
return self.translation_units[file_name].includes
258+
return None
259+
260+
261+
def get_macros(self) -> List[CMacro]:
262+
"""Returns a list of all macro definitions across all files in the C code.
263+
264+
Returns:
265+
List[CMacro]: A list of all macro definitions. Returns empty list if none found.
266+
"""
267+
all_macros = []
268+
for translation_unit in self.translation_units.values():
269+
all_macros.extend(translation_unit.macros)
270+
return all_macros
271+
272+
273+
def get_macros_in_file(self, file_name: str) -> List[CMacro] | None:
274+
"""Returns a list of all macro definitions in the given file.
275+
276+
Args:
277+
file_name (str): The name of the file to search in.
278+
279+
Returns:
280+
List[CMacro] | None: List of macros in the file, or None if file not found.
281+
"""
282+
if file_name in self.translation_units:
283+
return self.translation_units[file_name].macros
284+
return None
285+
286+
287+
def get_typedefs(self) -> List[CTypedef]:
288+
"""Returns a list of all typedef declarations across all files in the C code.
289+
290+
Returns:
291+
List[CTypedef]: A list of all typedef declarations. Returns empty list if none found.
292+
"""
293+
all_typedefs = []
294+
for translation_unit in self.translation_units.values():
295+
all_typedefs.extend(translation_unit.typedefs)
296+
return all_typedefs
297+
298+
299+
def get_typedefs_in_file(self, file_name: str) -> List[CTypedef] | None:
300+
"""Returns a list of all typedef declarations in the given file.
301+
302+
Args:
303+
file_name (str): The name of the file to search in.
304+
305+
Returns:
306+
List[CTypedef] | None: List of typedefs in the file, or None if file not found.
307+
"""
308+
if file_name in self.translation_units:
309+
return self.translation_units[file_name].typedefs
310+
return None
311+
312+
313+
def get_structs(self) -> List[CStruct]:
314+
"""Returns a list of all struct/union declarations across all files in the C code.
315+
316+
Returns:
317+
List[CStruct]: A list of all struct/union declarations. Returns empty list if none found.
318+
"""
319+
all_structs = []
320+
for translation_unit in self.translation_units.values():
321+
all_structs.extend(translation_unit.structs)
322+
return all_structs
323+
324+
325+
def get_structs_in_file(self, file_name: str) -> List[CStruct] | None:
326+
"""Returns a list of all struct/union declarations in the given file.
327+
328+
Args:
329+
file_name (str): The name of the file to search in.
330+
331+
Returns:
332+
List[CStruct] | None: List of structs in the file, or None if file not found.
333+
"""
334+
if file_name in self.translation_units:
335+
return self.translation_units[file_name].structs
336+
return None
337+
338+
339+
def get_enums(self) -> List[CEnum]:
340+
"""Returns a list of all enum declarations across all files in the C code.
341+
342+
Returns:
343+
List[CEnum]: A list of all enum declarations. Returns empty list if none found.
344+
"""
345+
all_enums = []
346+
for translation_unit in self.translation_units.values():
347+
all_enums.extend(translation_unit.enums)
348+
return all_enums
349+
350+
351+
def get_enums_in_file(self, file_name: str) -> List[CEnum] | None:
352+
"""Returns a list of all enum declarations in the given file.
353+
354+
Args:
355+
file_name (str): The name of the file to search in.
356+
357+
Returns:
358+
List[CEnum] | None: List of enums in the file, or None if file not found.
359+
"""
360+
if file_name in self.translation_units:
361+
return self.translation_units[file_name].enums
362+
return None
363+
364+
365+
def get_globals(self, file_name: str) -> List[CVariable] | None:
366+
"""Returns a list of all global variable declarations in the given file.
367+
368+
Args:
369+
file_name (str): The name of the file to search in.
370+
371+
Returns:
372+
List[CVariable] | None: List of globals in the file, or None if file not found.
373+
"""
374+
if file_name in self.translation_units:
375+
return self.translation_units[file_name].globals
376+
return None

0 commit comments

Comments
 (0)