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