@@ -52,6 +52,12 @@ def extract_parent_classes(
5252 )
5353 )
5454
55+ # C#: extract base class from base_list (not interfaces)
56+ if class_node .type == cs .TS_CLASS_DECLARATION :
57+ parent_classes .extend (
58+ extract_csharp_base_class (class_node , module_qn , resolve_to_qn )
59+ )
60+
5561 return parent_classes
5662
5763
@@ -297,19 +303,86 @@ def resolve_js_ts_parent_class(
297303 return resolve_to_qn (parent_name , module_qn )
298304
299305
306+ def extract_interface_names_raw (class_node : Node ) -> list [str ]:
307+ """Extract raw interface names without qualified name resolution.
308+
309+ Used for deferred IMPLEMENTS relationship creation where resolution
310+ happens after all files are processed.
311+ """
312+ interface_names : list [str ] = []
313+
314+ # Java: uses 'interfaces' field
315+ interfaces_node = class_node .child_by_field_name (cs .FIELD_INTERFACES )
316+ if interfaces_node :
317+ for child in interfaces_node .children :
318+ if child .type == cs .TS_TYPE_LIST :
319+ for type_child in child .children :
320+ if type_child .type == cs .TS_TYPE_IDENTIFIER and type_child .text :
321+ if name := safe_decode_text (type_child ):
322+ interface_names .append (name )
323+
324+ # C#: uses 'base_list' child node (contains both base class and interfaces)
325+ for child in class_node .children :
326+ if child .type == cs .TS_CS_BASE_LIST :
327+ for subchild in child .children :
328+ if subchild .type == cs .TS_IDENTIFIER and subchild .text :
329+ if name := safe_decode_text (subchild ):
330+ # C# convention: interfaces start with 'I' followed by uppercase
331+ if len (name ) > 1 and name .startswith ("I" ) and name [1 ].isupper ():
332+ interface_names .append (name )
333+ break
334+
335+ return interface_names
336+
337+
338+ def extract_base_class_names_raw (class_node : Node ) -> list [str ]:
339+ """Extract raw base class names without qualified name resolution.
340+
341+ Used for deferred INHERITS relationship creation where resolution
342+ happens after all files are processed.
343+
344+ For C#, extracts non-interface identifiers from base_list.
345+ """
346+ base_class_names : list [str ] = []
347+
348+ # C#: uses 'base_list' child node (contains both base class and interfaces)
349+ for child in class_node .children :
350+ if child .type == cs .TS_CS_BASE_LIST :
351+ for subchild in child .children :
352+ if subchild .type == cs .TS_IDENTIFIER and subchild .text :
353+ if name := safe_decode_text (subchild ):
354+ # Skip interfaces (start with 'I' followed by uppercase)
355+ if len (name ) > 1 and name .startswith ("I" ) and name [1 ].isupper ():
356+ continue
357+ # This is a base class
358+ base_class_names .append (name )
359+ break
360+
361+ return base_class_names
362+
363+
300364def extract_implemented_interfaces (
301365 class_node : Node ,
302366 module_qn : str ,
303367 resolve_to_qn : Callable [[str , str ], str ],
304368) -> list [str ]:
305369 implemented_interfaces : list [str ] = []
306370
371+ # Java: uses 'interfaces' field
307372 interfaces_node = class_node .child_by_field_name (cs .FIELD_INTERFACES )
308373 if interfaces_node :
309374 extract_java_interface_names (
310375 interfaces_node , implemented_interfaces , module_qn , resolve_to_qn
311376 )
312377
378+ # C#: uses 'base_list' child node (contains both base class and interfaces)
379+ for child in class_node .children :
380+ if child .type == cs .TS_CS_BASE_LIST :
381+ extract_csharp_interface_names (
382+ child , implemented_interfaces , module_qn , resolve_to_qn
383+ )
384+ break
385+
313386 return implemented_interfaces
314387
315388
@@ -325,3 +398,59 @@ def extract_java_interface_names(
325398 if type_child .type == cs .TS_TYPE_IDENTIFIER and type_child .text :
326399 if interface_name := safe_decode_text (type_child ):
327400 interface_list .append (resolve_to_qn (interface_name , module_qn ))
401+
402+
403+ def extract_csharp_interface_names (
404+ base_list_node : Node ,
405+ interface_list : list [str ],
406+ module_qn : str ,
407+ resolve_to_qn : Callable [[str , str ], str ],
408+ ) -> None :
409+ """Extract interface names from C# base_list.
410+
411+ In C#, base_list contains both base class and interfaces:
412+ - class Foo : BaseClass, IInterface1, IInterface2
413+ - By convention, interfaces start with 'I'
414+ - We extract names starting with 'I' as interfaces
415+ """
416+ for child in base_list_node .children :
417+ if child .type == cs .TS_IDENTIFIER and child .text :
418+ if name := safe_decode_text (child ):
419+ # C# convention: interfaces start with 'I' followed by uppercase
420+ if len (name ) > 1 and name .startswith ("I" ) and name [1 ].isupper ():
421+ interface_list .append (resolve_to_qn (name , module_qn ))
422+ logger .debug (logs .CLASS_FOUND_INTERFACE .format (name = name , qn = resolve_to_qn (name , module_qn )))
423+
424+
425+ def extract_csharp_base_class (
426+ class_node : Node ,
427+ module_qn : str ,
428+ resolve_to_qn : Callable [[str , str ], str ],
429+ ) -> list [str ]:
430+ """Extract base class name from C# class declaration.
431+
432+ In C#, base_list contains both base class and interfaces:
433+ - class Foo : BaseClass, IInterface1, IInterface2
434+ - The base class is the first non-interface identifier
435+ - By convention, interfaces start with 'I' followed by uppercase
436+ """
437+ parent_classes : list [str ] = []
438+
439+ for child in class_node .children :
440+ if child .type == cs .TS_CS_BASE_LIST :
441+ for subchild in child .children :
442+ if subchild .type == cs .TS_IDENTIFIER and subchild .text :
443+ if name := safe_decode_text (subchild ):
444+ # Skip interfaces (start with 'I' followed by uppercase)
445+ if len (name ) > 1 and name .startswith ("I" ) and name [1 ].isupper ():
446+ continue
447+ # This is a base class
448+ parent_classes .append (resolve_to_qn (name , module_qn ))
449+ logger .debug (
450+ logs .CLASS_FOUND_BASE_CLASS .format (
451+ name = name , qn = resolve_to_qn (name , module_qn )
452+ )
453+ )
454+ break
455+
456+ return parent_classes
0 commit comments