Skip to content

Commit ca762ba

Browse files
committed
rustc: Disallow machine applicability in foreign macros
Recent changes to lints disallowed lints from being emitted against code located in foreign macros, except for future-incompatible lints. For a future incompatible lint, however, the automatic suggestions may not be applicable! This commit updates this code path to force all applicability suggestions made to foreign macros to never be `MachineApplicable`. This should avoid rustfix actually attempting fixing these suggestions, causing non-compiling code to be produced. Closes rust-lang/cargo#5799
1 parent 54628c8 commit ca762ba

8 files changed

+217
-35
lines changed

src/librustc/lint/mod.rs

+16-14
Original file line numberDiff line numberDiff line change
@@ -575,7 +575,8 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
575575
// Check for future incompatibility lints and issue a stronger warning.
576576
let lints = sess.lint_store.borrow();
577577
let lint_id = LintId::of(lint);
578-
if let Some(future_incompatible) = lints.future_incompatible(lint_id) {
578+
let future_incompatible = lints.future_incompatible(lint_id);
579+
if let Some(future_incompatible) = future_incompatible {
579580
const STANDARD_MESSAGE: &str =
580581
"this was previously accepted by the compiler but is being phased out; \
581582
it will become a hard error";
@@ -593,20 +594,21 @@ pub fn struct_lint_level<'a>(sess: &'a Session,
593594
future_incompatible.reference);
594595
err.warn(&explanation);
595596
err.note(&citation);
597+
}
596598

597-
// If this lint is *not* a future incompatibility warning then we want to be
598-
// sure to not be too noisy in some situations. If this code originates in a
599-
// foreign macro, aka something that this crate did not itself author, then
600-
// it's likely that there's nothing this crate can do about it. We probably
601-
// want to skip the lint entirely.
602-
//
603-
// For some lints though (like unreachable code) there's clear actionable
604-
// items to take care of (delete the macro invocation). As a result we have
605-
// a few lints we whitelist here for allowing a lint even though it's in a
606-
// foreign macro invocation.
607-
} else if !lint.report_in_external_macro {
608-
if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
609-
err.cancel();
599+
// If this code originates in a foreign macro, aka something that this crate
600+
// did not itself author, then it's likely that there's nothing this crate
601+
// can do about it. We probably want to skip the lint entirely.
602+
if err.span.primary_spans().iter().any(|s| in_external_macro(sess, *s)) {
603+
// Any suggestions made here are likely to be incorrect, so anything we
604+
// emit shouldn't be automatically fixed by rustfix.
605+
err.allow_suggestions(false);
606+
607+
// If this is a future incompatible lint it'll become a hard error, so
608+
// we have to emit *something*. Also allow lints to whitelist themselves
609+
// on a case-by-case basis for emission in a foreign macro.
610+
if future_incompatible.is_none() && !lint.report_in_external_macro {
611+
err.cancel()
610612
}
611613
}
612614

src/librustc_errors/diagnostic_builder.rs

+64-19
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ use syntax_pos::{MultiSpan, Span};
2626
pub struct DiagnosticBuilder<'a> {
2727
pub handler: &'a Handler,
2828
diagnostic: Diagnostic,
29+
allow_suggestions: bool,
2930
}
3031

3132
/// In general, the `DiagnosticBuilder` uses deref to allow access to
@@ -186,27 +187,67 @@ impl<'a> DiagnosticBuilder<'a> {
186187
msg: &str,
187188
suggestions: Vec<String>)
188189
-> &mut Self);
189-
forward!(pub fn span_suggestion_with_applicability(&mut self,
190-
sp: Span,
191-
msg: &str,
192-
suggestion: String,
193-
applicability: Applicability)
194-
-> &mut Self);
195-
forward!(pub fn span_suggestions_with_applicability(&mut self,
196-
sp: Span,
197-
msg: &str,
198-
suggestions: Vec<String>,
199-
applicability: Applicability)
200-
-> &mut Self);
201-
forward!(pub fn span_suggestion_short_with_applicability(&mut self,
202-
sp: Span,
203-
msg: &str,
204-
suggestion: String,
205-
applicability: Applicability)
206-
-> &mut Self);
190+
pub fn span_suggestion_with_applicability(&mut self,
191+
sp: Span,
192+
msg: &str,
193+
suggestion: String,
194+
applicability: Applicability)
195+
-> &mut Self {
196+
if !self.allow_suggestions {
197+
return self
198+
}
199+
self.diagnostic.span_suggestion_with_applicability(
200+
sp,
201+
msg,
202+
suggestion,
203+
applicability,
204+
);
205+
self
206+
}
207+
208+
pub fn span_suggestions_with_applicability(&mut self,
209+
sp: Span,
210+
msg: &str,
211+
suggestions: Vec<String>,
212+
applicability: Applicability)
213+
-> &mut Self {
214+
if !self.allow_suggestions {
215+
return self
216+
}
217+
self.diagnostic.span_suggestions_with_applicability(
218+
sp,
219+
msg,
220+
suggestions,
221+
applicability,
222+
);
223+
self
224+
}
225+
226+
pub fn span_suggestion_short_with_applicability(&mut self,
227+
sp: Span,
228+
msg: &str,
229+
suggestion: String,
230+
applicability: Applicability)
231+
-> &mut Self {
232+
if !self.allow_suggestions {
233+
return self
234+
}
235+
self.diagnostic.span_suggestion_short_with_applicability(
236+
sp,
237+
msg,
238+
suggestion,
239+
applicability,
240+
);
241+
self
242+
}
207243
forward!(pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self);
208244
forward!(pub fn code(&mut self, s: DiagnosticId) -> &mut Self);
209245

246+
pub fn allow_suggestions(&mut self, allow: bool) -> &mut Self {
247+
self.allow_suggestions = allow;
248+
self
249+
}
250+
210251
/// Convenience function for internal use, clients should use one of the
211252
/// struct_* methods on Handler.
212253
pub fn new(handler: &'a Handler, level: Level, message: &str) -> DiagnosticBuilder<'a> {
@@ -228,7 +269,11 @@ impl<'a> DiagnosticBuilder<'a> {
228269
/// diagnostic.
229270
pub fn new_diagnostic(handler: &'a Handler, diagnostic: Diagnostic)
230271
-> DiagnosticBuilder<'a> {
231-
DiagnosticBuilder { handler, diagnostic }
272+
DiagnosticBuilder {
273+
handler,
274+
diagnostic,
275+
allow_suggestions: true,
276+
}
232277
}
233278
}
234279

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// no-prefer-dynamic
12+
13+
#![crate_type = "proc-macro"]
14+
15+
extern crate proc_macro;
16+
17+
use proc_macro::*;
18+
19+
#[proc_macro_attribute]
20+
pub fn foo(_attr: TokenStream, _f: TokenStream) -> TokenStream {
21+
"pub fn foo() -> ::Foo { ::Foo }".parse().unwrap()
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:suggestions-not-always-applicable.rs
12+
// compile-flags: --edition 2015
13+
// run-rustfix
14+
// rustfix-only-machine-applicable
15+
// compile-pass
16+
17+
#![feature(rust_2018_preview)]
18+
#![warn(rust_2018_compatibility)]
19+
20+
extern crate suggestions_not_always_applicable as foo;
21+
22+
pub struct Foo;
23+
24+
mod test {
25+
use crate::foo::foo;
26+
27+
#[foo] //~ WARN: absolute paths must start with
28+
//~| WARN: previously accepted
29+
//~| WARN: absolute paths
30+
//~| WARN: previously accepted
31+
fn main() {
32+
}
33+
}
34+
35+
fn main() {
36+
test::foo();
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2+
// file at the top-level directory of this distribution and at
3+
// http://rust-lang.org/COPYRIGHT.
4+
//
5+
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6+
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7+
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8+
// option. This file may not be copied, modified, or distributed
9+
// except according to those terms.
10+
11+
// aux-build:suggestions-not-always-applicable.rs
12+
// compile-flags: --edition 2015
13+
// run-rustfix
14+
// rustfix-only-machine-applicable
15+
// compile-pass
16+
17+
#![feature(rust_2018_preview)]
18+
#![warn(rust_2018_compatibility)]
19+
20+
extern crate suggestions_not_always_applicable as foo;
21+
22+
pub struct Foo;
23+
24+
mod test {
25+
use crate::foo::foo;
26+
27+
#[foo] //~ WARN: absolute paths must start with
28+
//~| WARN: previously accepted
29+
//~| WARN: absolute paths
30+
//~| WARN: previously accepted
31+
fn main() {
32+
}
33+
}
34+
35+
fn main() {
36+
test::foo();
37+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
warning: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
2+
--> $DIR/suggestions-not-always-applicable.rs:27:5
3+
|
4+
LL | #[foo] //~ WARN: absolute paths must start with
5+
| ^^^^^^
6+
|
7+
note: lint level defined here
8+
--> $DIR/suggestions-not-always-applicable.rs:18:9
9+
|
10+
LL | #![warn(rust_2018_compatibility)]
11+
| ^^^^^^^^^^^^^^^^^^^^^^^
12+
= note: #[warn(absolute_paths_not_starting_with_crate)] implied by #[warn(rust_2018_compatibility)]
13+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition!
14+
= note: for more information, see issue TBD
15+
16+
warning: absolute paths must start with `self`, `super`, `crate`, or an external crate name in the 2018 edition
17+
--> $DIR/suggestions-not-always-applicable.rs:27:5
18+
|
19+
LL | #[foo] //~ WARN: absolute paths must start with
20+
| ^^^^^^
21+
|
22+
= warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in the 2018 edition!
23+
= note: for more information, see issue TBD
24+

src/tools/compiletest/src/header.rs

+11
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,7 @@ pub struct TestProps {
231231
pub normalize_stderr: Vec<(String, String)>,
232232
pub failure_status: i32,
233233
pub run_rustfix: bool,
234+
pub rustfix_only_machine_applicable: bool,
234235
}
235236

236237
impl TestProps {
@@ -263,6 +264,7 @@ impl TestProps {
263264
normalize_stderr: vec![],
264265
failure_status: -1,
265266
run_rustfix: false,
267+
rustfix_only_machine_applicable: false,
266268
}
267269
}
268270

@@ -397,6 +399,11 @@ impl TestProps {
397399
if !self.run_rustfix {
398400
self.run_rustfix = config.parse_run_rustfix(ln);
399401
}
402+
403+
if !self.rustfix_only_machine_applicable {
404+
self.rustfix_only_machine_applicable =
405+
config.parse_rustfix_only_machine_applicable(ln);
406+
}
400407
});
401408

402409
if self.failure_status == -1 {
@@ -663,6 +670,10 @@ impl Config {
663670
self.parse_name_directive(line, "run-rustfix")
664671
}
665672

673+
fn parse_rustfix_only_machine_applicable(&self, line: &str) -> bool {
674+
self.parse_name_directive(line, "rustfix-only-machine-applicable")
675+
}
676+
666677
fn parse_edition(&self, line: &str) -> Option<String> {
667678
self.parse_name_value_directive(line, "edition")
668679
}

src/tools/compiletest/src/runtest.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -2624,7 +2624,11 @@ impl<'test> TestCx<'test> {
26242624
let suggestions = get_suggestions_from_json(
26252625
&proc_res.stderr,
26262626
&HashSet::new(),
2627-
Filter::Everything,
2627+
if self.props.rustfix_only_machine_applicable {
2628+
Filter::MachineApplicableOnly
2629+
} else {
2630+
Filter::Everything
2631+
},
26282632
).unwrap();
26292633
let fixed_code = apply_suggestions(&unfixed_code, &suggestions).expect(&format!(
26302634
"failed to apply suggestions for {:?} with rustfix",
@@ -2686,7 +2690,7 @@ impl<'test> TestCx<'test> {
26862690
if !res.status.success() {
26872691
self.fatal_proc_rec("failed to compile fixed code", &res);
26882692
}
2689-
if !res.stderr.is_empty() {
2693+
if !res.stderr.is_empty() && !self.props.rustfix_only_machine_applicable {
26902694
self.fatal_proc_rec("fixed code is still producing diagnostics", &res);
26912695
}
26922696
}

0 commit comments

Comments
 (0)