Skip to content

Commit 09e1210

Browse files
committed
Add support for removed library features
1 parent 8cde544 commit 09e1210

File tree

119 files changed

+1231
-733
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

119 files changed

+1231
-733
lines changed

Cargo.lock

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2167,6 +2167,7 @@ version = "0.1.0"
21672167
dependencies = [
21682168
"html5ever",
21692169
"regex",
2170+
"urlencoding",
21702171
]
21712172

21722173
[[package]]
@@ -4411,7 +4412,6 @@ dependencies = [
44114412
"rustc_middle",
44124413
"rustc_query_system",
44134414
"rustc_serialize",
4414-
"rustc_session",
44154415
"rustc_span",
44164416
"tracing",
44174417
]
@@ -5834,6 +5834,12 @@ dependencies = [
58345834
"percent-encoding",
58355835
]
58365836

5837+
[[package]]
5838+
name = "urlencoding"
5839+
version = "2.1.3"
5840+
source = "registry+https://github.com/rust-lang/crates.io-index"
5841+
checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da"
5842+
58375843
[[package]]
58385844
name = "utf-8"
58395845
version = "0.7.6"

compiler/rustc_attr_parsing/messages.ftl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -247,3 +247,7 @@ attr_parsing_raw_dylib_only_windows =
247247
248248
attr_parsing_whole_archive_needs_static =
249249
linking modifier `whole-archive` is only compatible with `static` linking kind
250+
251+
attr_parsing_limit_invalid =
252+
`limit` must be a non-negative integer
253+
.label = {$error_str}

compiler/rustc_attr_parsing/src/attributes/crate_level.rs

Lines changed: 145 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,40 @@
1-
use rustc_feature::AttributeType;
1+
use std::num::IntErrorKind;
2+
3+
use rustc_hir::limit::Limit;
24

35
use super::prelude::*;
6+
use crate::session_diagnostics::LimitInvalid;
7+
8+
impl<S: Stage> AcceptContext<'_, '_, S> {
9+
fn parse_limit_int(&self, nv: &NameValueParser) -> Option<Limit> {
10+
let Some(limit) = nv.value_as_str() else {
11+
self.expected_string_literal(nv.value_span, Some(nv.value_as_lit()));
12+
return None;
13+
};
14+
15+
let error_str = match limit.as_str().parse() {
16+
Ok(i) => return Some(Limit::new(i)),
17+
Err(e) => match e.kind() {
18+
IntErrorKind::PosOverflow => "`limit` is too large",
19+
IntErrorKind::Empty => "`limit` must be a non-negative integer",
20+
IntErrorKind::InvalidDigit => "not a valid integer",
21+
IntErrorKind::NegOverflow => {
22+
panic!(
23+
"`limit` should never negatively overflow since we're parsing into a usize and we'd get Empty instead"
24+
)
25+
}
26+
IntErrorKind::Zero => {
27+
panic!("zero is a valid `limit` so should have returned Ok() when parsing")
28+
}
29+
kind => panic!("unimplemented IntErrorKind variant: {:?}", kind),
30+
},
31+
};
32+
33+
self.emit_err(LimitInvalid { span: self.attr_span, value_span: nv.value_span, error_str });
34+
35+
None
36+
}
37+
}
438

539
pub(crate) struct CrateNameParser;
640

@@ -11,8 +45,8 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
1145
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "name");
1246
const TYPE: AttributeType = AttributeType::CrateLevel;
1347

14-
// FIXME: crate name is allowed on all targets and ignored,
15-
// even though it should only be valid on crates of course
48+
// because it's a crate-level attribute, we already warn about it.
49+
// Putting target limitations here would give duplicate warnings
1650
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
1751

1852
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
@@ -34,3 +68,111 @@ impl<S: Stage> SingleAttributeParser<S> for CrateNameParser {
3468
})
3569
}
3670
}
71+
72+
pub(crate) struct RecursionLimitParser;
73+
74+
impl<S: Stage> SingleAttributeParser<S> for RecursionLimitParser {
75+
const PATH: &[Symbol] = &[sym::recursion_limit];
76+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
77+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
78+
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N", "https://doc.rust-lang.org/reference/attributes/limits.html#the-recursion_limit-attribute");
79+
const TYPE: AttributeType = AttributeType::CrateLevel;
80+
81+
// because it's a crate-level attribute, we already warn about it.
82+
// Putting target limitations here would give duplicate warnings
83+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
84+
85+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
86+
let ArgParser::NameValue(nv) = args else {
87+
cx.expected_name_value(cx.attr_span, None);
88+
return None;
89+
};
90+
91+
Some(AttributeKind::RecursionLimit {
92+
limit: cx.parse_limit_int(nv)?,
93+
attr_span: cx.attr_span,
94+
limit_span: nv.value_span,
95+
})
96+
}
97+
}
98+
99+
pub(crate) struct MoveSizeLimitParser;
100+
101+
impl<S: Stage> SingleAttributeParser<S> for MoveSizeLimitParser {
102+
const PATH: &[Symbol] = &[sym::move_size_limit];
103+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
104+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
105+
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
106+
const TYPE: AttributeType = AttributeType::CrateLevel;
107+
108+
// because it's a crate-level attribute, we already warn about it.
109+
// Putting target limitations here would give duplicate warnings
110+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
111+
112+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
113+
let ArgParser::NameValue(nv) = args else {
114+
cx.expected_name_value(cx.attr_span, None);
115+
return None;
116+
};
117+
118+
Some(AttributeKind::MoveSizeLimit {
119+
limit: cx.parse_limit_int(nv)?,
120+
attr_span: cx.attr_span,
121+
limit_span: nv.value_span,
122+
})
123+
}
124+
}
125+
126+
pub(crate) struct TypeLengthLimitParser;
127+
128+
impl<S: Stage> SingleAttributeParser<S> for TypeLengthLimitParser {
129+
const PATH: &[Symbol] = &[sym::type_length_limit];
130+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
131+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::WarnButFutureError;
132+
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
133+
const TYPE: AttributeType = AttributeType::CrateLevel;
134+
135+
// because it's a crate-level attribute, we already warn about it.
136+
// Putting target limitations here would give duplicate warnings
137+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
138+
139+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
140+
let ArgParser::NameValue(nv) = args else {
141+
cx.expected_name_value(cx.attr_span, None);
142+
return None;
143+
};
144+
145+
Some(AttributeKind::TypeLengthLimit {
146+
limit: cx.parse_limit_int(nv)?,
147+
attr_span: cx.attr_span,
148+
limit_span: nv.value_span,
149+
})
150+
}
151+
}
152+
153+
pub(crate) struct PatternComplexityLimitParser;
154+
155+
impl<S: Stage> SingleAttributeParser<S> for PatternComplexityLimitParser {
156+
const PATH: &[Symbol] = &[sym::pattern_complexity_limit];
157+
const ATTRIBUTE_ORDER: AttributeOrder = AttributeOrder::KeepOutermost;
158+
const ON_DUPLICATE: OnDuplicate<S> = OnDuplicate::Error;
159+
const TEMPLATE: AttributeTemplate = template!(NameValueStr: "N");
160+
const TYPE: AttributeType = AttributeType::CrateLevel;
161+
162+
// because it's a crate-level attribute, we already warn about it.
163+
// Putting target limitations here would give duplicate warnings
164+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(ALL_TARGETS);
165+
166+
fn convert(cx: &mut AcceptContext<'_, '_, S>, args: &ArgParser<'_>) -> Option<AttributeKind> {
167+
let ArgParser::NameValue(nv) = args else {
168+
cx.expected_name_value(cx.attr_span, None);
169+
return None;
170+
};
171+
172+
Some(AttributeKind::PatternComplexityLimit {
173+
limit: cx.parse_limit_int(nv)?,
174+
attr_span: cx.attr_span,
175+
limit_span: nv.value_span,
176+
})
177+
}
178+
}

compiler/rustc_attr_parsing/src/attributes/prelude.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,7 @@
1-
// templates
2-
#[doc(hidden)]
3-
pub(super) use rustc_feature::{AttributeTemplate, template};
41
// data structures
52
#[doc(hidden)]
3+
pub(super) use rustc_feature::{AttributeTemplate, AttributeType, template};
4+
#[doc(hidden)]
65
pub(super) use rustc_hir::attrs::AttributeKind;
76
#[doc(hidden)]
87
pub(super) use rustc_hir::lints::AttributeLintKind;

compiler/rustc_attr_parsing/src/attributes/stability.rs

Lines changed: 148 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ use std::num::NonZero;
22

33
use rustc_errors::ErrorGuaranteed;
44
use rustc_hir::{
5-
DefaultBodyStability, MethodKind, PartialConstStability, Stability, StabilityLevel,
6-
StableSince, Target, UnstableReason, VERSION_PLACEHOLDER,
5+
DefaultBodyStability, MethodKind, PartialConstStability, RemovedFeature, Stability,
6+
StabilityLevel, StableSince, Target, UnstableReason, VERSION_PLACEHOLDER,
77
};
88

99
use super::prelude::*;
@@ -471,3 +471,149 @@ pub(crate) fn parse_unstability<S: Stage>(
471471
(Err(ErrorGuaranteed { .. }), _) | (_, Err(ErrorGuaranteed { .. })) => None,
472472
}
473473
}
474+
475+
#[allow(dead_code)]
476+
#[derive(Default)]
477+
pub(crate) struct RemovedFeatureParser {
478+
removed: Option<(RemovedFeature, Span)>,
479+
}
480+
481+
impl<S: Stage> AttributeParser<S> for RemovedFeatureParser {
482+
const ATTRIBUTES: AcceptMapping<Self, S> = &[(
483+
&[sym::unstable_removed],
484+
template!(List: &[r#"feature = "name", since = "1.2.3", reason = "...", issue = "N|none""#]),
485+
|this, cx, args| {
486+
// same staged-api restriction as other stability attrs
487+
reject_outside_std!(cx);
488+
489+
let ArgParser::List(list) = args else {
490+
cx.expected_list(cx.attr_span);
491+
return;
492+
};
493+
494+
let mut feature_opt: Option<Symbol> = None;
495+
let mut reason_opt: Option<Symbol> = None;
496+
let mut since_opt: Option<StableSince> = None;
497+
498+
for param in list.mixed() {
499+
let param_span = param.span();
500+
let Some(param) = param.meta_item() else {
501+
cx.emit_err(session_diagnostics::UnsupportedLiteral {
502+
span: param_span,
503+
reason: UnsupportedLiteralReason::Generic,
504+
is_bytestr: false,
505+
start_point_span: cx.sess().source_map().start_point(param_span),
506+
});
507+
return;
508+
};
509+
510+
let word = param.path().word();
511+
match word.map(|i| i.name) {
512+
Some(sym::feature) => {
513+
if insert_value_into_option_or_error(
514+
cx,
515+
&param,
516+
&mut feature_opt,
517+
word.unwrap(),
518+
)
519+
.is_none()
520+
{
521+
return;
522+
}
523+
}
524+
Some(sym::reason) => {
525+
if insert_value_into_option_or_error(
526+
cx,
527+
&param,
528+
&mut reason_opt,
529+
word.unwrap(),
530+
)
531+
.is_none()
532+
{
533+
return;
534+
}
535+
}
536+
Some(sym::since) => {
537+
if insert_value_into_option_or_error(
538+
cx,
539+
&param,
540+
&mut None::<Symbol>,
541+
word.unwrap(),
542+
)
543+
.is_none()
544+
{
545+
return;
546+
}
547+
// parse version string
548+
if let Some(s) = param.args().name_value().and_then(|nv| nv.value_as_str())
549+
{
550+
if s.as_str() == VERSION_PLACEHOLDER {
551+
since_opt = Some(StableSince::Current);
552+
} else if let Some(v) = super::util::parse_version(s) {
553+
since_opt = Some(StableSince::Version(v));
554+
} else {
555+
let err = cx.emit_err(session_diagnostics::InvalidSince {
556+
span: cx.attr_span,
557+
});
558+
since_opt = Some(StableSince::Err(err));
559+
}
560+
} else {
561+
cx.expected_name_value(param.span(), Some(word.unwrap().name));
562+
return;
563+
}
564+
}
565+
_ => {
566+
cx.emit_err(session_diagnostics::UnknownMetaItem {
567+
span: param_span,
568+
item: param.path().to_string(),
569+
expected: &["feature", "reason", "since"],
570+
});
571+
return;
572+
}
573+
}
574+
}
575+
576+
// Validate presence and form of fields:
577+
let feature = match feature_opt {
578+
Some(f) if rustc_lexer::is_ident(f.as_str()) => f,
579+
Some(_) => {
580+
cx.emit_err(session_diagnostics::NonIdentFeature { span: cx.attr_span });
581+
return;
582+
}
583+
None => {
584+
cx.emit_err(session_diagnostics::MissingFeature { span: cx.attr_span });
585+
return;
586+
}
587+
};
588+
589+
let since = match since_opt {
590+
Some(s) => s,
591+
None => {
592+
cx.emit_err(session_diagnostics::MissingSince { span: cx.attr_span });
593+
return;
594+
}
595+
};
596+
597+
let since_symbol = match since {
598+
StableSince::Version(v) => Symbol::intern(&v.to_string()), // dynamic string — ok
599+
StableSince::Current => sym::current,
600+
StableSince::Err(_) => sym::err,
601+
};
602+
603+
let removed = RemovedFeature {
604+
feature,
605+
reason: reason_opt.unwrap_or_else(|| sym::no_reason_given),
606+
since: since_symbol,
607+
span: cx.attr_span,
608+
};
609+
610+
this.removed = Some((removed, cx.attr_span));
611+
},
612+
)];
613+
614+
const ALLOWED_TARGETS: AllowedTargets = AllowedTargets::AllowList(&[Allow(Target::Crate)]);
615+
616+
fn finalize(self, _cx: &FinalizeContext<'_, '_, S>) -> Option<AttributeKind> {
617+
self.removed.map(|(removed_feature, _span)| AttributeKind::RemovedFeature(removed_feature))
618+
}
619+
}

0 commit comments

Comments
 (0)