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