mirror of
https://github.com/rust-lang/rust.git
synced 2024-12-03 04:04:06 +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"
|
||||
version = "0.1.0"
|
||||
dependencies = [
|
||||
"annotate-snippets",
|
||||
"fluent-bundle",
|
||||
"fluent-syntax",
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
"synstructure",
|
||||
"unic-langid",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -6,7 +6,7 @@
|
||||
use fluent_bundle::FluentResource;
|
||||
use fluent_syntax::parser::ParserError;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_macros::{Decodable, Encodable};
|
||||
use rustc_macros::{fluent_messages, Decodable, Encodable};
|
||||
use rustc_span::Span;
|
||||
use std::borrow::Cow;
|
||||
use std::error::Error;
|
||||
@ -29,8 +29,13 @@ use intl_memoizer::IntlLangMemoizer;
|
||||
pub use fluent_bundle::{FluentArgs, FluentError, FluentValue};
|
||||
pub use unic_langid::{langid, LanguageIdentifier};
|
||||
|
||||
pub static DEFAULT_LOCALE_RESOURCES: &'static [&'static str] =
|
||||
&[include_str!("../locales/en-US/typeck.ftl"), include_str!("../locales/en-US/parser.ftl")];
|
||||
// Generates `DEFAULT_LOCALE_RESOURCES` static and `fluent_generated` module.
|
||||
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>;
|
||||
|
||||
|
@ -31,8 +31,8 @@ use rustc_data_structures::stable_hasher::StableHasher;
|
||||
use rustc_data_structures::sync::{self, Lock, Lrc};
|
||||
use rustc_data_structures::AtomicRef;
|
||||
pub use rustc_error_messages::{
|
||||
fallback_fluent_bundle, fluent_bundle, DiagnosticMessage, FluentBundle, LanguageIdentifier,
|
||||
LazyFallbackBundle, MultiSpan, SpanLabel, DEFAULT_LOCALE_RESOURCES,
|
||||
fallback_fluent_bundle, fluent, fluent_bundle, DiagnosticMessage, FluentBundle,
|
||||
LanguageIdentifier, LazyFallbackBundle, MultiSpan, SpanLabel, DEFAULT_LOCALE_RESOURCES,
|
||||
};
|
||||
pub use rustc_lint_defs::{pluralize, Applicability};
|
||||
use rustc_span::source_map::SourceMap;
|
||||
|
@ -7,7 +7,11 @@ edition = "2021"
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
annotate-snippets = "0.8.0"
|
||||
fluent-bundle = "0.15.2"
|
||||
fluent-syntax = "0.11"
|
||||
synstructure = "0.12.1"
|
||||
syn = { version = "1", features = ["full"] }
|
||||
proc-macro2 = "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 error;
|
||||
mod fluent;
|
||||
mod subdiagnostic;
|
||||
mod utils;
|
||||
|
||||
use diagnostic::SessionDiagnosticDerive;
|
||||
pub(crate) use fluent::fluent_messages;
|
||||
use proc_macro2::TokenStream;
|
||||
use quote::format_ident;
|
||||
use subdiagnostic::SessionSubdiagnosticDerive;
|
||||
@ -12,7 +14,7 @@ use synstructure::Structure;
|
||||
/// Implements `#[derive(SessionDiagnostic)]`, which allows for errors to be specified as a struct,
|
||||
/// independent from the actual diagnostics emitting code.
|
||||
///
|
||||
/// ```ignore (pseudo-rust)
|
||||
/// ```ignore (rust)
|
||||
/// # extern crate rustc_errors;
|
||||
/// # use rustc_errors::Applicability;
|
||||
/// # extern crate rustc_span;
|
||||
@ -43,7 +45,7 @@ use synstructure::Structure;
|
||||
///
|
||||
/// Then, later, to emit the error:
|
||||
///
|
||||
/// ```ignore (pseudo-rust)
|
||||
/// ```ignore (rust)
|
||||
/// sess.emit_err(MoveOutOfBorrowError {
|
||||
/// expected,
|
||||
/// 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
|
||||
/// emitting code or diagnostic derives.
|
||||
///
|
||||
/// ```ignore (pseudo-rust)
|
||||
/// ```ignore (rust)
|
||||
/// #[derive(SessionSubdiagnostic)]
|
||||
/// pub enum ExpectedIdentifierLabel<'tcx> {
|
||||
/// #[label(slug = "parser-expected-identifier")]
|
||||
@ -104,7 +106,7 @@ pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream {
|
||||
///
|
||||
/// Then, later, to add the subdiagnostic:
|
||||
///
|
||||
/// ```ignore (pseudo-rust)
|
||||
/// ```ignore (rust)
|
||||
/// diag.subdiagnostic(ExpectedIdentifierLabel::WithoutFound { span });
|
||||
///
|
||||
/// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident });
|
||||
|
@ -2,6 +2,7 @@
|
||||
#![feature(let_else)]
|
||||
#![feature(never_type)]
|
||||
#![feature(proc_macro_diagnostic)]
|
||||
#![feature(proc_macro_span)]
|
||||
#![allow(rustc::default_hash_types)]
|
||||
#![recursion_limit = "128"]
|
||||
|
||||
@ -49,6 +50,64 @@ pub fn newtype_index(input: TokenStream) -> TokenStream {
|
||||
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_Generic, attributes(stable_hasher)] =>
|
||||
|
@ -1,7 +1,5 @@
|
||||
//! Errors emitted by typeck.
|
||||
use rustc_errors::{
|
||||
error_code, Applicability, DiagnosticBuilder, DiagnosticMessage, ErrorGuaranteed,
|
||||
};
|
||||
use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed};
|
||||
use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
|
||||
use rustc_middle::ty::Ty;
|
||||
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`.
|
||||
impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
|
||||
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(
|
||||
self.span,
|
||||
DiagnosticMessage::fluent(SLUG),
|
||||
rustc_errors::fluent::typeck::missing_type_params,
|
||||
error_code!(E0393),
|
||||
);
|
||||
err.set_arg("parameterCount", self.missing_type_params.len());
|
||||
@ -280,7 +277,7 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
|
||||
.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;
|
||||
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>`.
|
||||
err.span_suggestion(
|
||||
self.span,
|
||||
DiagnosticMessage::fluent_attr(SLUG, "suggestion"),
|
||||
rustc_errors::fluent::typeck::missing_type_params_suggestion,
|
||||
format!("{}<{}>", snippet, self.missing_type_params.join(", ")),
|
||||
Applicability::HasPlaceholders,
|
||||
);
|
||||
@ -306,10 +303,13 @@ impl<'a> SessionDiagnostic<'a> for MissingTypeParams {
|
||||
}
|
||||
}
|
||||
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
|
||||
}
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ const fn size_align<T>() -> (usize, usize) {
|
||||
/// like this are met, use specific allocators with looser
|
||||
/// requirements, or use the more lenient `Allocator` interface.)
|
||||
#[stable(feature = "alloc_layout", since = "1.28.0")]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
|
||||
#[lang = "alloc_layout"]
|
||||
pub struct Layout {
|
||||
// size of the requested block of memory, measured in bytes.
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::convert::TryFrom;
|
||||
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
|
||||
/// 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")]
|
||||
type ValidAlignEnum = ValidAlignEnum16;
|
||||
#[cfg(target_pointer_width = "32")]
|
||||
|
@ -499,14 +499,15 @@ impl<T> NonNull<[T]> {
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// #![feature(slice_ptr_len, nonnull_slice_from_raw_parts)]
|
||||
/// #![feature(nonnull_slice_from_raw_parts)]
|
||||
/// use std::ptr::NonNull;
|
||||
///
|
||||
/// let slice: NonNull<[i8]> = NonNull::slice_from_raw_parts(NonNull::dangling(), 3);
|
||||
/// assert_eq!(slice.len(), 3);
|
||||
/// ```
|
||||
#[unstable(feature = "slice_ptr_len", issue = "71146")]
|
||||
#[rustc_const_unstable(feature = "const_slice_ptr_len", issue = "71146")]
|
||||
#[stable(feature = "slice_ptr_len_nonnull", since = "1.63.0")]
|
||||
#[rustc_const_stable(feature = "const_slice_ptr_len_nonnull", since = "1.63.0")]
|
||||
#[rustc_allow_const_fn_unstable(const_slice_ptr_len)]
|
||||
#[must_use]
|
||||
#[inline]
|
||||
pub const fn len(self) -> usize {
|
||||
|
@ -104,7 +104,7 @@
|
||||
//! On Unix, [`OsStr`] implements the
|
||||
//! <code>std::os::unix::ffi::[OsStrExt][unix.OsStrExt]</code> trait, which
|
||||
//! 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
|
||||
//! <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)]
|
||||
pub struct Cargo {
|
||||
pub compiler: Compiler,
|
||||
|
@ -5,12 +5,12 @@
|
||||
|
||||
use std::env;
|
||||
use std::fs;
|
||||
use std::path::{Component, PathBuf};
|
||||
use std::path::{Component, Path, PathBuf};
|
||||
use std::process::Command;
|
||||
|
||||
use crate::util::t;
|
||||
|
||||
use crate::dist::{self, sanitize_sh};
|
||||
use crate::dist;
|
||||
use crate::tarball::GeneratedTarball;
|
||||
use crate::Compiler;
|
||||
|
||||
@ -22,6 +22,29 @@ const SHELL: &str = "bash";
|
||||
#[cfg(not(target_os = "illumos"))]
|
||||
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(
|
||||
builder: &Builder<'_>,
|
||||
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