|
1 | 1 | use std::borrow::Cow; |
2 | 2 |
|
3 | 3 | use rustc_ast::AttrStyle; |
4 | | -use rustc_errors::DiagArgValue; |
| 4 | +use rustc_errors::{DiagArgValue, MultiSpan, StashKey}; |
5 | 5 | use rustc_feature::Features; |
| 6 | +use rustc_hir::attrs::AttributeKind; |
6 | 7 | use rustc_hir::lints::AttributeLintKind; |
7 | | -use rustc_hir::{MethodKind, Target}; |
8 | | -use rustc_span::sym; |
| 8 | +use rustc_hir::{AttrItem, Attribute, MethodKind, Target}; |
| 9 | +use rustc_span::{BytePos, Span, Symbol, sym}; |
9 | 10 |
|
10 | 11 | use crate::AttributeParser; |
11 | 12 | use crate::context::{AcceptContext, Stage}; |
| 13 | +use crate::errors::{ |
| 14 | + InvalidAttrAtCrateLevel, ItemFollowingInnerAttr, UnsupportedAttributesInWhere, |
| 15 | +}; |
12 | 16 | use crate::session_diagnostics::InvalidTarget; |
13 | 17 | use crate::target_checking::Policy::Allow; |
14 | 18 |
|
@@ -96,6 +100,25 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { |
96 | 100 | return; |
97 | 101 | } |
98 | 102 |
|
| 103 | + if matches!(cx.attr_path.segments.as_ref(), [sym::repr]) && target == Target::Crate { |
| 104 | + // The allowed targets of `repr` depend on its arguments. They can't be checked using |
| 105 | + // the `AttributeParser` code. |
| 106 | + let span = cx.attr_span; |
| 107 | + let item = |
| 108 | + cx.cx.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span }); |
| 109 | + |
| 110 | + let pound_to_opening_bracket = cx.attr_span.until(cx.inner_span); |
| 111 | + |
| 112 | + cx.dcx() |
| 113 | + .create_err(InvalidAttrAtCrateLevel { |
| 114 | + span, |
| 115 | + pound_to_opening_bracket, |
| 116 | + name: sym::repr, |
| 117 | + item, |
| 118 | + }) |
| 119 | + .emit(); |
| 120 | + } |
| 121 | + |
99 | 122 | match allowed_targets.is_allowed(target) { |
100 | 123 | AllowedResult::Allowed => {} |
101 | 124 | AllowedResult::Warn => { |
@@ -163,6 +186,113 @@ impl<'sess, S: Stage> AttributeParser<'sess, S> { |
163 | 186 |
|
164 | 187 | cx.emit_lint(rustc_session::lint::builtin::UNUSED_ATTRIBUTES, kind, attr_span); |
165 | 188 | } |
| 189 | + |
| 190 | + // FIXME: Fix "Cannot determine resolution" error and remove built-in macros |
| 191 | + // from this check. |
| 192 | + pub(crate) fn check_invalid_crate_level_attr_item(&self, attr: &AttrItem, inner_span: Span) { |
| 193 | + // Check for builtin attributes at the crate level |
| 194 | + // which were unsuccessfully resolved due to cannot determine |
| 195 | + // resolution for the attribute macro error. |
| 196 | + const ATTRS_TO_CHECK: &[Symbol] = |
| 197 | + &[sym::derive, sym::test, sym::test_case, sym::global_allocator, sym::bench]; |
| 198 | + |
| 199 | + // FIXME(jdonszelmann): all attrs should be combined here cleaning this up some day. |
| 200 | + if let Some(name) = ATTRS_TO_CHECK.iter().find(|attr_to_check| matches!(attr.path.segments.as_ref(), [segment] if segment == *attr_to_check)) { |
| 201 | + let span = attr.span; |
| 202 | + let name = *name; |
| 203 | + |
| 204 | + let item = self.first_line_of_next_item(span).map(|span| ItemFollowingInnerAttr { span }); |
| 205 | + |
| 206 | + let err = self.dcx().create_err(InvalidAttrAtCrateLevel { |
| 207 | + span, |
| 208 | + pound_to_opening_bracket: span.until(inner_span), |
| 209 | + name, |
| 210 | + item, |
| 211 | + }); |
| 212 | + |
| 213 | + self.dcx().try_steal_replace_and_emit_err( |
| 214 | + attr.path.span, |
| 215 | + StashKey::UndeterminedMacroResolution, |
| 216 | + err, |
| 217 | + ); |
| 218 | + } |
| 219 | + } |
| 220 | + |
| 221 | + fn first_line_of_next_item(&self, span: Span) -> Option<Span> { |
| 222 | + // We can't exactly call `tcx.hir_free_items()` here because it's too early and querying |
| 223 | + // this would create a circular dependency. Instead, we resort to getting the original |
| 224 | + // source code that follows `span` and find the next item from here. |
| 225 | + |
| 226 | + self.sess() |
| 227 | + .source_map() |
| 228 | + .span_to_source(span, |content, _, span_end| { |
| 229 | + let mut source = &content[span_end..]; |
| 230 | + let initial_source_len = source.len(); |
| 231 | + let span = try { |
| 232 | + loop { |
| 233 | + let first = source.chars().next()?; |
| 234 | + |
| 235 | + if first.is_whitespace() { |
| 236 | + let split_idx = source.find(|c: char| !c.is_whitespace())?; |
| 237 | + source = &source[split_idx..]; |
| 238 | + } else if source.starts_with("//") { |
| 239 | + let line_idx = source.find('\n')?; |
| 240 | + source = &source[line_idx + '\n'.len_utf8()..]; |
| 241 | + } else if source.starts_with("/*") { |
| 242 | + // FIXME: support nested comments. |
| 243 | + let close_idx = source.find("*/")?; |
| 244 | + source = &source[close_idx + "*/".len()..]; |
| 245 | + } else if first == '#' { |
| 246 | + // FIXME: properly find the end of the attributes in order to accurately |
| 247 | + // skip them. This version just consumes the source code until the next |
| 248 | + // `]`. |
| 249 | + let close_idx = source.find(']')?; |
| 250 | + source = &source[close_idx + ']'.len_utf8()..]; |
| 251 | + } else { |
| 252 | + let lo = span_end + initial_source_len - source.len(); |
| 253 | + let last_line = source.split('\n').next().map(|s| s.trim_end())?; |
| 254 | + |
| 255 | + let hi = lo + last_line.len(); |
| 256 | + let lo = BytePos(lo as u32); |
| 257 | + let hi = BytePos(hi as u32); |
| 258 | + let next_item_span = Span::new(lo, hi, span.ctxt(), None); |
| 259 | + |
| 260 | + break next_item_span; |
| 261 | + } |
| 262 | + } |
| 263 | + }; |
| 264 | + |
| 265 | + Ok(span) |
| 266 | + }) |
| 267 | + .ok() |
| 268 | + .flatten() |
| 269 | + } |
| 270 | + |
| 271 | + pub(crate) fn check_invalid_where_predicate_attrs<'attr>( |
| 272 | + &self, |
| 273 | + attrs: impl IntoIterator<Item = &'attr Attribute>, |
| 274 | + ) { |
| 275 | + // FIXME(where_clause_attrs): Currently, as the following check shows, |
| 276 | + // only `#[cfg]` and `#[cfg_attr]` are allowed, but it should be removed |
| 277 | + // if we allow more attributes (e.g., tool attributes and `allow/deny/warn`) |
| 278 | + // in where clauses. After that, this function would become useless. |
| 279 | + let spans = attrs |
| 280 | + .into_iter() |
| 281 | + // FIXME: We shouldn't need to special-case `doc`! |
| 282 | + .filter(|attr| { |
| 283 | + matches!( |
| 284 | + attr, |
| 285 | + Attribute::Parsed(AttributeKind::DocComment { .. } | AttributeKind::Doc(_)) |
| 286 | + | Attribute::Unparsed(_) |
| 287 | + ) |
| 288 | + }) |
| 289 | + .map(|attr| attr.span()) |
| 290 | + .collect::<Vec<_>>(); |
| 291 | + if !spans.is_empty() { |
| 292 | + self.dcx() |
| 293 | + .emit_err(UnsupportedAttributesInWhere { span: MultiSpan::from_spans(spans) }); |
| 294 | + } |
| 295 | + } |
166 | 296 | } |
167 | 297 |
|
168 | 298 | /// Takes a list of `allowed_targets` for an attribute, and the `target` the attribute was applied to. |
|
0 commit comments