Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Dependency Analysis

The dependency analysis system tracks relationships between types, methods, and other symbols in C# code to identify coupling, circular dependencies, and architectural issues.


Overview

Location: src/bsharp_analysis/src/artifacts/dependencies.rs

The dependency analysis builds a directed graph of symbol relationships, where:

  • Nodes represent symbols (classes, interfaces, methods, etc.)
  • Edges represent dependencies (inheritance, method calls, field types, etc.)

Dependency Types

Type Dependencies

Inheritance:

public class Derived : Base { }  // Derived depends on Base

Interface Implementation:

public class MyClass : IInterface { }  // MyClass depends on IInterface

Field Types:

public class Container {
    private Helper helper;  // Container depends on Helper
}

Method Parameters and Return Types:

public Response Process(Request req) { }  // Process depends on Request and Response

Member Dependencies

Method Calls:

public void Caller() {
    Helper.DoSomething();  // Caller depends on Helper.DoSomething
}

Property Access:

var value = obj.Property;  // Depends on Property

Constructor Calls:

var instance = new MyClass();  // Depends on MyClass constructor

Dependency Graph Structure

DependencyGraph

#![allow(unused)]
fn main() {
pub struct DependencyGraph {
    // Symbol ID -> list of symbols it depends on
    dependencies: HashMap<SymbolId, Vec<SymbolId>>,
}
}

Operations

Adding Dependencies:

#![allow(unused)]
fn main() {
graph.add_dependency(from_symbol, to_symbol);
}

Querying Dependencies:

#![allow(unused)]
fn main() {
// Direct dependencies
let deps = graph.get_dependencies(symbol_id);

// Transitive dependencies
let all_deps = graph.get_transitive_dependencies(symbol_id);

// Reverse dependencies (who depends on this symbol)
let dependents = graph.get_dependents(symbol_id);
}

Circular Dependency Detection

Algorithm

The analysis uses depth-first search to detect cycles in the dependency graph:

  1. Start from each symbol
  2. Traverse dependencies depth-first
  3. Track visited nodes in current path
  4. If we revisit a node in current path, cycle detected

Example

public class A {
    private B b;  // A depends on B
}

public class B {
    private C c;  // B depends on C
}

public class C {
    private A a;  // C depends on A -> CYCLE: A -> B -> C -> A
}

Detection:

#![allow(unused)]
fn main() {
let cycles = graph.find_cycles();
for cycle in cycles {
    // Report diagnostic for circular dependency
    session.diagnostics.add(
        DiagnosticCode::CircularDependency,
        format!("Circular dependency detected: {:?}", cycle)
    );
}
}

Coupling Metrics

Afferent Coupling (Ca)

Definition: Number of types that depend on this type (incoming dependencies)

Interpretation:

  • High Ca = Many types depend on this type (responsibility)
  • Type is stable and hard to change

Efferent Coupling (Ce)

Definition: Number of types this type depends on (outgoing dependencies)

Interpretation:

  • High Ce = This type depends on many others
  • Type is unstable and sensitive to changes

Instability (I)

Formula: I = Ce / (Ca + Ce)

Range: 0.0 to 1.0

  • 0.0 = Maximally stable (only incoming dependencies)
  • 1.0 = Maximally unstable (only outgoing dependencies)

Example:

#![allow(unused)]
fn main() {
let ca = graph.afferent_coupling(symbol_id);
let ce = graph.efferent_coupling(symbol_id);
let instability = ce as f64 / (ca + ce) as f64;

if instability > 0.8 {
    // Highly unstable type - consider refactoring
}
}

Dependency Summary

DependencySummary

#![allow(unused)]
fn main() {
pub struct DependencySummary {
    pub total_nodes: usize,
    pub total_edges: usize,
    pub circular_dependencies: usize,
    pub max_depth: usize,
}
}

Generated by: DependencyGraph::summarize()

Included in: AnalysisReport


Usage in Analysis Pipeline

Phase: Global

Dependency analysis runs in the Global phase after symbol indexing:

#![allow(unused)]
fn main() {
// In AnalyzerPipeline
Phase::Index   -> Build SymbolIndex
Phase::Global  -> Build DependencyGraph
Phase::Semantic -> Use dependencies for semantic analysis
}

Integration with Passes

DependencyPass (if implemented):

#![allow(unused)]
fn main() {
impl AnalyzerPass for DependencyPass {
    fn id(&self) -> &'static str { "dependencies" }
    fn phase(&self) -> Phase { Phase::Global }
    
    fn run(&self, cu: &CompilationUnit, session: &mut AnalysisSession) {
        let graph = build_dependency_graph(cu, &session.artifacts.symbols);
        session.artifacts.dependencies = Some(graph);
    }
}
}

Building Dependency Graph

From CompilationUnit

#![allow(unused)]
fn main() {
pub fn build_dependency_graph(
    cu: &CompilationUnit,
    symbols: &SymbolIndex
) -> DependencyGraph {
    let mut graph = DependencyGraph::new();
    
    // Visit all declarations
    for decl in &cu.declarations {
        match decl {
            TopLevelDeclaration::Class(class) => {
                analyze_class_dependencies(class, symbols, &mut graph);
            }
            // ... other declaration types
        }
    }
    
    graph
}
}

From Class Declaration

#![allow(unused)]
fn main() {
fn analyze_class_dependencies(
    class: &ClassDeclaration,
    symbols: &SymbolIndex,
    graph: &mut DependencyGraph
) {
    let class_symbol = symbols.lookup(&class.identifier.name);
    
    // Base types
    for base_type in &class.base_types {
        if let Some(base_symbol) = resolve_type(base_type, symbols) {
            graph.add_dependency(class_symbol, base_symbol);
        }
    }
    
    // Members
    for member in &class.body_declarations {
        analyze_member_dependencies(member, class_symbol, symbols, graph);
    }
}
}

Dependency Visualization

Dependency Matrix

Generate a matrix showing which types depend on which:

        A  B  C  D
    A   -  X  -  X
    B   -  -  X  -
    C   X  -  -  -
    D   -  -  -  -
  • Row A, Column B = X means A depends on B

Dependency Tree

MyApp
├── Services
│   ├── UserService
│   │   ├── IUserRepository
│   │   └── IEmailService
│   └── OrderService
│       ├── IOrderRepository
│       └── IPaymentService
└── Models
    ├── User
    └── Order

Diagnostics

Circular Dependency Warning

warning[DEP001]: Circular dependency detected
  --> src/ClassA.cs:3:5
   |
 3 |     private ClassB b;
   |             ^^^^^^ ClassA depends on ClassB
   |
   = note: Dependency cycle: ClassA -> ClassB -> ClassC -> ClassA

High Coupling Warning

warning[DEP002]: High efferent coupling detected
  --> src/GodClass.cs:1:14
   |
 1 | public class GodClass {
   |              ^^^^^^^^ depends on 25 other types
   |
   = help: Consider breaking this class into smaller, focused classes

Unstable Dependency Warning

warning[DEP003]: Stable type depends on unstable type
  --> src/StableClass.cs:5:5
   |
 5 |     private UnstableClass helper;
   |             ^^^^^^^^^^^^^ instability = 0.95
   |
   = note: Stable types (instability < 0.2) should not depend on unstable types (instability > 0.8)

Configuration

Thresholds

[analysis.dependencies]
max_efferent_coupling = 20
max_afferent_coupling = 10
max_instability = 0.8
warn_circular_dependencies = true

CLI Usage

# Analyze dependencies
bsharp analyze MyProject.csproj --enable-pass dependencies

# Generate dependency report
bsharp analyze MyProject.sln --out deps.json --format pretty-json

Future Enhancements

Planned Features

  1. Package-Level Dependencies

    • Track dependencies between namespaces/assemblies
    • Identify layering violations
  2. Dependency Metrics Dashboard

    • Visual dependency graphs
    • Coupling heatmaps
    • Trend analysis over time
  3. Architectural Rules

    • Define allowed/forbidden dependencies
    • Enforce layered architecture
    • Prevent specific coupling patterns
  4. Dependency Injection Analysis

    • Track DI container registrations
    • Verify dependency lifetimes
    • Detect missing registrations

Implementation Status

Current State:

  • Basic dependency graph structure defined
  • Integration with analysis pipeline planned
  • Circular dependency detection algorithm ready

TODO:

  • Implement full dependency extraction from AST
  • Add coupling metrics calculation
  • Create dependency visualization tools
  • Add comprehensive tests


References

  • Implementation: src/bsharp_analysis/src/artifacts/dependencies.rs
  • Tests: src/bsharp_tests/src/analysis/dependencies/ (planned)
  • Related Passes: src/bsharp_analysis/src/passes/ (when implemented)
2025-11-17 15:18:26 • commit: 03a4e25