Line data Source code
1 : // Copyright (C) 2020-2026 Free Software Foundation, Inc.
2 :
3 : // This file is part of GCC.
4 :
5 : // GCC is free software; you can redistribute it and/or modify it under
6 : // the terms of the GNU General Public License as published by the Free
7 : // Software Foundation; either version 3, or (at your option) any later
8 : // version.
9 :
10 : // GCC is distributed in the hope that it will be useful, but WITHOUT ANY
11 : // WARRANTY; without even the implied warranty of MERCHANTABILITY or
12 : // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
13 : // for more details.
14 :
15 : // You should have received a copy of the GNU General Public License
16 : // along with GCC; see the file COPYING3. If not see
17 : // <http://www.gnu.org/licenses/>.
18 :
19 : #include "rust-feature-gate.h"
20 : #include "rust-abi.h"
21 : #include "rust-attribute-values.h"
22 : #include "rust-ast-visitor.h"
23 : #include "rust-feature.h"
24 : #include "rust-ast-full.h"
25 :
26 : namespace Rust {
27 :
28 : void
29 4509 : FeatureGate::check (AST::Crate &crate)
30 : {
31 4509 : visit (crate);
32 4509 : }
33 :
34 : void
35 4509 : FeatureGate::visit (AST::Crate &crate)
36 : {
37 4509 : valid_lang_features.clear ();
38 4509 : valid_lib_features.clear ();
39 :
40 : // avoid clearing defined features (?)
41 :
42 15731 : for (const auto &attr : crate.inner_attrs)
43 : {
44 11222 : if (attr.get_path ().as_string () == "feature")
45 : {
46 : // check for empty feature, such as `#![feature], this is an error
47 6585 : if (attr.empty_input ())
48 : {
49 1 : rust_error_at (attr.get_locus (), ErrorCode::E0556,
50 : "malformed %<feature%> attribute input");
51 1 : continue;
52 : }
53 6584 : const auto &attr_input = attr.get_attr_input ();
54 6584 : auto type = attr_input.get_attr_input_type ();
55 6584 : if (type == AST::AttrInput::AttrInputType::TOKEN_TREE)
56 : {
57 6583 : const auto &option = static_cast<const AST::DelimTokenTree &> (
58 6583 : attr.get_attr_input ());
59 6583 : std::unique_ptr<AST::AttrInputMetaItemContainer> meta_item (
60 6583 : option.parse_to_meta_item ());
61 13333 : for (const auto &item : meta_item->get_items ())
62 : {
63 6750 : const auto &name_str = item->as_string ();
64 :
65 : // TODO: detect duplicates
66 6750 : if (auto tname = Feature::as_name (name_str))
67 6745 : valid_lang_features.insert (tname.value ());
68 : else
69 5 : valid_lib_features.emplace (name_str, item->get_locus ());
70 6750 : }
71 6583 : }
72 1 : else if (type == AST::AttrInput::AttrInputType::META_ITEM)
73 : {
74 1 : const auto &meta_item
75 : = static_cast<const AST::AttrInputMetaItemContainer &> (
76 : attr_input);
77 2 : for (const auto &item : meta_item.get_items ())
78 : {
79 1 : const auto &name_str = item->as_string ();
80 :
81 : // TODO: detect duplicates
82 1 : if (auto tname = Feature::as_name (name_str))
83 1 : valid_lang_features.insert (tname.value ());
84 : else
85 0 : valid_lib_features.emplace (name_str, item->get_locus ());
86 1 : }
87 : }
88 : }
89 : }
90 :
91 4509 : AST::DefaultASTVisitor::visit (crate);
92 :
93 4514 : for (auto &ent : valid_lib_features)
94 : {
95 5 : const std::string &feature = ent.first;
96 5 : location_t locus = ent.second;
97 :
98 : // rustc treats these as valid,
99 : // but apparently has special handling for them
100 5 : if (feature == "libc" || feature == "test")
101 0 : continue;
102 :
103 5 : if (defined_lib_features.find (feature) != defined_lib_features.end ())
104 : {
105 : // TODO: emit warning if stable
106 0 : continue;
107 : }
108 :
109 5 : rust_error_at (locus, ErrorCode::E0635, "unknown feature %qs",
110 : feature.c_str ());
111 : }
112 4509 : }
113 :
114 : void
115 3835 : FeatureGate::gate (Feature::Name name, location_t loc,
116 : const std::string &error_msg)
117 : {
118 3835 : if (!valid_lang_features.count (name))
119 : {
120 13 : auto &feature = Feature::lookup (name);
121 13 : if (auto issue = feature.issue ())
122 : {
123 7 : auto issue_number = issue.value ();
124 7 : const char *fmt_str
125 : = "%s. see issue %u "
126 : "<https://github.com/rust-lang/rust/issues/%u> for more "
127 : "information. add `#![feature(%s)]` to the crate attributes to "
128 : "enable.";
129 7 : rust_error_at (loc, ErrorCode::E0658, fmt_str, error_msg.c_str (),
130 : issue_number, issue_number,
131 7 : feature.as_string ().c_str ());
132 : }
133 : else
134 : {
135 6 : const char *fmt_str
136 : = "%s. add `#![feature(%s)]` to the crate attributes to enable.";
137 6 : rust_error_at (loc, ErrorCode::E0658, fmt_str, error_msg.c_str (),
138 6 : feature.as_string ().c_str ());
139 : }
140 : }
141 3835 : }
142 :
143 : void
144 1471 : FeatureGate::visit (AST::ExternBlock &block)
145 : {
146 1471 : if (block.has_abi ())
147 : {
148 1471 : const auto abi = block.get_abi ();
149 :
150 1471 : if (get_abi_from_string (abi) == ABI::INTRINSIC)
151 409 : gate (Feature::Name::INTRINSICS, block.get_locus (),
152 818 : "intrinsics are subject to change");
153 1471 : }
154 1471 : AST::DefaultASTVisitor::visit (block);
155 1471 : }
156 :
157 : void
158 17040 : FeatureGate::check_rustc_attri (const std::vector<AST::Attribute> &attributes)
159 : {
160 27130 : for (const AST::Attribute &attr : attributes)
161 : {
162 10090 : auto name = attr.get_path ().as_string ();
163 10090 : if (name.rfind ("rustc_", 0) == 0)
164 : {
165 263 : gate (Feature::Name::RUSTC_ATTRS, attr.get_locus (),
166 526 : "internal implementation detail");
167 : }
168 10090 : }
169 17040 : }
170 :
171 : void
172 8505 : FeatureGate::check_may_dangle_attribute (
173 : const std::vector<AST::Attribute> &attributes)
174 : {
175 8510 : for (const AST::Attribute &attr : attributes)
176 : {
177 5 : if (attr.get_path ().as_string () == Values::Attributes::MAY_DANGLE)
178 5 : gate (Feature::Name::DROPCK_EYEPATCH, attr.get_locus (),
179 10 : "`may_dangle` has unstable semantics and may be removed in the "
180 : "future");
181 : }
182 8505 : }
183 :
184 : void
185 25350 : FeatureGate::check_lang_item_attribute (
186 : const std::vector<AST::Attribute> &attributes)
187 : {
188 40073 : for (const AST::Attribute &attr : attributes)
189 : {
190 14723 : const auto &str_path = attr.get_path ().as_string ();
191 14723 : bool is_lang_item = str_path == Values::Attributes::LANG
192 3105 : && attr.has_attr_input ()
193 17828 : && attr.get_attr_input ().get_attr_input_type ()
194 14723 : == AST::AttrInput::AttrInputType::LITERAL;
195 :
196 3105 : if (is_lang_item)
197 3105 : gate (Feature::Name::LANG_ITEMS, attr.get_locus (),
198 6210 : "lang items are subject to change");
199 14723 : }
200 25350 : }
201 :
202 : void
203 19252 : FeatureGate::note_stability_attribute (
204 : const std::vector<AST::Attribute> &attributes)
205 : {
206 29513 : for (const AST::Attribute &attr : attributes)
207 : {
208 10261 : std::string attr_name = attr.get_path ().as_string ();
209 :
210 10261 : Stability stability;
211 :
212 10261 : if (attr_name == Values::Attributes::STABLE)
213 643 : stability = Stability::STABLE;
214 9618 : else if (attr_name == Values::Attributes::UNSTABLE)
215 100 : stability = Stability::UNSTABLE;
216 9518 : else if (attr_name == Values::Attributes::RUSTC_CONST_STABLE)
217 158 : stability = Stability::STABLE;
218 9360 : else if (attr_name == Values::Attributes::RUSTC_CONST_UNSTABLE)
219 8 : stability = Stability::UNSTABLE;
220 : else
221 9352 : continue;
222 :
223 909 : if (attr.empty_input ())
224 : // TODO: error?
225 0 : continue;
226 :
227 909 : auto &attr_input = attr.get_attr_input ();
228 909 : if (attr_input.get_attr_input_type ()
229 : != AST::AttrInput::AttrInputType::TOKEN_TREE)
230 : // TODO: error?
231 0 : continue;
232 :
233 909 : std::unique_ptr<AST::AttrInputMetaItemContainer> meta_item (
234 : static_cast<const AST::DelimTokenTree &> (attr_input)
235 909 : .parse_to_meta_item ());
236 :
237 2727 : for (auto &item : meta_item->get_items ())
238 : {
239 : // TODO: more thorough error checking?
240 : // ~only the standard libraries should ever exercise this
241 1818 : if (item->is_key_value_pair ())
242 : {
243 1818 : auto &pair = static_cast<const AST::MetaNameValueStr &> (*item);
244 1818 : if (pair.get_name ().as_string () == "feature")
245 909 : defined_lib_features.emplace (pair.get_value (), stability);
246 : }
247 : }
248 10261 : }
249 19252 : }
250 :
251 : void
252 967 : FeatureGate::visit (AST::MacroRulesDefinition &rules_def)
253 : {
254 967 : check_rustc_attri (rules_def.get_outer_attrs ());
255 967 : note_stability_attribute (rules_def.get_outer_attrs ());
256 967 : }
257 :
258 : void
259 18285 : FeatureGate::visit (AST::Function &function)
260 : {
261 18285 : if (!function.is_external ())
262 16073 : check_rustc_attri (function.get_outer_attrs ());
263 :
264 18285 : check_lang_item_attribute (function.get_outer_attrs ());
265 :
266 18285 : note_stability_attribute (function.get_outer_attrs ());
267 :
268 18285 : AST::DefaultASTVisitor::visit (function);
269 18285 : }
270 :
271 : void
272 1 : FeatureGate::visit (AST::ExternalTypeItem &item)
273 : {
274 : // TODO(mxlol233): The gating needs a complete visiting chain to activate
275 : // `AST::ExternalTypeItem`.
276 1 : gate (Feature::Name::EXTERN_TYPES, item.get_locus (),
277 1 : "extern types are experimental");
278 1 : }
279 :
280 : void
281 4739 : FeatureGate::visit (AST::TraitImpl &impl)
282 : {
283 4739 : if (impl.is_exclam ())
284 6 : gate (Feature::Name::NEGATIVE_IMPLS, impl.get_locus (),
285 12 : "negative_impls are not yet implemented");
286 :
287 4739 : AST::DefaultASTVisitor::visit (impl);
288 4739 : }
289 :
290 : void
291 3736 : FeatureGate::visit (AST::Trait &trait)
292 : {
293 3736 : if (trait.is_auto ())
294 20 : gate (Feature::Name::OPTIN_BUILTIN_TRAITS, trait.get_locus (),
295 40 : "auto traits are experimental and possibly buggy");
296 3736 : check_lang_item_attribute (trait.get_outer_attrs ());
297 3736 : AST::DefaultASTVisitor::visit (trait);
298 3736 : }
299 :
300 : void
301 1 : FeatureGate::visit (AST::BoxExpr &expr)
302 : {
303 1 : gate (
304 : Feature::Name::BOX_SYNTAX, expr.get_locus (),
305 1 : "box expression syntax is experimental; you can call `Box::new` instead");
306 1 : AST::DefaultASTVisitor::visit (expr);
307 1 : }
308 :
309 : void
310 277 : FeatureGate::visit (AST::LifetimeParam &lifetime_param)
311 : {
312 277 : check_may_dangle_attribute (lifetime_param.get_outer_attrs ());
313 277 : AST::DefaultASTVisitor::visit (lifetime_param);
314 277 : }
315 :
316 : void
317 97 : FeatureGate::visit (AST::ConstGenericParam &const_param)
318 : {
319 97 : check_may_dangle_attribute (const_param.get_outer_attrs ());
320 97 : AST::DefaultASTVisitor::visit (const_param);
321 97 : }
322 :
323 : void
324 8131 : FeatureGate::visit (AST::TypeParam ¶m)
325 : {
326 8131 : check_may_dangle_attribute (param.get_outer_attrs ());
327 8131 : AST::DefaultASTVisitor::visit (param);
328 8131 : }
329 :
330 : void
331 1929 : FeatureGate::visit (AST::BorrowExpr &expr)
332 : {
333 1929 : if (expr.is_raw_borrow ())
334 5 : gate (Feature::Name::RAW_REF_OP, expr.get_locus (),
335 10 : "raw address of syntax is experimental");
336 1929 : }
337 :
338 : void
339 51 : FeatureGate::visit (AST::RangePattern &pattern)
340 : {
341 51 : if (pattern.get_range_kind () == AST::RangeKind::EXCLUDED)
342 20 : gate (Feature::Name::EXCLUSIVE_RANGE_PATTERN, pattern.get_locus (),
343 40 : "exclusive range pattern syntax is experimental");
344 51 : }
345 :
346 : void
347 10 : FeatureGate::visit (AST::UseTreeGlob &use)
348 : {
349 : // At the moment, UseTrees do not have outer attributes, but they should. we
350 : // need to eventually gate `#[prelude_import]` on use-trees based on the
351 : // #[feature(prelude_import)]
352 10 : }
353 :
354 : void
355 1591 : FeatureGate::visit (AST::StructStruct &struct_item)
356 : {
357 1591 : check_lang_item_attribute (struct_item.get_outer_attrs ());
358 1591 : AST::DefaultASTVisitor::visit (struct_item);
359 1591 : }
360 :
361 : void
362 738 : FeatureGate::visit (AST::TraitItemType &trait_item_type)
363 : {
364 738 : check_lang_item_attribute (trait_item_type.get_outer_attrs ());
365 738 : AST::DefaultASTVisitor::visit (trait_item_type);
366 738 : }
367 :
368 : void
369 544 : FeatureGate::visit (AST::Enum &enum_item)
370 : {
371 544 : check_lang_item_attribute (enum_item.get_outer_attrs ());
372 544 : AST::DefaultASTVisitor::visit (enum_item);
373 544 : }
374 :
375 : void
376 456 : FeatureGate::visit (AST::EnumItem &enum_variant)
377 : {
378 456 : check_lang_item_attribute (enum_variant.get_outer_attrs ());
379 456 : AST::DefaultASTVisitor::visit (enum_variant);
380 456 : }
381 :
382 : } // namespace Rust
|