Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Imports sorting adds newlines in groups #7944

Open
2 tasks done
beeb opened this issue May 18, 2024 · 3 comments
Open
2 tasks done

Imports sorting adds newlines in groups #7944

beeb opened this issue May 18, 2024 · 3 comments
Labels
T-bug Type: bug

Comments

@beeb
Copy link
Contributor

beeb commented May 18, 2024

Component

Forge

Have you ensured that all of these are up to date?

  • Foundry
  • Foundryup

What version of Foundry are you on?

master

What command(s) is the bug in?

forge fmt

Operating System

Linux

Describe the bug

Imports sorting sometimes adds empty lines in the middle of a group. Repro:

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import { ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol"`;

Output:

import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";

import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

As you can see, there is an empty line before the last element.

I dug a bit into the formatter code and I noticed that the Loc::File offsets are not updated when re-ordering the SourceUnitParts in:

import_directives.sort_by_cached_key(|item| match item {
SourceUnitPart::ImportDirective(import) => match import {
Import::Plain(path, _) => path.to_string(),
Import::GlobalSymbol(path, _, _) => path.to_string(),
Import::Rename(path, _, _) => path.to_string(),
},
_ => {
unreachable!("import group contains non-import statement")
}
});

Since the rest of the formatter makes heavy use of those locations, I assumed they are important.

I attempted a dirty fix by re-writing the Loc start and end offsets according to the new ordering and that seems to fix the bug described above, but my dirty fix does not take into account the spacing between the various SourceUnitParts (due to semicolon and line break, or a potential comment on the same line).

// Before sorting
let group_start_loc = import_directives[0].loc();
// After sorting
let mut offset = group_start_loc.start();
for source_unit_part in import_directives.iter_mut() {
    let loc = source_unit_part.loc();
    let len = loc.end() - loc.start();
    match source_unit_part {
        SourceUnitPart::ImportDirective(import) => match import {
            Import::Plain(_, loc) => {
                *loc = loc.with_start(offset).with_end(offset + len);
            }
            Import::GlobalSymbol(_, _, loc) => {
                *loc = loc.with_start(offset).with_end(offset + len);
            }
            Import::Rename(_, _, loc) => {
                *loc = loc.with_start(offset).with_end(offset + len);
            }
        },
        _ => {
            unreachable!("import group contains non-import statement")
        }
    }
    offset += len;
}

Maybe someone smarter can create a better fix? Also, I haven't even attempted to update the location of the renames in the Import::Rename variant, but that should also probably be done?

@beeb beeb added the T-bug Type: bug label May 18, 2024
@beeb
Copy link
Contributor Author

beeb commented May 18, 2024

Pinging @meetmangukiya who authored #5442 and @mattsse who contributed too

@beeb
Copy link
Contributor Author

beeb commented May 18, 2024

Another consequence of this is that any comment at the end of an import statement line (not on a separate line) is moved to a random position after formatting. (e.g. in my case on the pragma line a few lines above).

Original:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20;

import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";
import { ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol"; // test
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";

Formatted:

// SPDX-License-Identifier: MIT
pragma solidity 0.8.20; // test

import { AccessControl } from "@openzeppelin/contracts/access/AccessControl.sol";
import { ERC20 } from "@openzeppelin/contracts/token/ERC20/ERC20.sol";
import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { ERC20Burnable } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Burnable.sol";
import { ERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/ERC20Permit.sol";

import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";

@ArkFunds
Copy link

ArkFunds commented May 19, 2024 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
T-bug Type: bug
Projects
None yet
Development

No branches or pull requests

2 participants