diff --git a/compiler/rustc_expand/src/config.rs b/compiler/rustc_expand/src/config.rs index 93dfab10077ee..7e80f6e4cb897 100644 --- a/compiler/rustc_expand/src/config.rs +++ b/compiler/rustc_expand/src/config.rs @@ -7,10 +7,7 @@ use rustc_ast::token::{Delimiter, Token, TokenKind}; use rustc_ast::tokenstream::{ AttrTokenStream, AttrTokenTree, LazyAttrTokenStream, Spacing, TokenTree, WithTokens, }; -use rustc_ast::{ - self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, MetaItem, MetaItemInner, NodeId, - SyntheticAttr, -}; +use rustc_ast::{self as ast, AttrStyle, Attribute, HasAttrs, HasTokens, NodeId, SyntheticAttr}; use rustc_attr_ir::target::Target; use rustc_attr_ir::{self as attrs, AttributeKind}; use rustc_attr_parsing::parser::AllowExprMetavar; @@ -32,7 +29,7 @@ use tracing::instrument; use crate::diagnostics::{ CrateNameInCfgAttr, CrateTypeInCfgAttr, FeatureNotAllowed, FeatureRemoved, - FeatureRemovedReason, InvalidCfg, RemoveExprNotSupported, + FeatureRemovedReason, RemoveExprNotSupported, }; /// A folder that strips out items that do not belong in the current configuration. @@ -442,32 +439,6 @@ impl<'a> StripUnconfigured<'a> { } } -/// FIXME: Still used by Rustdoc, should be removed after -pub fn parse_cfg_old<'a>(meta_item: &'a MetaItem, sess: &Session) -> Option<&'a MetaItemInner> { - let span = meta_item.span; - match meta_item.meta_item_list() { - None => { - sess.dcx().emit_err(InvalidCfg::NotFollowedByParens { span }); - None - } - Some([]) => { - sess.dcx().emit_err(InvalidCfg::NoPredicate { span }); - None - } - Some([_, .., l]) => { - sess.dcx().emit_err(InvalidCfg::MultiplePredicates { span: l.span() }); - None - } - Some([single]) => match single.meta_item_or_bool() { - Some(meta_item) => Some(meta_item), - None => { - sess.dcx().emit_err(InvalidCfg::PredicateLiteral { span: single.span() }); - None - } - }, - } -} - fn is_cfg(attr: &Attribute) -> bool { attr.has_name(sym::cfg) } diff --git a/compiler/rustc_expand/src/diagnostics.rs b/compiler/rustc_expand/src/diagnostics.rs index 5d2066ef88773..bc8222feb14bf 100644 --- a/compiler/rustc_expand/src/diagnostics.rs +++ b/compiler/rustc_expand/src/diagnostics.rs @@ -189,40 +189,6 @@ pub(crate) struct RemoveExprNotSupported { pub span: Span, } -#[derive(Diagnostic)] -pub(crate) enum InvalidCfg { - #[diag("`cfg` is not followed by parentheses")] - NotFollowedByParens { - #[primary_span] - #[suggestion( - "expected syntax is", - code = "cfg(/* predicate */)", - applicability = "has-placeholders" - )] - span: Span, - }, - #[diag("`cfg` predicate is not specified")] - NoPredicate { - #[primary_span] - #[suggestion( - "expected syntax is", - code = "cfg(/* predicate */)", - applicability = "has-placeholders" - )] - span: Span, - }, - #[diag("multiple `cfg` predicates are specified")] - MultiplePredicates { - #[primary_span] - span: Span, - }, - #[diag("`cfg` predicate key cannot be a literal")] - PredicateLiteral { - #[primary_span] - span: Span, - }, -} - #[derive(Diagnostic)] #[diag("non-{$kind} macro in {$kind} position: {$name}")] pub(crate) struct WrongFragmentKind<'a> { diff --git a/compiler/rustc_metadata/src/locator.rs b/compiler/rustc_metadata/src/locator.rs index 687776386e36e..5691b1c9eb399 100644 --- a/compiler/rustc_metadata/src/locator.rs +++ b/compiler/rustc_metadata/src/locator.rs @@ -970,17 +970,19 @@ fn get_flavor_from_path(path: &Path) -> CrateFlavor { } } -/// A function to fetch about all macros inside a proc-macro crate. +/// A function to fetch all macros inside a proc-macro crate. /// /// Used by rust-analyzer-proc-macro-srv. pub fn get_proc_macros( - target: &Target, path: &Path, metadata_loader: &dyn MetadataLoader, cfg_version: &'static str, ) -> IoResult> { + let host_tuple = TargetTuple::from_tuple(config::host_tuple()); + let (host, _) = Target::search(&host_tuple, Path::new(""), false).unwrap(); + let metadata = - get_metadata_section(target, CrateFlavor::Dylib, path, metadata_loader, cfg_version, None) + get_metadata_section(&host, CrateFlavor::Dylib, path, metadata_loader, cfg_version, None) .map_err(|err| io::Error::other(err.to_string()))?; let stable_crate_id = metadata.get_root().stable_crate_id(); diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index f81727eda4fb6..3e03730ab632b 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2777,6 +2777,17 @@ impl<'a> Parser<'a> { "you likely meant to continue parsing the let-chain starting here", ); } else { + if self.prev_token == token::Semi + && (self.token == token::OpenBrace || AssocOp::from_token(&self.token).is_some()) + { + err.span_suggestion_verbose( + self.prev_token.span, + "remove this semicolon", + "", + Applicability::MaybeIncorrect, + ); + } + // Look for usages of '=>' where '>=' might be intended if maybe_fatarrow == token::FatArrow { err.span_suggestion_verbose( diff --git a/src/librustdoc/lint.rs b/src/librustdoc/lint.rs index 91f92b799889b..f3052dd9ead82 100644 --- a/src/librustdoc/lint.rs +++ b/src/librustdoc/lint.rs @@ -196,6 +196,20 @@ declare_rustdoc_lint! { "detects redundant explicit links in doc comments" } +declare_rustdoc_lint! { + /// This lint checks for uses of footnote references without definition. + BROKEN_FOOTNOTE, + Warn, + "detects footnote references with no associated definition" +} + +declare_rustdoc_lint! { + /// This lint checks if all footnote definitions are used. + UNUSED_FOOTNOTE_DEFINITION, + Warn, + "detects unused footnote definitions" +} + pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { vec![ BROKEN_INTRA_DOC_LINKS, @@ -209,6 +223,8 @@ pub(crate) static RUSTDOC_LINTS: Lazy> = Lazy::new(|| { MISSING_CRATE_LEVEL_DOCS, UNESCAPED_BACKTICKS, REDUNDANT_EXPLICIT_LINKS, + BROKEN_FOOTNOTE, + UNUSED_FOOTNOTE_DEFINITION, ] }); diff --git a/src/librustdoc/passes/lint.rs b/src/librustdoc/passes/lint.rs index 7740d14148bf0..bb952b32393cf 100644 --- a/src/librustdoc/passes/lint.rs +++ b/src/librustdoc/passes/lint.rs @@ -3,6 +3,7 @@ mod bare_urls; mod check_code_block_syntax; +mod footnotes; mod html_tags; mod redundant_explicit_links; mod unescaped_backticks; @@ -41,6 +42,7 @@ impl DocVisitor<'_> for Linter<'_, '_> { if may_have_link { bare_urls::visit_item(self.cx, item, hir_id, &dox); redundant_explicit_links::visit_item(self.cx, item, hir_id); + footnotes::visit_item(self.cx, item, hir_id, &dox); } if may_have_code { check_code_block_syntax::visit_item(self.cx, item, &dox); diff --git a/src/librustdoc/passes/lint/footnotes.rs b/src/librustdoc/passes/lint/footnotes.rs new file mode 100644 index 0000000000000..b67babbcdb754 --- /dev/null +++ b/src/librustdoc/passes/lint/footnotes.rs @@ -0,0 +1,152 @@ +use std::ops::Range; + +use rustc_data_structures::fx::{FxHashMap, FxHashSet}; +use rustc_errors::DiagDecorator; +use rustc_hir::HirId; +use rustc_lint_defs::Applicability; +use rustc_resolve::rustdoc::pulldown_cmark::{Event, Options, Parser, Tag, TagEnd}; +use rustc_resolve::rustdoc::source_span_for_markdown_range; + +use crate::clean::Item; +use crate::core::DocContext; + +// based on +// https://github.com/pulldown-cmark/pulldown-cmark/blob/fc8fe713f58d7f4495038b48fe76c1f101fb3af1/pulldown-cmark/src/linklabel.rs#L65 + +fn scan_ch(ch: u8, dox: &[u8], i: &mut usize) -> Option<()> { + if dox.get(*i) == Some(&ch) { + *i += 1; + Some(()) + } else { + None + } +} + +fn scan_footnote_ref(dox: &[u8], in_table: bool) -> Option { + let mut i = 0; + scan_ch(b'[', dox, &mut i)?; + scan_ch(b'^', dox, &mut i)?; + if dox.get(i) == Some(&b']') { + return None; + } + while let Some(&ch) = dox.get(i) { + if ch == b']' + || ch == b'[' + || ch == b'\r' + || ch == b'\n' + || (in_table && ch == b'|') + // these two cause false negatives in obscure corner cases, + // but there's another warning from the unescaped_backticks + // and invalid_html_tags lints when they do + || ch == b'`' + || ch == b'<' + { + break; + } else if in_table + && ch == b'\\' + && dox.get(i + 1) == Some(&b'\\') + && dox.get(i + 2) == Some(&b'|') + { + i += 3; + } else if ch == b'\\' && dox.get(i + 1).copied().map_or(false, is_ascii_punctuation) { + i += 2; + } else { + i += 1; + } + } + scan_ch(b']', dox, &mut i)?; + Some(i) +} + +fn is_ascii_punctuation(c: u8) -> bool { + c < 128 && (PUNCT_MASKS_ASCII[(c / 16) as usize] & (1 << (c & 15))) != 0 +} + +const PUNCT_MASKS_ASCII: [u16; 8] = [ + 0x0000, // U+0000...U+000F + 0x0000, // U+0010...U+001F + 0xfffe, // U+0020...U+002F + 0xfc00, // U+0030...U+003F + 0x0001, // U+0040...U+004F + 0xf800, // U+0050...U+005F + 0x0001, // U+0060...U+006F + 0x7800, // U+0070...U+007F +]; + +pub(crate) fn visit_item(cx: &DocContext<'_>, item: &Item, hir_id: HirId, dox: &str) { + let tcx = cx.tcx; + + let mut missing_footnote_references = FxHashSet::default(); + let mut footnote_references = FxHashSet::default(); + let mut footnote_definitions = FxHashMap::default(); + let mut in_table = false; + + let options = Options::ENABLE_FOOTNOTES | Options::ENABLE_TABLES; + let mut parser = Parser::new_ext(dox, options).into_offset_iter().peekable(); + while let Some((event, span)) = parser.next() { + match event { + Event::Text(text) + if text.starts_with("[") + && (span.start == 0 || dox.as_bytes()[span.start - 1] != b'\\') + && let Some(len) = + scan_footnote_ref(&dox.as_bytes()[span.start..], in_table) => + { + missing_footnote_references + .insert(Range { start: span.start, end: span.start + len }); + } + Event::FootnoteReference(label) => { + footnote_references.insert(label); + } + Event::Start(Tag::FootnoteDefinition(label)) => { + footnote_definitions.insert(label, span.start + 1); + } + Event::Start(Tag::Table(_)) => in_table = true, + Event::End(TagEnd::Table) => in_table = false, + _ => {} + } + } + + #[allow(rustc::potential_query_instability)] + for (footnote, span) in footnote_definitions { + if !footnote_references.contains(&footnote) { + let (span, _) = source_span_for_markdown_range( + tcx, + dox, + &(span..span + 1), + &item.attrs.doc_strings, + ) + .unwrap_or_else(|| (item.attr_span(tcx), false)); + + tcx.emit_node_span_lint( + crate::lint::UNUSED_FOOTNOTE_DEFINITION, + hir_id, + span, + DiagDecorator(|lint| { + lint.primary_message("unused footnote definition"); + }), + ); + } + } + + #[allow(rustc::potential_query_instability)] + for span in missing_footnote_references { + let ref_span = source_span_for_markdown_range(tcx, dox, &span, &item.attrs.doc_strings) + .map(|(span, _)| span) + .unwrap_or_else(|| item.attr_span(tcx)); + + tcx.emit_node_span_lint( + crate::lint::BROKEN_FOOTNOTE, + hir_id, + ref_span, + DiagDecorator(|lint| { + lint.primary_message("no footnote definition matching this footnote"); + lint.span_suggestion( + ref_span.shrink_to_lo(), + "if it should not be a footnote, escape it", + format!("\\{}", &dox[span]), + Applicability::MaybeIncorrect, + ); + }), + ); + } +} diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs index 1978a68dd959c..3b9c345fc27f5 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/dylib.rs @@ -5,9 +5,6 @@ mod proc_macros; use rustc_codegen_ssa::back::metadata::DefaultMetadataLoader; use rustc_interface::util::rustc_version_str; use rustc_proc_macro::bridge; -use rustc_session::config::host_tuple; -use rustc_target::spec::{Target, TargetTuple}; -use std::path::Path; use std::{fs, io, time::SystemTime}; use temp_dir::TempDir; @@ -78,11 +75,7 @@ struct ProcMacroLibrary { impl ProcMacroLibrary { fn open(path: &Utf8Path) -> io::Result { let proc_macros = rustc_span::create_default_session_globals_then(|| { - let (target, _) = - Target::search(&TargetTuple::from_tuple(host_tuple()), Path::new(""), false) - .unwrap(); rustc_metadata::locator::get_proc_macros( - &target, path.as_ref(), &DefaultMetadataLoader, rustc_version_str().unwrap_or("unknown"), diff --git a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs index 2a3a1bc002601..28570e1af4426 100644 --- a/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs +++ b/src/tools/rust-analyzer/crates/proc-macro-srv/src/lib.rs @@ -21,9 +21,7 @@ extern crate rustc_interface; extern crate rustc_lexer; extern crate rustc_metadata; extern crate rustc_proc_macro; -extern crate rustc_session; extern crate rustc_span; -extern crate rustc_target; mod bridge; mod dylib; diff --git a/tests/rustdoc-ui/lints/broken-footnote.rs b/tests/rustdoc-ui/lints/broken-footnote.rs new file mode 100644 index 0000000000000..3b97cbe89e865 --- /dev/null +++ b/tests/rustdoc-ui/lints/broken-footnote.rs @@ -0,0 +1,69 @@ +#![deny(rustdoc::broken_footnote)] + +//! Footnote referenced [^1]. And [^2]. And [^bla]. +//! +//! [^1]: footnote defined +//~^^^ ERROR: no footnote definition matching this footnote +//~| ERROR: no footnote definition matching this footnote + +//! [^*] special characters can appear within footnote references +//~^ ERROR: no footnote definition matching this footnote +//! +//! [^**] +//! +//! [^**]: not an error +//! +//! [^\_] so can escaped characters +//~^ ERROR: no footnote definition matching this footnote + +// Backslash escaped footnotes should not be recognized: +//! [\^4] +//! +//! [^5\] +//! +//! \[^yup] +//! +//! [^foo\ +//! bar] + +//! [^*] special characters can appear within footnote references +//~^ ERROR: no footnote definition matching this footnote +//! +//! [^**] +//! +//! [^**]: not an error +//! +//! [^\_] [^\[\]] so can escaped characters +//~^ ERROR: no footnote definition matching this footnote +//~| ERROR: no footnote definition matching this footnote +//! +//! Mixed with actual emphasis: +//! +//! [^a ***b] foobar [^c*** d] +//~^ ERROR: no footnote definition matching this footnote +//~| ERROR: no footnote definition matching this footnote +//! [^e ***f] foobar [^g*** h] +//! +//! [^e ***f]: test footnote +//! [^g*** h]: test footnote` +//! +//! [^foo | bar] +//~^ ERROR: no footnote definition matching this footnote +//! +//! | col | col | +//! |-------|------| +//! | [^foo | bar] | +//! +//! | col | col | +//! |-------|-------| +//! | [^foo \| bar] | +//~^ ERROR: no footnote definition matching this footnote +//! +//! | col | col | +//! |-------|--------| +//! | [^foo \\| bar] | +//~^ ERROR: no footnote definition matching this footnote +//! +//! Code spans and HTML have higher binding power than footnotes: +//! +//! [^foo `bar] baz` [^foo diff --git a/tests/rustdoc-ui/lints/broken-footnote.stderr b/tests/rustdoc-ui/lints/broken-footnote.stderr new file mode 100644 index 0000000000000..80d7c93333e4e --- /dev/null +++ b/tests/rustdoc-ui/lints/broken-footnote.stderr @@ -0,0 +1,104 @@ +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:36:11 + | +LL | //! [^\_] [^\[\]] so can escaped characters + | -^^^^^^ + | | + | help: if it should not be a footnote, escape it: `\[^\[\]]` + | +note: the lint level is defined here + --> $DIR/broken-footnote.rs:1:9 + | +LL | #![deny(rustdoc::broken_footnote)] + | ^^^^^^^^^^^^^^^^^^^^^^^^ + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:29:5 + | +LL | //! [^*] special characters can appear within footnote references + | -^^^ + | | + | help: if it should not be a footnote, escape it: `\[^*]` + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:50:5 + | +LL | //! [^foo | bar] + | -^^^^^^^^^^^ + | | + | help: if it should not be a footnote, escape it: `\[^foo | bar]` + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:3:45 + | +LL | //! Footnote referenced [^1]. And [^2]. And [^bla]. + | -^^^^^ + | | + | help: if it should not be a footnote, escape it: `\[^bla]` + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:59:7 + | +LL | //! | [^foo \| bar] | + | -^^^^^^^^^^^^ + | | + | help: if it should not be a footnote, escape it: `\[^foo \| bar]` + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:36:5 + | +LL | //! [^\_] [^\[\]] so can escaped characters + | -^^^^ + | | + | help: if it should not be a footnote, escape it: `\[^\_]` + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:9:5 + | +LL | //! [^*] special characters can appear within footnote references + | -^^^ + | | + | help: if it should not be a footnote, escape it: `\[^*]` + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:3:35 + | +LL | //! Footnote referenced [^1]. And [^2]. And [^bla]. + | -^^^ + | | + | help: if it should not be a footnote, escape it: `\[^2]` + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:64:7 + | +LL | //! | [^foo \| bar] | + | -^^^^^^^^^^^^^ + | | + | help: if it should not be a footnote, escape it: `\[^foo \| bar]` + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:16:5 + | +LL | //! [^\_] so can escaped characters + | -^^^^ + | | + | help: if it should not be a footnote, escape it: `\[^\_]` + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:42:22 + | +LL | //! [^a ***b] foobar [^c*** d] + | -^^^^^^^^ + | | + | help: if it should not be a footnote, escape it: `\[^c*** d]` + +error: no footnote definition matching this footnote + --> $DIR/broken-footnote.rs:42:5 + | +LL | //! [^a ***b] foobar [^c*** d] + | -^^^^^^^^ + | | + | help: if it should not be a footnote, escape it: `\[^a ***b]` + +error: aborting due to 12 previous errors + diff --git a/tests/rustdoc-ui/lints/unused-footnote.rs b/tests/rustdoc-ui/lints/unused-footnote.rs new file mode 100644 index 0000000000000..a71e20ff6d500 --- /dev/null +++ b/tests/rustdoc-ui/lints/unused-footnote.rs @@ -0,0 +1,9 @@ +// This test ensures that the `rustdoc::unused_footnote` lint is working as expected. + +#![deny(rustdoc::unused_footnote_definition)] + +//! Footnote referenced. [^2] +//! +//! [^1]: footnote defined +//! [^2]: footnote defined +//~^^ ERROR: unused_footnote_definition diff --git a/tests/rustdoc-ui/lints/unused-footnote.stderr b/tests/rustdoc-ui/lints/unused-footnote.stderr new file mode 100644 index 0000000000000..d227cef181df3 --- /dev/null +++ b/tests/rustdoc-ui/lints/unused-footnote.stderr @@ -0,0 +1,14 @@ +error: unused footnote definition + --> $DIR/unused-footnote.rs:7:6 + | +LL | //! [^1]: footnote defined + | ^ + | +note: the lint level is defined here + --> $DIR/unused-footnote.rs:3:9 + | +LL | #![deny(rustdoc::unused_footnote_definition)] + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error + diff --git a/tests/ui/parser/if-semi-before-block.fixed b/tests/ui/parser/if-semi-before-block.fixed new file mode 100644 index 0000000000000..f7b7d63a9670c --- /dev/null +++ b/tests/ui/parser/if-semi-before-block.fixed @@ -0,0 +1,47 @@ +//@ edition:2024 +//@ run-rustfix + +#![allow(dead_code)] + +fn block() { + if let Some(_) = Some(2) {} + //~^ ERROR expected `{`, found `;` + //~| NOTE expected `{` + //~| HELP remove this semicolon + //~| NOTE the `if` expression is missing a block after this condition +} + +fn and() { + if let Some(x) = Some(2) && x != 1 {} + //~^ ERROR expected `{`, found `;` + //~| NOTE expected `{` + //~| HELP remove this semicolon + //~| NOTE the `if` expression is missing a block after this condition +} + +fn and_paren_cond() { + if let Some(x) = Some(2) && (x > 0 && x != 1) {} + //~^ ERROR expected `{`, found `;` + //~| NOTE expected `{` + //~| HELP remove this semicolon + //~| NOTE the `if` expression is missing a block after this condition +} + +fn and_some() { + if let Some(x) = Some(2) && Some(1) == Some(x) {} + //~^ ERROR expected `{`, found `;` + //~| NOTE expected `{` + //~| HELP remove this semicolon + //~| NOTE the `if` expression is missing a block after this condition +} + +fn or() { + if true || false { + //~^ ERROR expected `{`, found `;` + //~| NOTE expected `{` + //~| HELP remove this semicolon + //~| NOTE the `if` expression is missing a block after this condition + } +} + +fn main() {} diff --git a/tests/ui/parser/if-semi-before-block.rs b/tests/ui/parser/if-semi-before-block.rs new file mode 100644 index 0000000000000..84d132fd08cec --- /dev/null +++ b/tests/ui/parser/if-semi-before-block.rs @@ -0,0 +1,47 @@ +//@ edition:2024 +//@ run-rustfix + +#![allow(dead_code)] + +fn block() { + if let Some(_) = Some(2); {} + //~^ ERROR expected `{`, found `;` + //~| NOTE expected `{` + //~| HELP remove this semicolon + //~| NOTE the `if` expression is missing a block after this condition +} + +fn and() { + if let Some(x) = Some(2); && x != 1 {} + //~^ ERROR expected `{`, found `;` + //~| NOTE expected `{` + //~| HELP remove this semicolon + //~| NOTE the `if` expression is missing a block after this condition +} + +fn and_paren_cond() { + if let Some(x) = Some(2); && (x > 0 && x != 1) {} + //~^ ERROR expected `{`, found `;` + //~| NOTE expected `{` + //~| HELP remove this semicolon + //~| NOTE the `if` expression is missing a block after this condition +} + +fn and_some() { + if let Some(x) = Some(2); && Some(1) == Some(x) {} + //~^ ERROR expected `{`, found `;` + //~| NOTE expected `{` + //~| HELP remove this semicolon + //~| NOTE the `if` expression is missing a block after this condition +} + +fn or() { + if true; || false { + //~^ ERROR expected `{`, found `;` + //~| NOTE expected `{` + //~| HELP remove this semicolon + //~| NOTE the `if` expression is missing a block after this condition + } +} + +fn main() {} diff --git a/tests/ui/parser/if-semi-before-block.stderr b/tests/ui/parser/if-semi-before-block.stderr new file mode 100644 index 0000000000000..d2454f278b17a --- /dev/null +++ b/tests/ui/parser/if-semi-before-block.stderr @@ -0,0 +1,87 @@ +error: expected `{`, found `;` + --> $DIR/if-semi-before-block.rs:7:29 + | +LL | if let Some(_) = Some(2); {} + | ^ expected `{` + | +note: the `if` expression is missing a block after this condition + --> $DIR/if-semi-before-block.rs:7:8 + | +LL | if let Some(_) = Some(2); {} + | ^^^^^^^^^^^^^^^^^^^^^ +help: remove this semicolon + | +LL - if let Some(_) = Some(2); {} +LL + if let Some(_) = Some(2) {} + | + +error: expected `{`, found `;` + --> $DIR/if-semi-before-block.rs:15:29 + | +LL | if let Some(x) = Some(2); && x != 1 {} + | ^ expected `{` + | +note: the `if` expression is missing a block after this condition + --> $DIR/if-semi-before-block.rs:15:8 + | +LL | if let Some(x) = Some(2); && x != 1 {} + | ^^^^^^^^^^^^^^^^^^^^^ +help: remove this semicolon + | +LL - if let Some(x) = Some(2); && x != 1 {} +LL + if let Some(x) = Some(2) && x != 1 {} + | + +error: expected `{`, found `;` + --> $DIR/if-semi-before-block.rs:23:29 + | +LL | if let Some(x) = Some(2); && (x > 0 && x != 1) {} + | ^ expected `{` + | +note: the `if` expression is missing a block after this condition + --> $DIR/if-semi-before-block.rs:23:8 + | +LL | if let Some(x) = Some(2); && (x > 0 && x != 1) {} + | ^^^^^^^^^^^^^^^^^^^^^ +help: remove this semicolon + | +LL - if let Some(x) = Some(2); && (x > 0 && x != 1) {} +LL + if let Some(x) = Some(2) && (x > 0 && x != 1) {} + | + +error: expected `{`, found `;` + --> $DIR/if-semi-before-block.rs:31:29 + | +LL | if let Some(x) = Some(2); && Some(1) == Some(x) {} + | ^ expected `{` + | +note: the `if` expression is missing a block after this condition + --> $DIR/if-semi-before-block.rs:31:8 + | +LL | if let Some(x) = Some(2); && Some(1) == Some(x) {} + | ^^^^^^^^^^^^^^^^^^^^^ +help: remove this semicolon + | +LL - if let Some(x) = Some(2); && Some(1) == Some(x) {} +LL + if let Some(x) = Some(2) && Some(1) == Some(x) {} + | + +error: expected `{`, found `;` + --> $DIR/if-semi-before-block.rs:39:12 + | +LL | if true; || false { + | ^ expected `{` + | +note: the `if` expression is missing a block after this condition + --> $DIR/if-semi-before-block.rs:39:8 + | +LL | if true; || false { + | ^^^^ +help: remove this semicolon + | +LL - if true; || false { +LL + if true || false { + | + +error: aborting due to 5 previous errors + diff --git a/tests/ui/parser/semi-in-let-chain.stderr b/tests/ui/parser/semi-in-let-chain.stderr index f36d5e041e5d5..b742979d033df 100644 --- a/tests/ui/parser/semi-in-let-chain.stderr +++ b/tests/ui/parser/semi-in-let-chain.stderr @@ -28,6 +28,11 @@ LL | if let () = () | ________^ LL | | && () == (); | |___________________^ +help: remove this semicolon + | +LL - && () == (); +LL + && () == () + | error: expected `{`, found `;` --> $DIR/semi-in-let-chain.rs:22:20