| author | bors <bors@rust-lang.org> 2026-05-27 08:25:14 UTC |
| committer | bors <bors@rust-lang.org> 2026-05-27 08:25:14 UTC |
| log | ddc1a64229890506f57bae5b270f9e5f604294ec |
| tree | b6dfb5034184991a20758937dcdade30651044be |
| parent | 2cfb951a24de2520de67f6911fd1fc0045a2662e |
| parent | a64a3f207521980f69381c2c0511ae913d702efe |
Rollup of 9 pull requests
Successful merges:
- rust-lang/rust#156796 (Fix missing suggestion when matching `String` with `&str`)
- rust-lang/rust#156933 (rustc_parse_format: improve the error diagnostic for `+` sign flag)
- rust-lang/rust#156545 (fix issue-144595)
- rust-lang/rust#156814 (Extend macOS deployment target mismatch filter to cover dylib and new ld formats)
- rust-lang/rust#156851 (rustdoc: avoid ICE when rendering body-less type consts)
- rust-lang/rust#156942 (Remove unneeded `#[skip_arg]` attributes)
- rust-lang/rust#156972 (add field_projections fixme)
- rust-lang/rust#156975 (Move check_cfg out of diagnostic attr module)
- rust-lang/rust#156989 (Add missing --set rust.codegen-backends=["gcc"] in the gcc codegen Dockerfile for the core tests)30 files changed, 753 insertions(+), 479 deletions(-)
compiler/rustc_attr_parsing/src/attributes/cfg.rs+1-2| ... | ... | @@ -20,7 +20,6 @@ use rustc_span::{ErrorGuaranteed, Span, Symbol, sym}; |
| 20 | 20 | use thin_vec::ThinVec; |
| 21 | 21 | |
| 22 | 22 | use crate::attributes::AttributeSafety; |
| 23 | use crate::attributes::diagnostic::check_cfg; | |
| 24 | 23 | use crate::context::{AcceptContext, ShouldEmit}; |
| 25 | 24 | use crate::parser::{ |
| 26 | 25 | AllowExprMetavar, ArgParser, MetaItemListParser, MetaItemOrLitParser, NameValueParser, |
| ... | ... | @@ -29,7 +28,7 @@ use crate::session_diagnostics::{ |
| 29 | 28 | AttributeParseError, AttributeParseErrorReason, CfgAttrBadDelim, MetaBadDelimSugg, |
| 30 | 29 | ParsedDescription, |
| 31 | 30 | }; |
| 32 | use crate::{AttributeParser, parse_version, session_diagnostics}; | |
| 31 | use crate::{AttributeParser, check_cfg, parse_version, session_diagnostics}; | |
| 33 | 32 | |
| 34 | 33 | pub const CFG_TEMPLATE: AttributeTemplate = template!( |
| 35 | 34 | List: &["predicate"], |
compiler/rustc_attr_parsing/src/attributes/diagnostic/check_cfg.rs deleted-444| ... | ... | @@ -1,444 +0,0 @@ |
| 1 | use rustc_session::Session; | |
| 2 | use rustc_session::config::ExpectedValues; | |
| 3 | use rustc_span::def_id::LOCAL_CRATE; | |
| 4 | use rustc_span::edit_distance::find_best_match_for_name; | |
| 5 | use rustc_span::{ExpnKind, Ident, Span, Symbol, sym}; | |
| 6 | ||
| 7 | use crate::errors; | |
| 8 | ||
| 9 | const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; | |
| 10 | ||
| 11 | enum FilterWellKnownNames { | |
| 12 | Yes, | |
| 13 | No, | |
| 14 | } | |
| 15 | ||
| 16 | fn sort_and_truncate_possibilities( | |
| 17 | sess: &Session, | |
| 18 | mut possibilities: Vec<Symbol>, | |
| 19 | filter_well_known_names: FilterWellKnownNames, | |
| 20 | ) -> (Vec<Symbol>, usize) { | |
| 21 | let possibilities_len = possibilities.len(); | |
| 22 | ||
| 23 | let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { | |
| 24 | possibilities.len() | |
| 25 | } else { | |
| 26 | match filter_well_known_names { | |
| 27 | FilterWellKnownNames::Yes => { | |
| 28 | possibilities | |
| 29 | .retain(|cfg_name| !sess.check_config.well_known_names.contains(cfg_name)); | |
| 30 | } | |
| 31 | FilterWellKnownNames::No => {} | |
| 32 | }; | |
| 33 | std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) | |
| 34 | }; | |
| 35 | ||
| 36 | possibilities.sort_by(|s1, s2| s1.as_str().cmp(s2.as_str())); | |
| 37 | ||
| 38 | let and_more = possibilities_len.saturating_sub(n_possibilities); | |
| 39 | possibilities.truncate(n_possibilities); | |
| 40 | (possibilities, and_more) | |
| 41 | } | |
| 42 | ||
| 43 | enum EscapeQuotes { | |
| 44 | Yes, | |
| 45 | No, | |
| 46 | } | |
| 47 | ||
| 48 | fn to_check_cfg_arg(name: Ident, value: Option<Symbol>, quotes: EscapeQuotes) -> String { | |
| 49 | if let Some(value) = value { | |
| 50 | let value = str::escape_debug(value.as_str()).to_string(); | |
| 51 | let values = match quotes { | |
| 52 | EscapeQuotes::Yes => format!("\\\"{}\\\"", value.replace("\"", "\\\\\\\\\"")), | |
| 53 | EscapeQuotes::No => format!("\"{value}\""), | |
| 54 | }; | |
| 55 | format!("cfg({name}, values({values}))") | |
| 56 | } else { | |
| 57 | format!("cfg({name})") | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 61 | fn cargo_help_sub( | |
| 62 | sess: &Session, | |
| 63 | inst: &impl Fn(EscapeQuotes) -> String, | |
| 64 | ) -> errors::UnexpectedCfgCargoHelp { | |
| 65 | // We don't want to suggest the `build.rs` way to expected cfgs if we are already in a | |
| 66 | // `build.rs`. We therefor do a best effort check (looking if the `--crate-name` is | |
| 67 | // `build_script_build`) to try to figure out if we are building a Cargo build script | |
| 68 | ||
| 69 | let unescaped = &inst(EscapeQuotes::No); | |
| 70 | if let Some("build_script_build") = sess.opts.crate_name.as_deref() { | |
| 71 | errors::UnexpectedCfgCargoHelp::lint_cfg(unescaped) | |
| 72 | } else { | |
| 73 | errors::UnexpectedCfgCargoHelp::lint_cfg_and_build_rs(unescaped, &inst(EscapeQuotes::Yes)) | |
| 74 | } | |
| 75 | } | |
| 76 | ||
| 77 | fn rustc_macro_help(span: Span) -> Option<errors::UnexpectedCfgRustcMacroHelp> { | |
| 78 | let oexpn = span.ctxt().outer_expn_data(); | |
| 79 | if let Some(def_id) = oexpn.macro_def_id | |
| 80 | && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind | |
| 81 | && def_id.krate != LOCAL_CRATE | |
| 82 | { | |
| 83 | Some(errors::UnexpectedCfgRustcMacroHelp { macro_kind: macro_kind.descr(), macro_name }) | |
| 84 | } else { | |
| 85 | None | |
| 86 | } | |
| 87 | } | |
| 88 | ||
| 89 | fn cargo_macro_help(span: Span) -> Option<errors::UnexpectedCfgCargoMacroHelp> { | |
| 90 | let oexpn = span.ctxt().outer_expn_data(); | |
| 91 | if let Some(def_id) = oexpn.macro_def_id | |
| 92 | && def_id.krate != LOCAL_CRATE | |
| 93 | && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind | |
| 94 | { | |
| 95 | Some(errors::UnexpectedCfgCargoMacroHelp { macro_kind: macro_kind.descr(), macro_name }) | |
| 96 | } else { | |
| 97 | None | |
| 98 | } | |
| 99 | } | |
| 100 | ||
| 101 | pub(crate) fn unexpected_cfg_name( | |
| 102 | sess: &Session, | |
| 103 | (name, name_span): (Symbol, Span), | |
| 104 | value: Option<(Symbol, Span)>, | |
| 105 | ) -> errors::UnexpectedCfgName { | |
| 106 | #[allow(rustc::potential_query_instability)] | |
| 107 | let possibilities: Vec<Symbol> = sess.check_config.expecteds.keys().copied().collect(); | |
| 108 | ||
| 109 | let mut names_possibilities: Vec<_> = if value.is_none() { | |
| 110 | // We later sort and display all the possibilities, so the order here does not matter. | |
| 111 | #[allow(rustc::potential_query_instability)] | |
| 112 | sess.check_config | |
| 113 | .expecteds | |
| 114 | .iter() | |
| 115 | .filter_map(|(k, v)| match v { | |
| 116 | ExpectedValues::Some(v) if v.contains(&Some(name)) => Some(k), | |
| 117 | _ => None, | |
| 118 | }) | |
| 119 | .collect() | |
| 120 | } else { | |
| 121 | Vec::new() | |
| 122 | }; | |
| 123 | ||
| 124 | let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); | |
| 125 | let is_from_external_macro = name_span.in_external_macro(sess.source_map()); | |
| 126 | let mut is_feature_cfg = name == sym::feature; | |
| 127 | ||
| 128 | fn miscapitalized_boolean(name: Symbol) -> Option<bool> { | |
| 129 | if name.as_str().eq_ignore_ascii_case("false") { | |
| 130 | Some(false) | |
| 131 | } else if name.as_str().eq_ignore_ascii_case("true") { | |
| 132 | Some(true) | |
| 133 | } else { | |
| 134 | None | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | let code_sugg = if is_feature_cfg && is_from_cargo { | |
| 139 | errors::unexpected_cfg_name::CodeSuggestion::DefineFeatures | |
| 140 | // Suggest correct `version("..")` predicate syntax | |
| 141 | } else if let Some((_value, value_span)) = value | |
| 142 | && name == sym::version | |
| 143 | { | |
| 144 | errors::unexpected_cfg_name::CodeSuggestion::VersionSyntax { | |
| 145 | between_name_and_value: name_span.between(value_span), | |
| 146 | after_value: value_span.shrink_to_hi(), | |
| 147 | } | |
| 148 | // Suggest a literal `false` instead | |
| 149 | // Detect miscapitalized `False`/`FALSE` etc, ensuring that this isn't `r#false` | |
| 150 | } else if value.is_none() | |
| 151 | // If this is a miscapitalized False/FALSE, suggest the boolean literal instead | |
| 152 | && let Some(boolean) = miscapitalized_boolean(name) | |
| 153 | // Check this isn't a raw identifier | |
| 154 | && sess | |
| 155 | .source_map() | |
| 156 | .span_to_snippet(name_span) | |
| 157 | .map_or(true, |snippet| !snippet.contains("r#")) | |
| 158 | { | |
| 159 | errors::unexpected_cfg_name::CodeSuggestion::BooleanLiteral { | |
| 160 | span: name_span, | |
| 161 | literal: boolean, | |
| 162 | } | |
| 163 | // Suggest the most probable if we found one | |
| 164 | } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { | |
| 165 | is_feature_cfg |= best_match == sym::feature; | |
| 166 | ||
| 167 | if let Some(ExpectedValues::Some(best_match_values)) = | |
| 168 | sess.check_config.expecteds.get(&best_match) | |
| 169 | { | |
| 170 | // We will soon sort, so the initial order does not matter. | |
| 171 | #[allow(rustc::potential_query_instability)] | |
| 172 | let mut possibilities = best_match_values.iter().flatten().collect::<Vec<_>>(); | |
| 173 | possibilities.sort_by_key(|s| s.as_str()); | |
| 174 | ||
| 175 | let get_possibilities_sub = || { | |
| 176 | if !possibilities.is_empty() { | |
| 177 | let possibilities = | |
| 178 | possibilities.iter().copied().cloned().collect::<Vec<_>>().into(); | |
| 179 | Some(errors::unexpected_cfg_name::ExpectedValues { best_match, possibilities }) | |
| 180 | } else { | |
| 181 | None | |
| 182 | } | |
| 183 | }; | |
| 184 | ||
| 185 | let best_match = Ident::new(best_match, name_span); | |
| 186 | if let Some((value, value_span)) = value { | |
| 187 | if best_match_values.contains(&Some(value)) { | |
| 188 | errors::unexpected_cfg_name::CodeSuggestion::SimilarNameAndValue { | |
| 189 | span: name_span, | |
| 190 | code: best_match.to_string(), | |
| 191 | } | |
| 192 | } else if best_match_values.contains(&None) { | |
| 193 | errors::unexpected_cfg_name::CodeSuggestion::SimilarNameNoValue { | |
| 194 | span: name_span.to(value_span), | |
| 195 | code: best_match.to_string(), | |
| 196 | } | |
| 197 | } else if let Some(first_value) = possibilities.first() { | |
| 198 | errors::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { | |
| 199 | span: name_span.to(value_span), | |
| 200 | code: format!("{best_match} = \"{first_value}\""), | |
| 201 | expected: get_possibilities_sub(), | |
| 202 | } | |
| 203 | } else { | |
| 204 | errors::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { | |
| 205 | span: name_span.to(value_span), | |
| 206 | code: best_match.to_string(), | |
| 207 | expected: get_possibilities_sub(), | |
| 208 | } | |
| 209 | } | |
| 210 | } else { | |
| 211 | errors::unexpected_cfg_name::CodeSuggestion::SimilarName { | |
| 212 | span: name_span, | |
| 213 | code: best_match.to_string(), | |
| 214 | expected: get_possibilities_sub(), | |
| 215 | } | |
| 216 | } | |
| 217 | } else { | |
| 218 | errors::unexpected_cfg_name::CodeSuggestion::SimilarName { | |
| 219 | span: name_span, | |
| 220 | code: best_match.to_string(), | |
| 221 | expected: None, | |
| 222 | } | |
| 223 | } | |
| 224 | } else { | |
| 225 | let similar_values = if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { | |
| 226 | names_possibilities.sort(); | |
| 227 | names_possibilities | |
| 228 | .iter() | |
| 229 | .map(|cfg_name| errors::unexpected_cfg_name::FoundWithSimilarValue { | |
| 230 | span: name_span, | |
| 231 | code: format!("{cfg_name} = \"{name}\""), | |
| 232 | }) | |
| 233 | .collect() | |
| 234 | } else { | |
| 235 | vec![] | |
| 236 | }; | |
| 237 | ||
| 238 | let (possibilities, and_more) = | |
| 239 | sort_and_truncate_possibilities(sess, possibilities, FilterWellKnownNames::Yes); | |
| 240 | let expected_names = if !possibilities.is_empty() { | |
| 241 | let possibilities: Vec<_> = | |
| 242 | possibilities.into_iter().map(|s| Ident::new(s, name_span)).collect(); | |
| 243 | Some(errors::unexpected_cfg_name::ExpectedNames { | |
| 244 | possibilities: possibilities.into(), | |
| 245 | and_more, | |
| 246 | }) | |
| 247 | } else { | |
| 248 | None | |
| 249 | }; | |
| 250 | errors::unexpected_cfg_name::CodeSuggestion::SimilarValues { | |
| 251 | with_similar_values: similar_values, | |
| 252 | expected_names, | |
| 253 | } | |
| 254 | }; | |
| 255 | ||
| 256 | let inst = |escape_quotes| { | |
| 257 | to_check_cfg_arg(Ident::new(name, name_span), value.map(|(v, _s)| v), escape_quotes) | |
| 258 | }; | |
| 259 | ||
| 260 | let invocation_help = if is_from_cargo { | |
| 261 | let help = if !is_feature_cfg && !is_from_external_macro { | |
| 262 | Some(cargo_help_sub(sess, &inst)) | |
| 263 | } else { | |
| 264 | None | |
| 265 | }; | |
| 266 | errors::unexpected_cfg_name::InvocationHelp::Cargo { | |
| 267 | help, | |
| 268 | macro_help: cargo_macro_help(name_span), | |
| 269 | } | |
| 270 | } else { | |
| 271 | let help = errors::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No)); | |
| 272 | errors::unexpected_cfg_name::InvocationHelp::Rustc { | |
| 273 | help, | |
| 274 | macro_help: rustc_macro_help(name_span), | |
| 275 | } | |
| 276 | }; | |
| 277 | ||
| 278 | errors::UnexpectedCfgName { code_sugg, invocation_help, name } | |
| 279 | } | |
| 280 | ||
| 281 | pub(crate) fn unexpected_cfg_value( | |
| 282 | sess: &Session, | |
| 283 | (name, name_span): (Symbol, Span), | |
| 284 | value: Option<(Symbol, Span)>, | |
| 285 | ) -> errors::UnexpectedCfgValue { | |
| 286 | let Some(ExpectedValues::Some(values)) = &sess.check_config.expecteds.get(&name) else { | |
| 287 | panic!( | |
| 288 | "it shouldn't be possible to have a diagnostic on a value whose name is not in values" | |
| 289 | ); | |
| 290 | }; | |
| 291 | let mut have_none_possibility = false; | |
| 292 | // We later sort possibilities if it is not empty, so the | |
| 293 | // order here does not matter. | |
| 294 | #[allow(rustc::potential_query_instability)] | |
| 295 | let possibilities: Vec<Symbol> = values | |
| 296 | .iter() | |
| 297 | .inspect(|a| have_none_possibility |= a.is_none()) | |
| 298 | .copied() | |
| 299 | .flatten() | |
| 300 | .collect(); | |
| 301 | ||
| 302 | let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); | |
| 303 | let is_from_external_macro = name_span.in_external_macro(sess.source_map()); | |
| 304 | ||
| 305 | let code_sugg = if let Some((value, _)) = value | |
| 306 | && sess.check_config.well_known_names.contains(&name) | |
| 307 | && let valid_names = possible_well_known_names_for_cfg_value(sess, value) | |
| 308 | && !valid_names.is_empty() | |
| 309 | { | |
| 310 | // Suggest changing the name to something for which `value` is an expected value. | |
| 311 | let max_suggestions = 3; | |
| 312 | let suggestions = valid_names | |
| 313 | .iter() | |
| 314 | .take(max_suggestions) | |
| 315 | .copied() | |
| 316 | .map(|name| errors::unexpected_cfg_value::ChangeNameSuggestion { | |
| 317 | span: name_span, | |
| 318 | name, | |
| 319 | value, | |
| 320 | }) | |
| 321 | .collect::<Vec<_>>(); | |
| 322 | errors::unexpected_cfg_value::CodeSuggestion::ChangeName { suggestions } | |
| 323 | } else if !possibilities.is_empty() { | |
| 324 | // Show the full list if all possible values for a given name, but don't do it | |
| 325 | // for names as the possibilities could be very long | |
| 326 | let expected_values = { | |
| 327 | let (possibilities, and_more) = sort_and_truncate_possibilities( | |
| 328 | sess, | |
| 329 | possibilities.clone(), | |
| 330 | FilterWellKnownNames::No, | |
| 331 | ); | |
| 332 | errors::unexpected_cfg_value::ExpectedValues { | |
| 333 | name, | |
| 334 | have_none_possibility, | |
| 335 | possibilities: possibilities.into(), | |
| 336 | and_more, | |
| 337 | } | |
| 338 | }; | |
| 339 | ||
| 340 | let suggestion = if let Some((value, value_span)) = value { | |
| 341 | // Suggest the most probable if we found one | |
| 342 | if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) { | |
| 343 | Some(errors::unexpected_cfg_value::ChangeValueSuggestion::SimilarName { | |
| 344 | span: value_span, | |
| 345 | best_match, | |
| 346 | }) | |
| 347 | } else { | |
| 348 | None | |
| 349 | } | |
| 350 | } else if let &[first_possibility] = &possibilities[..] { | |
| 351 | Some(errors::unexpected_cfg_value::ChangeValueSuggestion::SpecifyValue { | |
| 352 | span: name_span.shrink_to_hi(), | |
| 353 | first_possibility, | |
| 354 | }) | |
| 355 | } else { | |
| 356 | None | |
| 357 | }; | |
| 358 | ||
| 359 | errors::unexpected_cfg_value::CodeSuggestion::ChangeValue { expected_values, suggestion } | |
| 360 | } else if have_none_possibility { | |
| 361 | let suggestion = | |
| 362 | value.map(|(_value, value_span)| errors::unexpected_cfg_value::RemoveValueSuggestion { | |
| 363 | span: name_span.shrink_to_hi().to(value_span), | |
| 364 | }); | |
| 365 | errors::unexpected_cfg_value::CodeSuggestion::RemoveValue { suggestion, name } | |
| 366 | } else { | |
| 367 | let span = if let Some((_value, value_span)) = value { | |
| 368 | name_span.to(value_span) | |
| 369 | } else { | |
| 370 | name_span | |
| 371 | }; | |
| 372 | let suggestion = errors::unexpected_cfg_value::RemoveConditionSuggestion { span }; | |
| 373 | errors::unexpected_cfg_value::CodeSuggestion::RemoveCondition { suggestion, name } | |
| 374 | }; | |
| 375 | ||
| 376 | // We don't want to encourage people to add values to a well-known names, as these are | |
| 377 | // defined by rustc/Rust itself. Users can still do this if they wish, but should not be | |
| 378 | // encouraged to do so. | |
| 379 | let can_suggest_adding_value = !sess.check_config.well_known_names.contains(&name) | |
| 380 | // Except when working on rustc or the standard library itself, in which case we want to | |
| 381 | // suggest adding these cfgs to the "normal" place because of bootstrapping reasons. As a | |
| 382 | // basic heuristic, we use the "cheat" unstable feature enable method and the | |
| 383 | // non-ui-testing enabled option. | |
| 384 | || (matches!(sess.unstable_features, rustc_feature::UnstableFeatures::Cheat) | |
| 385 | && !sess.opts.unstable_opts.ui_testing); | |
| 386 | ||
| 387 | let inst = |escape_quotes| { | |
| 388 | to_check_cfg_arg(Ident::new(name, name_span), value.map(|(v, _s)| v), escape_quotes) | |
| 389 | }; | |
| 390 | ||
| 391 | let invocation_help = if is_from_cargo { | |
| 392 | let help = if name == sym::feature && !is_from_external_macro { | |
| 393 | if let Some((value, _value_span)) = value { | |
| 394 | Some(errors::unexpected_cfg_value::CargoHelp::AddFeature { value }) | |
| 395 | } else { | |
| 396 | Some(errors::unexpected_cfg_value::CargoHelp::DefineFeatures) | |
| 397 | } | |
| 398 | } else if can_suggest_adding_value && !is_from_external_macro { | |
| 399 | Some(errors::unexpected_cfg_value::CargoHelp::Other(cargo_help_sub(sess, &inst))) | |
| 400 | } else { | |
| 401 | None | |
| 402 | }; | |
| 403 | errors::unexpected_cfg_value::InvocationHelp::Cargo { | |
| 404 | help, | |
| 405 | macro_help: cargo_macro_help(name_span), | |
| 406 | } | |
| 407 | } else { | |
| 408 | let help = if can_suggest_adding_value { | |
| 409 | Some(errors::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No))) | |
| 410 | } else { | |
| 411 | None | |
| 412 | }; | |
| 413 | errors::unexpected_cfg_value::InvocationHelp::Rustc { | |
| 414 | help, | |
| 415 | macro_help: rustc_macro_help(name_span), | |
| 416 | } | |
| 417 | }; | |
| 418 | ||
| 419 | errors::UnexpectedCfgValue { | |
| 420 | code_sugg, | |
| 421 | invocation_help, | |
| 422 | has_value: value.is_some(), | |
| 423 | value: value.map_or_else(String::new, |(v, _span)| v.to_string()), | |
| 424 | } | |
| 425 | } | |
| 426 | ||
| 427 | fn possible_well_known_names_for_cfg_value(sess: &Session, value: Symbol) -> Vec<Symbol> { | |
| 428 | #[allow(rustc::potential_query_instability)] | |
| 429 | let mut names = sess | |
| 430 | .check_config | |
| 431 | .well_known_names | |
| 432 | .iter() | |
| 433 | .filter(|name| { | |
| 434 | sess.check_config | |
| 435 | .expecteds | |
| 436 | .get(*name) | |
| 437 | .map(|expected_values| expected_values.contains(&Some(value))) | |
| 438 | .unwrap_or_default() | |
| 439 | }) | |
| 440 | .copied() | |
| 441 | .collect::<Vec<_>>(); | |
| 442 | names.sort_by(|a, b| a.as_str().cmp(b.as_str())); | |
| 443 | names | |
| 444 | } |
compiler/rustc_attr_parsing/src/attributes/diagnostic/mod.rs-1| ... | ... | @@ -23,7 +23,6 @@ use crate::errors::{ |
| 23 | 23 | }; |
| 24 | 24 | use crate::parser::{ArgParser, MetaItemListParser, MetaItemOrLitParser, MetaItemParser}; |
| 25 | 25 | |
| 26 | pub(crate) mod check_cfg; | |
| 27 | 26 | pub(crate) mod do_not_recommend; |
| 28 | 27 | pub(crate) mod on_const; |
| 29 | 28 | pub(crate) mod on_move; |
compiler/rustc_attr_parsing/src/check_cfg.rs created+444| ... | ... | @@ -0,0 +1,444 @@ |
| 1 | use rustc_session::Session; | |
| 2 | use rustc_session::config::ExpectedValues; | |
| 3 | use rustc_span::def_id::LOCAL_CRATE; | |
| 4 | use rustc_span::edit_distance::find_best_match_for_name; | |
| 5 | use rustc_span::{ExpnKind, Ident, Span, Symbol, sym}; | |
| 6 | ||
| 7 | use crate::errors; | |
| 8 | ||
| 9 | const MAX_CHECK_CFG_NAMES_OR_VALUES: usize = 35; | |
| 10 | ||
| 11 | enum FilterWellKnownNames { | |
| 12 | Yes, | |
| 13 | No, | |
| 14 | } | |
| 15 | ||
| 16 | fn sort_and_truncate_possibilities( | |
| 17 | sess: &Session, | |
| 18 | mut possibilities: Vec<Symbol>, | |
| 19 | filter_well_known_names: FilterWellKnownNames, | |
| 20 | ) -> (Vec<Symbol>, usize) { | |
| 21 | let possibilities_len = possibilities.len(); | |
| 22 | ||
| 23 | let n_possibilities = if sess.opts.unstable_opts.check_cfg_all_expected { | |
| 24 | possibilities.len() | |
| 25 | } else { | |
| 26 | match filter_well_known_names { | |
| 27 | FilterWellKnownNames::Yes => { | |
| 28 | possibilities | |
| 29 | .retain(|cfg_name| !sess.check_config.well_known_names.contains(cfg_name)); | |
| 30 | } | |
| 31 | FilterWellKnownNames::No => {} | |
| 32 | }; | |
| 33 | std::cmp::min(possibilities.len(), MAX_CHECK_CFG_NAMES_OR_VALUES) | |
| 34 | }; | |
| 35 | ||
| 36 | possibilities.sort_by(|s1, s2| s1.as_str().cmp(s2.as_str())); | |
| 37 | ||
| 38 | let and_more = possibilities_len.saturating_sub(n_possibilities); | |
| 39 | possibilities.truncate(n_possibilities); | |
| 40 | (possibilities, and_more) | |
| 41 | } | |
| 42 | ||
| 43 | enum EscapeQuotes { | |
| 44 | Yes, | |
| 45 | No, | |
| 46 | } | |
| 47 | ||
| 48 | fn to_check_cfg_arg(name: Ident, value: Option<Symbol>, quotes: EscapeQuotes) -> String { | |
| 49 | if let Some(value) = value { | |
| 50 | let value = str::escape_debug(value.as_str()).to_string(); | |
| 51 | let values = match quotes { | |
| 52 | EscapeQuotes::Yes => format!("\\\"{}\\\"", value.replace("\"", "\\\\\\\\\"")), | |
| 53 | EscapeQuotes::No => format!("\"{value}\""), | |
| 54 | }; | |
| 55 | format!("cfg({name}, values({values}))") | |
| 56 | } else { | |
| 57 | format!("cfg({name})") | |
| 58 | } | |
| 59 | } | |
| 60 | ||
| 61 | fn cargo_help_sub( | |
| 62 | sess: &Session, | |
| 63 | inst: &impl Fn(EscapeQuotes) -> String, | |
| 64 | ) -> errors::UnexpectedCfgCargoHelp { | |
| 65 | // We don't want to suggest the `build.rs` way to expected cfgs if we are already in a | |
| 66 | // `build.rs`. We therefor do a best effort check (looking if the `--crate-name` is | |
| 67 | // `build_script_build`) to try to figure out if we are building a Cargo build script | |
| 68 | ||
| 69 | let unescaped = &inst(EscapeQuotes::No); | |
| 70 | if let Some("build_script_build") = sess.opts.crate_name.as_deref() { | |
| 71 | errors::UnexpectedCfgCargoHelp::lint_cfg(unescaped) | |
| 72 | } else { | |
| 73 | errors::UnexpectedCfgCargoHelp::lint_cfg_and_build_rs(unescaped, &inst(EscapeQuotes::Yes)) | |
| 74 | } | |
| 75 | } | |
| 76 | ||
| 77 | fn rustc_macro_help(span: Span) -> Option<errors::UnexpectedCfgRustcMacroHelp> { | |
| 78 | let oexpn = span.ctxt().outer_expn_data(); | |
| 79 | if let Some(def_id) = oexpn.macro_def_id | |
| 80 | && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind | |
| 81 | && def_id.krate != LOCAL_CRATE | |
| 82 | { | |
| 83 | Some(errors::UnexpectedCfgRustcMacroHelp { macro_kind: macro_kind.descr(), macro_name }) | |
| 84 | } else { | |
| 85 | None | |
| 86 | } | |
| 87 | } | |
| 88 | ||
| 89 | fn cargo_macro_help(span: Span) -> Option<errors::UnexpectedCfgCargoMacroHelp> { | |
| 90 | let oexpn = span.ctxt().outer_expn_data(); | |
| 91 | if let Some(def_id) = oexpn.macro_def_id | |
| 92 | && def_id.krate != LOCAL_CRATE | |
| 93 | && let ExpnKind::Macro(macro_kind, macro_name) = oexpn.kind | |
| 94 | { | |
| 95 | Some(errors::UnexpectedCfgCargoMacroHelp { macro_kind: macro_kind.descr(), macro_name }) | |
| 96 | } else { | |
| 97 | None | |
| 98 | } | |
| 99 | } | |
| 100 | ||
| 101 | pub(crate) fn unexpected_cfg_name( | |
| 102 | sess: &Session, | |
| 103 | (name, name_span): (Symbol, Span), | |
| 104 | value: Option<(Symbol, Span)>, | |
| 105 | ) -> errors::UnexpectedCfgName { | |
| 106 | #[allow(rustc::potential_query_instability)] | |
| 107 | let possibilities: Vec<Symbol> = sess.check_config.expecteds.keys().copied().collect(); | |
| 108 | ||
| 109 | let mut names_possibilities: Vec<_> = if value.is_none() { | |
| 110 | // We later sort and display all the possibilities, so the order here does not matter. | |
| 111 | #[allow(rustc::potential_query_instability)] | |
| 112 | sess.check_config | |
| 113 | .expecteds | |
| 114 | .iter() | |
| 115 | .filter_map(|(k, v)| match v { | |
| 116 | ExpectedValues::Some(v) if v.contains(&Some(name)) => Some(k), | |
| 117 | _ => None, | |
| 118 | }) | |
| 119 | .collect() | |
| 120 | } else { | |
| 121 | Vec::new() | |
| 122 | }; | |
| 123 | ||
| 124 | let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); | |
| 125 | let is_from_external_macro = name_span.in_external_macro(sess.source_map()); | |
| 126 | let mut is_feature_cfg = name == sym::feature; | |
| 127 | ||
| 128 | fn miscapitalized_boolean(name: Symbol) -> Option<bool> { | |
| 129 | if name.as_str().eq_ignore_ascii_case("false") { | |
| 130 | Some(false) | |
| 131 | } else if name.as_str().eq_ignore_ascii_case("true") { | |
| 132 | Some(true) | |
| 133 | } else { | |
| 134 | None | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | let code_sugg = if is_feature_cfg && is_from_cargo { | |
| 139 | errors::unexpected_cfg_name::CodeSuggestion::DefineFeatures | |
| 140 | // Suggest correct `version("..")` predicate syntax | |
| 141 | } else if let Some((_value, value_span)) = value | |
| 142 | && name == sym::version | |
| 143 | { | |
| 144 | errors::unexpected_cfg_name::CodeSuggestion::VersionSyntax { | |
| 145 | between_name_and_value: name_span.between(value_span), | |
| 146 | after_value: value_span.shrink_to_hi(), | |
| 147 | } | |
| 148 | // Suggest a literal `false` instead | |
| 149 | // Detect miscapitalized `False`/`FALSE` etc, ensuring that this isn't `r#false` | |
| 150 | } else if value.is_none() | |
| 151 | // If this is a miscapitalized False/FALSE, suggest the boolean literal instead | |
| 152 | && let Some(boolean) = miscapitalized_boolean(name) | |
| 153 | // Check this isn't a raw identifier | |
| 154 | && sess | |
| 155 | .source_map() | |
| 156 | .span_to_snippet(name_span) | |
| 157 | .map_or(true, |snippet| !snippet.contains("r#")) | |
| 158 | { | |
| 159 | errors::unexpected_cfg_name::CodeSuggestion::BooleanLiteral { | |
| 160 | span: name_span, | |
| 161 | literal: boolean, | |
| 162 | } | |
| 163 | // Suggest the most probable if we found one | |
| 164 | } else if let Some(best_match) = find_best_match_for_name(&possibilities, name, None) { | |
| 165 | is_feature_cfg |= best_match == sym::feature; | |
| 166 | ||
| 167 | if let Some(ExpectedValues::Some(best_match_values)) = | |
| 168 | sess.check_config.expecteds.get(&best_match) | |
| 169 | { | |
| 170 | // We will soon sort, so the initial order does not matter. | |
| 171 | #[allow(rustc::potential_query_instability)] | |
| 172 | let mut possibilities = best_match_values.iter().flatten().collect::<Vec<_>>(); | |
| 173 | possibilities.sort_by_key(|s| s.as_str()); | |
| 174 | ||
| 175 | let get_possibilities_sub = || { | |
| 176 | if !possibilities.is_empty() { | |
| 177 | let possibilities = | |
| 178 | possibilities.iter().copied().cloned().collect::<Vec<_>>().into(); | |
| 179 | Some(errors::unexpected_cfg_name::ExpectedValues { best_match, possibilities }) | |
| 180 | } else { | |
| 181 | None | |
| 182 | } | |
| 183 | }; | |
| 184 | ||
| 185 | let best_match = Ident::new(best_match, name_span); | |
| 186 | if let Some((value, value_span)) = value { | |
| 187 | if best_match_values.contains(&Some(value)) { | |
| 188 | errors::unexpected_cfg_name::CodeSuggestion::SimilarNameAndValue { | |
| 189 | span: name_span, | |
| 190 | code: best_match.to_string(), | |
| 191 | } | |
| 192 | } else if best_match_values.contains(&None) { | |
| 193 | errors::unexpected_cfg_name::CodeSuggestion::SimilarNameNoValue { | |
| 194 | span: name_span.to(value_span), | |
| 195 | code: best_match.to_string(), | |
| 196 | } | |
| 197 | } else if let Some(first_value) = possibilities.first() { | |
| 198 | errors::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { | |
| 199 | span: name_span.to(value_span), | |
| 200 | code: format!("{best_match} = \"{first_value}\""), | |
| 201 | expected: get_possibilities_sub(), | |
| 202 | } | |
| 203 | } else { | |
| 204 | errors::unexpected_cfg_name::CodeSuggestion::SimilarNameDifferentValues { | |
| 205 | span: name_span.to(value_span), | |
| 206 | code: best_match.to_string(), | |
| 207 | expected: get_possibilities_sub(), | |
| 208 | } | |
| 209 | } | |
| 210 | } else { | |
| 211 | errors::unexpected_cfg_name::CodeSuggestion::SimilarName { | |
| 212 | span: name_span, | |
| 213 | code: best_match.to_string(), | |
| 214 | expected: get_possibilities_sub(), | |
| 215 | } | |
| 216 | } | |
| 217 | } else { | |
| 218 | errors::unexpected_cfg_name::CodeSuggestion::SimilarName { | |
| 219 | span: name_span, | |
| 220 | code: best_match.to_string(), | |
| 221 | expected: None, | |
| 222 | } | |
| 223 | } | |
| 224 | } else { | |
| 225 | let similar_values = if !names_possibilities.is_empty() && names_possibilities.len() <= 3 { | |
| 226 | names_possibilities.sort(); | |
| 227 | names_possibilities | |
| 228 | .iter() | |
| 229 | .map(|cfg_name| errors::unexpected_cfg_name::FoundWithSimilarValue { | |
| 230 | span: name_span, | |
| 231 | code: format!("{cfg_name} = \"{name}\""), | |
| 232 | }) | |
| 233 | .collect() | |
| 234 | } else { | |
| 235 | vec![] | |
| 236 | }; | |
| 237 | ||
| 238 | let (possibilities, and_more) = | |
| 239 | sort_and_truncate_possibilities(sess, possibilities, FilterWellKnownNames::Yes); | |
| 240 | let expected_names = if !possibilities.is_empty() { | |
| 241 | let possibilities: Vec<_> = | |
| 242 | possibilities.into_iter().map(|s| Ident::new(s, name_span)).collect(); | |
| 243 | Some(errors::unexpected_cfg_name::ExpectedNames { | |
| 244 | possibilities: possibilities.into(), | |
| 245 | and_more, | |
| 246 | }) | |
| 247 | } else { | |
| 248 | None | |
| 249 | }; | |
| 250 | errors::unexpected_cfg_name::CodeSuggestion::SimilarValues { | |
| 251 | with_similar_values: similar_values, | |
| 252 | expected_names, | |
| 253 | } | |
| 254 | }; | |
| 255 | ||
| 256 | let inst = |escape_quotes| { | |
| 257 | to_check_cfg_arg(Ident::new(name, name_span), value.map(|(v, _s)| v), escape_quotes) | |
| 258 | }; | |
| 259 | ||
| 260 | let invocation_help = if is_from_cargo { | |
| 261 | let help = if !is_feature_cfg && !is_from_external_macro { | |
| 262 | Some(cargo_help_sub(sess, &inst)) | |
| 263 | } else { | |
| 264 | None | |
| 265 | }; | |
| 266 | errors::unexpected_cfg_name::InvocationHelp::Cargo { | |
| 267 | help, | |
| 268 | macro_help: cargo_macro_help(name_span), | |
| 269 | } | |
| 270 | } else { | |
| 271 | let help = errors::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No)); | |
| 272 | errors::unexpected_cfg_name::InvocationHelp::Rustc { | |
| 273 | help, | |
| 274 | macro_help: rustc_macro_help(name_span), | |
| 275 | } | |
| 276 | }; | |
| 277 | ||
| 278 | errors::UnexpectedCfgName { code_sugg, invocation_help, name } | |
| 279 | } | |
| 280 | ||
| 281 | pub(crate) fn unexpected_cfg_value( | |
| 282 | sess: &Session, | |
| 283 | (name, name_span): (Symbol, Span), | |
| 284 | value: Option<(Symbol, Span)>, | |
| 285 | ) -> errors::UnexpectedCfgValue { | |
| 286 | let Some(ExpectedValues::Some(values)) = &sess.check_config.expecteds.get(&name) else { | |
| 287 | panic!( | |
| 288 | "it shouldn't be possible to have a diagnostic on a value whose name is not in values" | |
| 289 | ); | |
| 290 | }; | |
| 291 | let mut have_none_possibility = false; | |
| 292 | // We later sort possibilities if it is not empty, so the | |
| 293 | // order here does not matter. | |
| 294 | #[allow(rustc::potential_query_instability)] | |
| 295 | let possibilities: Vec<Symbol> = values | |
| 296 | .iter() | |
| 297 | .inspect(|a| have_none_possibility |= a.is_none()) | |
| 298 | .copied() | |
| 299 | .flatten() | |
| 300 | .collect(); | |
| 301 | ||
| 302 | let is_from_cargo = rustc_session::utils::was_invoked_from_cargo(); | |
| 303 | let is_from_external_macro = name_span.in_external_macro(sess.source_map()); | |
| 304 | ||
| 305 | let code_sugg = if let Some((value, _)) = value | |
| 306 | && sess.check_config.well_known_names.contains(&name) | |
| 307 | && let valid_names = possible_well_known_names_for_cfg_value(sess, value) | |
| 308 | && !valid_names.is_empty() | |
| 309 | { | |
| 310 | // Suggest changing the name to something for which `value` is an expected value. | |
| 311 | let max_suggestions = 3; | |
| 312 | let suggestions = valid_names | |
| 313 | .iter() | |
| 314 | .take(max_suggestions) | |
| 315 | .copied() | |
| 316 | .map(|name| errors::unexpected_cfg_value::ChangeNameSuggestion { | |
| 317 | span: name_span, | |
| 318 | name, | |
| 319 | value, | |
| 320 | }) | |
| 321 | .collect::<Vec<_>>(); | |
| 322 | errors::unexpected_cfg_value::CodeSuggestion::ChangeName { suggestions } | |
| 323 | } else if !possibilities.is_empty() { | |
| 324 | // Show the full list if all possible values for a given name, but don't do it | |
| 325 | // for names as the possibilities could be very long | |
| 326 | let expected_values = { | |
| 327 | let (possibilities, and_more) = sort_and_truncate_possibilities( | |
| 328 | sess, | |
| 329 | possibilities.clone(), | |
| 330 | FilterWellKnownNames::No, | |
| 331 | ); | |
| 332 | errors::unexpected_cfg_value::ExpectedValues { | |
| 333 | name, | |
| 334 | have_none_possibility, | |
| 335 | possibilities: possibilities.into(), | |
| 336 | and_more, | |
| 337 | } | |
| 338 | }; | |
| 339 | ||
| 340 | let suggestion = if let Some((value, value_span)) = value { | |
| 341 | // Suggest the most probable if we found one | |
| 342 | if let Some(best_match) = find_best_match_for_name(&possibilities, value, None) { | |
| 343 | Some(errors::unexpected_cfg_value::ChangeValueSuggestion::SimilarName { | |
| 344 | span: value_span, | |
| 345 | best_match, | |
| 346 | }) | |
| 347 | } else { | |
| 348 | None | |
| 349 | } | |
| 350 | } else if let &[first_possibility] = &possibilities[..] { | |
| 351 | Some(errors::unexpected_cfg_value::ChangeValueSuggestion::SpecifyValue { | |
| 352 | span: name_span.shrink_to_hi(), | |
| 353 | first_possibility, | |
| 354 | }) | |
| 355 | } else { | |
| 356 | None | |
| 357 | }; | |
| 358 | ||
| 359 | errors::unexpected_cfg_value::CodeSuggestion::ChangeValue { expected_values, suggestion } | |
| 360 | } else if have_none_possibility { | |
| 361 | let suggestion = | |
| 362 | value.map(|(_value, value_span)| errors::unexpected_cfg_value::RemoveValueSuggestion { | |
| 363 | span: name_span.shrink_to_hi().to(value_span), | |
| 364 | }); | |
| 365 | errors::unexpected_cfg_value::CodeSuggestion::RemoveValue { suggestion, name } | |
| 366 | } else { | |
| 367 | let span = if let Some((_value, value_span)) = value { | |
| 368 | name_span.to(value_span) | |
| 369 | } else { | |
| 370 | name_span | |
| 371 | }; | |
| 372 | let suggestion = errors::unexpected_cfg_value::RemoveConditionSuggestion { span }; | |
| 373 | errors::unexpected_cfg_value::CodeSuggestion::RemoveCondition { suggestion, name } | |
| 374 | }; | |
| 375 | ||
| 376 | // We don't want to encourage people to add values to a well-known names, as these are | |
| 377 | // defined by rustc/Rust itself. Users can still do this if they wish, but should not be | |
| 378 | // encouraged to do so. | |
| 379 | let can_suggest_adding_value = !sess.check_config.well_known_names.contains(&name) | |
| 380 | // Except when working on rustc or the standard library itself, in which case we want to | |
| 381 | // suggest adding these cfgs to the "normal" place because of bootstrapping reasons. As a | |
| 382 | // basic heuristic, we use the "cheat" unstable feature enable method and the | |
| 383 | // non-ui-testing enabled option. | |
| 384 | || (matches!(sess.unstable_features, rustc_feature::UnstableFeatures::Cheat) | |
| 385 | && !sess.opts.unstable_opts.ui_testing); | |
| 386 | ||
| 387 | let inst = |escape_quotes| { | |
| 388 | to_check_cfg_arg(Ident::new(name, name_span), value.map(|(v, _s)| v), escape_quotes) | |
| 389 | }; | |
| 390 | ||
| 391 | let invocation_help = if is_from_cargo { | |
| 392 | let help = if name == sym::feature && !is_from_external_macro { | |
| 393 | if let Some((value, _value_span)) = value { | |
| 394 | Some(errors::unexpected_cfg_value::CargoHelp::AddFeature { value }) | |
| 395 | } else { | |
| 396 | Some(errors::unexpected_cfg_value::CargoHelp::DefineFeatures) | |
| 397 | } | |
| 398 | } else if can_suggest_adding_value && !is_from_external_macro { | |
| 399 | Some(errors::unexpected_cfg_value::CargoHelp::Other(cargo_help_sub(sess, &inst))) | |
| 400 | } else { | |
| 401 | None | |
| 402 | }; | |
| 403 | errors::unexpected_cfg_value::InvocationHelp::Cargo { | |
| 404 | help, | |
| 405 | macro_help: cargo_macro_help(name_span), | |
| 406 | } | |
| 407 | } else { | |
| 408 | let help = if can_suggest_adding_value { | |
| 409 | Some(errors::UnexpectedCfgRustcHelp::new(&inst(EscapeQuotes::No))) | |
| 410 | } else { | |
| 411 | None | |
| 412 | }; | |
| 413 | errors::unexpected_cfg_value::InvocationHelp::Rustc { | |
| 414 | help, | |
| 415 | macro_help: rustc_macro_help(name_span), | |
| 416 | } | |
| 417 | }; | |
| 418 | ||
| 419 | errors::UnexpectedCfgValue { | |
| 420 | code_sugg, | |
| 421 | invocation_help, | |
| 422 | has_value: value.is_some(), | |
| 423 | value: value.map_or_else(String::new, |(v, _span)| v.to_string()), | |
| 424 | } | |
| 425 | } | |
| 426 | ||
| 427 | fn possible_well_known_names_for_cfg_value(sess: &Session, value: Symbol) -> Vec<Symbol> { | |
| 428 | #[allow(rustc::potential_query_instability)] | |
| 429 | let mut names = sess | |
| 430 | .check_config | |
| 431 | .well_known_names | |
| 432 | .iter() | |
| 433 | .filter(|name| { | |
| 434 | sess.check_config | |
| 435 | .expecteds | |
| 436 | .get(*name) | |
| 437 | .map(|expected_values| expected_values.contains(&Some(value))) | |
| 438 | .unwrap_or_default() | |
| 439 | }) | |
| 440 | .copied() | |
| 441 | .collect::<Vec<_>>(); | |
| 442 | names.sort_by(|a, b| a.as_str().cmp(b.as_str())); | |
| 443 | names | |
| 444 | } |
compiler/rustc_attr_parsing/src/lib.rs+1| ... | ... | @@ -106,6 +106,7 @@ mod interface; |
| 106 | 106 | /// like lists or name-value pairs. |
| 107 | 107 | pub mod parser; |
| 108 | 108 | |
| 109 | mod check_cfg; | |
| 109 | 110 | mod early_parsed; |
| 110 | 111 | mod errors; |
| 111 | 112 | mod safety; |
compiler/rustc_attr_parsing/src/session_diagnostics.rs-8| ... | ... | @@ -274,11 +274,7 @@ pub(crate) enum IncorrectReprFormatGenericCause { |
| 274 | 274 | Int { |
| 275 | 275 | #[primary_span] |
| 276 | 276 | span: Span, |
| 277 | ||
| 278 | #[skip_arg] | |
| 279 | 277 | name: Symbol, |
| 280 | ||
| 281 | #[skip_arg] | |
| 282 | 278 | value: u128, |
| 283 | 279 | }, |
| 284 | 280 | |
| ... | ... | @@ -290,11 +286,7 @@ pub(crate) enum IncorrectReprFormatGenericCause { |
| 290 | 286 | Symbol { |
| 291 | 287 | #[primary_span] |
| 292 | 288 | span: Span, |
| 293 | ||
| 294 | #[skip_arg] | |
| 295 | 289 | name: Symbol, |
| 296 | ||
| 297 | #[skip_arg] | |
| 298 | 290 | value: Symbol, |
| 299 | 291 | }, |
| 300 | 292 | } |
compiler/rustc_codegen_ssa/src/back/link.rs+9-3| ... | ... | @@ -781,9 +781,15 @@ fn report_linker_output( |
| 781 | 781 | |
| 782 | 782 | // FIXME: Tracked by https://github.com/rust-lang/rust/issues/136113 |
| 783 | 783 | let deployment_mismatch = |line: &str| { |
| 784 | line.starts_with("ld: warning: object file (") | |
| 785 | && line.contains("was built for newer 'macOS' version") | |
| 786 | && line.contains("than being linked") | |
| 784 | // ld64 (object files + dylibs) and ld_prime (object files only): | |
| 785 | (line.starts_with("ld: ") | |
| 786 | && line.contains("was built for newer") | |
| 787 | && line.contains("than being linked")) | |
| 788 | // ld_prime (Xcode 15+, dylibs only): | |
| 789 | || (line.starts_with("ld: ") | |
| 790 | && line.contains("building for") | |
| 791 | && line.contains("but linking with") | |
| 792 | && line.contains("which was built for newer version")) | |
| 787 | 793 | }; |
| 788 | 794 | // FIXME: This is a real warning we would like to show, but it hits too many crates |
| 789 | 795 | // to want to turn it on immediately. |
compiler/rustc_hir_typeck/src/pat.rs+16-1| ... | ... | @@ -1007,7 +1007,22 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { |
| 1007 | 1007 | // |
| 1008 | 1008 | // then that's equivalent to there existing a LUB. |
| 1009 | 1009 | let cause = self.pattern_cause(ti, span); |
| 1010 | if let Err(err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) { | |
| 1010 | if let Err(mut err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) { | |
| 1011 | // If scrutinee is String and pattern is &str, suggest .as_str() | |
| 1012 | let expected = self.resolve_vars_with_obligations(expected); | |
| 1013 | if let ty::Adt(adt, _) = expected.kind() | |
| 1014 | && self.tcx.is_lang_item(adt.did(), LangItem::String) | |
| 1015 | && pat_ty.is_ref() | |
| 1016 | && pat_ty.peel_refs().is_str() | |
| 1017 | && let Some(origin_expr) = ti.origin_expr | |
| 1018 | { | |
| 1019 | err.span_suggestion_verbose( | |
| 1020 | origin_expr.span.shrink_to_hi(), | |
| 1021 | "consider converting the `String` to a `&str` using `.as_str()`", | |
| 1022 | ".as_str()", | |
| 1023 | Applicability::MachineApplicable, | |
| 1024 | ); | |
| 1025 | } | |
| 1011 | 1026 | err.emit(); |
| 1012 | 1027 | } |
| 1013 | 1028 |
compiler/rustc_next_trait_solver/src/solve/trait_goals.rs+2| ... | ... | @@ -898,6 +898,8 @@ where |
| 898 | 898 | predicate: TraitRef::new(ecx.cx(), sized_trait, [ty]).upcast(ecx.cx()), |
| 899 | 899 | }, |
| 900 | 900 | ); |
| 901 | // FIXME(field_projections): This function does some questionable incomplete stuff by | |
| 902 | // returning `Err(NoSolution)` on ambiguity. | |
| 901 | 903 | ecx.try_evaluate_added_goals()? == Certainty::Yes |
| 902 | 904 | } |
| 903 | 905 | && match base.kind() { |
compiler/rustc_parse/src/errors.rs+25| ... | ... | @@ -4639,3 +4639,28 @@ pub(crate) struct ReservedMultihashLint { |
| 4639 | 4639 | )] |
| 4640 | 4640 | pub suggestion: Span, |
| 4641 | 4641 | } |
| 4642 | ||
| 4643 | #[derive(Subdiagnostic)] | |
| 4644 | #[suggestion( | |
| 4645 | "if you meant to write a path, use a double colon:", | |
| 4646 | code = "::", | |
| 4647 | applicability = "maybe-incorrect" | |
| 4648 | )] | |
| 4649 | pub(crate) struct UseDoubleColonSuggestion { | |
| 4650 | #[primary_span] | |
| 4651 | pub colon: Span, | |
| 4652 | } | |
| 4653 | ||
| 4654 | #[derive(Subdiagnostic)] | |
| 4655 | #[multipart_suggestion( | |
| 4656 | "if you meant to create a regular struct, use curly braces:", | |
| 4657 | applicability = "maybe-incorrect" | |
| 4658 | )] | |
| 4659 | pub(crate) struct UseRegularStructSuggestion { | |
| 4660 | #[suggestion_part(code = "{{")] | |
| 4661 | pub open: Span, | |
| 4662 | #[suggestion_part(code = "}}")] | |
| 4663 | pub close: Span, | |
| 4664 | #[suggestion_part(code = "")] | |
| 4665 | pub semicolon: Option<Span>, | |
| 4666 | } |
compiler/rustc_parse/src/parser/item.rs+23-4| ... | ... | @@ -23,7 +23,10 @@ use super::{ |
| 23 | 23 | AllowConstBlockItems, AttrWrapper, ExpKeywordPair, ExpTokenPair, FollowedByType, ForceCollect, |
| 24 | 24 | Parser, PathStyle, Recovered, Trailing, UsePreAttrPos, |
| 25 | 25 | }; |
| 26 | use crate::errors::{self, FnPointerCannotBeAsync, FnPointerCannotBeConst, MacroExpandsToAdtField}; | |
| 26 | use crate::errors::{ | |
| 27 | self, FnPointerCannotBeAsync, FnPointerCannotBeConst, MacroExpandsToAdtField, | |
| 28 | UseDoubleColonSuggestion, UseRegularStructSuggestion, | |
| 29 | }; | |
| 27 | 30 | use crate::exp; |
| 28 | 31 | |
| 29 | 32 | impl<'a> Parser<'a> { |
| ... | ... | @@ -2084,10 +2087,11 @@ impl<'a> Parser<'a> { |
| 2084 | 2087 | Safety::Default |
| 2085 | 2088 | } |
| 2086 | 2089 | } |
| 2087 | ||
| 2090 | /// This is the case where we find `struct Foo<T>(T) where T: Copy;` | |
| 2091 | /// Unit like structs are handled in parse_item_struct function | |
| 2088 | 2092 | pub(super) fn parse_tuple_struct_body(&mut self) -> PResult<'a, ThinVec<FieldDef>> { |
| 2089 | // This is the case where we find `struct Foo<T>(T) where T: Copy;` | |
| 2090 | // Unit like structs are handled in parse_item_struct function | |
| 2093 | let openparen_span = self.token.span; | |
| 2094 | let mut encountered_colon = false; | |
| 2091 | 2095 | self.parse_paren_comma_seq(|p| { |
| 2092 | 2096 | let attrs = p.parse_outer_attributes()?; |
| 2093 | 2097 | p.collect_tokens(None, attrs, ForceCollect::No, |p, attrs| { |
| ... | ... | @@ -2109,6 +2113,8 @@ impl<'a> Parser<'a> { |
| 2109 | 2113 | } |
| 2110 | 2114 | }; |
| 2111 | 2115 | let mut_restriction = p.parse_mut_restriction()?; |
| 2116 | encountered_colon |= | |
| 2117 | p.token.is_ident() && p.look_ahead(1, |tok| tok == &token::Colon); | |
| 2112 | 2118 | // Unsafe fields are not supported in tuple structs, as doing so would result in a |
| 2113 | 2119 | // parsing ambiguity for `struct X(unsafe fn())`. |
| 2114 | 2120 | let ty = match p.parse_ty() { |
| ... | ... | @@ -2156,6 +2162,19 @@ impl<'a> Parser<'a> { |
| 2156 | 2162 | }) |
| 2157 | 2163 | }) |
| 2158 | 2164 | .map(|(r, _)| r) |
| 2165 | .map_err(|mut error| { | |
| 2166 | if encountered_colon { | |
| 2167 | error.subdiagnostic(UseDoubleColonSuggestion { colon: self.token.span }); | |
| 2168 | self.eat_to_tokens(&[exp!(CloseParen)]); | |
| 2169 | self.bump(); | |
| 2170 | error.subdiagnostic(UseRegularStructSuggestion { | |
| 2171 | open: openparen_span, | |
| 2172 | close: self.prev_token.span, | |
| 2173 | semicolon: if self.token == token::Semi { Some(self.token.span) } else { None }, | |
| 2174 | }); | |
| 2175 | } | |
| 2176 | error | |
| 2177 | }) | |
| 2159 | 2178 | } |
| 2160 | 2179 | |
| 2161 | 2180 | /// Parses an element of a struct declaration. |
compiler/rustc_parse_format/src/lib.rs+18| ... | ... | @@ -491,6 +491,7 @@ impl<'input> Parser<'input> { |
| 491 | 491 | ('<' | '^' | '>', _) => self.suggest_format_align(c), |
| 492 | 492 | (',', _) => self.suggest_unsupported_python_numeric_grouping(), |
| 493 | 493 | ('=', '}') => self.suggest_rust_debug_printing_macro(), |
| 494 | ('+', _) => self.suggest_format_missing_colon_for_sign(), | |
| 494 | 495 | _ => self.suggest_positional_arg_instead_of_captured_arg(arg), |
| 495 | 496 | } |
| 496 | 497 | } |
| ... | ... | @@ -939,6 +940,23 @@ impl<'input> Parser<'input> { |
| 939 | 940 | } |
| 940 | 941 | } |
| 941 | 942 | |
| 943 | fn suggest_format_missing_colon_for_sign(&mut self) { | |
| 944 | if let Some((range, _)) = self.consume_pos('+') { | |
| 945 | self.errors.insert( | |
| 946 | 0, | |
| 947 | ParseError { | |
| 948 | description: "the `+` sign flag must appear after `:` in a format string" | |
| 949 | .to_owned(), | |
| 950 | note: Some("`+` comes after `:`, try `{:+}` instead of `{+}`".to_owned()), | |
| 951 | label: "expected `:` before `+` sign flag".to_owned(), | |
| 952 | span: range, | |
| 953 | secondary_label: None, | |
| 954 | suggestion: Suggestion::None, | |
| 955 | }, | |
| 956 | ); | |
| 957 | } | |
| 958 | } | |
| 959 | ||
| 942 | 960 | fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: &Argument<'_>) { |
| 943 | 961 | // If the argument is not an identifier, it is not a field access. |
| 944 | 962 | if !arg.is_identifier() { |
compiler/rustc_parse_format/src/tests.rs+10| ... | ... | @@ -595,3 +595,13 @@ fn diagnostic_format_mod() { |
| 595 | 595 | assert_eq!(parser.line_spans, &[]); |
| 596 | 596 | assert!(parser.errors.is_empty()); |
| 597 | 597 | } |
| 598 | #[test] | |
| 599 | fn format_plus_sign_missing_colon_error() { | |
| 600 | // `{+}` should produce an error suggesting `:` before `+` | |
| 601 | let mut p = Parser::new("{+}", None, None, false, ParseMode::Format); | |
| 602 | let _ = p.by_ref().collect::<Vec<Piece<'static>>>(); | |
| 603 | assert!(!p.errors.is_empty()); | |
| 604 | assert!(p.errors[0].description.contains("`+` sign flag must appear after `:`")); | |
| 605 | assert!(p.errors[0].label.contains("expected `:` before `+` sign flag")); | |
| 606 | assert_eq!(p.errors[0].span, 2..3); | |
| 607 | } |
compiler/rustc_trait_selection/src/errors.rs-9| ... | ... | @@ -869,7 +869,6 @@ pub(crate) enum ExplicitLifetimeRequired<'a> { |
| 869 | 869 | style = "verbose" |
| 870 | 870 | )] |
| 871 | 871 | new_ty_span: Span, |
| 872 | #[skip_arg] | |
| 873 | 872 | new_ty: Ty<'a>, |
| 874 | 873 | }, |
| 875 | 874 | #[diag("explicit lifetime required in parameter type", code = E0621)] |
| ... | ... | @@ -885,7 +884,6 @@ pub(crate) enum ExplicitLifetimeRequired<'a> { |
| 885 | 884 | style = "verbose" |
| 886 | 885 | )] |
| 887 | 886 | new_ty_span: Span, |
| 888 | #[skip_arg] | |
| 889 | 887 | new_ty: Ty<'a>, |
| 890 | 888 | }, |
| 891 | 889 | } |
| ... | ... | @@ -1519,7 +1517,6 @@ pub(crate) enum FunctionPointerSuggestion<'a> { |
| 1519 | 1517 | RemoveRef { |
| 1520 | 1518 | #[primary_span] |
| 1521 | 1519 | span: Span, |
| 1522 | #[skip_arg] | |
| 1523 | 1520 | fn_name: String, |
| 1524 | 1521 | }, |
| 1525 | 1522 | #[suggestion( |
| ... | ... | @@ -1531,9 +1528,7 @@ pub(crate) enum FunctionPointerSuggestion<'a> { |
| 1531 | 1528 | CastRef { |
| 1532 | 1529 | #[primary_span] |
| 1533 | 1530 | span: Span, |
| 1534 | #[skip_arg] | |
| 1535 | 1531 | fn_name: String, |
| 1536 | #[skip_arg] | |
| 1537 | 1532 | sig: Binder<'a, FnSig<'a>>, |
| 1538 | 1533 | }, |
| 1539 | 1534 | #[suggestion( |
| ... | ... | @@ -1545,7 +1540,6 @@ pub(crate) enum FunctionPointerSuggestion<'a> { |
| 1545 | 1540 | Cast { |
| 1546 | 1541 | #[primary_span] |
| 1547 | 1542 | span: Span, |
| 1548 | #[skip_arg] | |
| 1549 | 1543 | sig: Binder<'a, FnSig<'a>>, |
| 1550 | 1544 | }, |
| 1551 | 1545 | #[suggestion( |
| ... | ... | @@ -1557,7 +1551,6 @@ pub(crate) enum FunctionPointerSuggestion<'a> { |
| 1557 | 1551 | CastBoth { |
| 1558 | 1552 | #[primary_span] |
| 1559 | 1553 | span: Span, |
| 1560 | #[skip_arg] | |
| 1561 | 1554 | found_sig: Binder<'a, FnSig<'a>>, |
| 1562 | 1555 | expected_sig: Binder<'a, FnSig<'a>>, |
| 1563 | 1556 | }, |
| ... | ... | @@ -1570,9 +1563,7 @@ pub(crate) enum FunctionPointerSuggestion<'a> { |
| 1570 | 1563 | CastBothRef { |
| 1571 | 1564 | #[primary_span] |
| 1572 | 1565 | span: Span, |
| 1573 | #[skip_arg] | |
| 1574 | 1566 | fn_name: String, |
| 1575 | #[skip_arg] | |
| 1576 | 1567 | found_sig: Binder<'a, FnSig<'a>>, |
| 1577 | 1568 | expected_sig: Binder<'a, FnSig<'a>>, |
| 1578 | 1569 | }, |
src/ci/docker/host-x86_64/x86_64-gnu-gcc-core-tests/Dockerfile+2-1| ... | ... | @@ -44,4 +44,5 @@ ENV RUST_CONFIGURE_ARGS="--build=x86_64-unknown-linux-gnu \ |
| 44 | 44 | --set rust.codegen-backends=[\\\"gcc\\\"]" |
| 45 | 45 | ENV SCRIPT="python3 ../x.py \ |
| 46 | 46 | --stage 1 \ |
| 47 | test library/coretests" | |
| 47 | test library/coretests \ | |
| 48 | --set rust.codegen-backends=[\\\"gcc\\\"]" |
src/librustdoc/clean/utils.rs+4-2| ... | ... | @@ -351,8 +351,10 @@ pub(crate) fn name_from_pat(p: &hir::Pat<'_>) -> Symbol { |
| 351 | 351 | pub(crate) fn print_const(tcx: TyCtxt<'_>, n: ty::Const<'_>) -> String { |
| 352 | 352 | match n.kind() { |
| 353 | 353 | ty::ConstKind::Unevaluated(ty::UnevaluatedConst { def, args: _ }) => { |
| 354 | if let Some(def) = def.as_local() { | |
| 355 | rendered_const(tcx, tcx.hir_body_owned_by(def), def) | |
| 354 | if let Some(def) = def.as_local() | |
| 355 | && let Some(body_id) = tcx.hir_maybe_body_owned_by(def) | |
| 356 | { | |
| 357 | rendered_const(tcx, body_id, def) | |
| 356 | 358 | } else { |
| 357 | 359 | inline::print_inlined_const(tcx, def) |
| 358 | 360 | } |
tests/run-make/macos-deployment-target-warning/dylib_warnings.txt created+11| ... | ... | @@ -0,0 +1,11 @@ |
| 1 | warning: NORMALIZED_DYLIB_DEPLOYMENT_MISMATCH_LINKER_WARNING | |
| 2 | ||
| 3 | | | |
| 4 | note: the lint level is defined here | |
| 5 | --> main_dylib.rs:1:9 | |
| 6 | | | |
| 7 | 1 | #![warn(linker_info, linker_messages)] | |
| 8 | | ^^^^^^^^^^^ | |
| 9 | ||
| 10 | warning: 1 warning emitted | |
| 11 |
tests/run-make/macos-deployment-target-warning/main_dylib.rs created+8| ... | ... | @@ -0,0 +1,8 @@ |
| 1 | #![warn(linker_info, linker_messages)] | |
| 2 | unsafe extern "C" { | |
| 3 | safe fn foo(); | |
| 4 | } | |
| 5 | ||
| 6 | fn main() { | |
| 7 | foo(); | |
| 8 | } |
tests/run-make/macos-deployment-target-warning/rmake.rs+32-1| ... | ... | @@ -1,4 +1,6 @@ |
| 1 | // ignore-tidy-linelength | |
| 1 | 2 | //! Tests that deployment target linker warnings are shown as `linker-info`, not `linker-messages` |
| 3 | //! See <https://github.com/rust-lang/rust/issues/156714> | |
| 2 | 4 | |
| 3 | 5 | //@ only-macos |
| 4 | 6 | |
| ... | ... | @@ -7,6 +9,12 @@ use run_make_support::external_deps::llvm::llvm_ar; |
| 7 | 9 | use run_make_support::{diff, rustc}; |
| 8 | 10 | |
| 9 | 11 | fn main() { |
| 12 | let ld64_obj = r"ld: warning: object file \(.*\) was built for newer .+ version \(\d+\.\d+\) than being linked \(\d+\.\d+\)"; | |
| 13 | let ld_prime_obj = r"ld: warning: object file \(.*\) was built for newer '.+' version \(\d+\.\d+\) than being linked \(\d+\.\d+\)"; | |
| 14 | let ld64_dylib = r"ld: warning: dylib \(.*\) was built for newer .+ version \(\d+\.\d+\) than being linked \(\d+\.\d+\)"; | |
| 15 | let ld_prime_dylib = r"ld: warning: building for [^ ,]+, but linking with dylib '[^']*' which was built for newer version [0-9.]+"; | |
| 16 | ||
| 17 | // Test 1: static archive (object file mismatch) | |
| 10 | 18 | cc().arg("-c").arg("-mmacosx-version-min=15.5").output("foo.o").input("foo.c").run(); |
| 11 | 19 | llvm_ar().obj_to_ar().output_input("libfoo.a", "foo.o").run(); |
| 12 | 20 | |
| ... | ... | @@ -21,6 +29,29 @@ fn main() { |
| 21 | 29 | diff() |
| 22 | 30 | .expected_file("warnings.txt") |
| 23 | 31 | .actual_text("(rustc -W linker-info)", &warnings) |
| 24 | .normalize(r"\(.*/rmake_out/", "(TEST_DIR/") | |
| 32 | .normalize(ld64_obj, "NORMALIZED_OBJECT_DEPLOYMENT_MISMATCH_LINKER_WARNING") | |
| 33 | .normalize(ld_prime_obj, "NORMALIZED_OBJECT_DEPLOYMENT_MISMATCH_LINKER_WARNING") | |
| 34 | .run(); | |
| 35 | ||
| 36 | // Test 2: shared library (dylib mismatch) | |
| 37 | cc().arg("-shared") | |
| 38 | .arg("-mmacosx-version-min=15.5") | |
| 39 | .output("libbar.dylib") | |
| 40 | .input("foo.c") | |
| 41 | .run(); | |
| 42 | ||
| 43 | let dylib_warnings = rustc() | |
| 44 | .arg("-lbar") | |
| 45 | .link_arg("-mmacosx-version-min=11.2") | |
| 46 | .input("main_dylib.rs") | |
| 47 | .crate_type("bin") | |
| 25 | 48 | .run() |
| 49 | .stderr_utf8(); | |
| 50 | ||
| 51 | diff() | |
| 52 | .expected_file("dylib_warnings.txt") | |
| 53 | .actual_text("(rustc -W linker-info dylib)", &dylib_warnings) | |
| 54 | .normalize(ld64_dylib, "NORMALIZED_DYLIB_DEPLOYMENT_MISMATCH_LINKER_WARNING") | |
| 55 | .normalize(ld_prime_dylib, "NORMALIZED_DYLIB_DEPLOYMENT_MISMATCH_LINKER_WARNING") | |
| 56 | .run(); | |
| 26 | 57 | } |
tests/run-make/macos-deployment-target-warning/warnings.txt+1-1| ... | ... | @@ -1,4 +1,4 @@ |
| 1 | warning: ld: warning: object file (TEST_DIR/libfoo.a[2](foo.o)) was built for newer 'macOS' version (15.5) than being linked (11.2) | |
| 1 | warning: NORMALIZED_OBJECT_DEPLOYMENT_MISMATCH_LINKER_WARNING | |
| 2 | 2 | |
| 3 | 3 | | |
| 4 | 4 | note: the lint level is defined here |
tests/rustdoc-ui/type-const-associated-const-no-body.rs created+16| ... | ... | @@ -0,0 +1,16 @@ |
| 1 | //! Regression test for <https://github.com/rust-lang/rust/issues/149287> | |
| 2 | //! Ensure that rustdoc does not ICE when a body-less type const is used | |
| 3 | //! as an associated const. | |
| 4 | //@ check-pass | |
| 5 | ||
| 6 | #![feature(min_generic_const_args)] | |
| 7 | ||
| 8 | pub trait Tr { | |
| 9 | type const SIZE: usize; | |
| 10 | } | |
| 11 | ||
| 12 | fn mk_array<T: Tr>() -> [(); <T as Tr>::SIZE] { | |
| 13 | [(); T::SIZE] | |
| 14 | } | |
| 15 | ||
| 16 | fn main() {} |
tests/ui/fmt/format-string-wrong-order.rs+4| ... | ... | @@ -20,4 +20,8 @@ fn main() { |
| 20 | 20 | //~^ ERROR invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}` |
| 21 | 21 | println!("{0:#X>18}", 12345); |
| 22 | 22 | //~^ ERROR invalid format string: expected alignment specifier after `:` in format string; example: `{:>?}` |
| 23 | format!("{+:}"); | |
| 24 | //~^ ERROR invalid format string: the `+` sign flag must appear after `:` in a format string | |
| 25 | format!("{bar+:}"); | |
| 26 | //~^ ERROR invalid format string: the `+` sign flag must appear after `:` in a format string | |
| 23 | 27 | } |
tests/ui/fmt/format-string-wrong-order.stderr+17-1| ... | ... | @@ -74,5 +74,21 @@ error: invalid format string: expected alignment specifier after `:` in format s |
| 74 | 74 | LL | println!("{0:#X>18}", 12345); |
| 75 | 75 | | ^ expected `>` to occur after `:` in format string |
| 76 | 76 | |
| 77 | error: aborting due to 10 previous errors | |
| 77 | error: invalid format string: the `+` sign flag must appear after `:` in a format string | |
| 78 | --> $DIR/format-string-wrong-order.rs:23:15 | |
| 79 | | | |
| 80 | LL | format!("{+:}"); | |
| 81 | | ^ expected `:` before `+` sign flag in format string | |
| 82 | | | |
| 83 | = note: `+` comes after `:`, try `{:+}` instead of `{+}` | |
| 84 | ||
| 85 | error: invalid format string: the `+` sign flag must appear after `:` in a format string | |
| 86 | --> $DIR/format-string-wrong-order.rs:25:18 | |
| 87 | | | |
| 88 | LL | format!("{bar+:}"); | |
| 89 | | ^ expected `:` before `+` sign flag in format string | |
| 90 | | | |
| 91 | = note: `+` comes after `:`, try `{:+}` instead of `{+}` | |
| 92 | ||
| 93 | error: aborting due to 12 previous errors | |
| 78 | 94 |
tests/ui/parser/field-name-in-tuple-struct.rs created+6| ... | ... | @@ -0,0 +1,6 @@ |
| 1 | // Provide diagnostics when the user writes field names in tuple struct.(issue#144595) | |
| 2 | ||
| 3 | struct Foo(a:u8,b:u8); | |
| 4 | //~^ ERROR expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `:` | |
| 5 | //~| HELP if you meant to write a path, use a double colon: | |
| 6 | //~| HELP if you meant to create a regular struct, use curly braces: |
tests/ui/parser/field-name-in-tuple-struct.stderr created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | error: expected one of `!`, `(`, `)`, `+`, `,`, `::`, or `<`, found `:` | |
| 2 | --> $DIR/field-name-in-tuple-struct.rs:3:13 | |
| 3 | | | |
| 4 | LL | struct Foo(a:u8,b:u8); | |
| 5 | | ^ expected one of 7 possible tokens | |
| 6 | | | |
| 7 | help: if you meant to write a path, use a double colon: | |
| 8 | | | |
| 9 | LL | struct Foo(a::u8,b:u8); | |
| 10 | | + | |
| 11 | help: if you meant to create a regular struct, use curly braces: | |
| 12 | | | |
| 13 | LL - struct Foo(a:u8,b:u8); | |
| 14 | LL + struct Foo{a:u8,b:u8} | |
| 15 | | | |
| 16 | ||
| 17 | error: aborting due to 1 previous error | |
| 18 |
tests/ui/pattern/deref-patterns/needs-gate.stderr+5| ... | ... | @@ -76,6 +76,11 @@ LL | match "str".to_owned() { |
| 76 | 76 | | ---------------- this expression has type `String` |
| 77 | 77 | LL | "str" => {} |
| 78 | 78 | | ^^^^^ expected `String`, found `&str` |
| 79 | | | |
| 80 | help: consider converting the `String` to a `&str` using `.as_str()` | |
| 81 | | | |
| 82 | LL | match "str".to_owned().as_str() { | |
| 83 | | +++++++++ | |
| 79 | 84 | |
| 80 | 85 | error[E0308]: mismatched types |
| 81 | 86 | --> $DIR/needs-gate.rs:52:12 |
tests/ui/pattern/string-match-as-str-suggestion.fixed created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | //@ run-rustfix | |
| 2 | ||
| 3 | fn main() { | |
| 4 | let s = "yes".to_owned(); | |
| 5 | ||
| 6 | let _ = match s.as_str() { | |
| 7 | "yes" => Some(true), | |
| 8 | //~^ ERROR mismatched types | |
| 9 | "no" => Some(false), | |
| 10 | //~^ ERROR mismatched types | |
| 11 | _ => None, | |
| 12 | }; | |
| 13 | ||
| 14 | let s2 = String::from("hello"); | |
| 15 | if let "hello" = s2.as_str() { | |
| 16 | //~^ ERROR mismatched types | |
| 17 | } | |
| 18 | } |
tests/ui/pattern/string-match-as-str-suggestion.rs created+18| ... | ... | @@ -0,0 +1,18 @@ |
| 1 | //@ run-rustfix | |
| 2 | ||
| 3 | fn main() { | |
| 4 | let s = "yes".to_owned(); | |
| 5 | ||
| 6 | let _ = match s { | |
| 7 | "yes" => Some(true), | |
| 8 | //~^ ERROR mismatched types | |
| 9 | "no" => Some(false), | |
| 10 | //~^ ERROR mismatched types | |
| 11 | _ => None, | |
| 12 | }; | |
| 13 | ||
| 14 | let s2 = String::from("hello"); | |
| 15 | if let "hello" = s2 { | |
| 16 | //~^ ERROR mismatched types | |
| 17 | } | |
| 18 | } |
tests/ui/pattern/string-match-as-str-suggestion.stderr created+43| ... | ... | @@ -0,0 +1,43 @@ |
| 1 | error[E0308]: mismatched types | |
| 2 | --> $DIR/string-match-as-str-suggestion.rs:7:9 | |
| 3 | | | |
| 4 | LL | let _ = match s { | |
| 5 | | - this expression has type `String` | |
| 6 | LL | "yes" => Some(true), | |
| 7 | | ^^^^^ expected `String`, found `&str` | |
| 8 | | | |
| 9 | help: consider converting the `String` to a `&str` using `.as_str()` | |
| 10 | | | |
| 11 | LL | let _ = match s.as_str() { | |
| 12 | | +++++++++ | |
| 13 | ||
| 14 | error[E0308]: mismatched types | |
| 15 | --> $DIR/string-match-as-str-suggestion.rs:9:9 | |
| 16 | | | |
| 17 | LL | let _ = match s { | |
| 18 | | - this expression has type `String` | |
| 19 | ... | |
| 20 | LL | "no" => Some(false), | |
| 21 | | ^^^^ expected `String`, found `&str` | |
| 22 | | | |
| 23 | help: consider converting the `String` to a `&str` using `.as_str()` | |
| 24 | | | |
| 25 | LL | let _ = match s.as_str() { | |
| 26 | | +++++++++ | |
| 27 | ||
| 28 | error[E0308]: mismatched types | |
| 29 | --> $DIR/string-match-as-str-suggestion.rs:15:12 | |
| 30 | | | |
| 31 | LL | if let "hello" = s2 { | |
| 32 | | ^^^^^^^ -- this expression has type `String` | |
| 33 | | | | |
| 34 | | expected `String`, found `&str` | |
| 35 | | | |
| 36 | help: consider converting the `String` to a `&str` using `.as_str()` | |
| 37 | | | |
| 38 | LL | if let "hello" = s2.as_str() { | |
| 39 | | +++++++++ | |
| 40 | ||
| 41 | error: aborting due to 3 previous errors | |
| 42 | ||
| 43 | For more information about this error, try `rustc --explain E0308`. |
triagebot.toml+1-1| ... | ... | @@ -1050,7 +1050,7 @@ cc = ["@Nadrieril"] |
| 1050 | 1050 | message = "Some changes occurred in cfg and check-cfg configuration" |
| 1051 | 1051 | cc = ["@Urgau"] |
| 1052 | 1052 | |
| 1053 | [mentions."compiler/rustc_attr_parsing/src/attributes/diagnostic/check_cfg.rs"] | |
| 1053 | [mentions."compiler/rustc_attr_parsing/src/check_cfg.rs"] | |
| 1054 | 1054 | message = "Some changes occurred in check-cfg diagnostics" |
| 1055 | 1055 | cc = ["@Urgau"] |
| 1056 | 1056 |