mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-03 20:23:59 +00:00
Auto merge of #97476 - Dylan-DPC:rollup-t53nxoe, r=Dylan-DPC
Rollup of 5 pull requests Successful merges: - #94640 (Partially stabilize `(const_)slice_ptr_len` feature by stabilizing `NonNull::len`) - #97034 (Implement `Hash` for `core::alloc::Layout`) - #97327 (macros: introduce `fluent_messages` macro ) - #97448 (docs: Don't imply that OsStr on Unix is always UTF-8) - #97466 ([bootstrap] Move `sanitize_sh` from `dist` to `install`) Failed merges: r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
19abca1172
@ -4010,10 +4010,14 @@ dependencies = [
|
|||||||
name = "rustc_macros"
|
name = "rustc_macros"
|
||||||
version = "0.1.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"annotate-snippets",
|
||||||
|
"fluent-bundle",
|
||||||
|
"fluent-syntax",
|
||||||
"proc-macro2",
|
"proc-macro2",
|
||||||
"quote",
|
"quote",
|
||||||
"syn",
|
"syn",
|
||||||
"synstructure",
|
"synstructure",
|
||||||
|
"unic-langid",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
use fluent_bundle::FluentResource;
|
use fluent_bundle::FluentResource;
|
||||||
use fluent_syntax::parser::ParserError;
|
use fluent_syntax::parser::ParserError;
|
||||||
use rustc_data_structures::sync::Lrc;
|
use rustc_data_structures::sync::Lrc;
|
||||||
use rustc_macros::{Decodable, Encodable};
|
use rustc_macros::{fluent_messages, Decodable, Encodable};
|
||||||
use rustc_span::Span;
|
use rustc_span::Span;
|
||||||
use std::borrow::Cow;
|
use std::borrow::Cow;
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
@ -29,8 +29,13 @@ use intl_memoizer::IntlLangMemoizer;
|
|||||||
pub use fluent_bundle::{FluentArgs, FluentError, FluentValue};
|
pub use fluent_bundle::{FluentArgs, FluentError, FluentValue};
|
||||||
pub use unic_langid::{langid, LanguageIdentifier};
|
pub use unic_langid::{langid, LanguageIdentifier};
|
||||||
|
|
||||||
pub static DEFAULT_LOCALE_RESOURCES: &'static [&'static str] =
|
// Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module.
|
||||||
&[include_str!("../locales/en-US/typeck.ftl"), include_str!("../locales/en-US/parser.ftl")];
|
fluent_messages! {
|
||||||
|
parser => "../locales/en-US/parser.ftl",
|
||||||
|
typeck => "../locales/en-US/typeck.ftl",
|
||||||
|
}
|
||||||
|
|
||||||
|
pub use fluent_generated::{self as fluent, DEFAULT_LOCALE_RESOURCES};
|
||||||
|
|
||||||
pub type FluentBundle = fluent_bundle::bundle::FluentBundle<FluentResource, IntlLangMemoizer>;
|
pub type FluentBundle = fluent_bundle::bundle::FluentBundle<FluentResource, IntlLangMemoizer>;
|
||||||
|
|
||||||
|
@ -31,8 +31,8 @@ use rustc_data_structures::stable_hasher::StableHasher;
|
|||||||
use rustc_data_structures::sync::{self, Lock, Lrc};
|
use rustc_data_structures::sync::{self, Lock, Lrc};
|
||||||
use rustc_data_structures::AtomicRef;
|
use rustc_data_structures::AtomicRef;
|
||||||
pub use rustc_error_messages::{
|
pub use rustc_error_messages::{
|
||||||
fallback_fluent_bundle, fluent_bundle, DiagnosticMessage, FluentBundle, LanguageIdentifier,
|
fallback_fluent_bundle, fluent, fluent_bundle, DiagnosticMessage, FluentBundle,
|
||||||
LazyFallbackBundle, MultiSpan, SpanLabel, DEFAULT_LOCALE_RESOURCES,
|
LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, DEFAULT_LOCALE_RESOURCES,
|
||||||
};
|
};
|
||||||
pub use rustc_lint_defs::{pluralize, Applicability};
|
pub use rustc_lint_defs::{pluralize, Applicability};
|
||||||
use rustc_span::source_map::SourceMap;
|
use rustc_span::source_map::SourceMap;
|
||||||
|
@ -7,7 +7,11 @@ edition = "2021"
|
|||||||
proc-macro = true
|
proc-macro = true
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
annotate-snippets = "0.8.0"
|
||||||
|
fluent-bundle = "0.15.2"
|
||||||
|
fluent-syntax = "0.11"
|
||||||
synstructure = "0.12.1"
|
synstructure = "0.12.1"
|
||||||
syn = { version = "1", features = ["full"] }
|
syn = { version = "1", features = ["full"] }
|
||||||
proc-macro2 = "1"
|
proc-macro2 = "1"
|
||||||
quote = "1"
|
quote = "1"
|
||||||
|
unic-langid = { version = "0.9.0", features = ["macros"] }
|
||||||
|
254
compiler/rustc_macros/src/diagnostics/fluent.rs
Normal file
254
compiler/rustc_macros/src/diagnostics/fluent.rs
Normal file
@ -0,0 +1,254 @@
|
|||||||
|
use annotate_snippets::{
|
||||||
|
display_list::DisplayList,
|
||||||
|
snippet::{Annotation, AnnotationType, Slice, Snippet, SourceAnnotation},
|
||||||
|
};
|
||||||
|
use fluent_bundle::{FluentBundle, FluentError, FluentResource};
|
||||||
|
use fluent_syntax::{
|
||||||
|
ast::{Attribute, Entry, Identifier, Message},
|
||||||
|
parser::ParserError,
|
||||||
|
};
|
||||||
|
use proc_macro::{Diagnostic, Level, Span};
|
||||||
|
use proc_macro2::TokenStream;
|
||||||
|
use quote::quote;
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fs::File,
|
||||||
|
io::Read,
|
||||||
|
path::{Path, PathBuf},
|
||||||
|
};
|
||||||
|
use syn::{
|
||||||
|
parse::{Parse, ParseStream},
|
||||||
|
parse_macro_input,
|
||||||
|
punctuated::Punctuated,
|
||||||
|
token, Ident, LitStr, Result,
|
||||||
|
};
|
||||||
|
use unic_langid::langid;
|
||||||
|
|
||||||
|
struct Resource {
|
||||||
|
ident: Ident,
|
||||||
|
#[allow(dead_code)]
|
||||||
|
fat_arrow_token: token::FatArrow,
|
||||||
|
resource: LitStr,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for Resource {
|
||||||
|
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||||
|
Ok(Resource {
|
||||||
|
ident: input.parse()?,
|
||||||
|
fat_arrow_token: input.parse()?,
|
||||||
|
resource: input.parse()?,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Resources(Punctuated<Resource, token::Comma>);
|
||||||
|
|
||||||
|
impl Parse for Resources {
|
||||||
|
fn parse(input: ParseStream<'_>) -> Result<Self> {
|
||||||
|
let mut resources = Punctuated::new();
|
||||||
|
loop {
|
||||||
|
if input.is_empty() || input.peek(token::Brace) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let value = input.parse()?;
|
||||||
|
resources.push_value(value);
|
||||||
|
if !input.peek(token::Comma) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
let punct = input.parse()?;
|
||||||
|
resources.push_punct(punct);
|
||||||
|
}
|
||||||
|
Ok(Resources(resources))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Helper function for returning an absolute path for macro-invocation relative file paths.
|
||||||
|
///
|
||||||
|
/// If the input is already absolute, then the input is returned. If the input is not absolute,
|
||||||
|
/// then it is appended to the directory containing the source file with this macro invocation.
|
||||||
|
fn invocation_relative_path_to_absolute(span: Span, path: &str) -> PathBuf {
|
||||||
|
let path = Path::new(path);
|
||||||
|
if path.is_absolute() {
|
||||||
|
path.to_path_buf()
|
||||||
|
} else {
|
||||||
|
// `/a/b/c/foo/bar.rs` contains the current macro invocation
|
||||||
|
let mut source_file_path = span.source_file().path();
|
||||||
|
// `/a/b/c/foo/`
|
||||||
|
source_file_path.pop();
|
||||||
|
// `/a/b/c/foo/../locales/en-US/example.ftl`
|
||||||
|
source_file_path.push(path);
|
||||||
|
source_file_path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// See [rustc_macros::fluent_messages].
|
||||||
|
pub(crate) fn fluent_messages(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
|
||||||
|
let resources = parse_macro_input!(input as Resources);
|
||||||
|
|
||||||
|
// Cannot iterate over individual messages in a bundle, so do that using the
|
||||||
|
// `FluentResource` instead. Construct a bundle anyway to find out if there are conflicting
|
||||||
|
// messages in the resources.
|
||||||
|
let mut bundle = FluentBundle::new(vec![langid!("en-US")]);
|
||||||
|
|
||||||
|
// Map of Fluent identifiers to the `Span` of the resource that defined them, used for better
|
||||||
|
// diagnostics.
|
||||||
|
let mut previous_defns = HashMap::new();
|
||||||
|
|
||||||
|
let mut includes = TokenStream::new();
|
||||||
|
let mut generated = TokenStream::new();
|
||||||
|
for res in resources.0 {
|
||||||
|
let ident_span = res.ident.span().unwrap();
|
||||||
|
let path_span = res.resource.span().unwrap();
|
||||||
|
|
||||||
|
let relative_ftl_path = res.resource.value();
|
||||||
|
let absolute_ftl_path =
|
||||||
|
invocation_relative_path_to_absolute(ident_span, &relative_ftl_path);
|
||||||
|
// As this macro also outputs an `include_str!` for this file, the macro will always be
|
||||||
|
// re-executed when the file changes.
|
||||||
|
let mut resource_file = match File::open(absolute_ftl_path) {
|
||||||
|
Ok(resource_file) => resource_file,
|
||||||
|
Err(e) => {
|
||||||
|
Diagnostic::spanned(path_span, Level::Error, "could not open Fluent resource")
|
||||||
|
.note(e.to_string())
|
||||||
|
.emit();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut resource_contents = String::new();
|
||||||
|
if let Err(e) = resource_file.read_to_string(&mut resource_contents) {
|
||||||
|
Diagnostic::spanned(path_span, Level::Error, "could not read Fluent resource")
|
||||||
|
.note(e.to_string())
|
||||||
|
.emit();
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let resource = match FluentResource::try_new(resource_contents) {
|
||||||
|
Ok(resource) => resource,
|
||||||
|
Err((this, errs)) => {
|
||||||
|
Diagnostic::spanned(path_span, Level::Error, "could not parse Fluent resource")
|
||||||
|
.help("see additional errors emitted")
|
||||||
|
.emit();
|
||||||
|
for ParserError { pos, slice: _, kind } in errs {
|
||||||
|
let mut err = kind.to_string();
|
||||||
|
// Entirely unnecessary string modification so that the error message starts
|
||||||
|
// with a lowercase as rustc errors do.
|
||||||
|
err.replace_range(
|
||||||
|
0..1,
|
||||||
|
&err.chars().next().unwrap().to_lowercase().to_string(),
|
||||||
|
);
|
||||||
|
|
||||||
|
let line_starts: Vec<usize> = std::iter::once(0)
|
||||||
|
.chain(
|
||||||
|
this.source()
|
||||||
|
.char_indices()
|
||||||
|
.filter_map(|(i, c)| Some(i + 1).filter(|_| c == '\n')),
|
||||||
|
)
|
||||||
|
.collect();
|
||||||
|
let line_start = line_starts
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(line, idx)| (line + 1, idx))
|
||||||
|
.filter(|(_, idx)| **idx <= pos.start)
|
||||||
|
.last()
|
||||||
|
.unwrap()
|
||||||
|
.0;
|
||||||
|
|
||||||
|
let snippet = Snippet {
|
||||||
|
title: Some(Annotation {
|
||||||
|
label: Some(&err),
|
||||||
|
id: None,
|
||||||
|
annotation_type: AnnotationType::Error,
|
||||||
|
}),
|
||||||
|
footer: vec![],
|
||||||
|
slices: vec![Slice {
|
||||||
|
source: this.source(),
|
||||||
|
line_start,
|
||||||
|
origin: Some(&relative_ftl_path),
|
||||||
|
fold: true,
|
||||||
|
annotations: vec![SourceAnnotation {
|
||||||
|
label: "",
|
||||||
|
annotation_type: AnnotationType::Error,
|
||||||
|
range: (pos.start, pos.end - 1),
|
||||||
|
}],
|
||||||
|
}],
|
||||||
|
opt: Default::default(),
|
||||||
|
};
|
||||||
|
let dl = DisplayList::from(snippet);
|
||||||
|
eprintln!("{}\n", dl);
|
||||||
|
}
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
let mut constants = TokenStream::new();
|
||||||
|
for entry in resource.entries() {
|
||||||
|
let span = res.ident.span();
|
||||||
|
if let Entry::Message(Message { id: Identifier { name }, attributes, .. }) = entry {
|
||||||
|
let _ = previous_defns.entry(name.to_string()).or_insert(ident_span);
|
||||||
|
|
||||||
|
// `typeck-foo-bar` => `foo_bar`
|
||||||
|
let snake_name = Ident::new(
|
||||||
|
&name.replace(&format!("{}-", res.ident), "").replace("-", "_"),
|
||||||
|
span,
|
||||||
|
);
|
||||||
|
constants.extend(quote! {
|
||||||
|
pub const #snake_name: crate::DiagnosticMessage =
|
||||||
|
crate::DiagnosticMessage::FluentIdentifier(
|
||||||
|
std::borrow::Cow::Borrowed(#name),
|
||||||
|
None
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
for Attribute { id: Identifier { name: attr_name }, .. } in attributes {
|
||||||
|
let attr_snake_name = attr_name.replace("-", "_");
|
||||||
|
let snake_name = Ident::new(&format!("{snake_name}_{attr_snake_name}"), span);
|
||||||
|
constants.extend(quote! {
|
||||||
|
pub const #snake_name: crate::DiagnosticMessage =
|
||||||
|
crate::DiagnosticMessage::FluentIdentifier(
|
||||||
|
std::borrow::Cow::Borrowed(#name),
|
||||||
|
Some(std::borrow::Cow::Borrowed(#attr_name))
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Err(errs) = bundle.add_resource(resource) {
|
||||||
|
for e in errs {
|
||||||
|
match e {
|
||||||
|
FluentError::Overriding { kind, id } => {
|
||||||
|
Diagnostic::spanned(
|
||||||
|
ident_span,
|
||||||
|
Level::Error,
|
||||||
|
format!("overrides existing {}: `{}`", kind, id),
|
||||||
|
)
|
||||||
|
.span_help(previous_defns[&id], "previously defined in this resource")
|
||||||
|
.emit();
|
||||||
|
}
|
||||||
|
FluentError::ResolverError(_) | FluentError::ParserError(_) => unreachable!(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
includes.extend(quote! { include_str!(#relative_ftl_path), });
|
||||||
|
|
||||||
|
let ident = res.ident;
|
||||||
|
generated.extend(quote! {
|
||||||
|
pub mod #ident {
|
||||||
|
#constants
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[allow(non_upper_case_globals)]
|
||||||
|
#[doc(hidden)]
|
||||||
|
pub mod fluent_generated {
|
||||||
|
pub static DEFAULT_LOCALE_RESOURCES: &'static [&'static str] = &[
|
||||||
|
#includes
|
||||||
|
];
|
||||||
|
|
||||||
|
#generated
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.into()
|
||||||
|
}
|
@ -1,9 +1,11 @@
|
|||||||
mod diagnostic;
|
mod diagnostic;
|
||||||
mod error;
|
mod error;
|
||||||
|
mod fluent;
|
||||||
mod subdiagnostic;
|
mod subdiagnostic;
|
||||||
mod utils;
|
mod utils;
|
||||||
|
|
||||||
use diagnostic::SessionDiagnosticDerive;
|
use diagnostic::SessionDiagnosticDerive;
|
||||||
|
pub(crate) use fluent::fluent_messages;
|
||||||
use proc_macro2::TokenStream;
|
use proc_macro2::TokenStream;
|
||||||
use quote::format_ident;
|
use quote::format_ident;
|
||||||
use subdiagnostic::SessionSubdiagnosticDerive;
|
use subdiagnostic::SessionSubdiagnosticDerive;
|
||||||
@ -12,7 +14,7 @@ use synstructure::Structure;
|
|||||||
/// Implements `#[derive(SessionDiagnostic)]`, which allows for errors to be specified as a struct,
|
/// Implements `#[derive(SessionDiagnostic)]`, which allows for errors to be specified as a struct,
|
||||||
/// independent from the actual diagnostics emitting code.
|
/// independent from the actual diagnostics emitting code.
|
||||||
///
|
///
|
||||||
/// ```ignore (pseudo-rust)
|
/// ```ignore (rust)
|
||||||
/// # extern crate rustc_errors;
|
/// # extern crate rustc_errors;
|
||||||
/// # use rustc_errors::Applicability;
|
/// # use rustc_errors::Applicability;
|
||||||
/// # extern crate rustc_span;
|
/// # extern crate rustc_span;
|
||||||
@ -43,7 +45,7 @@ use synstructure::Structure;
|
|||||||
///
|
///
|
||||||
/// Then, later, to emit the error:
|
/// Then, later, to emit the error:
|
||||||
///
|
///
|
||||||
/// ```ignore (pseudo-rust)
|
/// ```ignore (rust)
|
||||||
/// sess.emit_err(MoveOutOfBorrowError {
|
/// sess.emit_err(MoveOutOfBorrowError {
|
||||||
/// expected,
|
/// expected,
|
||||||
/// actual,
|
/// actual,
|
||||||
@ -67,7 +69,7 @@ pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream {
|
|||||||
/// suggestions to be specified as a structs or enums, independent from the actual diagnostics
|
/// suggestions to be specified as a structs or enums, independent from the actual diagnostics
|
||||||
/// emitting code or diagnostic derives.
|
/// emitting code or diagnostic derives.
|
||||||
///
|
///
|
||||||
/// ```ignore (pseudo-rust)
|
/// ```ignore (rust)
|
||||||
/// #[derive(SessionSubdiagnostic)]
|
/// #[derive(SessionSubdiagnostic)]
|
||||||
/// pub enum ExpectedIdentifierLabel<'tcx> {
|
/// pub enum ExpectedIdentifierLabel<'tcx> {
|
||||||
/// #[label(slug = "parser-expected-identifier")]
|
/// #[label(slug = "parser-expected-identifier")]
|
||||||
@ -104,7 +106,7 @@ pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream {
|
|||||||
///
|
///
|
||||||
/// Then, later, to add the subdiagnostic:
|
/// Then, later, to add the subdiagnostic:
|
||||||
///
|
///
|
||||||
/// ```ignore (pseudo-rust)
|
/// ```ignore (rust)
|
||||||
/// diag.subdiagnostic(ExpectedIdentifierLabel::WithoutFound { span });
|
/// diag.subdiagnostic(ExpectedIdentifierLabel::WithoutFound { span });
|
||||||
///
|
///
|
||||||
/// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident });
|
/// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident });
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
#![feature(let_else)]
|
#![feature(let_else)]
|
||||||
#![feature(never_type)]
|
#![feature(never_type)]
|
||||||
#![feature(proc_macro_diagnostic)]
|
#![feature(proc_macro_diagnostic)]
|
||||||
|
#![feature(proc_macro_span)]
|
||||||
#![allow(rustc::default_hash_types)]
|
#![allow(rustc::default_hash_types)]
|
||||||
#![recursion_limit = "128"]
|
#![recursion_limit = "128"]
|
||||||
|
|
||||||
@ -49,6 +50,64 @@ pub fn newtype_index(input: TokenStream) -> TokenStream {
|
|||||||
newtype::newtype(input)
|
newtype::newtype(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Implements the `fluent_messages` macro, which performs compile-time validation of the
|
||||||
|
/// compiler's Fluent resources (i.e. that the resources parse and don't multiply define the same
|
||||||
|
/// messages) and generates constants that make using those messages in diagnostics more ergonomic.
|
||||||
|
///
|
||||||
|
/// For example, given the following invocation of the macro..
|
||||||
|
///
|
||||||
|
/// ```ignore (rust)
|
||||||
|
/// fluent_messages! {
|
||||||
|
/// typeck => "./typeck.ftl",
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// ..where `typeck.ftl` has the following contents..
|
||||||
|
///
|
||||||
|
/// ```fluent
|
||||||
|
/// typeck-field-multiply-specified-in-initializer =
|
||||||
|
/// field `{$ident}` specified more than once
|
||||||
|
/// .label = used more than once
|
||||||
|
/// .label-previous-use = first use of `{$ident}`
|
||||||
|
/// ```
|
||||||
|
/// ...then the macro parse the Fluent resource, emitting a diagnostic if it fails to do so, and
|
||||||
|
/// will generate the following code:
|
||||||
|
///
|
||||||
|
/// ```ignore (rust)
|
||||||
|
/// pub static DEFAULT_LOCALE_RESOURCES: &'static [&'static str] = &[
|
||||||
|
/// include_str!("./typeck.ftl"),
|
||||||
|
/// ];
|
||||||
|
///
|
||||||
|
/// mod fluent_generated {
|
||||||
|
/// mod typeck {
|
||||||
|
/// pub const field_multiply_specified_in_initializer: DiagnosticMessage =
|
||||||
|
/// DiagnosticMessage::fluent("typeck-field-multiply-specified-in-initializer");
|
||||||
|
/// pub const field_multiply_specified_in_initializer_label_previous_use: DiagnosticMessage =
|
||||||
|
/// DiagnosticMessage::fluent_attr(
|
||||||
|
/// "typeck-field-multiply-specified-in-initializer",
|
||||||
|
/// "previous-use-label"
|
||||||
|
/// );
|
||||||
|
/// }
|
||||||
|
/// }
|
||||||
|
/// ```
|
||||||
|
/// When emitting a diagnostic, the generated constants can be used as follows:
|
||||||
|
///
|
||||||
|
/// ```ignore (rust)
|
||||||
|
/// let mut err = sess.struct_span_err(
|
||||||
|
/// span,
|
||||||
|
/// fluent::typeck::field_multiply_specified_in_initializer
|
||||||
|
/// );
|
||||||
|
/// err.span_default_label(span);
|
||||||
|
/// err.span_label(
|
||||||
|
/// previous_use_span,
|
||||||
|
/// fluent::typeck::field_multiply_specified_in_initializer_label_previous_use
|
||||||
|
/// );
|
||||||
|
/// err.emit();
|
||||||
|
/// ```
|
||||||
|
#[proc_macro]
|
||||||
|
pub fn fluent_messages(input: TokenStream) -> TokenStream {
|
||||||
|
diagnostics::fluent_messages(input)
|
||||||
|
}
|
||||||
|
|
||||||
decl_derive!([HashStable, attributes(stable_hasher)] => hash_stable::hash_stable_derive);
|
decl_derive!([HashStable, attributes(stable_hasher)] => hash_stable::hash_stable_derive);
|
||||||
decl_derive!(
|
decl_derive!(
|
||||||
[HashStable_Generic, attributes(stable_hasher)] =>
|
[HashStable_Generic, attributes(stable_hasher)] =>
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
//! Errors emitted by typeck.
|
//! Errors emitted by typeck.
|
||||||
use rustc_errors::{
|
use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed};
|
||||||
error_code, Applicability, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
|
|
||||||
};
|
|
||||||
use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
|
use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
|
||||||
use rustc_middle::ty::Ty;
|
use rustc_middle::ty::Ty;
|
||||||
use rustc_session::{parse::ParseSess, SessionDiagnostic};
|
use rustc_session::{parse::ParseSess, SessionDiagnostic};
|
||||||
@ -264,10 +262,9 @@ pub struct MissingTypeParams {
|
|||||||
// Manual implementation of `SessionDiagnostic` to be able to call `span_to_snippet`.
|
// Manual implementation of `SessionDiagnostic` to be able to call `span_to_snippet`.
|
||||||
impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
|
impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
|
||||||
fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
|
fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
|
||||||
static SLUG: &'static str = "typeck-missing-type-params";
|
|
||||||
let mut err = sess.span_diagnostic.struct_span_err_with_code(
|
let mut err = sess.span_diagnostic.struct_span_err_with_code(
|
||||||
self.span,
|
self.span,
|
||||||
DiagnosticMessage::fluent(SLUG),
|
rustc_errors::fluent::typeck::missing_type_params,
|
||||||
error_code!(E0393),
|
error_code!(E0393),
|
||||||
);
|
);
|
||||||
err.set_arg("parameterCount", self.missing_type_params.len());
|
err.set_arg("parameterCount", self.missing_type_params.len());
|
||||||
@ -280,7 +277,7 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
|
|||||||
.join(", "),
|
.join(", "),
|
||||||
);
|
);
|
||||||
|
|
||||||
err.span_label(self.def_span, DiagnosticMessage::fluent_attr(SLUG, "label"));
|
err.span_label(self.def_span, rustc_errors::fluent::typeck::missing_type_params_label);
|
||||||
|
|
||||||
let mut suggested = false;
|
let mut suggested = false;
|
||||||
if let (Ok(snippet), true) = (
|
if let (Ok(snippet), true) = (
|
||||||
@ -298,7 +295,7 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
|
|||||||
// least we can clue them to the correct syntax `Iterator<Type>`.
|
// least we can clue them to the correct syntax `Iterator<Type>`.
|
||||||
err.span_suggestion(
|
err.span_suggestion(
|
||||||
self.span,
|
self.span,
|
||||||
DiagnosticMessage::fluent_attr(SLUG, "suggestion"),
|
rustc_errors::fluent::typeck::missing_type_params_suggestion,
|
||||||
format!("{}<{}>", snippet, self.missing_type_params.join(", ")),
|
format!("{}<{}>", snippet, self.missing_type_params.join(", ")),
|
||||||
Applicability::HasPlaceholders,
|
Applicability::HasPlaceholders,
|
||||||
);
|
);
|
||||||
@ -306,10 +303,13 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if !suggested {
|
if !suggested {
|
||||||
err.span_label(self.span, DiagnosticMessage::fluent_attr(SLUG, "no-suggestion-label"));
|
err.span_label(
|
||||||
|
self.span,
|
||||||
|
rustc_errors::fluent::typeck::missing_type_params_no_suggestion_label,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
err.note(DiagnosticMessage::fluent_attr(SLUG, "note"));
|
err.note(rustc_errors::fluent::typeck::missing_type_params_note);
|
||||||
err
|
err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ const fn size_align<T>() -> (usize, usize) {
|
|||||||
/// like this are met, use specific allocators with looser
|
/// like this are met, use specific allocators with looser
|
||||||
/// requirements, or use the more lenient `Allocator` interface.)
|
/// requirements, or use the more lenient `Allocator` interface.)
|
||||||
#[stable(feature = "alloc_layout", since = "1.28.0")]
|
#[stable(feature = "alloc_layout", since = "1.28.0")]
|
||||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||||
#[lang = "alloc_layout"]
|
#[lang = "alloc_layout"]
|
||||||
pub struct Layout {
|
pub struct Layout {
|
||||||
// size of the requested block of memory, measured in bytes.
|
// size of the requested block of memory, measured in bytes.
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
use crate::convert::TryFrom;
|
use crate::convert::TryFrom;
|
||||||
use crate::num::NonZeroUsize;
|
use crate::num::NonZeroUsize;
|
||||||
use crate::{cmp, fmt, mem, num};
|
use crate::{cmp, fmt, hash, mem, num};
|
||||||
|
|
||||||
/// A type storing a `usize` which is a power of two, and thus
|
/// A type storing a `usize` which is a power of two, and thus
|
||||||
/// represents a possible alignment in the rust abstract machine.
|
/// represents a possible alignment in the rust abstract machine.
|
||||||
@ -105,6 +105,13 @@ impl cmp::PartialOrd for ValidAlign {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl hash::Hash for ValidAlign {
|
||||||
|
#[inline]
|
||||||
|
fn hash<H: hash::Hasher>(&self, state: &mut H) {
|
||||||
|
self.as_nonzero().hash(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(target_pointer_width = "16")]
|
#[cfg(target_pointer_width = "16")]
|
||||||
type ValidAlignEnum = ValidAlignEnum16;
|
type ValidAlignEnum = ValidAlignEnum16;
|
||||||
#[cfg(target_pointer_width = "32")]
|
#[cfg(target_pointer_width = "32")]
|
||||||
|
@ -499,14 +499,15 @@ impl<T> NonNull<[T]> {
|
|||||||
/// # Examples
|
/// # Examples
|
||||||
///
|
///
|
||||||
/// ```rust
|
/// ```rust
|
||||||
/// #![feature(slice_ptr_len, nonnull_slice_from_raw_parts)]
|
/// #![feature(nonnull_slice_from_raw_parts)]
|
||||||
/// use std::ptr::NonNull;
|
/// use std::ptr::NonNull;
|
||||||
///
|
///
|
||||||
/// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3);
|
/// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3);
|
||||||
/// assert_eq!(slice.len(), 3);
|
/// assert_eq!(slice.len(), 3);
|
||||||
/// ```
|
/// ```
|
||||||
#[unstable(feature = "slice_ptr_len", issue = "71146")]
|
#[stable(feature = "slice_ptr_len_nonnull", since = "1.63.0")]
|
||||||
#[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")]
|
#[rustc_const_stable(feature = "const_slice_ptr_len_nonnull", since = "1.63.0")]
|
||||||
|
#[rustc_allow_const_fn_unstable(const_slice_ptr_len)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub const fn len(self) -> usize {
|
pub const fn len(self) -> usize {
|
||||||
|
@ -104,7 +104,7 @@
|
|||||||
//! On Unix, [`OsStr`] implements the
|
//! On Unix, [`OsStr`] implements the
|
||||||
//! <code>std::os::unix::ffi::[OsStrExt][unix.OsStrExt]</code> trait, which
|
//! <code>std::os::unix::ffi::[OsStrExt][unix.OsStrExt]</code> trait, which
|
||||||
//! augments it with two methods, [`from_bytes`] and [`as_bytes`].
|
//! augments it with two methods, [`from_bytes`] and [`as_bytes`].
|
||||||
//! These do inexpensive conversions from and to UTF-8 byte slices.
|
//! These do inexpensive conversions from and to byte slices.
|
||||||
//!
|
//!
|
||||||
//! Additionally, on Unix [`OsString`] implements the
|
//! Additionally, on Unix [`OsString`] implements the
|
||||||
//! <code>std::os::unix::ffi::[OsStringExt][unix.OsStringExt]</code> trait,
|
//! <code>std::os::unix::ffi::[OsStringExt][unix.OsStringExt]</code> trait,
|
||||||
|
@ -897,29 +897,6 @@ impl Step for PlainSourceTarball {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// We have to run a few shell scripts, which choke quite a bit on both `\`
|
|
||||||
// characters and on `C:\` paths, so normalize both of them away.
|
|
||||||
pub fn sanitize_sh(path: &Path) -> String {
|
|
||||||
let path = path.to_str().unwrap().replace("\\", "/");
|
|
||||||
return change_drive(unc_to_lfs(&path)).unwrap_or(path);
|
|
||||||
|
|
||||||
fn unc_to_lfs(s: &str) -> &str {
|
|
||||||
s.strip_prefix("//?/").unwrap_or(s)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn change_drive(s: &str) -> Option<String> {
|
|
||||||
let mut ch = s.chars();
|
|
||||||
let drive = ch.next().unwrap_or('C');
|
|
||||||
if ch.next() != Some(':') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
if ch.next() != Some('/') {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
Some(format!("/{}/{}", drive, &s[drive.len_utf8() + 2..]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, PartialOrd, Ord, Copy, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct Cargo {
|
pub struct Cargo {
|
||||||
pub compiler: Compiler,
|
pub compiler: Compiler,
|
||||||
|
@ -5,12 +5,12 @@
|
|||||||
|
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs;
|
use std::fs;
|
||||||
use std::path::{Component, PathBuf};
|
use std::path::{Component, Path, PathBuf};
|
||||||
use std::process::Command;
|
use std::process::Command;
|
||||||
|
|
||||||
use crate::util::t;
|
use crate::util::t;
|
||||||
|
|
||||||
use crate::dist::{self, sanitize_sh};
|
use crate::dist;
|
||||||
use crate::tarball::GeneratedTarball;
|
use crate::tarball::GeneratedTarball;
|
||||||
use crate::Compiler;
|
use crate::Compiler;
|
||||||
|
|
||||||
@ -22,6 +22,29 @@ const SHELL: &str = "bash";
|
|||||||
#[cfg(not(target_os = "illumos"))]
|
#[cfg(not(target_os = "illumos"))]
|
||||||
const SHELL: &str = "sh";
|
const SHELL: &str = "sh";
|
||||||
|
|
||||||
|
// We have to run a few shell scripts, which choke quite a bit on both `\`
|
||||||
|
// characters and on `C:\` paths, so normalize both of them away.
|
||||||
|
fn sanitize_sh(path: &Path) -> String {
|
||||||
|
let path = path.to_str().unwrap().replace("\\", "/");
|
||||||
|
return change_drive(unc_to_lfs(&path)).unwrap_or(path);
|
||||||
|
|
||||||
|
fn unc_to_lfs(s: &str) -> &str {
|
||||||
|
s.strip_prefix("//?/").unwrap_or(s)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn change_drive(s: &str) -> Option<String> {
|
||||||
|
let mut ch = s.chars();
|
||||||
|
let drive = ch.next().unwrap_or('C');
|
||||||
|
if ch.next() != Some(':') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
if ch.next() != Some('/') {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(format!("/{}/{}", drive, &s[drive.len_utf8() + 2..]))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn install_sh(
|
fn install_sh(
|
||||||
builder: &Builder<'_>,
|
builder: &Builder<'_>,
|
||||||
package: &str,
|
package: &str,
|
||||||
|
1
src/test/ui-fulldeps/fluent-messages/duplicate-a.ftl
Normal file
1
src/test/ui-fulldeps/fluent-messages/duplicate-a.ftl
Normal file
@ -0,0 +1 @@
|
|||||||
|
key = Value
|
1
src/test/ui-fulldeps/fluent-messages/duplicate-b.ftl
Normal file
1
src/test/ui-fulldeps/fluent-messages/duplicate-b.ftl
Normal file
@ -0,0 +1 @@
|
|||||||
|
key = Value
|
1
src/test/ui-fulldeps/fluent-messages/missing-message.ftl
Normal file
1
src/test/ui-fulldeps/fluent-messages/missing-message.ftl
Normal file
@ -0,0 +1 @@
|
|||||||
|
missing-message =
|
60
src/test/ui-fulldeps/fluent-messages/test.rs
Normal file
60
src/test/ui-fulldeps/fluent-messages/test.rs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
// normalize-stderr-test "note.*" -> "note: os-specific message"
|
||||||
|
|
||||||
|
#![feature(rustc_private)]
|
||||||
|
#![crate_type = "lib"]
|
||||||
|
|
||||||
|
extern crate rustc_macros;
|
||||||
|
use rustc_macros::fluent_messages;
|
||||||
|
|
||||||
|
/// Copy of the relevant `DiagnosticMessage` variant constructed by `fluent_messages` as it
|
||||||
|
/// expects `crate::DiagnosticMessage` to exist.
|
||||||
|
pub enum DiagnosticMessage {
|
||||||
|
FluentIdentifier(std::borrow::Cow<'static, str>, Option<std::borrow::Cow<'static, str>>),
|
||||||
|
}
|
||||||
|
|
||||||
|
mod missing_absolute {
|
||||||
|
use super::fluent_messages;
|
||||||
|
|
||||||
|
fluent_messages! {
|
||||||
|
missing_absolute => "/definitely_does_not_exist.ftl",
|
||||||
|
//~^ ERROR could not open Fluent resource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod missing_relative {
|
||||||
|
use super::fluent_messages;
|
||||||
|
|
||||||
|
fluent_messages! {
|
||||||
|
missing_relative => "../definitely_does_not_exist.ftl",
|
||||||
|
//~^ ERROR could not open Fluent resource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod missing_message {
|
||||||
|
use super::fluent_messages;
|
||||||
|
|
||||||
|
fluent_messages! {
|
||||||
|
missing_message => "./missing-message.ftl",
|
||||||
|
//~^ ERROR could not parse Fluent resource
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod duplicate {
|
||||||
|
use super::fluent_messages;
|
||||||
|
|
||||||
|
fluent_messages! {
|
||||||
|
a => "./duplicate-a.ftl",
|
||||||
|
b => "./duplicate-b.ftl",
|
||||||
|
//~^ ERROR overrides existing message: `key`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mod valid {
|
||||||
|
use super::fluent_messages;
|
||||||
|
|
||||||
|
fluent_messages! {
|
||||||
|
valid => "./valid.ftl",
|
||||||
|
}
|
||||||
|
|
||||||
|
use self::fluent_generated::{DEFAULT_LOCALE_RESOURCES, valid::valid};
|
||||||
|
}
|
45
src/test/ui-fulldeps/fluent-messages/test.stderr
Normal file
45
src/test/ui-fulldeps/fluent-messages/test.stderr
Normal file
@ -0,0 +1,45 @@
|
|||||||
|
error: could not open Fluent resource
|
||||||
|
--> $DIR/test.rs:19:29
|
||||||
|
|
|
||||||
|
LL | missing_absolute => "/definitely_does_not_exist.ftl",
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: os-specific message
|
||||||
|
|
||||||
|
error: could not open Fluent resource
|
||||||
|
--> $DIR/test.rs:28:29
|
||||||
|
|
|
||||||
|
LL | missing_relative => "../definitely_does_not_exist.ftl",
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: os-specific message
|
||||||
|
|
||||||
|
error: could not parse Fluent resource
|
||||||
|
--> $DIR/test.rs:37:28
|
||||||
|
|
|
||||||
|
LL | missing_message => "./missing-message.ftl",
|
||||||
|
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= help: see additional errors emitted
|
||||||
|
|
||||||
|
error: expected a message field for "missing-message"
|
||||||
|
--> ./missing-message.ftl:1:1
|
||||||
|
|
|
||||||
|
1 | missing-message =
|
||||||
|
| ^^^^^^^^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
|
||||||
|
error: overrides existing message: `key`
|
||||||
|
--> $DIR/test.rs:47:9
|
||||||
|
|
|
||||||
|
LL | b => "./duplicate-b.ftl",
|
||||||
|
| ^
|
||||||
|
|
|
||||||
|
help: previously defined in this resource
|
||||||
|
--> $DIR/test.rs:46:9
|
||||||
|
|
|
||||||
|
LL | a => "./duplicate-a.ftl",
|
||||||
|
| ^
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
1
src/test/ui-fulldeps/fluent-messages/valid.ftl
Normal file
1
src/test/ui-fulldeps/fluent-messages/valid.ftl
Normal file
@ -0,0 +1 @@
|
|||||||
|
valid = Valid!
|
Loading…
Reference in New Issue
Block a user