diff --git a/src/doc/rustc/src/command-line-arguments.md b/src/doc/rustc/src/command-line-arguments.md
index 577d03d1038f8..659f8f65e65d2 100644
--- a/src/doc/rustc/src/command-line-arguments.md
+++ b/src/doc/rustc/src/command-line-arguments.md
@@ -215,21 +215,29 @@ This controls which [target](targets/index.md) to produce.
This flag will set which lints should be set to the [warn level](lints/levels.md#warn).
+_Note:_ The order of these lint level arguments is taken into account, see [lint level via compiler flag](lints/levels.md#via-compiler-flag) for more information.
+
## `-A`: set lint allowed
This flag will set which lints should be set to the [allow level](lints/levels.md#allow).
+_Note:_ The order of these lint level arguments is taken into account, see [lint level via compiler flag](lints/levels.md#via-compiler-flag) for more information.
+
## `-D`: set lint denied
This flag will set which lints should be set to the [deny level](lints/levels.md#deny).
+_Note:_ The order of these lint level arguments is taken into account, see [lint level via compiler flag](lints/levels.md#via-compiler-flag) for more information.
+
## `-F`: set lint forbidden
This flag will set which lints should be set to the [forbid level](lints/levels.md#forbid).
+_Note:_ The order of these lint level arguments is taken into account, see [lint level via compiler flag](lints/levels.md#via-compiler-flag) for more information.
+
## `-Z`: set unstable options
diff --git a/src/doc/rustc/src/lints/levels.md b/src/doc/rustc/src/lints/levels.md
index 2944e86566313..3cfe2f698f3e0 100644
--- a/src/doc/rustc/src/lints/levels.md
+++ b/src/doc/rustc/src/lints/levels.md
@@ -164,6 +164,18 @@ And of course, you can mix these four flags together:
$ rustc lib.rs --crate-type=lib -D missing-docs -A unused-variables
```
+The order of these command line arguments is taken into account. The following allows the `unused-variables` lint, because it is the last argument for that lint:
+
+```bash
+$ rustc lib.rs --crate-type=lib -D unused-variables -A unused-variables
+```
+
+You can make use of this behavior by overriding the level of one specific lint out of a group of lints. The following example denies all the lints in the `unused` group, but explicitly allows the `unused-variables` lint in that group:
+
+```bash
+$ rustc lib.rs --crate-type=lib -D unused -A unused-variables
+```
+
### Via an attribute
You can also modify the lint level with a crate-wide attribute:
diff --git a/src/librustc_session/config.rs b/src/librustc_session/config.rs
index b6b22e298ca62..fd4e47baad5d7 100644
--- a/src/librustc_session/config.rs
+++ b/src/librustc_session/config.rs
@@ -986,19 +986,26 @@ pub fn get_cmd_lint_options(
matches: &getopts::Matches,
error_format: ErrorOutputType,
) -> (Vec<(String, lint::Level)>, bool, Option) {
- let mut lint_opts = vec![];
+ let mut lint_opts_with_position = vec![];
let mut describe_lints = false;
for &level in &[lint::Allow, lint::Warn, lint::Deny, lint::Forbid] {
- for lint_name in matches.opt_strs(level.as_str()) {
+ for (arg_pos, lint_name) in matches.opt_strs_pos(level.as_str()) {
if lint_name == "help" {
describe_lints = true;
} else {
- lint_opts.push((lint_name.replace("-", "_"), level));
+ lint_opts_with_position.push((arg_pos, lint_name.replace("-", "_"), level));
}
}
}
+ lint_opts_with_position.sort_by_key(|x| x.0);
+ let lint_opts = lint_opts_with_position
+ .iter()
+ .cloned()
+ .map(|(_, lint_name, level)| (lint_name, level))
+ .collect();
+
let lint_cap = matches.opt_str("cap-lints").map(|cap| {
lint::Level::from_str(&cap)
.unwrap_or_else(|| early_error(error_format, &format!("unknown lint level: `{}`", cap)))
diff --git a/src/test/ui-fulldeps/lint-group-denied-lint-allowed.rs b/src/test/ui-fulldeps/lint-group-denied-lint-allowed.rs
new file mode 100644
index 0000000000000..7498745f20699
--- /dev/null
+++ b/src/test/ui-fulldeps/lint-group-denied-lint-allowed.rs
@@ -0,0 +1,7 @@
+// aux-build:lint-group-plugin-test.rs
+// check-pass
+// compile-flags: -D unused -A unused-variables
+
+fn main() {
+ let x = 1;
+}
diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs
index 226a12c6734b7..84bae6cd27edd 100644
--- a/src/tools/compiletest/src/runtest.rs
+++ b/src/tools/compiletest/src/runtest.rs
@@ -1478,11 +1478,7 @@ impl<'test> TestCx<'test> {
WillExecute::No => TargetLocation::ThisDirectory(self.output_base_dir()),
};
- let mut rustc = self.make_compile_args(&self.testpaths.file, output_file, emit_metadata);
-
- rustc.arg("-L").arg(&self.aux_output_dir_name());
-
- match self.config.mode {
+ let allow_unused = match self.config.mode {
CompileFail | Ui => {
// compile-fail and ui tests tend to have tons of unused code as
// it's just testing various pieces of the compile, but we don't
@@ -1495,11 +1491,18 @@ impl<'test> TestCx<'test> {
// via command line flags.
&& local_pm != Some(PassMode::Run)
{
- rustc.args(&["-A", "unused"]);
+ AllowUnused::Yes
+ } else {
+ AllowUnused::No
}
}
- _ => {}
- }
+ _ => AllowUnused::No,
+ };
+
+ let mut rustc =
+ self.make_compile_args(&self.testpaths.file, output_file, emit_metadata, allow_unused);
+
+ rustc.arg("-L").arg(&self.aux_output_dir_name());
self.compose_and_run_compiler(rustc, None)
}
@@ -1710,7 +1713,8 @@ impl<'test> TestCx<'test> {
// Create the directory for the stdout/stderr files.
create_dir_all(aux_cx.output_base_dir()).unwrap();
let input_file = &aux_testpaths.file;
- let mut aux_rustc = aux_cx.make_compile_args(input_file, aux_output, EmitMetadata::No);
+ let mut aux_rustc =
+ aux_cx.make_compile_args(input_file, aux_output, EmitMetadata::No, AllowUnused::No);
let (dylib, crate_type) = if aux_props.no_prefer_dynamic {
(true, None)
@@ -1819,6 +1823,7 @@ impl<'test> TestCx<'test> {
input_file: &Path,
output_file: TargetLocation,
emit_metadata: EmitMetadata,
+ allow_unused: AllowUnused,
) -> Command {
let is_rustdoc = self.is_rustdoc();
let mut rustc = if !is_rustdoc {
@@ -1951,6 +1956,10 @@ impl<'test> TestCx<'test> {
rustc.arg("-Ctarget-feature=-crt-static");
}
+ if let AllowUnused::Yes = allow_unused {
+ rustc.args(&["-A", "unused"]);
+ }
+
rustc.args(&self.props.compile_flags);
rustc
@@ -2134,7 +2143,8 @@ impl<'test> TestCx<'test> {
let output_file = TargetLocation::ThisDirectory(self.output_base_dir());
let input_file = &self.testpaths.file;
- let mut rustc = self.make_compile_args(input_file, output_file, EmitMetadata::No);
+ let mut rustc =
+ self.make_compile_args(input_file, output_file, EmitMetadata::No, AllowUnused::No);
rustc.arg("-L").arg(aux_dir).arg("--emit=llvm-ir");
self.compose_and_run_compiler(rustc, None)
@@ -2147,7 +2157,8 @@ impl<'test> TestCx<'test> {
let output_file = TargetLocation::ThisFile(output_path.clone());
let input_file = &self.testpaths.file;
- let mut rustc = self.make_compile_args(input_file, output_file, EmitMetadata::No);
+ let mut rustc =
+ self.make_compile_args(input_file, output_file, EmitMetadata::No, AllowUnused::No);
rustc.arg("-L").arg(self.aux_output_dir_name());
@@ -2999,6 +3010,7 @@ impl<'test> TestCx<'test> {
&self.testpaths.file.with_extension(UI_FIXED),
TargetLocation::ThisFile(self.make_exe_name()),
emit_metadata,
+ AllowUnused::No,
);
rustc.arg("-L").arg(&self.aux_output_dir_name());
let res = self.compose_and_run_compiler(rustc, None);
@@ -3486,6 +3498,11 @@ enum ExpectedLine> {
Text(T),
}
+enum AllowUnused {
+ Yes,
+ No,
+}
+
impl fmt::Debug for ExpectedLine
where
T: AsRef + fmt::Debug,