Merge commit '1d8491b120223272b13451fc81265aa64f7f4d5b' into sync-from-rustfmt

This commit is contained in:
Caleb Cartwright
2023-01-24 14:16:03 -06:00
108 changed files with 2591 additions and 187 deletions

View File

@@ -22,7 +22,7 @@ dependencies = [
[[package]]
name = "rustfmt-config_proc_macro"
version = "0.2.0"
version = "0.3.0"
dependencies = [
"proc-macro2",
"quote",

View File

@@ -1,6 +1,6 @@
[package]
name = "rustfmt-config_proc_macro"
version = "0.2.0"
version = "0.3.0"
edition = "2018"
description = "A collection of procedural macros for rustfmt"
license = "Apache-2.0/MIT"

View File

@@ -1,8 +1,10 @@
//! This module provides utilities for handling attributes on variants
//! of `config_type` enum. Currently there are two types of attributes
//! that could appear on the variants of `config_type` enum: `doc_hint`
//! and `value`. Both comes in the form of name-value pair whose value
//! is string literal.
//! of `config_type` enum. Currently there are the following attributes
//! that could appear on the variants of `config_type` enum:
//!
//! - `doc_hint`: name-value pair whose value is string literal
//! - `value`: name-value pair whose value is string literal
//! - `unstable_variant`: name only
/// Returns the value of the first `doc_hint` attribute in the given slice or
/// `None` if `doc_hint` attribute is not available.
@@ -27,6 +29,11 @@ pub fn find_config_value(attrs: &[syn::Attribute]) -> Option<String> {
attrs.iter().filter_map(config_value).next()
}
/// Returns `true` if the there is at least one `unstable` attribute in the given slice.
pub fn any_unstable_variant(attrs: &[syn::Attribute]) -> bool {
attrs.iter().any(is_unstable_variant)
}
/// Returns a string literal value if the given attribute is `value`
/// attribute or `None` otherwise.
pub fn config_value(attr: &syn::Attribute) -> Option<String> {
@@ -38,6 +45,11 @@ pub fn is_config_value(attr: &syn::Attribute) -> bool {
is_attr_name_value(attr, "value")
}
/// Returns `true` if the given attribute is an `unstable` attribute.
pub fn is_unstable_variant(attr: &syn::Attribute) -> bool {
is_attr_path(attr, "unstable_variant")
}
fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool {
attr.parse_meta().ok().map_or(false, |meta| match meta {
syn::Meta::NameValue(syn::MetaNameValue { ref path, .. }) if path.is_ident(name) => true,
@@ -45,6 +57,13 @@ fn is_attr_name_value(attr: &syn::Attribute, name: &str) -> bool {
})
}
fn is_attr_path(attr: &syn::Attribute, name: &str) -> bool {
attr.parse_meta().ok().map_or(false, |meta| match meta {
syn::Meta::Path(path) if path.is_ident(name) => true,
_ => false,
})
}
fn get_name_value_str_lit(attr: &syn::Attribute, name: &str) -> Option<String> {
attr.parse_meta().ok().and_then(|meta| match meta {
syn::Meta::NameValue(syn::MetaNameValue {

View File

@@ -1,5 +1,6 @@
use proc_macro2::TokenStream;
use quote::quote;
use quote::{quote, quote_spanned};
use syn::spanned::Spanned;
use crate::attrs::*;
use crate::utils::*;
@@ -47,12 +48,23 @@ fn process_variant(variant: &syn::Variant) -> TokenStream {
let metas = variant
.attrs
.iter()
.filter(|attr| !is_doc_hint(attr) && !is_config_value(attr));
.filter(|attr| !is_doc_hint(attr) && !is_config_value(attr) && !is_unstable_variant(attr));
let attrs = fold_quote(metas, |meta| quote!(#meta));
let syn::Variant { ident, fields, .. } = variant;
quote!(#attrs #ident #fields)
}
/// Return the correct syntax to pattern match on the enum variant, discarding all
/// internal field data.
fn fields_in_variant(variant: &syn::Variant) -> TokenStream {
// With thanks to https://stackoverflow.com/a/65182902
match &variant.fields {
syn::Fields::Unnamed(_) => quote_spanned! { variant.span() => (..) },
syn::Fields::Unit => quote_spanned! { variant.span() => },
syn::Fields::Named(_) => quote_spanned! { variant.span() => {..} },
}
}
fn impl_doc_hint(ident: &syn::Ident, variants: &Variants) -> TokenStream {
let doc_hint = variants
.iter()
@@ -60,12 +72,26 @@ fn impl_doc_hint(ident: &syn::Ident, variants: &Variants) -> TokenStream {
.collect::<Vec<_>>()
.join("|");
let doc_hint = format!("[{}]", doc_hint);
let variant_stables = variants
.iter()
.map(|v| (&v.ident, fields_in_variant(&v), !unstable_of_variant(v)));
let match_patterns = fold_quote(variant_stables, |(v, fields, stable)| {
quote! {
#ident::#v #fields => #stable,
}
});
quote! {
use crate::config::ConfigType;
impl ConfigType for #ident {
fn doc_hint() -> String {
#doc_hint.to_owned()
}
fn stable_variant(&self) -> bool {
match self {
#match_patterns
}
}
}
}
}
@@ -123,13 +149,21 @@ fn impl_from_str(ident: &syn::Ident, variants: &Variants) -> TokenStream {
}
fn doc_hint_of_variant(variant: &syn::Variant) -> String {
find_doc_hint(&variant.attrs).unwrap_or(variant.ident.to_string())
let mut text = find_doc_hint(&variant.attrs).unwrap_or(variant.ident.to_string());
if unstable_of_variant(&variant) {
text.push_str(" (unstable)")
};
text
}
fn config_value_of_variant(variant: &syn::Variant) -> String {
find_config_value(&variant.attrs).unwrap_or(variant.ident.to_string())
}
fn unstable_of_variant(variant: &syn::Variant) -> bool {
any_unstable_variant(&variant.attrs)
}
fn impl_serde(ident: &syn::Ident, variants: &Variants) -> TokenStream {
let arms = fold_quote(variants.iter(), |v| {
let v_ident = &v.ident;

View File

@@ -69,3 +69,16 @@ pub fn stable_only_test(_args: TokenStream, input: TokenStream) -> TokenStream {
TokenStream::from_str("").unwrap()
}
}
/// Used to conditionally output the TokenStream for tests that should be run as part of rustfmts
/// test suite, but should be ignored when running in the rust-lang/rust test suite.
#[proc_macro_attribute]
pub fn rustfmt_only_ci_test(_args: TokenStream, input: TokenStream) -> TokenStream {
if option_env!("RUSTFMT_CI").is_some() {
input
} else {
let mut token_stream = TokenStream::from_str("#[ignore]").unwrap();
token_stream.extend(input);
token_stream
}
}

View File

@@ -1,6 +1,7 @@
pub mod config {
pub trait ConfigType: Sized {
fn doc_hint() -> String;
fn stable_variant(&self) -> bool;
}
}