Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 20 additions & 5 deletions src/metricsAnalyzer/languages/rustAnalyzer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,12 +180,27 @@ export class RustMetricsAnalyzer {
if (parent && parent.type === "declaration_list") {
const implNode = parent.parent;
if (implNode && implNode.type === "impl_item") {
const typeNode = implNode.children.find(
(child) =>
child.type === "type_identifier" ||
child.type === "generic_type" ||
child.type === "scoped_type_identifier"
// For `impl Trait for Type`, prefer the implementing type (after `for`).
// For inherent `impl Type`, use the only type present.
const forIndex = implNode.children.findIndex(
(child) => child.type === "for"
);
const typeNode =
forIndex !== -1
? implNode.children
.slice(forIndex + 1)
.find(
(child) =>
child.type === "type_identifier" ||
child.type === "generic_type" ||
child.type === "scoped_type_identifier"
)
: implNode.children.find(
(child) =>
child.type === "type_identifier" ||
child.type === "generic_type" ||
child.type === "scoped_type_identifier"
);
if (typeNode) {
const typeName = this.sourceText.substring(
typeNode.startIndex,
Expand Down
19 changes: 19 additions & 0 deletions src/test/metricsAnalyzer/languages/rustAnalyzer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,25 @@ impl Counter {
assert.strictEqual(results[0].complexity, 0);
});

test("should use implementing type name for trait impl (impl Trait for Type)", () => {
const sourceCode = `
use std::fmt;

struct Point { x: f64, y: f64 }

impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
`;
const results = analyzer.analyzeFunctions(sourceCode);

assert.strictEqual(results.length, 1);
// Should use implementing type "Point", not trait "Display"
assert.strictEqual(results[0].name, "Point::fmt");
});

test("should analyze impl method with complexity", () => {
const sourceCode = `
struct Validator { max: i32 }
Expand Down
60 changes: 60 additions & 0 deletions src/unit/unit.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1598,6 +1598,66 @@ fn loops() {
)
);
});

it("should use implementing type name for trait impl (impl Trait for Type)", () => {
const sourceCode = `
use std::fmt;

struct Point { x: f64, y: f64 }

impl fmt::Display for Point {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "({}, {})", self.x, self.y)
}
}
`;
const results = RustMetricsAnalyzer.analyzeFile(sourceCode);
assert.strictEqual(results.length, 1);
// Should use implementing type "Point", not trait "Display"
assert.strictEqual(results[0].name, "Point::fmt");
});

it("should use inherent impl type name when no trait (impl Type)", () => {
const sourceCode = `
struct Rectangle { width: f64, height: f64 }

impl Rectangle {
fn area(&self) -> f64 {
self.width * self.height
}
}
`;
const results = RustMetricsAnalyzer.analyzeFile(sourceCode);
assert.strictEqual(results.length, 1);
assert.strictEqual(results[0].name, "Rectangle::area");
});

it("should handle multiple trait impl methods independently", () => {
const sourceCode = `
use std::fmt;

struct Counter { value: i32 }

impl fmt::Display for Counter {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}

impl Counter {
fn increment(&mut self) {
if self.value < i32::MAX {
self.value += 1;
}
}
}
`;
const results = RustMetricsAnalyzer.analyzeFile(sourceCode);
assert.strictEqual(results.length, 2);
assert.strictEqual(results[0].name, "Counter::fmt");
assert.strictEqual(results[1].name, "Counter::increment");
assert.strictEqual(results[1].complexity, 1); // single if
});
});

describe("Go Analyzer Additional Coverage", () => {
Expand Down
Loading