Skip to content

fix(csharp): classify interface base types as inherits, not implements#1817

Open
Synvoya wants to merge 1 commit into
Graphify-Labs:v8from
Synvoya:fix-csharp-interface-inherits
Open

fix(csharp): classify interface base types as inherits, not implements#1817
Synvoya wants to merge 1 commit into
Graphify-Labs:v8from
Synvoya:fix-csharp-interface-inherits

Conversation

@Synvoya

@Synvoya Synvoya commented Jul 12, 2026

Copy link
Copy Markdown
Contributor

Summary

A C# interface's base_list holds base interfaces, so extending another
interface is interface inheritance, not implementation. The C# base_list
emitter in _extract_generic classified every base-list entry with the
name-based class-vs-interface heuristic (_csharp_classify_base), so an
interface extending another interface produced an implements edge.

This diverges from the Java extractor in the same engine, which emits
inherits for extends_interfaces. The result is an inconsistent type
hierarchy: traversing inherits edges misses C# interface hierarchies that
it would capture for Java.

Reproduction (v8 HEAD)

public interface IBase {}
public interface IDerived : IBase {}
public class Impl : IBase {}
inherits:    <none>
implements:  IDerived -> IBase      # wrong: interface extension is inheritance
             Impl     -> IBase      # correct

The equivalent Java (interface IDerived extends IBase) already yields
IDerived --inherits--> IBase.

Fix

When the declaring node is an interface_declaration, classify each base as
inherits (all interface base-list entries are inherited base interfaces).
Class/struct/record declarations keep the existing name-based split — the
first non-interface base stays inherits, interface bases stay implements
— so implements now means only a class/struct/record realizing an
interface, matching the other language extractors.

Base extraction, generic-argument references edges, and the
ref_token/qualified/ref_qualifier metadata used by cross-file
resolution are unchanged; only the emitted relation for interface-declared
bases changes.

Testing

  • New regression test test_csharp_interface_extends_is_inherits_not_implements
    covers single- and multi-interface extension and a class implementer as a
    negative control. It fails on v8 HEAD (interface bases land in
    implements) and passes with this change.
  • tests/test_languages.py: 315 passed, 13 skipped.
  • C# suites test_csharp_type_resolution.py, test_csharp_member_calls.py,
    test_dotnet.py, test_cross_language_call_resolution.py: 89 passed.

A C# `interface`'s base_list holds base interfaces, so extending another
interface is interface *inheritance*, not implementation. The base_list
emitter classified every entry with the name-based class-vs-interface
heuristic (`_csharp_classify_base`), so `interface IDerived : IBase`
produced an `implements` edge -- diverging from the Java extractor, which
emits `inherits` for `extends_interfaces`.

Classify base types as `inherits` when the declaring node is an
`interface_declaration`; class/struct/record declarations keep the
existing name-based split (first non-interface base = inherits, interfaces
= implements). `implements` now means only a class/struct/record realizing
an interface, consistent with the rest of the extractors.

Adds a regression test covering single- and multi-interface extension plus
a class implementer as a negative control.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant