1use rustc_ast::tokenstream::TokenStream;
2use rustc_ast::{self as ast, AttrStyle, Attribute, MetaItem, attr, token};
3use rustc_attr_parsing::validate_attr;
4use rustc_errors::{Applicability, Diag, ErrorGuaranteed};
5use rustc_expand::base::{Annotatable, ExpandResult, ExtCtxt};
6use rustc_expand::expand::AstFragment;
7use rustc_feature::AttributeTemplate;
8use rustc_lint_defs::builtin::DUPLICATE_MACRO_ATTRIBUTES;
9use rustc_parse::{exp, parser};
10use rustc_session::errors::report_lit_error;
11use rustc_span::{BytePos, Span, Symbol};
12
13use crate::errors;
14
15pub(crate) fn check_builtin_macro_attribute(ecx: &ExtCtxt<'_>, meta_item: &MetaItem, name: Symbol) {
16 let template = AttributeTemplate { word: true, ..Default::default() };
18 validate_attr::check_builtin_meta_item(
19 &ecx.sess.psess,
20 meta_item,
21 AttrStyle::Outer,
22 name,
23 template,
24 true,
25 );
26}
27
28pub(crate) fn warn_on_duplicate_attribute(ecx: &ExtCtxt<'_>, item: &Annotatable, name: Symbol) {
31 let attrs: Option<&[Attribute]> = match item {
32 Annotatable::Item(item) => Some(&item.attrs),
33 Annotatable::AssocItem(item, _) => Some(&item.attrs),
34 Annotatable::ForeignItem(item) => Some(&item.attrs),
35 Annotatable::Expr(expr) => Some(&expr.attrs),
36 Annotatable::Arm(arm) => Some(&arm.attrs),
37 Annotatable::ExprField(field) => Some(&field.attrs),
38 Annotatable::PatField(field) => Some(&field.attrs),
39 Annotatable::GenericParam(param) => Some(¶m.attrs),
40 Annotatable::Param(param) => Some(¶m.attrs),
41 Annotatable::FieldDef(def) => Some(&def.attrs),
42 Annotatable::Variant(variant) => Some(&variant.attrs),
43 _ => None,
44 };
45 if let Some(attrs) = attrs {
46 if let Some(attr) = attr::find_by_name(attrs, name) {
47 ecx.psess().buffer_lint(
48 DUPLICATE_MACRO_ATTRIBUTES,
49 attr.span,
50 ecx.current_expansion.lint_node_id,
51 errors::DuplicateMacroAttribute,
52 );
53 }
54 }
55}
56
57pub(crate) type ExprToSpannedStringResult<'a> = Result<ExprToSpannedString, UnexpectedExprKind<'a>>;
60
61pub(crate) struct ExprToSpannedString {
62 pub symbol: Symbol,
63 pub style: ast::StrStyle,
64 pub span: Span,
65 pub uncooked_symbol: (ast::token::LitKind, Symbol),
69}
70
71type UnexpectedExprKind<'a> = Result<(Diag<'a>, bool ), ErrorGuaranteed>;
75
76pub(crate) fn expr_to_spanned_string<'a>(
82 cx: &'a mut ExtCtxt<'_>,
83 expr: Box<ast::Expr>,
84 err_msg: &'static str,
85) -> ExpandResult<ExprToSpannedStringResult<'a>, ()> {
86 if !cx.force_mode
87 && let ast::ExprKind::MacCall(m) = &expr.kind
88 && cx.resolver.macro_accessible(cx.current_expansion.id, &m.path).is_err()
89 {
90 return ExpandResult::Retry(());
91 }
92
93 let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
96
97 ExpandResult::Ready(Err(match expr.kind {
98 ast::ExprKind::Lit(token_lit) => match ast::LitKind::from_token_lit(token_lit) {
99 Ok(ast::LitKind::Str(s, style)) => {
100 return ExpandResult::Ready(Ok(ExprToSpannedString {
101 symbol: s,
102 style,
103 span: expr.span,
104 uncooked_symbol: (token_lit.kind, token_lit.symbol),
105 }));
106 }
107 Ok(ast::LitKind::ByteStr(..)) => {
108 let mut err = cx.dcx().struct_span_err(expr.span, err_msg);
109 let span = expr.span.shrink_to_lo();
110 err.span_suggestion(
111 span.with_hi(span.lo() + BytePos(1)),
112 "consider removing the leading `b`",
113 "",
114 Applicability::MaybeIncorrect,
115 );
116 Ok((err, true))
117 }
118 Ok(ast::LitKind::Err(guar)) => Err(guar),
119 Err(err) => Err(report_lit_error(&cx.sess.psess, err, token_lit, expr.span)),
120 _ => Ok((cx.dcx().struct_span_err(expr.span, err_msg), false)),
121 },
122 ast::ExprKind::Err(guar) => Err(guar),
123 ast::ExprKind::Dummy => {
124 cx.dcx().span_bug(expr.span, "tried to get a string literal from `ExprKind::Dummy`")
125 }
126 _ => Ok((cx.dcx().struct_span_err(expr.span, err_msg), false)),
127 }))
128}
129
130pub(crate) fn expr_to_string(
134 cx: &mut ExtCtxt<'_>,
135 expr: Box<ast::Expr>,
136 err_msg: &'static str,
137) -> ExpandResult<Result<(Symbol, ast::StrStyle), ErrorGuaranteed>, ()> {
138 expr_to_spanned_string(cx, expr, err_msg).map(|res| {
139 res.map_err(|err| match err {
140 Ok((err, _)) => err.emit(),
141 Err(guar) => guar,
142 })
143 .map(|ExprToSpannedString { symbol, style, .. }| (symbol, style))
144 })
145}
146
147pub(crate) fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str) {
152 if !tts.is_empty() {
153 cx.dcx().emit_err(errors::TakesNoArguments { span, name });
154 }
155}
156
157pub(crate) fn parse_expr(p: &mut parser::Parser<'_>) -> Result<Box<ast::Expr>, ErrorGuaranteed> {
159 let guar = match p.parse_expr() {
160 Ok(expr) => return Ok(expr),
161 Err(err) => err.emit(),
162 };
163 while p.token != token::Eof {
164 p.bump();
165 }
166 Err(guar)
167}
168
169pub(crate) fn get_single_str_from_tts(
172 cx: &mut ExtCtxt<'_>,
173 span: Span,
174 tts: TokenStream,
175 name: &str,
176) -> ExpandResult<Result<Symbol, ErrorGuaranteed>, ()> {
177 get_single_str_spanned_from_tts(cx, span, tts, name).map(|res| res.map(|(s, _)| s))
178}
179
180pub(crate) fn get_single_str_spanned_from_tts(
181 cx: &mut ExtCtxt<'_>,
182 span: Span,
183 tts: TokenStream,
184 name: &str,
185) -> ExpandResult<Result<(Symbol, Span), ErrorGuaranteed>, ()> {
186 let ExpandResult::Ready(ret) = get_single_expr_from_tts(cx, span, tts, name) else {
187 return ExpandResult::Retry(());
188 };
189 let ret = match ret {
190 Ok(ret) => ret,
191 Err(e) => return ExpandResult::Ready(Err(e)),
192 };
193 expr_to_spanned_string(cx, ret, "argument must be a string literal").map(|res| {
194 res.map_err(|err| match err {
195 Ok((err, _)) => err.emit(),
196 Err(guar) => guar,
197 })
198 .map(|ExprToSpannedString { symbol, span, .. }| (symbol, span))
199 })
200}
201
202pub(crate) fn get_single_expr_from_tts(
205 cx: &mut ExtCtxt<'_>,
206 span: Span,
207 tts: TokenStream,
208 name: &str,
209) -> ExpandResult<Result<Box<ast::Expr>, ErrorGuaranteed>, ()> {
210 let mut p = cx.new_parser_from_tts(tts);
211 if p.token == token::Eof {
212 let guar = cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
213 return ExpandResult::Ready(Err(guar));
214 }
215 let ret = match parse_expr(&mut p) {
216 Ok(ret) => ret,
217 Err(guar) => return ExpandResult::Ready(Err(guar)),
218 };
219 let _ = p.eat(::rustc_parse::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Comma,
token_type: ::rustc_parse::parser::token_type::TokenType::Comma,
}exp!(Comma));
220
221 if p.token != token::Eof {
222 cx.dcx().emit_err(errors::OnlyOneArgument { span, name });
223 }
224 ExpandResult::Ready(Ok(ret))
225}
226
227pub(crate) fn get_exprs_from_tts(
230 cx: &mut ExtCtxt<'_>,
231 tts: TokenStream,
232) -> ExpandResult<Result<Vec<Box<ast::Expr>>, ErrorGuaranteed>, ()> {
233 let mut p = cx.new_parser_from_tts(tts);
234 let mut es = Vec::new();
235 while p.token != token::Eof {
236 let expr = match parse_expr(&mut p) {
237 Ok(expr) => expr,
238 Err(guar) => return ExpandResult::Ready(Err(guar)),
239 };
240 if !cx.force_mode
241 && let ast::ExprKind::MacCall(m) = &expr.kind
242 && cx.resolver.macro_accessible(cx.current_expansion.id, &m.path).is_err()
243 {
244 return ExpandResult::Retry(());
245 }
246
247 let expr = cx.expander().fully_expand_fragment(AstFragment::Expr(expr)).make_expr();
250
251 es.push(expr);
252 if p.eat(::rustc_parse::parser::token_type::ExpTokenPair {
tok: rustc_ast::token::Comma,
token_type: ::rustc_parse::parser::token_type::TokenType::Comma,
}exp!(Comma)) {
253 continue;
254 }
255 if p.token != token::Eof {
256 let guar = cx.dcx().emit_err(errors::ExpectedCommaInList { span: p.token.span });
257 return ExpandResult::Ready(Err(guar));
258 }
259 }
260 ExpandResult::Ready(Ok(es))
261}