code-buster 0.11.0

Repository-aware static analysis for AI-assisted development
Documentation
#[cfg(test)]
mod tests {
    use std::collections::{BTreeMap, BTreeSet};

    use super::analyze;

    const EXPECTED: [&str; 17] = [
        "oop-data-clump",
        "oop-interface-segregation-pressure",
        "oop-refused-bequest",
        "oop-middle-man-delegation",
        "oop-single-use-abstraction",
        "oop-repeated-strategy-dispatch",
        "oop-state-behavior-candidate",
        "oop-feature-envy",
        "oop-service-locator-dependency",
        "oop-repeated-observer-notification",
        "oop-message-chain",
        "oop-factory-bypass",
        "oop-facade-bypass",
        "oop-repository-bypass",
        "oop-proxy-bypass",
        "oop-repeated-adapter-mapping",
        "oop-template-workflow-candidate",
    ];

    #[test]
    fn reports_all_shared_oop_rules_for_csharp_projects() {
        let codes = analyze(&fixture())
            .into_iter()
            .map(|finding| finding.code)
            .collect::<BTreeSet<_>>();

        assert_eq!(codes, EXPECTED.into_iter().map(str::to_owned).collect());
    }

    #[test]
    fn ignores_masked_and_below_threshold_evidence() {
        let sources = BTreeMap::from([(
            "Safe.cs".to_owned(),
            r#"
// class Fake { public void Run(string tenant, int region, bool active) { } }
class SafeGateway : IGateway {
  private readonly Client _client;
  public Result Load(Id id) => _client.Load(id);
  public Result Save(Id id) => _client.Save(id);
  public Result Delete(Id id) => _client.Delete(id);
  public Result Refresh(Id id) => _client.Refresh(id);
  public Result Inspect(Id id) => _client.Inspect(id);
  public string Example() => "throw new NotSupportedException()";
  public string RawExample() => """
    class FakeRaw {
      public void Run(string tenant, int region, bool active) { }
    }
    """;
}
class One { public void Run(string tenant, int region, bool active) { } }
class Two { public void Run(int region, string tenant, bool active) { } }
"#
            .to_owned(),
        )]);

        assert!(analyze(&sources).is_empty());
    }
    #[test]
    fn includes_top_level_typescript_functions_in_data_clumps() {
        let sources = BTreeMap::from([
            (
                "first.ts".to_owned(),
                r#"
export function createOrder(
  tenant: string,
  region: number,
  active: string | boolean,
): void {}
class Service {
  update(tenant: string, region: number, active: string | boolean): void {}
}
"#
                .to_owned(),
            ),
            (
                "second.ts".to_owned(),
                r#"
const message = `value doesn't have '${value}'`;
interface Gateway {
  send(tenant: string, region: number, active: string | boolean): void;
}
"#
                .to_owned(),
            ),
        ]);

        let findings = analyze(&sources);
        let finding = findings
            .iter()
            .find(|finding| finding.code == "oop-data-clump")
            .expect("TypeScript data clump must be reported");
        assert_eq!(finding.path, "first.ts");
        assert_eq!(finding.related_files, ["second.ts"]);
        assert_eq!(finding.line, 2);
        assert_eq!(
            finding.message,
            "Parameters tenant, region, active recur together in 3 declarations across 2 files"
        );
    }

    #[test]
    fn ignores_repeated_interface_implementation_signatures() {
        let sources = BTreeMap::from([
            (
                "contract.ts".to_owned(),
                "interface Gateway {\n  send(tenant: string, region: number, active: boolean): void;\n}\nclass Primary {\n  send(tenant: string, region: number, active: boolean): void {}\n}"
                    .to_owned(),
            ),
            (
                "fallback.ts".to_owned(),
                "class Fallback {\n  send(tenant: string, region: number, active: boolean): void {}\n}"
                    .to_owned(),
            ),
        ]);

        assert!(
            analyze(&sources)
                .iter()
                .all(|finding| finding.code != "oop-data-clump")
        );
    }

    #[test]
    fn ignores_overload_families_concentrated_in_two_type_declarations() {
        let sources = BTreeMap::from([
            (
                "Logger.cs".to_owned(),
                "class Logger {\n void Debug(string message, int eventId, bool flush) {}\n void Info(string message, int eventId, bool flush) {}\n void Error(string message, int eventId, bool flush) {}\n}"
                    .to_owned(),
            ),
            (
                "SilentLogger.cs".to_owned(),
                "class SilentLogger {\n void Debug(string message, int eventId, bool flush) {}\n void Info(string message, int eventId, bool flush) {}\n void Error(string message, int eventId, bool flush) {}\n}"
                    .to_owned(),
            ),
        ]);

        assert!(
            analyze(&sources)
                .iter()
                .all(|finding| finding.code != "oop-data-clump")
        );
    }

    #[test]
    fn does_not_combine_evidence_between_language_analyzers() {
        let sources = BTreeMap::from([
            (
                "One.cs".to_owned(),
                "class One { void Run(string tenant, int region, bool active) {} }".to_owned(),
            ),
            (
                "Two.java".to_owned(),
                "class Two { void run(String tenant, int region, boolean active) {} }".to_owned(),
            ),
            (
                "Three.ts".to_owned(),
                "class Three { run(tenant: string, region: number, active: boolean): void {} }"
                    .to_owned(),
            ),
        ]);

        assert!(
            analyze(&sources)
                .iter()
                .all(|finding| finding.code != "oop-data-clump")
        );
    }

    fn fixture() -> BTreeMap<String, String> {
        [
            (
                "Contracts.cs",
                r#"
public interface IStorage {
  void Read();
  void Write();
  void Delete();
  void Archive();
}
public interface IOne { int Run(int value); }
class OneImpl : IOne {
  public int Run(int value) => value;
}
class OneCaller {
  private OneImpl _one = new OneImpl();
}
"#,
            ),
            (
                "Cold.cs",
                r#"
class ColdStorage : IStorage {
  public void Read() { }
  public void Write() { throw new NotSupportedException(); }
  public void Delete() { throw new NotImplementedException(); }
  public void Archive() { }
}
"#,
            ),
            (
                "ReadOnly.cs",
                r#"
class ReadOnlyStorage : IStorage {
  public void Read() { }
  public void Write() { throw new NotSupportedException(); }
  public void Delete() { }
  public void Archive() { throw new NotImplementedException(); }
}
"#,
            ),
            (
                "Channel.cs",
                r#"
class Channel {
  public virtual void Open() { }
  public virtual void Close() { }
  public virtual void Send() { }
  public virtual void Receive() { }
}
"#,
            ),
            (
                "Input.cs",
                r#"
class InputChannel : Channel {
  public override void Send() { throw new NotSupportedException(); }
  public override void Close() { throw new NotImplementedException(); }
}
"#,
            ),
            (
                "Output.cs",
                r#"
class OutputChannel : Channel {
  public override void Receive() { throw new NotSupportedException(); }
  public override void Open() { throw new NotImplementedException(); }
}
"#,
            ),
            (
                "Create.cs",
                r#"
class CreateService {
  public void Create(string tenant, int region, bool active) { }
  public void Update(string tenant, int region, bool active) { }
}
"#,
            ),
            (
                "Delete.cs",
                r#"
class DeleteService {
  public void Delete(string tenant, int region, bool active) { }
}
"#,
            ),
            (
                "Gateway.cs",
                r#"
class Gateway {
  private readonly Client _client;
  public Result Load(Id id) => _client.Load(id);
  public Result Save(Id id) => _client.Save(id);
  public Result Delete(Id id) => _client.Delete(id);
  public Result Refresh(Id id) => _client.Refresh(id);
  public Result Inspect(Id id) => _client.Inspect(id);
}
"#,
            ),
            (
                "BehaviorA.cs",
                r#"
class StrategyA {
  void Run(Kind k) { switch(k) { case Kind.A: A1(); break; case Kind.B: B1(); break; case Kind.C: C1(); break; } }
}
class StrategyB {
  void Run(Kind k) { switch(k) { case Kind.A: A2(); break; case Kind.B: B2(); break; case Kind.C: C2(); break; } }
}
class Envious {
  void Use(Customer c) { c.Name(); c.Name(); c.Address(); c.Address(); c.Credit(); }
  void Locate() { Services.Resolve<Mail>(); }
}
class Chain {
  void A() { var x = root.A.B.C.D; }
  void B() { var x = root.A.B.C.D; var y = root.E.F.G.H; }
}
"#,
            ),
            (
                "BehaviorB.cs",
                r#"
class StrategyC {
  void Run(Kind k) { switch(k) { case Kind.A: A3(); break; case Kind.B: B3(); break; case Kind.C: C3(); break; } }
}
class LocatorB { void Locate() { Services.Resolve<Clock>(); } }
"#,
            ),
            (
                "BehaviorC.cs",
                "class LocatorC { void Locate() { Services.Resolve<Store>(); } }",
            ),
            (
                "State.cs",
                r#"
class Machine {
  private Status _status;
  void A() { switch(_status) { case Status.Ready: Go(); break; case Status.Busy: Wait(); break; case Status.Done: Stop(); break; } }
  void B() { switch(_status) { case Status.Ready: Go(); break; case Status.Busy: Wait(); break; case Status.Done: Stop(); break; } }
  void C() { switch(_status) { case Status.Ready: Go(); break; case Status.Busy: Wait(); break; case Status.Done: Stop(); break; } }
}
"#,
            ),
            (
                "Observer.cs",
                r#"
class Subject {
  private List<Listener> listeners;
  void Add(Listener listener) { listeners.Add(listener); }
  void Remove(Listener listener) { listeners.Remove(listener); }
  void A() { foreach (var listener in listeners) listener.Changed(); }
  void B() { foreach (var listener in listeners) listener.Changed(); }
  void C() { foreach (var listener in listeners) listener.Changed(); }
}
"#,
            ),
            (
                "Boundaries.cs",
                r#"
class WidgetFactory { Widget Make() => new Widget(); }
class OrderFacade {
  private OrderService orders;
  private PaymentGateway payments;
  private MailClient mail;
}
class OrderRepository { private OrderDatabase database; }
class OrderProxy { private AuditService service; }
"#,
            ),
            (
                "BoundaryUserA.cs",
                "class UserA { WidgetFactory factory; OrderFacade facade; OrderRepository repository; OrderProxy proxy; }",
            ),
            (
                "BoundaryUserB.cs",
                "class UserB { WidgetFactory factory; OrderFacade facade; OrderRepository repository; OrderProxy proxy; }",
            ),
            (
                "BoundaryUserC.cs",
                "class UserC { WidgetFactory factory; OrderFacade facade; OrderRepository repository; OrderProxy proxy; }",
            ),
            (
                "BoundaryBypass.cs",
                "class Bypass { Widget Make() => new Widget(); OrderService orders; PaymentGateway payments; OrderDatabase database; AuditService service; }",
            ),
            (
                "MappingA.cs",
                r#"
class MapperA {
  Target Map(Source s) => new Target { Name = s.Name, Age = s.Age, City = s.City };
}
class MapperB {
  Target Map(Source s) => new Target { Name = s.Name, Age = s.Age, City = s.City };
}
class WorkflowOne : WorkflowBase {
  public override void Run() { Open(); Read(); OneStep(); Close(); }
}
"#,
            ),
            (
                "MappingB.cs",
                r#"
class MapperC {
  Target Map(Source s) => new Target { Name = s.Name, Age = s.Age, City = s.City };
}
class WorkflowTwo : WorkflowBase {
  public override void Run() { Open(); Read(); TwoStep(); Close(); }
}
"#,
            ),
        ]
        .into_iter()
        .map(|(path, source)| (path.to_owned(), source.to_owned()))
        .collect()
    }

}