Previous form of proposal (for archive purposes only)
I propose to add a new lint for making imports in codebases more consistent, which fires when items are imported inconsistently.
For example, many items in the std::fs, std::io, std::iter, std::env and other modules are conventionally referred to via the parent path:
use std::fs;
use std::fmt;
use std::io;
fs::read("Cargo.toml")?;
impl fmt::Write for Struct { ... }
fn save_to_disk(file: &str) -> io::Result<()> { ... }
It is discouraged to import those items directly:
use std::fs::read;
use std::fmt::Write;
use std::io::Result; // shadows std::result::Result, causing confusion
read("Cargo.toml")?;
// is this io::Write or fmt::Write?
impl Write for Struct { ... }
// shadows std::result::Result, causing confusion
fn save_to_disk(file: &str) -> Result<()> {
To enforce this style, there is a new attribute #[import_style]. This attribute configures the new wrong_import_style_via_super warn-by-default lint.
The #[import_style] attribute tells how others should import your item.
For example, we can annotate fs::read with it:
[!NOTE]
All syntax in this proposal is just placeholder.
// std::fs
#[import_style(via = super)]
fn read() {}
When someone imports read directly, the lint wrong_import_style_via_super fires:
use std::fs::read;
// ^^^^ do not directly import
// instead, `use std::fs` then refer as `fs::read`
The basic MVP would be to implement #[import_style(via = super)], with no further configuration.
Extensions
The rest of this proposal goes into how this feature can be further extended to cover other common patterns.
Specifically, the use-cases the extensions support are:
-
Items imported directly
use rustc_middle::ty::Ty commonly is referred to directly as Ty.
We should allow linting other ways of referring to Ty, for consumer
crates, the current crate, or a specific module.
-
Types imported , but the module name should be renamed. Like use rustc_hir as hir, then refer as hir::Ty. Other ways of referring are linted.
-
Override import style for external paths
-
Ignore the import style for external path
The proposal provides a bird's eye view of the feature, there are many details left out that can be figured out during the implementation.
Feature: Direct import
We can require that the item is imported directly. Let's say we want to require that rustc_middle::ty::Ty is always used as Ty, with no path segments:
// rustc_middle::ty
#[import_style(via = self)]
struct Ty;
Then not using the item directly triggers the lint wrong_import_style_via_self:
use rustc_middle::ty;
// + ::Ty
// import `Ty` directly
fn f(_: ty::Ty) {}
// ^^^^ wrong import style, `Ty` should be
// imported directly.
The following is required:
use rustc_middle::ty::Ty;
fn f(_: Ty) {}
The wrong_import_style_via_self lint is allow-by-default
to prevent libraries from polluting the user's scope.
Feature: Renaming
The import_style field also accepts an as key, which requires the item to be renamed when it is imported directly.
For example:
// syn::parse
#[import_style(via = self, as = SynParse)]
trait Parse {
All of the following usage is wrong:
// refer as `syn::parse::Parse`
use syn::parse; // then refer as `parse::Parse`
use syn::parse::Parse; // then refer as Parse
You must import Parse directly, then rename it:
use syn::parse::Parse as SynParse
If via = self is omitted, then Parse needs to be renamed only when imported directly.
This cannot be used with via = super.
Feature: Configure external items
We have an issue with paths that are too awkward:
impl syn::parse::Parse for RenameRule {
syn::parse::Parse is too long to type, and contains duplication in "parse".
use syn::parse, then refer as parse::Parse looks odd + not clear that it's from syn
use syn::parse::Parse then refer as Parse has large potential to conflict with other types, it's also not clear that the type is from syn
Given that, one may reasonably require the following style in their codebase:
use syn::parse::Parse as SynParse;
impl SynParse for RenameRule {
To enforce this, the syn crate could add import_style attribute to Parse:
#[import_style(via = self, as = SynParse)]
trait Parse {
But they might not do that, and we still want to enforce that style in our codebase.
To accomodate this, we can configure the import style for other items:
#![import_style(for = syn::parse::Parse, via = self, as = SynParse)]
This works much like a #[warn]:
- On functions, sets the import style within the body
- On modules, sets the import style for the item to all sub-items recursively
- On the crate root, sets the import style for the whole crate
Ignoring
It may also be desireable to ignore the import style of specific items, which can be done with ::ignore:
#![import_style::ignore(std::fs::read)]
That accepts a list of paths. For any path that is ignored, all sub-items in that path is also ignored.
This ignores all #[import_style] attributes on items in std::io and std::fs:
#![import_style::ignore(std::fs, std::io)]
In which case this no longer triggers the wrong_import_style_via_super lint:
use std::fs::read;
read("Cargo.toml"); // OK
I propose to add a new lint for making imports in codebases more consistent, which fires when certain items are imported directly.
Experimental feature gate:
#![feature(discourage_direct_import)]Previous form of proposal (for archive purposes only)
I propose to add a new lint for making imports in codebases more consistent, which fires when items are imported inconsistently.
For example, many items in the
std::fs,std::io,std::iter,std::envand other modules are conventionally referred to via the parent path:It is discouraged to import those items directly:
To enforce this style, there is a new attribute
#[import_style]. This attribute configures the newwrong_import_style_via_superwarn-by-default lint.The
#[import_style]attribute tells how others should import your item.For example, we can annotate
fs::readwith it:When someone imports
readdirectly, the lintwrong_import_style_via_superfires:The basic MVP would be to implement
#[import_style(via = super)], with no further configuration.Extensions
The rest of this proposal goes into how this feature can be further extended to cover other common patterns.
Specifically, the use-cases the extensions support are:
Items imported directly
use rustc_middle::ty::Tycommonly is referred to directly asTy.We should allow linting other ways of referring to
Ty, for consumercrates, the current crate, or a specific module.
Types imported , but the module name should be renamed. Like
use rustc_hir as hir, then refer ashir::Ty. Other ways of referring are linted.Override import style for external paths
Ignore the import style for external path
The proposal provides a bird's eye view of the feature, there are many details left out that can be figured out during the implementation.
Feature: Direct import
We can require that the item is imported directly. Let's say we want to require that
rustc_middle::ty::Tyis always used asTy, with no path segments:Then not using the item directly triggers the lint
wrong_import_style_via_self:The following is required:
The
wrong_import_style_via_selflint is allow-by-defaultto prevent libraries from polluting the user's scope.
Feature: Renaming
The
import_stylefield also accepts anaskey, which requires the item to be renamed when it is imported directly.For example:
All of the following usage is wrong:
You must import
Parsedirectly, then rename it:If
via = selfis omitted, thenParseneeds to be renamed only when imported directly.This cannot be used with
via = super.Feature: Configure external items
We have an issue with paths that are too awkward:
syn::parse::Parseis too long to type, and contains duplication in "parse".use syn::parse, then refer asparse::Parselooks odd + not clear that it's fromsynuse syn::parse::Parsethen refer asParsehas large potential to conflict with other types, it's also not clear that the type is fromsynGiven that, one may reasonably require the following style in their codebase:
To enforce this, the
syncrate could addimport_styleattribute toParse:But they might not do that, and we still want to enforce that style in our codebase.
To accomodate this, we can configure the import style for other items:
#![import_style(for = syn::parse::Parse, via = self, as = SynParse)]This works much like a
#[warn]:Ignoring
It may also be desireable to ignore the import style of specific items, which can be done with
::ignore:#![import_style::ignore(std::fs::read)]That accepts a list of paths. For any path that is ignored, all sub-items in that path is also ignored.
This ignores all
#[import_style]attributes on items instd::ioandstd::fs:#![import_style::ignore(std::fs, std::io)]In which case this no longer triggers the
wrong_import_style_via_superlint:I propose to add a new lint for making imports in codebases more consistent, which fires when certain items are imported directly.
For example, many items in the
std::fs,std::io,std::iter,std::envand other modules are conventionally referred to via the parent path:It is discouraged to import those items directly:
To enforce this style, there is a new attribute
#[discourage_direct_import]. This attribute configures the newdiscouraged_direct_importswarn-by-default lint.The
#[import_style]attribute tells how others should import your item.For example, we can annotate
fs::readwith it:When someone imports
readdirectly, the lintwrong_import_style_via_superfires:Another example use-case: One of my code bases has 80 types named
Request, each in their individual modules. This API is meant to be used via a full type toRequest, you'd never import it directly.Contacts