Skip to content

Commit a14e35f

Browse files
authored
Rollup merge of #65398 - estebank:capitalization-only, r=varkor
Bring attention to suggestions when the only difference is capitalization CC #65386.
2 parents 8d1123d + 8bf6d35 commit a14e35f

28 files changed

+115
-52
lines changed

src/librustc/session/config.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ use syntax::feature_gate::UnstableFeatures;
2323
use syntax::source_map::SourceMap;
2424

2525
use errors::emitter::HumanReadableErrorType;
26-
use errors::{ColorConfig, FatalError, Handler};
26+
use errors::{ColorConfig, FatalError, Handler, SourceMapperDyn};
2727

2828
use getopts;
2929

@@ -1857,6 +1857,7 @@ struct NullEmitter;
18571857

18581858
impl errors::emitter::Emitter for NullEmitter {
18591859
fn emit_diagnostic(&mut self, _: &errors::Diagnostic) {}
1860+
fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> { None }
18601861
}
18611862

18621863
// Converts strings provided as `--cfg [cfgspec]` into a `crate_cfg`.

src/librustc_codegen_ssa/back/write.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,8 @@ use rustc::util::common::{time_depth, set_time_depth, print_time_passes_entry};
2222
use rustc::util::profiling::SelfProfilerRef;
2323
use rustc_fs_util::link_or_copy;
2424
use rustc_data_structures::svh::Svh;
25-
use rustc_errors::{Handler, Level, FatalError, DiagnosticId};
25+
use rustc_data_structures::sync::Lrc;
26+
use rustc_errors::{Handler, Level, FatalError, DiagnosticId, SourceMapperDyn};
2627
use rustc_errors::emitter::{Emitter};
2728
use rustc_target::spec::MergeFunctions;
2829
use syntax::attr;
@@ -1681,6 +1682,9 @@ impl Emitter for SharedEmitter {
16811682
}
16821683
drop(self.sender.send(SharedEmitterMessage::AbortIfErrors));
16831684
}
1685+
fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> {
1686+
None
1687+
}
16841688
}
16851689

16861690
impl SharedEmitterMain {

src/librustc_errors/annotate_snippet_emitter_writer.rs

+4
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@ impl Emitter for AnnotateSnippetEmitterWriter {
4949
&suggestions);
5050
}
5151

52+
fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> {
53+
self.source_map.as_ref()
54+
}
55+
5256
fn should_show_explain(&self) -> bool {
5357
!self.short_message
5458
}

src/librustc_errors/emitter.rs

+44-5
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use syntax_pos::{SourceFile, Span, MultiSpan};
1313

1414
use crate::{
1515
Level, CodeSuggestion, Diagnostic, SubDiagnostic,
16-
SuggestionStyle, SourceMapperDyn, DiagnosticId,
16+
SuggestionStyle, SourceMapper, SourceMapperDyn, DiagnosticId,
1717
};
1818
use crate::Level::Error;
1919
use crate::snippet::{Annotation, AnnotationType, Line, MultilineAnnotation, StyledString, Style};
@@ -192,6 +192,8 @@ pub trait Emitter {
192192
true
193193
}
194194

195+
fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>>;
196+
195197
/// Formats the substitutions of the primary_span
196198
///
197199
/// The are a lot of conditions to this method, but in short:
@@ -204,7 +206,7 @@ pub trait Emitter {
204206
/// we return the original `primary_span` and the original suggestions.
205207
fn primary_span_formatted<'a>(
206208
&mut self,
207-
db: &'a Diagnostic
209+
db: &'a Diagnostic,
208210
) -> (MultiSpan, &'a [CodeSuggestion]) {
209211
let mut primary_span = db.span.clone();
210212
if let Some((sugg, rest)) = db.suggestions.split_first() {
@@ -234,7 +236,20 @@ pub trait Emitter {
234236
format!("help: {}", sugg.msg)
235237
} else {
236238
// Show the default suggestion text with the substitution
237-
format!("help: {}: `{}`", sugg.msg, substitution)
239+
format!(
240+
"help: {}{}: `{}`",
241+
sugg.msg,
242+
if self.source_map().map(|sm| is_case_difference(
243+
&**sm,
244+
substitution,
245+
sugg.substitutions[0].parts[0].span,
246+
)).unwrap_or(false) {
247+
" (notice the capitalization)"
248+
} else {
249+
""
250+
},
251+
substitution,
252+
)
238253
};
239254
primary_span.push_span_label(sugg.substitutions[0].parts[0].span, msg);
240255

@@ -382,6 +397,10 @@ pub trait Emitter {
382397
}
383398

384399
impl Emitter for EmitterWriter {
400+
fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> {
401+
self.sm.as_ref()
402+
}
403+
385404
fn emit_diagnostic(&mut self, db: &Diagnostic) {
386405
let mut children = db.children.clone();
387406
let (mut primary_span, suggestions) = self.primary_span_formatted(&db);
@@ -1461,7 +1480,9 @@ impl EmitterWriter {
14611480
let suggestions = suggestion.splice_lines(&**sm);
14621481

14631482
let mut row_num = 2;
1464-
for &(ref complete, ref parts) in suggestions.iter().take(MAX_SUGGESTIONS) {
1483+
let mut notice_capitalization = false;
1484+
for (complete, parts, only_capitalization) in suggestions.iter().take(MAX_SUGGESTIONS) {
1485+
notice_capitalization |= only_capitalization;
14651486
// Only show underline if the suggestion spans a single line and doesn't cover the
14661487
// entirety of the code output. If you have multiple replacements in the same line
14671488
// of code, show the underline.
@@ -1552,7 +1573,10 @@ impl EmitterWriter {
15521573
}
15531574
if suggestions.len() > MAX_SUGGESTIONS {
15541575
let msg = format!("and {} other candidates", suggestions.len() - MAX_SUGGESTIONS);
1555-
buffer.puts(row_num, 0, &msg, Style::NoStyle);
1576+
buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle);
1577+
} else if notice_capitalization {
1578+
let msg = "notice the capitalization difference";
1579+
buffer.puts(row_num, max_line_num_len + 3, &msg, Style::NoStyle);
15561580
}
15571581
emit_to_destination(&buffer.render(), level, &mut self.dst, self.short_message)?;
15581582
Ok(())
@@ -2034,3 +2058,18 @@ impl<'a> Drop for WritableDst<'a> {
20342058
}
20352059
}
20362060
}
2061+
2062+
/// Whether the original and suggested code are visually similar enough to warrant extra wording.
2063+
pub fn is_case_difference(sm: &dyn SourceMapper, suggested: &str, sp: Span) -> bool {
2064+
// FIXME: this should probably be extended to also account for `FO0` → `FOO` and unicode.
2065+
let found = sm.span_to_snippet(sp).unwrap();
2066+
let ascii_confusables = &['c', 'f', 'i', 'k', 'o', 's', 'u', 'v', 'w', 'x', 'y', 'z'];
2067+
// All the chars that differ in capitalization are confusable (above):
2068+
let confusable = found.chars().zip(suggested.chars()).filter(|(f, s)| f != s).all(|(f, s)| {
2069+
(ascii_confusables.contains(&f) || ascii_confusables.contains(&s))
2070+
});
2071+
confusable && found.to_lowercase() == suggested.to_lowercase()
2072+
// FIXME: We sometimes suggest the same thing we already have, which is a
2073+
// bug, but be defensive against that here.
2074+
&& found != suggested
2075+
}

src/librustc_errors/lib.rs

+20-12
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ pub use emitter::ColorConfig;
1313

1414
use Level::*;
1515

16-
use emitter::{Emitter, EmitterWriter};
16+
use emitter::{Emitter, EmitterWriter, is_case_difference};
1717
use registry::Registry;
1818

1919
use rustc_data_structures::sync::{self, Lrc, Lock};
@@ -37,13 +37,16 @@ pub mod registry;
3737
mod styled_buffer;
3838
mod lock;
3939

40-
use syntax_pos::{BytePos,
41-
Loc,
42-
FileLinesResult,
43-
SourceFile,
44-
FileName,
45-
MultiSpan,
46-
Span};
40+
use syntax_pos::{
41+
BytePos,
42+
FileLinesResult,
43+
FileName,
44+
Loc,
45+
MultiSpan,
46+
SourceFile,
47+
Span,
48+
SpanSnippetError,
49+
};
4750

4851
/// Indicates the confidence in the correctness of a suggestion.
4952
///
@@ -147,6 +150,7 @@ pub trait SourceMapper {
147150
fn lookup_char_pos(&self, pos: BytePos) -> Loc;
148151
fn span_to_lines(&self, sp: Span) -> FileLinesResult;
149152
fn span_to_string(&self, sp: Span) -> String;
153+
fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError>;
150154
fn span_to_filename(&self, sp: Span) -> FileName;
151155
fn merge_spans(&self, sp_lhs: Span, sp_rhs: Span) -> Option<Span>;
152156
fn call_span_if_macro(&self, sp: Span) -> Span;
@@ -155,9 +159,12 @@ pub trait SourceMapper {
155159
}
156160

157161
impl CodeSuggestion {
158-
/// Returns the assembled code suggestions and whether they should be shown with an underline.
159-
pub fn splice_lines(&self, cm: &SourceMapperDyn)
160-
-> Vec<(String, Vec<SubstitutionPart>)> {
162+
/// Returns the assembled code suggestions, whether they should be shown with an underline
163+
/// and whether the substitution only differs in capitalization.
164+
pub fn splice_lines(
165+
&self,
166+
cm: &SourceMapperDyn,
167+
) -> Vec<(String, Vec<SubstitutionPart>, bool)> {
161168
use syntax_pos::{CharPos, Pos};
162169

163170
fn push_trailing(buf: &mut String,
@@ -232,6 +239,7 @@ impl CodeSuggestion {
232239
prev_hi = cm.lookup_char_pos(part.span.hi());
233240
prev_line = fm.get_line(prev_hi.line - 1);
234241
}
242+
let only_capitalization = is_case_difference(cm, &buf, bounding_span);
235243
// if the replacement already ends with a newline, don't print the next line
236244
if !buf.ends_with('\n') {
237245
push_trailing(&mut buf, prev_line.as_ref(), &prev_hi, None);
@@ -240,7 +248,7 @@ impl CodeSuggestion {
240248
while buf.ends_with('\n') {
241249
buf.pop();
242250
}
243-
(buf, substitution.parts)
251+
(buf, substitution.parts, only_capitalization)
244252
}).collect()
245253
}
246254
}

src/libsyntax/json.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
use crate::source_map::{SourceMap, FilePathMapping};
1313

1414
use errors::registry::Registry;
15-
use errors::{SubDiagnostic, CodeSuggestion, SourceMapper};
15+
use errors::{SubDiagnostic, CodeSuggestion, SourceMapper, SourceMapperDyn};
1616
use errors::{DiagnosticId, Applicability};
1717
use errors::emitter::{Emitter, HumanReadableErrorType};
1818

@@ -113,6 +113,10 @@ impl Emitter for JsonEmitter {
113113
}
114114
}
115115

116+
fn source_map(&self) -> Option<&Lrc<SourceMapperDyn>> {
117+
Some(&self.sm)
118+
}
119+
116120
fn should_show_explain(&self) -> bool {
117121
match self.json_rendered {
118122
HumanReadableErrorType::Short(_) => false,

src/libsyntax/source_map.rs

+3
Original file line numberDiff line numberDiff line change
@@ -970,6 +970,9 @@ impl SourceMapper for SourceMap {
970970
fn span_to_string(&self, sp: Span) -> String {
971971
self.span_to_string(sp)
972972
}
973+
fn span_to_snippet(&self, sp: Span) -> Result<String, SpanSnippetError> {
974+
self.span_to_snippet(sp)
975+
}
973976
fn span_to_filename(&self, sp: Span) -> FileName {
974977
self.span_to_filename(sp)
975978
}

src/test/ui/const-generics/const-parameter-uppercase-lint.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ error: const parameter `x` should have an upper case name
1010
--> $DIR/const-parameter-uppercase-lint.rs:6:15
1111
|
1212
LL | fn noop<const x: u32>() {
13-
| ^ help: convert the identifier to upper case: `X`
13+
| ^ help: convert the identifier to upper case (notice the capitalization): `X`
1414
|
1515
note: lint level defined here
1616
--> $DIR/const-parameter-uppercase-lint.rs:4:9

src/test/ui/did_you_mean/issue-43871-enum-instead-of-variant.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ LL | let z = ManyVariants::Three();
5555
| ^^^^^^^^^^^^^^^^^^^
5656
LL | let z = ManyVariants::Four();
5757
| ^^^^^^^^^^^^^^^^^^
58-
and 6 other candidates
58+
and 6 other candidates
5959

6060
error: aborting due to 5 previous errors
6161

src/test/ui/did_you_mean/issue-56028-there-is-an-enum-variant.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ LL | fn setup() -> Determine { Set }
1313
| ^^^^^^^^^
1414
LL | fn setup() -> PutDown { Set }
1515
| ^^^^^^^
16-
and 3 other candidates
16+
and 3 other candidates
1717

1818
error[E0425]: cannot find value `Set` in this scope
1919
--> $DIR/issue-56028-there-is-an-enum-variant.rs:9:21
@@ -30,7 +30,7 @@ LL | use Determine::Set;
3030
|
3131
LL | use PutDown::Set;
3232
|
33-
and 3 other candidates
33+
and 3 other candidates
3434

3535
error: aborting due to 2 previous errors
3636

src/test/ui/error-codes/E0423.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ LL | let f = Foo();
3434
| ^^^
3535
| |
3636
| did you mean `Foo { /* fields */ }`?
37-
| help: a function with a similar name exists: `foo`
37+
| help: a function with a similar name exists (notice the capitalization): `foo`
3838

3939
error[E0423]: expected value, found struct `T`
4040
--> $DIR/E0423.rs:14:8

src/test/ui/expr_attr_paren_order.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error: variable `X` should have a snake case name
22
--> $DIR/expr_attr_paren_order.rs:19:17
33
|
44
LL | let X = 0;
5-
| ^ help: convert the identifier to snake case: `x`
5+
| ^ help: convert the identifier to snake case (notice the capitalization): `x`
66
|
77
note: lint level defined here
88
--> $DIR/expr_attr_paren_order.rs:17:17

src/test/ui/hygiene/globs.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ LL | use foo::test2::test::g;
3434
|
3535
LL | use foo::test::g;
3636
|
37-
and 2 other candidates
37+
and 2 other candidates
3838

3939
error[E0425]: cannot find function `f` in this scope
4040
--> $DIR/globs.rs:61:12

src/test/ui/hygiene/rustc-macro-transparency.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0425]: cannot find value `Opaque` in this scope
22
--> $DIR/rustc-macro-transparency.rs:26:5
33
|
44
LL | Opaque;
5-
| ^^^^^^ help: a local variable with a similar name exists: `opaque`
5+
| ^^^^^^ help: a local variable with a similar name exists (notice the capitalization): `opaque`
66

77
error[E0423]: expected value, found macro `semitransparent`
88
--> $DIR/rustc-macro-transparency.rs:29:5

src/test/ui/issues/issue-10200.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0532]: expected tuple struct/variant, found function `foo`
22
--> $DIR/issue-10200.rs:6:9
33
|
44
LL | foo(x)
5-
| ^^^ help: a tuple struct with a similar name exists: `Foo`
5+
| ^^^ help: a tuple struct with a similar name exists (notice the capitalization): `Foo`
66

77
error: aborting due to previous error
88

src/test/ui/issues/issue-17546.stderr

+2-2
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ LL | use std::prelude::v1::Result;
2727
|
2828
LL | use std::result::Result;
2929
|
30-
and 1 other candidates
30+
and 1 other candidates
3131

3232
error[E0573]: expected type, found variant `Result`
3333
--> $DIR/issue-17546.rs:28:13
@@ -44,7 +44,7 @@ LL | use std::prelude::v1::Result;
4444
|
4545
LL | use std::result::Result;
4646
|
47-
and 1 other candidates
47+
and 1 other candidates
4848

4949
error[E0573]: expected type, found variant `NoResult`
5050
--> $DIR/issue-17546.rs:33:15

src/test/ui/issues/issue-17718-const-naming.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ error: constant `foo` should have an upper case name
1515
--> $DIR/issue-17718-const-naming.rs:4:7
1616
|
1717
LL | const foo: isize = 3;
18-
| ^^^ help: convert the identifier to upper case: `FOO`
18+
| ^^^ help: convert the identifier to upper case (notice the capitalization): `FOO`
1919
|
2020
note: lint level defined here
2121
--> $DIR/issue-17718-const-naming.rs:2:9

src/test/ui/issues/issue-46332.stderr

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ error[E0422]: cannot find struct, variant or union type `TyUInt` in this scope
22
--> $DIR/issue-46332.rs:9:5
33
|
44
LL | TyUInt {};
5-
| ^^^^^^ help: a struct with a similar name exists: `TyUint`
5+
| ^^^^^^ help: a struct with a similar name exists (notice the capitalization): `TyUint`
66

77
error: aborting due to previous error
88

0 commit comments

Comments
 (0)