//! Tests for `[lints]` use cargo_test_support::prelude::*; use cargo_test_support::project; use cargo_test_support::registry::Package; use cargo_test_support::str; #[cargo_test] fn dependency_warning_ignored() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [dependencies] bar.path = "../bar" "#, ) .file("src/lib.rs", "") .build(); let _bar = project() .at("bar") .file( "Cargo.toml", r#" [package] name = "bar" version = "0.0.1" edition = "2015" authors = [] [lints.rust] unsafe_code = "forbid" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [LOCKING] 2 packages to latest compatible versions [CHECKING] bar v0.0.1 ([ROOT]/bar) [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn malformed_on_stable() { let foo = project() .file( "Cargo.toml", r#" lints = 20 [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: integer `20`, expected a lints table --> Cargo.toml:2:25 | 2 | lints = 20 | ^^ | "#]]) .run(); } #[cargo_test] fn fail_on_invalid_tool() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace.lints.super-awesome-linter] unsafe_code = "forbid" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check").with_stderr_data(str![[r#" [WARNING] [ROOT]/foo/Cargo.toml: unrecognized lint tool `lints.super-awesome-linter`, specifying unrecognized tools may break in the future. supported tools: cargo, clippy, rust, rustdoc [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]).run(); } #[cargo_test] fn invalid_type_in_lint_value() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [workspace.lints.rust] rust-2018-idioms = -1 "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] invalid type: integer `-1`, expected a string or map --> Cargo.toml:8:36 | 8 | rust-2018-idioms = -1 | ^^ | "#]]) .run(); } #[cargo_test] fn warn_on_unused_key() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [workspace.lints.rust] rust-2018-idioms = { level = "allow", unused = true } [lints.rust] rust-2018-idioms = { level = "allow", unused = true } "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: `lints.rust.rust-2018-idioms.unused` [WARNING] [ROOT]/foo/Cargo.toml: unused manifest key: `lints.rust.rust-2018-idioms.unused` [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn fail_on_tool_injection() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace.lints.rust] "clippy::cyclomatic_complexity" = "warn" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `lints.rust.clippy::cyclomatic_complexity` is not valid lint name; try `lints.clippy.cyclomatic_complexity` "#]]) .run(); } #[cargo_test] fn fail_on_redundant_tool() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace.lints.rust] "rust::unsafe_code" = "forbid" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `lints.rust.rust::unsafe_code` is not valid lint name; try `lints.rust.unsafe_code` "#]]) .run(); } #[cargo_test] fn fail_on_conflicting_tool() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [workspace.lints.rust] "super-awesome-tool::unsafe_code" = "forbid" "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: `lints.rust.super-awesome-tool::unsafe_code` is not a valid lint name "#]]) .run(); } #[cargo_test] fn package_lint_deny() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [ERROR] usage of an `unsafe` block ... "#]]) .run(); } #[cargo_test] fn workspace_cant_be_false() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints] workspace = false [workspace.lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] `workspace` cannot be false --> Cargo.toml:9:29 | 9 | workspace = false | ^^^^^ | "#]]) .run(); } #[cargo_test] fn workspace_lint_deny() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints] workspace = true [workspace.lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [ERROR] usage of an `unsafe` block ... "#]]) .run(); } #[cargo_test] fn workspace_and_package_lints() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints] workspace = true [lints.rust] "unsafe_code" = "allow" [workspace.lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" [ERROR] failed to parse manifest at `[ROOT]/foo/Cargo.toml` Caused by: cannot override `workspace.lints` in `lints`, either remove the overrides or `lints.workspace = true` and manually specify the lints "#]]) .run(); } #[cargo_test] fn attribute_has_precedence() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " #![allow(unsafe_code)] pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .arg("-v") // Show order of rustflags on failure .run(); } #[cargo_test] fn rustflags_has_precedence() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.rust] "unsafe_code" = "deny" "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .arg("-v") // Show order of rustflags on failure .env("RUSTFLAGS", "-Aunsafe_code") .run(); } #[cargo_test] fn profile_rustflags_has_precedence() { let foo = project() .file( "Cargo.toml", r#" cargo-features = ["profile-rustflags"] [package] name = "foo" version = "0.0.1" edition = "2015" [lints.rust] "unsafe_code" = "deny" [profile.dev] rustflags = ["-A", "unsafe_code"] "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .arg("-v") // Show order of rustflags on failure .masquerade_as_nightly_cargo(&["profile-rustflags"]) .run(); } #[cargo_test] fn build_rustflags_has_precedence() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" [lints.rust] "unsafe_code" = "deny" "#, ) .file( ".cargo/config.toml", r#" [build] rustflags = ["-A", "unsafe_code"] "#, ) .file( "src/lib.rs", " pub fn foo(num: i32) -> u32 { unsafe { std::mem::transmute(num) } } ", ) .build(); foo.cargo("check") .arg("-v") // Show order of rustflags on failure .run(); } #[cargo_test] fn without_priority() { Package::new("reg-dep", "1.0.0").publish(); let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2018" authors = [] [dependencies] reg-dep = "1.0.0" [lints.rust] "rust-2018-idioms" = "deny" "unused-extern-crates" = "allow" "#, ) .file( "src/lib.rs", " extern crate reg_dep; pub fn foo() -> u32 { 2 } ", ) .build(); foo.cargo("check") .with_status(101) .with_stderr_data(str![[r#" ... [ERROR] unused extern crate ... "#]]) .run(); } #[cargo_test] fn with_priority() { Package::new("reg-dep", "1.0.0").publish(); let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2018" authors = [] [dependencies] reg-dep = "1.0.0" [lints.rust] "rust-2018-idioms" = { level = "deny", priority = -1 } "unused-extern-crates" = "allow" "#, ) .file( "src/lib.rs", " extern crate reg_dep; pub fn foo() -> u32 { 2 } ", ) .build(); foo.cargo("check").run(); } #[cargo_test] fn rustdoc_lint() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.rustdoc] broken_intra_doc_links = "deny" "#, ) .file( "src/lib.rs", " /// [`bar`] doesn't exist pub fn foo() -> u32 { } ", ) .build(); foo.cargo("doc") .with_status(101) .with_stderr_data(str![[r#" [DOCUMENTING] foo v0.0.1 ([ROOT]/foo) [ERROR] unresolved link to `bar` ... "#]]) .run(); } #[cargo_test] fn doctest_respects_lints() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.rust] confusable-idents = 'allow' "#, ) .file( "src/lib.rs", r#" /// Test /// /// [`Foo`] /// /// ``` /// let s = "rust"; /// let s_s = "rust2"; /// ``` pub fn f() {} pub const Ě: i32 = 1; pub const Ĕ: i32 = 2; "#, ) .build(); foo.cargo("check") .with_stderr_data(str![[r#" [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); foo.cargo("test --doc") .with_stderr_data(str![[r#" [COMPILING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `test` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s [DOCTEST] foo "#]]) .run(); } #[cargo_test] fn cargo_lints_nightly_required() { let foo = project() .file( "Cargo.toml", r#" [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] [lints.cargo] "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .with_stderr_data(str![[r#" [WARNING] unused manifest key `lints.cargo` (may be supported in a future version) this Cargo does not support nightly features, but if you switch to nightly channel you can pass `-Zcargo-lints` to enable this feature. [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_lints_no_z_flag() { let foo = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true [lints.cargo] "#, ) .file("src/lib.rs", "") .build(); foo.cargo("check") .masquerade_as_nightly_cargo(&["cargo-lints", "test-dummy-unstable"]) .with_stderr_data(str![[r#" [WARNING] unused manifest key `lints.cargo` (may be supported in a future version) consider passing `-Zcargo-lints` to enable this feature. [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); } #[cargo_test] fn cargo_lints_success() { let p = project() .file( "Cargo.toml", r#" cargo-features = ["test-dummy-unstable"] [package] name = "foo" version = "0.0.1" edition = "2015" authors = [] im-a-teapot = true [lints.cargo] im_a_teapot = "warn" "#, ) .file("src/lib.rs", "") .build(); p.cargo("check -Zcargo-lints") .masquerade_as_nightly_cargo(&["cargo-lints", "test-dummy-unstable"]) .with_stderr_data(str![[r#" [WARNING] `im_a_teapot` is specified --> Cargo.toml:9:1 | 9 | im-a-teapot = true | ------------------ | = [NOTE] `cargo::im_a_teapot` is set to `warn` in `[lints]` [CHECKING] foo v0.0.1 ([ROOT]/foo) [FINISHED] `dev` profile [unoptimized + debuginfo] target(s) in [ELAPSED]s "#]]) .run(); }