diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index baabc1afe3fac..87ccbed842fcd 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -1100,8 +1100,7 @@ fn report_arm_reachability<'p, 'tcx>( let arm_span = cx.tcx.hir_span(hir_id); let whole_arm_span = if is_match_arm { // If the arm is followed by a comma, extend the span to include it. - let with_whitespace = sm.span_extend_while_whitespace(arm_span); - if let Some(comma) = sm.span_look_ahead(with_whitespace, ",", Some(1)) { + if let Some(comma) = sm.span_followed_by(arm_span, ",") { Some(arm_span.to(comma)) } else { Some(arm_span) diff --git a/compiler/rustc_resolve/src/ident.rs b/compiler/rustc_resolve/src/ident.rs index 069018139145d..c2869a5b6f350 100644 --- a/compiler/rustc_resolve/src/ident.rs +++ b/compiler/rustc_resolve/src/ident.rs @@ -1467,11 +1467,9 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> { // `const name: Ty = expr;`. This is a heuristic, it will // break down in the presence of macros. let sm = self.tcx.sess.source_map(); - let type_span = match sm.span_look_ahead( - original_rib_ident_def.span, - ":", - None, - ) { + let type_span = match sm + .span_followed_by(original_rib_ident_def.span, ":") + { None => { Some(original_rib_ident_def.span.shrink_to_hi()) } diff --git a/compiler/rustc_resolve/src/late/diagnostics.rs b/compiler/rustc_resolve/src/late/diagnostics.rs index cf048231bd607..08f9ce23e48f4 100644 --- a/compiler/rustc_resolve/src/late/diagnostics.rs +++ b/compiler/rustc_resolve/src/late/diagnostics.rs @@ -1987,10 +1987,23 @@ impl<'ast, 'ra, 'tcx> LateResolutionVisitor<'_, 'ast, 'ra, 'tcx> { // where a brace being opened means a block is being started. Look // ahead for the next text to see if `span` is followed by a `{`. let sm = self.r.tcx.sess.source_map(); - if let Some(followed_brace_span) = sm.span_look_ahead(span, "{", Some(50)) { + if let Some(open_brace_span) = sm.span_followed_by(span, "{") { // In case this could be a struct literal that needs to be surrounded // by parentheses, find the appropriate span. - let close_brace_span = sm.span_look_ahead(followed_brace_span, "}", Some(50)); + let close_brace_span = + sm.span_to_next_source(open_brace_span).ok().and_then(|next_source| { + let offset = next_source.find('}')?; + if next_source[..offset].chars().count() >= 50 { + return None; + } + let start = open_brace_span.hi() + rustc_span::BytePos(offset as u32); + Some(Span::new( + start, + start + rustc_span::BytePos(1_u32), + open_brace_span.ctxt(), + None, + )) + }); let closing_brace = close_brace_span.map(|sp| span.to(sp)); (true, closing_brace) } else { diff --git a/compiler/rustc_span/src/source_map.rs b/compiler/rustc_span/src/source_map.rs index ec335e7b43390..a42d4e0d05557 100644 --- a/compiler/rustc_span/src/source_map.rs +++ b/compiler/rustc_span/src/source_map.rs @@ -964,21 +964,13 @@ impl SourceMap { Span::new(BytePos(start_of_next_point), end_of_next_point, sp.ctxt, None) } - /// Check whether span is followed by some specified expected string in limit scope - pub fn span_look_ahead(&self, span: Span, expect: &str, limit: Option) -> Option { - let mut sp = span; - for _ in 0..limit.unwrap_or(100_usize) { - sp = self.next_point(sp); - if let Ok(ref snippet) = self.span_to_snippet(sp) { - if snippet == expect { - return Some(sp); - } - if snippet.chars().any(|c| !c.is_whitespace()) { - break; - } - } - } - None + /// Check whether span is followed by some specified target string, ignoring whitespace. + /// *Only suitable for diagnostics.* + pub fn span_followed_by(&self, span: Span, target: &str) -> Option { + let span = self.span_extend_while_whitespace(span); + self.span_to_next_source(span).ok()?.strip_prefix(target).map(|_| { + Span::new(span.hi(), span.hi() + BytePos(target.len() as u32), span.ctxt(), None) + }) } /// Finds the width of the character, either before or after the end of provided span, diff --git a/compiler/rustc_span/src/source_map/tests.rs b/compiler/rustc_span/src/source_map/tests.rs index 16d28f393d7f2..4cc243667f224 100644 --- a/compiler/rustc_span/src/source_map/tests.rs +++ b/compiler/rustc_span/src/source_map/tests.rs @@ -752,6 +752,25 @@ fn test_next_point() { assert!(sm.span_to_snippet(span).is_err()); } +#[test] +fn test_span_followed_by_stops_at_end_of_file() { + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(filename(&sm, "example.rs"), "x".to_string()); + + let span = Span::with_root_ctxt(BytePos(0), BytePos(1)); + assert_eq!(sm.span_followed_by(span, "y"), None); +} + +#[test] +fn test_span_followed_by_skips_whitespace() { + let sm = SourceMap::new(FilePathMapping::empty()); + sm.new_source_file(filename(&sm, "example.rs"), "x \n yz".to_string()); + + let span = Span::with_root_ctxt(BytePos(0), BytePos(1)); + let span = sm.span_followed_by(span, "yz").unwrap(); + assert_eq!(sm.span_to_snippet(span), Ok("yz".to_string())); +} + #[cfg(target_os = "linux")] #[test] fn read_binary_file_handles_lying_stat() { diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs index d0358b03af197..d266228e8613e 100644 --- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs +++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs @@ -464,7 +464,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> { (cand.self_ty().kind(), main_trait_predicate.self_ty().skip_binder().kind()) { // Wrap method receivers and `&`-references in parens - let suggestion = if self.tcx.sess.source_map().span_look_ahead(span, ".", Some(50)).is_some() { + let suggestion = if self.tcx.sess.source_map().span_followed_by(span, ".").is_some() { vec![ (span.shrink_to_lo(), format!("(")), (span.shrink_to_hi(), format!(" as {})", cand.self_ty())), diff --git a/tests/ui/error-codes/E0423-struct-literal-comment.rs b/tests/ui/error-codes/E0423-struct-literal-comment.rs new file mode 100644 index 0000000000000..f22d27ac37680 --- /dev/null +++ b/tests/ui/error-codes/E0423-struct-literal-comment.rs @@ -0,0 +1,18 @@ +#[derive(PartialEq)] +struct T { pub x: i32 } + +#[derive(PartialEq)] +struct U { } + +fn main() { + // Parser will report an error here + if T { x: 10 } == T {} {} + //~^ ERROR struct literals are not allowed here + //~| ERROR expected value, found struct `T` + + // Regression test for the `followed_by_brace` helper: + // comments inside the braces should not suppress the parenthesized struct literal suggestion. + if U { /* keep comment here */ } == U {} + //~^ ERROR E0423 + //~| ERROR expected expression, found `==` +} diff --git a/tests/ui/error-codes/E0423-struct-literal-comment.stderr b/tests/ui/error-codes/E0423-struct-literal-comment.stderr new file mode 100644 index 0000000000000..952200a753ebe --- /dev/null +++ b/tests/ui/error-codes/E0423-struct-literal-comment.stderr @@ -0,0 +1,42 @@ +error: struct literals are not allowed here + --> $DIR/E0423-struct-literal-comment.rs:9:8 + | +LL | if T { x: 10 } == T {} {} + | ^^^^^^^^^^^ + | +help: surround the struct literal with parentheses + | +LL | if (T { x: 10 }) == T {} {} + | + + + +error: expected expression, found `==` + --> $DIR/E0423-struct-literal-comment.rs:15:38 + | +LL | if U { /* keep comment here */ } == U {} + | ^^ expected expression + +error[E0423]: expected value, found struct `T` + --> $DIR/E0423-struct-literal-comment.rs:9:23 + | +LL | if T { x: 10 } == T {} {} + | ^ not a value + | +help: surround the struct literal with parentheses + | +LL | if T { x: 10 } == (T {}) {} + | + + + +error[E0423]: expected value, found struct `U` + --> $DIR/E0423-struct-literal-comment.rs:15:8 + | +LL | if U { /* keep comment here */ } == U {} + | ^ not a value + | +help: surround the struct literal with parentheses + | +LL | if (U { /* keep comment here */ }) == U {} + | + + + +error: aborting due to 4 previous errors + +For more information about this error, try `rustc --explain E0423`.