pub trait LanguageSemantics<M: Default + Clone + PartialEq = ()> {
Show 19 methods
// Required methods
fn is_member_addition_breaking(
&self,
container: &Symbol<M>,
member: &Symbol<M>,
) -> bool;
fn same_family(&self, a: &Symbol<M>, b: &Symbol<M>) -> bool;
fn same_identity(&self, a: &Symbol<M>, b: &Symbol<M>) -> bool;
fn visibility_rank(&self, v: Visibility) -> u8;
// Provided methods
fn parse_union_values(&self, _type_str: &str) -> Option<BTreeSet<String>> { ... }
fn is_async_wrapper(&self, _type_str: &str) -> bool { ... }
fn format_import_change(
&self,
symbol: &str,
old_path: &str,
new_path: &str,
) -> String { ... }
fn should_skip_symbol(&self, _sym: &Symbol<M>) -> bool { ... }
fn member_label(&self) -> &'static str { ... }
fn extract_rename_fallback_key(&self, _sym: &Symbol<M>) -> Option<String> { ... }
fn canonical_name_for_relocation(&self, qualified_name: &str) -> String { ... }
fn classify_relocation(
&self,
_old_qname: &str,
_new_qname: &str,
) -> Option<&'static str> { ... }
fn derive_import_subpath(
&self,
package: Option<&str>,
_qualified_name: &str,
) -> String { ... }
fn diff_language_data(
&self,
_old: &Symbol<M>,
_new: &Symbol<M>,
) -> Vec<StructuralChange> { ... }
fn post_process(&self, _changes: &mut Vec<StructuralChange>) { ... }
fn hierarchy(&self) -> Option<&dyn HierarchySemantics<M>> { ... }
fn renames(&self) -> Option<&dyn RenameSemantics> { ... }
fn body_analyzer(&self) -> Option<&dyn BodyAnalysisSemantics> { ... }
fn primitive_type_names(&self) -> &[&str] { ... }
}Expand description
Language-specific semantic rules consumed by the diff engine.
These encode the places where “is this breaking?” or “are these related?” differ fundamentally by language. The diff engine calls these methods instead of hardcoding language-specific rules.
Required Methods§
Sourcefn is_member_addition_breaking(
&self,
container: &Symbol<M>,
member: &Symbol<M>,
) -> bool
fn is_member_addition_breaking( &self, container: &Symbol<M>, member: &Symbol<M>, ) -> bool
Is adding this member to this container a breaking change?
This is the single rule that differs most fundamentally by language:
- TypeScript: breaking only if the member is required (non-optional).
- Go: ALWAYS breaking for interfaces (all implementors must add it).
- Java: breaking for abstract methods, not for default methods.
- C#: breaking for abstract members on interfaces.
- Python: breaking for abstract methods on Protocol/ABC.
Sourcefn same_family(&self, a: &Symbol<M>, b: &Symbol<M>) -> bool
fn same_family(&self, a: &Symbol<M>, b: &Symbol<M>) -> bool
Are these two symbols part of the same logical family/group?
Used to scope migration detection. When a symbol is removed, only symbols in the same family are considered as potential absorption targets.
- TypeScript/React: same component directory
- Go: same package
- Java: same package
- Python: same module
Sourcefn same_identity(&self, a: &Symbol<M>, b: &Symbol<M>) -> bool
fn same_identity(&self, a: &Symbol<M>, b: &Symbol<M>) -> bool
Are these two symbols the same concept, possibly at different paths?
When true, migration detection does a full member comparison (all members, not just newly-added ones) because the candidate is assumed to be a direct replacement for the removed symbol.
Resolves companion types linked by naming convention:
- TypeScript:
ButtonandButtonProps(component + its props interface) - Go:
ClientandClientOptions(struct + its configuration) - Java:
UserServiceandUserServiceImpl(interface + implementation)
Sourcefn visibility_rank(&self, v: Visibility) -> u8
fn visibility_rank(&self, v: Visibility) -> u8
Numeric rank for a visibility level (higher = more visible).
Used to determine if visibility was reduced (breaking) or increased. The ordering differs by language:
- TypeScript: Private(0) < Internal(1) < Protected(1) < Public(2) < Exported(3)
- Java: Private(0) < PackagePrivate(1) < Protected(2) < Public(3)
- Go: Internal(0) < Exported(1)
Provided Methods§
Sourcefn parse_union_values(&self, _type_str: &str) -> Option<BTreeSet<String>>
fn parse_union_values(&self, _type_str: &str) -> Option<BTreeSet<String>>
Parse union/constrained type values for fine-grained diffing.
TypeScript: parse 'primary' | 'secondary' | 'danger'.
Python: parse Literal['a', 'b'].
Most other languages return None.
Sourcefn is_async_wrapper(&self, _type_str: &str) -> bool
fn is_async_wrapper(&self, _type_str: &str) -> bool
Whether a return type string represents an async wrapper.
Used by the diff engine to detect sync→async and async→sync changes, which are always breaking regardless of the inner type.
TypeScript/JavaScript: Promise<T>
Python: Coroutine[...], Awaitable[...]
Java: CompletableFuture<T>, Future<T>
Go: returns false (async handled via goroutines, not return types)
Sourcefn format_import_change(
&self,
symbol: &str,
old_path: &str,
new_path: &str,
) -> String
fn format_import_change( &self, symbol: &str, old_path: &str, new_path: &str, ) -> String
Format an import/use statement change hint for migration descriptions.
When a symbol is renamed across packages, the diff engine includes import guidance so consumers know to update their import paths.
TypeScript: "replace \import { X } from ‘old-pkg’` with `import { X } from ‘new-pkg’`“Go:“replace `"old/pkg"` with `"new/pkg"`”`
Default: generic format without language-specific syntax.
Sourcefn should_skip_symbol(&self, _sym: &Symbol<M>) -> bool
fn should_skip_symbol(&self, _sym: &Symbol<M>) -> bool
Should this symbol be excluded from diff analysis?
Called by the diff engine to filter out symbols that should not be
compared. The most common case is TypeScript’s export * from '...'
star re-export directives.
TypeScript: sym.name == "*" (star re-exports)
Default: false (all symbols are analyzed)
Sourcefn member_label(&self) -> &'static str
fn member_label(&self) -> &'static str
Human-readable label for members when building migration descriptions.
TypeScript: "props" (component properties)
Go: "fields" (struct fields)
Default: "members"
Sourcefn extract_rename_fallback_key(&self, _sym: &Symbol<M>) -> Option<String>
fn extract_rename_fallback_key(&self, _sym: &Symbol<M>) -> Option<String>
Extract a fallback key for rename matching from a symbol’s metadata.
When fingerprint-based rename detection fails, the diff engine uses
this method to extract an alternative matching key. For TypeScript
CSS tokens, this parses the resolved CSS value from the .d.ts
type annotation (e.g., the string "#151515" from a CSS variable).
TypeScript: parses ["value"]: "..." from the return type annotation
Default: None (no fallback key)
Sourcefn canonical_name_for_relocation(&self, qualified_name: &str) -> String
fn canonical_name_for_relocation(&self, qualified_name: &str) -> String
Normalize a qualified name for relocation detection.
Strips language-specific path segments that represent lifecycle
modifiers (e.g., TypeScript’s /deprecated/ and /next/ directories).
Symbols with matching canonical names are detected as relocations
rather than separate removals and additions.
TypeScript: strips /deprecated/ and /next/ segments
Default: returns the name unchanged
Sourcefn classify_relocation(
&self,
_old_qname: &str,
_new_qname: &str,
) -> Option<&'static str>
fn classify_relocation( &self, _old_qname: &str, _new_qname: &str, ) -> Option<&'static str>
Classify a relocation based on old and new qualified names.
Returns a human-readable label describing the relocation direction
(e.g., “moved to deprecated exports”, “promoted from next to stable”).
Returns None for generic relocations with no special classification.
TypeScript: detects /deprecated/ and /next/ transitions
Default: None (no classification)
Sourcefn derive_import_subpath(
&self,
package: Option<&str>,
_qualified_name: &str,
) -> String
fn derive_import_subpath( &self, package: Option<&str>, _qualified_name: &str, ) -> String
Derive the import subpath for a symbol, used in migration descriptions.
When a symbol moves between submodules (e.g., from main exports to
/deprecated/ exports), the import path changes. This method derives
the effective import path from the package name and qualified name.
TypeScript: appends /deprecated or /next based on qualified name
Default: returns the package name unchanged
Sourcefn diff_language_data(
&self,
_old: &Symbol<M>,
_new: &Symbol<M>,
) -> Vec<StructuralChange>
fn diff_language_data( &self, _old: &Symbol<M>, _new: &Symbol<M>, ) -> Vec<StructuralChange>
Produce additional structural changes by diffing language-specific metadata on two matched symbols.
Called by the diff engine for each pair of symbols that matched by qualified name. The default implementation returns no changes.
TypeScript: could diff rendered_components or css metadata.
Default: empty (no language-specific metadata diffing)
Sourcefn post_process(&self, _changes: &mut Vec<StructuralChange>)
fn post_process(&self, _changes: &mut Vec<StructuralChange>)
Post-process the change list before returning from diff_surfaces.
TypeScript: dedup default export changes. Most languages: no-op.
Sourcefn hierarchy(&self) -> Option<&dyn HierarchySemantics<M>>
fn hierarchy(&self) -> Option<&dyn HierarchySemantics<M>>
If this language supports component hierarchy inference (e.g., React, Vue, Django templates), return the hierarchy semantics implementation.
The orchestrator uses this to prepare data for LLM hierarchy inference. The trait is NOT responsible for LLM calls or prompt construction.
Sourcefn renames(&self) -> Option<&dyn RenameSemantics>
fn renames(&self) -> Option<&dyn RenameSemantics>
If this language supports LLM-based rename inference (e.g., CSS physical→logical property renames, interface rename mappings), return the rename semantics implementation.
The orchestrator uses this to prepare data for LLM rename inference. The trait is NOT responsible for LLM calls or prompt construction.
Sourcefn body_analyzer(&self) -> Option<&dyn BodyAnalysisSemantics>
fn body_analyzer(&self) -> Option<&dyn BodyAnalysisSemantics>
If this language has deterministic body-level analysis (e.g., JSX diff, CSS variable scanning for TypeScript), return the body analysis implementation.
The orchestrator calls this during BU Phase 1 to detect behavioral breaks from function body changes without LLM assistance.
Sourcefn primitive_type_names(&self) -> &[&str]
fn primitive_type_names(&self) -> &[&str]
Primitive type names for this language, used by the diff engine’s structural similarity comparison.
When two types are compared for structural similarity (e.g., during rename detection), types matching these names are classified as primitives. Two primitives of different names are structurally similar (both are scalars), whereas a primitive vs a reference type is not.
Default: common cross-language primitives (string, number, boolean,
void, null). Languages should override to add their own (e.g.,
TypeScript adds undefined, never, any, unknown; Java adds
int, long, double, float, char, byte, short).