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
5 changes: 5 additions & 0 deletions .changeset/yellow-cycles-grow.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"hardhat": patch
---

Fix remappings duplication
Original file line number Diff line number Diff line change
Expand Up @@ -205,13 +205,15 @@ export class DependencyGraphImplementation implements DependencyGraph {
}

public getAllRemappings(): readonly string[] {
return this.#dependenciesMap
.values()
.flatMap((dependencies) =>
dependencies.values().flatMap((remappings) => remappings.values()),
)
.toArray()
.sort();
return [
...new Set(
this.#dependenciesMap
.values()
.flatMap((dependencies) =>
dependencies.values().flatMap((remappings) => remappings.values()),
),
),
].sort();
}

public toJSON(): DependencyGraphImplementationJson {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.31;

library C {
function value() internal pure returns (uint256) {
return 3;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.31;

library A {
function value() internal pure returns (uint256) {
return 1;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.31;

library B {
function value() internal pure returns (uint256) {
return 2;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.31;

import {A} from "@dup/A.sol";

contract ImportA {
function value() external pure returns (uint256) {
return A.value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.31;

import {A} from "@dup/A.sol";
import {B} from "@dup/B.sol";

contract ImportAB {
function value() external pure returns (uint256) {
return A.value() + B.value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.31;

import {B} from "@dup/B.sol";

contract ImportB {
function value() external pure returns (uint256) {
return B.value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.31;

import {C} from "@alt/C.sol";

contract ImportC {
function value() external pure returns (uint256) {
return C.value();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { HardhatUserConfig } from "../../../src/types/config.js";

const config: HardhatUserConfig = {
solidity: {
version: "0.8.31",
},
};

export default config;
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"name": "hardhat-duplicate-remappings-regression",
"private": true,
"type": "module"
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
@dup/=contracts/
@dup/=contracts/
@alt/=contracts-alt/
Original file line number Diff line number Diff line change
Expand Up @@ -713,5 +713,43 @@ describe("DependencyGraphImplementation", () => {

assert.deepEqual(mergedGraph.toJSON(), expectedJson);
});

it("should deduplicate remappings across edges after merge", () => {
const root1 = createProjectResolvedFile("root1.sol");
const root2 = createProjectResolvedFile("root2.sol");
const dep = createProjectResolvedFile("dep.sol");

dependencyGraph.addRootFile(root1.inputSourceName, root1);
dependencyGraph.addDependency(root1, dep, "r1");

const otherDependencyGraph = new DependencyGraphImplementation();
otherDependencyGraph.addRootFile(root2.inputSourceName, root2);
otherDependencyGraph.addDependency(root2, dep, "r1");

const mergedGraph = dependencyGraph.merge(otherDependencyGraph);

assert.deepEqual(mergedGraph.getAllRemappings(), ["r1"]);
});

it("should deduplicate remappings in the same edge after merge", () => {
const root1 = createProjectResolvedFile("root1.sol");
const dep = createProjectResolvedFile("dep.sol");

dependencyGraph.addRootFile(root1.inputSourceName, root1);
dependencyGraph.addDependency(root1, dep, "r1");

const otherDependencyGraph = new DependencyGraphImplementation();
otherDependencyGraph.addRootFile(root1.inputSourceName, root1);
otherDependencyGraph.addDependency(root1, dep, "r1");

const mergedGraph = dependencyGraph.merge(otherDependencyGraph);

const dependencies = mergedGraph.getDependencies(root1);
assert.equal(dependencies.size, 1);

const dependency = [...dependencies.values()][0];
assert.equal(dependency.file, dep);
assert.deepEqual([...dependency.remappings], ["r1"]);
});
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
import type { HardhatRuntimeEnvironment } from "../../../../../../src/types/hre.js";
import type { GetCompilationJobsResult } from "../../../../../../src/types/solidity.js";

import assert from "node:assert/strict";
import path from "node:path";
import { before, describe, it } from "node:test";

import { useFixtureProject } from "@nomicfoundation/hardhat-test-utils";

import {
createHardhatRuntimeEnvironment,
importUserConfig,
resolveHardhatConfigPath,
} from "../../../../../../src/hre.js";

const DUP_REMAPPING = "project/:@dup/=project/contracts/";
const ALT_REMAPPING = "project/:@alt/=project/contracts-alt/";

async function getCompilationJobs(
hre: HardhatRuntimeEnvironment,
rootFileNames: string[],
options = {},
) {
const allRootFiles = await hre.solidity.getRootFilePaths();
const selectedRootFiles = rootFileNames.map((name) => {
const match = allRootFiles.find((f) => f.endsWith(`${path.sep}${name}`));
assert.ok(match !== undefined, `Root file not found: ${name}`);
Comment thread
alcuadrado marked this conversation as resolved.
return match;
});

const result = await hre.solidity.getCompilationJobs(selectedRootFiles, {
force: true,
...options,
});

assert.equal(result.success, true);

return result;
}

function getUniqueJobs(compilationJobs: GetCompilationJobsResult) {
return [...new Set(compilationJobs.compilationJobsPerFile.values())];
}

describe("Solidity build system regression tests: no duplicated remappings", () => {
useFixtureProject("no-duplicated-remappings");
let hre: HardhatRuntimeEnvironment;
before(async () => {
const configPath = await resolveHardhatConfigPath();
const config = await importUserConfig(configPath);
hre = await createHardhatRuntimeEnvironment(config, { config: configPath });
});

it("should not duplicate remappings from duplicate remappings.txt entries", async () => {
const jobs = getUniqueJobs(await getCompilationJobs(hre, ["ImportA.sol"]));

assert.equal(jobs.length, 1);

const solcInput = await jobs[0].getSolcInput();
assert.deepEqual(solcInput.settings.remappings, [DUP_REMAPPING]);
});

it("should deduplicate remappings across edges within a single graph", async () => {
const jobs = getUniqueJobs(await getCompilationJobs(hre, ["ImportAB.sol"]));

assert.equal(jobs.length, 1);

const solcInput = await jobs[0].getSolcInput();
assert.deepEqual(solcInput.settings.remappings, [DUP_REMAPPING]);
});

it("should deduplicate remappings when merging graphs", async () => {
const jobs = getUniqueJobs(
await getCompilationJobs(hre, ["ImportA.sol", "ImportB.sol"]),
);

assert.equal(jobs.length, 1);

const solcInput = await jobs[0].getSolcInput();
assert.deepEqual(solcInput.settings.remappings, [DUP_REMAPPING]);
});

it("should not duplicate remappings in isolated mode", async () => {
const jobs = getUniqueJobs(
await getCompilationJobs(hre, ["ImportA.sol", "ImportB.sol"], {
buildProfile: "production",
}),
);

assert.equal(jobs.length, 2);

for (const job of jobs) {
const solcInput = await job.getSolcInput();
assert.deepEqual(solcInput.settings.remappings, [DUP_REMAPPING]);
}
});

it("should preserve distinct remappings when merging graphs", async () => {
const jobs = getUniqueJobs(
await getCompilationJobs(hre, ["ImportA.sol", "ImportC.sol"]),
);

assert.equal(jobs.length, 1);

const solcInput = await jobs[0].getSolcInput();
assert.deepEqual(solcInput.settings.remappings, [
ALT_REMAPPING,
DUP_REMAPPING,
]);
});
});
Loading