DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Git (a80ec3b3b1)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
use crate::base::{self, *};
use crate::proc_macro_server;

use rustc_ast::ast::{self, ItemKind, MetaItemKind, NestedMetaItem};
use rustc_ast::token;
use rustc_ast::tokenstream::{self, TokenStream};
use rustc_data_structures::sync::Lrc;
use rustc_errors::{Applicability, ErrorReported};
use rustc_span::symbol::sym;
use rustc_span::{Span, DUMMY_SP};

const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;

pub struct BangProcMacro {
    pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
}

impl base::ProcMacro for BangProcMacro {
    fn expand<'cx>(
        &self,
        ecx: &'cx mut ExtCtxt<'_>,
        span: Span,
        input: TokenStream,
    ) -> Result<TokenStream, ErrorReported> {
        let server = proc_macro_server::Rustc::new(ecx);
        self.client.run(&EXEC_STRATEGY, server, input).map_err(|e| {
            let mut err = ecx.struct_span_err(span, "proc macro panicked");
            if let Some(s) = e.as_str() {
                err.help(&format!("message: {}", s));
            }
            err.emit();
            ErrorReported
        })
    }
}

pub struct AttrProcMacro {
    pub client: pm::bridge::client::Client<fn(pm::TokenStream, pm::TokenStream) -> pm::TokenStream>,
}

impl base::AttrProcMacro for AttrProcMacro {
    fn expand<'cx>(
        &self,
        ecx: &'cx mut ExtCtxt<'_>,
        span: Span,
        annotation: TokenStream,
        annotated: TokenStream,
    ) -> Result<TokenStream, ErrorReported> {
        let server = proc_macro_server::Rustc::new(ecx);
        self.client.run(&EXEC_STRATEGY, server, annotation, annotated).map_err(|e| {
            let mut err = ecx.struct_span_err(span, "custom attribute panicked");
            if let Some(s) = e.as_str() {
                err.help(&format!("message: {}", s));
            }
            err.emit();
            ErrorReported
        })
    }
}

pub struct ProcMacroDerive {
    pub client: pm::bridge::client::Client<fn(pm::TokenStream) -> pm::TokenStream>,
}

impl MultiItemModifier for ProcMacroDerive {
    fn expand(
        &self,
        ecx: &mut ExtCtxt<'_>,
        span: Span,
        _meta_item: &ast::MetaItem,
        item: Annotatable,
    ) -> ExpandResult<Vec<Annotatable>, Annotatable> {
        let item = match item {
            Annotatable::Arm(..)
            | Annotatable::Field(..)
            | Annotatable::FieldPat(..)
            | Annotatable::GenericParam(..)
            | Annotatable::Param(..)
            | Annotatable::StructField(..)
            | Annotatable::Variant(..) => panic!("unexpected annotatable"),
            Annotatable::Item(item) => item,
            Annotatable::ImplItem(_)
            | Annotatable::TraitItem(_)
            | Annotatable::ForeignItem(_)
            | Annotatable::Stmt(_)
            | Annotatable::Expr(_) => {
                ecx.span_err(
                    span,
                    "proc-macro derives may only be applied to a struct, enum, or union",
                );
                return ExpandResult::Ready(Vec::new());
            }
        };
        match item.kind {
            ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..) => {}
            _ => {
                ecx.span_err(
                    span,
                    "proc-macro derives may only be applied to a struct, enum, or union",
                );
                return ExpandResult::Ready(Vec::new());
            }
        }

        let token = token::Interpolated(Lrc::new(token::NtItem(item)));
        let input = tokenstream::TokenTree::token(token, DUMMY_SP).into();

        let server = proc_macro_server::Rustc::new(ecx);
        let stream = match self.client.run(&EXEC_STRATEGY, server, input) {
            Ok(stream) => stream,
            Err(e) => {
                let mut err = ecx.struct_span_err(span, "proc-macro derive panicked");
                if let Some(s) = e.as_str() {
                    err.help(&format!("message: {}", s));
                }
                err.emit();
                return ExpandResult::Ready(vec![]);
            }
        };

        let error_count_before = ecx.parse_sess.span_diagnostic.err_count();
        let mut parser =
            rustc_parse::stream_to_parser(ecx.parse_sess, stream, Some("proc-macro derive"));
        let mut items = vec![];

        loop {
            match parser.parse_item() {
                Ok(None) => break,
                Ok(Some(item)) => items.push(Annotatable::Item(item)),
                Err(mut err) => {
                    err.emit();
                    break;
                }
            }
        }

        // fail if there have been errors emitted
        if ecx.parse_sess.span_diagnostic.err_count() > error_count_before {
            ecx.struct_span_err(span, "proc-macro derive produced unparseable tokens").emit();
        }

        ExpandResult::Ready(items)
    }
}

crate fn collect_derives(cx: &mut ExtCtxt<'_>, attrs: &mut Vec<ast::Attribute>) -> Vec<ast::Path> {
    let mut result = Vec::new();
    attrs.retain(|attr| {
        if !attr.has_name(sym::derive) {
            return true;
        }

        // 1) First let's ensure that it's a meta item.
        let nmis = match attr.meta_item_list() {
            None => {
                cx.struct_span_err(attr.span, "malformed `derive` attribute input")
                    .span_suggestion(
                        attr.span,
                        "missing traits to be derived",
                        "#[derive(Trait1, Trait2, ...)]".to_owned(),
                        Applicability::HasPlaceholders,
                    )
                    .emit();
                return false;
            }
            Some(x) => x,
        };

        let mut error_reported_filter_map = false;
        let mut error_reported_map = false;
        let traits = nmis
            .into_iter()
            // 2) Moreover, let's ensure we have a path and not `#[derive("foo")]`.
            .filter_map(|nmi| match nmi {
                NestedMetaItem::Literal(lit) => {
                    error_reported_filter_map = true;
                    cx.struct_span_err(lit.span, "expected path to a trait, found literal")
                        .help("for example, write `#[derive(Debug)]` for `Debug`")
                        .emit();
                    None
                }
                NestedMetaItem::MetaItem(mi) => Some(mi),
            })
            // 3) Finally, we only accept `#[derive($path_0, $path_1, ..)]`
            // but not e.g. `#[derive($path_0 = "value", $path_1(abc))]`.
            // In this case we can still at least determine that the user
            // wanted this trait to be derived, so let's keep it.
            .map(|mi| {
                let mut traits_dont_accept = |title, action| {
                    error_reported_map = true;
                    let sp = mi.span.with_lo(mi.path.span.hi());
                    cx.struct_span_err(sp, title)
                        .span_suggestion(
                            sp,
                            action,
                            String::new(),
                            Applicability::MachineApplicable,
                        )
                        .emit();
                };
                match &mi.kind {
                    MetaItemKind::List(..) => traits_dont_accept(
                        "traits in `#[derive(...)]` don't accept arguments",
                        "remove the arguments",
                    ),
                    MetaItemKind::NameValue(..) => traits_dont_accept(
                        "traits in `#[derive(...)]` don't accept values",
                        "remove the value",
                    ),
                    MetaItemKind::Word => {}
                }
                mi.path
            });

        result.extend(traits);
        !error_reported_filter_map && !error_reported_map
    });
    result
}