diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 24bfb5d9088f7..e1f1d95b8ab51 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -1766,11 +1766,19 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_path(&mut self, path: &'a ast::Path, _id: NodeId) { for segment in &path.segments { + // Identifiers we are going to check could come from a legacy macro (e.g. `#[test]`). + // For such macros identifiers must have empty context, because this context is + // used during name resolution and produced names must be unhygienic for compatibility. + // On the other hand, we need the actual non-empty context for feature gate checking + // because it's hygienic even for legacy macros. As previously stated, such context + // cannot be kept in identifiers, so it's kept in paths instead and we take it from + // there while keeping location info from the ident span. + let span = segment.ident.span.with_ctxt(path.span.ctxt()); if segment.ident.name == keywords::Crate.name() { - gate_feature_post!(&self, crate_in_paths, segment.ident.span, + gate_feature_post!(&self, crate_in_paths, span, "`crate` in paths is experimental"); } else if segment.ident.name == keywords::Extern.name() { - gate_feature_post!(&self, extern_in_paths, segment.ident.span, + gate_feature_post!(&self, extern_in_paths, span, "`extern` in paths is experimental"); } }