aboutsummaryrefslogtreecommitdiff
path: root/crates/ide_completion/src/completions/attribute.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ide_completion/src/completions/attribute.rs')
-rw-r--r--crates/ide_completion/src/completions/attribute.rs927
1 files changed, 573 insertions, 354 deletions
diff --git a/crates/ide_completion/src/completions/attribute.rs b/crates/ide_completion/src/completions/attribute.rs
index b1505c74b..76d926157 100644
--- a/crates/ide_completion/src/completions/attribute.rs
+++ b/crates/ide_completion/src/completions/attribute.rs
@@ -3,9 +3,9 @@
3//! This module uses a bit of static metadata to provide completions 3//! This module uses a bit of static metadata to provide completions
4//! for built-in attributes. 4//! for built-in attributes.
5 5
6use itertools::Itertools; 6use once_cell::sync::Lazy;
7use rustc_hash::FxHashSet; 7use rustc_hash::{FxHashMap, FxHashSet};
8use syntax::{ast, AstNode, T}; 8use syntax::{ast, AstNode, NodeOrToken, SyntaxKind, T};
9 9
10use crate::{ 10use crate::{
11 context::CompletionContext, 11 context::CompletionContext,
@@ -14,33 +14,40 @@ use crate::{
14 Completions, 14 Completions,
15}; 15};
16 16
17pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> { 17mod derive;
18 if ctx.mod_declaration_under_caret.is_some() { 18mod lint;
19 return None; 19pub(crate) use self::lint::LintCompletion;
20 }
21 20
21pub(crate) fn complete_attribute(acc: &mut Completions, ctx: &CompletionContext) -> Option<()> {
22 let attribute = ctx.attribute_under_caret.as_ref()?; 22 let attribute = ctx.attribute_under_caret.as_ref()?;
23 match (attribute.path(), attribute.token_tree()) { 23 match (attribute.path().and_then(|p| p.as_single_name_ref()), attribute.token_tree()) {
24 (Some(path), Some(token_tree)) => { 24 (Some(path), Some(token_tree)) => match path.text().as_str() {
25 let path = path.syntax().text(); 25 "derive" => derive::complete_derive(acc, ctx, token_tree),
26 if path == "derive" { 26 "feature" => lint::complete_lint(acc, ctx, token_tree, FEATURES),
27 complete_derive(acc, ctx, token_tree) 27 "allow" | "warn" | "deny" | "forbid" => {
28 } else if path == "feature" { 28 lint::complete_lint(acc, ctx, token_tree.clone(), lint::DEFAULT_LINT_COMPLETIONS);
29 complete_lint(acc, ctx, token_tree, FEATURES) 29 lint::complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
30 } else if path == "allow" || path == "warn" || path == "deny" || path == "forbid" {
31 complete_lint(acc, ctx, token_tree.clone(), DEFAULT_LINT_COMPLETIONS);
32 complete_lint(acc, ctx, token_tree, CLIPPY_LINTS);
33 } 30 }
34 } 31 _ => (),
35 (_, Some(_token_tree)) => {} 32 },
36 _ => complete_attribute_start(acc, ctx, attribute), 33 (None, Some(_)) => (),
34 _ => complete_new_attribute(acc, ctx, attribute),
37 } 35 }
38 Some(()) 36 Some(())
39} 37}
40 38
41fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) { 39fn complete_new_attribute(acc: &mut Completions, ctx: &CompletionContext, attribute: &ast::Attr) {
40 let attribute_annotated_item_kind = attribute.syntax().parent().map(|it| it.kind());
41 let attributes = attribute_annotated_item_kind.and_then(|kind| {
42 if ast::Expr::can_cast(kind) {
43 Some(EXPR_ATTRIBUTES)
44 } else {
45 KIND_TO_ATTRIBUTES.get(&kind).copied()
46 }
47 });
42 let is_inner = attribute.kind() == ast::AttrKind::Inner; 48 let is_inner = attribute.kind() == ast::AttrKind::Inner;
43 for attr_completion in ATTRIBUTES.iter().filter(|compl| is_inner || !compl.prefer_inner) { 49
50 let add_completion = |attr_completion: &AttrCompletion| {
44 let mut item = CompletionItem::new( 51 let mut item = CompletionItem::new(
45 CompletionKind::Attribute, 52 CompletionKind::Attribute,
46 ctx.source_range(), 53 ctx.source_range(),
@@ -56,9 +63,19 @@ fn complete_attribute_start(acc: &mut Completions, ctx: &CompletionContext, attr
56 item.insert_snippet(cap, snippet); 63 item.insert_snippet(cap, snippet);
57 } 64 }
58 65
59 if attribute.kind() == ast::AttrKind::Inner || !attr_completion.prefer_inner { 66 if is_inner || !attr_completion.prefer_inner {
60 acc.add(item.build()); 67 acc.add(item.build());
61 } 68 }
69 };
70
71 match attributes {
72 Some(applicable) => applicable
73 .iter()
74 .flat_map(|name| ATTRIBUTES.binary_search_by(|attr| attr.key().cmp(name)).ok())
75 .flat_map(|idx| ATTRIBUTES.get(idx))
76 .for_each(add_completion),
77 None if is_inner => ATTRIBUTES.iter().for_each(add_completion),
78 None => ATTRIBUTES.iter().filter(|compl| !compl.prefer_inner).for_each(add_completion),
62 } 79 }
63} 80}
64 81
@@ -70,6 +87,10 @@ struct AttrCompletion {
70} 87}
71 88
72impl AttrCompletion { 89impl AttrCompletion {
90 fn key(&self) -> &'static str {
91 self.lookup.unwrap_or(self.label)
92 }
93
73 const fn prefer_inner(self) -> AttrCompletion { 94 const fn prefer_inner(self) -> AttrCompletion {
74 AttrCompletion { prefer_inner: true, ..self } 95 AttrCompletion { prefer_inner: true, ..self }
75 } 96 }
@@ -83,30 +104,122 @@ const fn attr(
83 AttrCompletion { label, lookup, snippet, prefer_inner: false } 104 AttrCompletion { label, lookup, snippet, prefer_inner: false }
84} 105}
85 106
107macro_rules! attrs {
108 // attributes applicable to all items
109 [@ { item $($tt:tt)* } {$($acc:tt)*}] => {
110 attrs!(@ { $($tt)* } { $($acc)*, "deprecated", "doc", "dochidden", "docalias", "must_use", "no_mangle" })
111 };
112 // attributes applicable to all adts
113 [@ { adt $($tt:tt)* } {$($acc:tt)*}] => {
114 attrs!(@ { $($tt)* } { $($acc)*, "derive", "repr" })
115 };
116 // attributes applicable to all linkable things aka functions/statics
117 [@ { linkable $($tt:tt)* } {$($acc:tt)*}] => {
118 attrs!(@ { $($tt)* } { $($acc)*, "export_name", "link_name", "link_section" })
119 };
120 // error fallback for nicer error message
121 [@ { $ty:ident $($tt:tt)* } {$($acc:tt)*}] => {
122 compile_error!(concat!("unknown attr subtype ", stringify!($ty)))
123 };
124 // general push down accumulation
125 [@ { $lit:literal $($tt:tt)*} {$($acc:tt)*}] => {
126 attrs!(@ { $($tt)* } { $($acc)*, $lit })
127 };
128 [@ {$($tt:tt)+} {$($tt2:tt)*}] => {
129 compile_error!(concat!("Unexpected input ", stringify!($($tt)+)))
130 };
131 // final output construction
132 [@ {} {$($tt:tt)*}] => { &[$($tt)*] as _ };
133 // starting matcher
134 [$($tt:tt),*] => {
135 attrs!(@ { $($tt)* } { "allow", "cfg", "cfg_attr", "deny", "forbid", "warn" })
136 };
137}
138
139#[rustfmt::skip]
140static KIND_TO_ATTRIBUTES: Lazy<FxHashMap<SyntaxKind, &[&str]>> = Lazy::new(|| {
141 use SyntaxKind::*;
142 std::array::IntoIter::new([
143 (
144 SOURCE_FILE,
145 attrs!(
146 item,
147 "crate_name", "feature", "no_implicit_prelude", "no_main", "no_std",
148 "recursion_limit", "type_length_limit", "windows_subsystem"
149 ),
150 ),
151 (MODULE, attrs!(item, "no_implicit_prelude", "path")),
152 (ITEM_LIST, attrs!(item, "no_implicit_prelude")),
153 (MACRO_RULES, attrs!(item, "macro_export", "macro_use")),
154 (MACRO_DEF, attrs!(item)),
155 (EXTERN_CRATE, attrs!(item, "macro_use", "no_link")),
156 (USE, attrs!(item)),
157 (TYPE_ALIAS, attrs!(item)),
158 (STRUCT, attrs!(item, adt, "non_exhaustive")),
159 (ENUM, attrs!(item, adt, "non_exhaustive")),
160 (UNION, attrs!(item, adt)),
161 (CONST, attrs!(item)),
162 (
163 FN,
164 attrs!(
165 item, linkable,
166 "cold", "ignore", "inline", "must_use", "panic_handler", "proc_macro",
167 "proc_macro_derive", "proc_macro_attribute", "should_panic", "target_feature",
168 "test", "track_caller"
169 ),
170 ),
171 (STATIC, attrs!(item, linkable, "global_allocator", "used")),
172 (TRAIT, attrs!(item, "must_use")),
173 (IMPL, attrs!(item, "automatically_derived")),
174 (ASSOC_ITEM_LIST, attrs!(item)),
175 (EXTERN_BLOCK, attrs!(item, "link")),
176 (EXTERN_ITEM_LIST, attrs!(item, "link")),
177 (MACRO_CALL, attrs!()),
178 (SELF_PARAM, attrs!()),
179 (PARAM, attrs!()),
180 (RECORD_FIELD, attrs!()),
181 (VARIANT, attrs!("non_exhaustive")),
182 (TYPE_PARAM, attrs!()),
183 (CONST_PARAM, attrs!()),
184 (LIFETIME_PARAM, attrs!()),
185 (LET_STMT, attrs!()),
186 (EXPR_STMT, attrs!()),
187 (LITERAL, attrs!()),
188 (RECORD_EXPR_FIELD_LIST, attrs!()),
189 (RECORD_EXPR_FIELD, attrs!()),
190 (MATCH_ARM_LIST, attrs!()),
191 (MATCH_ARM, attrs!()),
192 (IDENT_PAT, attrs!()),
193 (RECORD_PAT_FIELD, attrs!()),
194 ])
195 .collect()
196});
197const EXPR_ATTRIBUTES: &[&str] = attrs!();
198
86/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index 199/// https://doc.rust-lang.org/reference/attributes.html#built-in-attributes-index
200// Keep these sorted for the binary search!
87const ATTRIBUTES: &[AttrCompletion] = &[ 201const ATTRIBUTES: &[AttrCompletion] = &[
88 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")), 202 attr("allow(…)", Some("allow"), Some("allow(${0:lint})")),
89 attr("automatically_derived", None, None), 203 attr("automatically_derived", None, None),
90 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
91 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")), 204 attr("cfg(…)", Some("cfg"), Some("cfg(${0:predicate})")),
205 attr("cfg_attr(…)", Some("cfg_attr"), Some("cfg_attr(${1:predicate}, ${0:attr})")),
92 attr("cold", None, None), 206 attr("cold", None, None),
93 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#)) 207 attr(r#"crate_name = """#, Some("crate_name"), Some(r#"crate_name = "${0:crate_name}""#))
94 .prefer_inner(), 208 .prefer_inner(),
95 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")), 209 attr("deny(…)", Some("deny"), Some("deny(${0:lint})")),
96 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)), 210 attr(r#"deprecated"#, Some("deprecated"), Some(r#"deprecated"#)),
97 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)), 211 attr("derive(…)", Some("derive"), Some(r#"derive(${0:Debug})"#)),
212 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
213 attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
214 attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
98 attr( 215 attr(
99 r#"export_name = "…""#, 216 r#"export_name = "…""#,
100 Some("export_name"), 217 Some("export_name"),
101 Some(r#"export_name = "${0:exported_symbol_name}""#), 218 Some(r#"export_name = "${0:exported_symbol_name}""#),
102 ), 219 ),
103 attr(r#"doc(alias = "…")"#, Some("docalias"), Some(r#"doc(alias = "${0:docs}")"#)),
104 attr(r#"doc = "…""#, Some("doc"), Some(r#"doc = "${0:docs}""#)),
105 attr(r#"doc(hidden)"#, Some("dochidden"), Some(r#"doc(hidden)"#)),
106 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(), 220 attr("feature(…)", Some("feature"), Some("feature(${0:flag})")).prefer_inner(),
107 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")), 221 attr("forbid(…)", Some("forbid"), Some("forbid(${0:lint})")),
108 // FIXME: resolve through macro resolution? 222 attr("global_allocator", None, None),
109 attr("global_allocator", None, None).prefer_inner(),
110 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)), 223 attr(r#"ignore = "…""#, Some("ignore"), Some(r#"ignore = "${0:reason}""#)),
111 attr("inline", Some("inline"), Some("inline")), 224 attr("inline", Some("inline"), Some("inline")),
112 attr("link", None, None), 225 attr("link", None, None),
@@ -119,13 +232,13 @@ const ATTRIBUTES: &[AttrCompletion] = &[
119 attr("macro_export", None, None), 232 attr("macro_export", None, None),
120 attr("macro_use", None, None), 233 attr("macro_use", None, None),
121 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)), 234 attr(r#"must_use"#, Some("must_use"), Some(r#"must_use"#)),
122 attr("no_link", None, None).prefer_inner(),
123 attr("no_implicit_prelude", None, None).prefer_inner(), 235 attr("no_implicit_prelude", None, None).prefer_inner(),
236 attr("no_link", None, None).prefer_inner(),
124 attr("no_main", None, None).prefer_inner(), 237 attr("no_main", None, None).prefer_inner(),
125 attr("no_mangle", None, None), 238 attr("no_mangle", None, None),
126 attr("no_std", None, None).prefer_inner(), 239 attr("no_std", None, None).prefer_inner(),
127 attr("non_exhaustive", None, None), 240 attr("non_exhaustive", None, None),
128 attr("panic_handler", None, None).prefer_inner(), 241 attr("panic_handler", None, None),
129 attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)), 242 attr(r#"path = "…""#, Some("path"), Some(r#"path ="${0:path}""#)),
130 attr("proc_macro", None, None), 243 attr("proc_macro", None, None),
131 attr("proc_macro_attribute", None, None), 244 attr("proc_macro_attribute", None, None),
@@ -153,412 +266,518 @@ const ATTRIBUTES: &[AttrCompletion] = &[
153 .prefer_inner(), 266 .prefer_inner(),
154]; 267];
155 268
156fn complete_derive(acc: &mut Completions, ctx: &CompletionContext, derive_input: ast::TokenTree) { 269fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Option<FxHashSet<String>> {
157 if let Ok(existing_derives) = parse_comma_sep_input(derive_input) { 270 let (l_paren, r_paren) = derive_input.l_paren_token().zip(derive_input.r_paren_token())?;
158 for derive_completion in DEFAULT_DERIVE_COMPLETIONS 271 let mut input_derives = FxHashSet::default();
159 .iter() 272 let mut tokens = derive_input
160 .filter(|completion| !existing_derives.contains(completion.label)) 273 .syntax()
161 { 274 .children_with_tokens()
162 let mut components = vec![derive_completion.label]; 275 .filter_map(NodeOrToken::into_token)
163 components.extend( 276 .skip_while(|token| token != &l_paren)
164 derive_completion 277 .skip(1)
165 .dependencies 278 .take_while(|token| token != &r_paren)
166 .iter() 279 .peekable();
167 .filter(|&&dependency| !existing_derives.contains(dependency)), 280 let mut input = String::new();
168 ); 281 while tokens.peek().is_some() {
169 let lookup = components.join(", "); 282 for token in tokens.by_ref().take_while(|t| t.kind() != T![,]) {
170 let label = components.iter().rev().join(", "); 283 input.push_str(token.text());
171 let mut item =
172 CompletionItem::new(CompletionKind::Attribute, ctx.source_range(), label);
173 item.lookup_by(lookup).kind(CompletionItemKind::Attribute);
174 item.add_to(acc);
175 }
176
177 for custom_derive_name in get_derive_names_in_scope(ctx).difference(&existing_derives) {
178 let mut item = CompletionItem::new(
179 CompletionKind::Attribute,
180 ctx.source_range(),
181 custom_derive_name,
182 );
183 item.kind(CompletionItemKind::Attribute);
184 item.add_to(acc);
185 } 284 }
186 }
187}
188 285
189fn complete_lint( 286 if !input.is_empty() {
190 acc: &mut Completions, 287 input_derives.insert(input.trim().to_owned());
191 ctx: &CompletionContext,
192 derive_input: ast::TokenTree,
193 lints_completions: &[LintCompletion],
194) {
195 if let Ok(existing_lints) = parse_comma_sep_input(derive_input) {
196 for lint_completion in lints_completions
197 .into_iter()
198 .filter(|completion| !existing_lints.contains(completion.label))
199 {
200 let mut item = CompletionItem::new(
201 CompletionKind::Attribute,
202 ctx.source_range(),
203 lint_completion.label,
204 );
205 item.kind(CompletionItemKind::Attribute).detail(lint_completion.description);
206 item.add_to(acc)
207 } 288 }
208 }
209}
210 289
211fn parse_comma_sep_input(derive_input: ast::TokenTree) -> Result<FxHashSet<String>, ()> { 290 input.clear();
212 match (derive_input.left_delimiter_token(), derive_input.right_delimiter_token()) {
213 (Some(left_paren), Some(right_paren))
214 if left_paren.kind() == T!['('] && right_paren.kind() == T![')'] =>
215 {
216 let mut input_derives = FxHashSet::default();
217 let mut current_derive = String::new();
218 for token in derive_input
219 .syntax()
220 .children_with_tokens()
221 .filter_map(|token| token.into_token())
222 .skip_while(|token| token != &left_paren)
223 .skip(1)
224 .take_while(|token| token != &right_paren)
225 {
226 if T![,] == token.kind() {
227 if !current_derive.is_empty() {
228 input_derives.insert(current_derive);
229 current_derive = String::new();
230 }
231 } else {
232 current_derive.push_str(token.text().trim());
233 }
234 }
235
236 if !current_derive.is_empty() {
237 input_derives.insert(current_derive);
238 }
239 Ok(input_derives)
240 }
241 _ => Err(()),
242 } 291 }
243}
244
245fn get_derive_names_in_scope(ctx: &CompletionContext) -> FxHashSet<String> {
246 let mut result = FxHashSet::default();
247 ctx.scope.process_all_names(&mut |name, scope_def| {
248 if let hir::ScopeDef::MacroDef(mac) = scope_def {
249 // FIXME kind() doesn't check whether proc-macro is a derive
250 if mac.kind() == hir::MacroKind::Derive || mac.kind() == hir::MacroKind::ProcMacro {
251 result.insert(name.to_string());
252 }
253 }
254 });
255 result
256}
257
258struct DeriveCompletion {
259 label: &'static str,
260 dependencies: &'static [&'static str],
261}
262
263/// Standard Rust derives and the information about their dependencies
264/// (the dependencies are needed so that the main derive don't break the compilation when added)
265const DEFAULT_DERIVE_COMPLETIONS: &[DeriveCompletion] = &[
266 DeriveCompletion { label: "Clone", dependencies: &[] },
267 DeriveCompletion { label: "Copy", dependencies: &["Clone"] },
268 DeriveCompletion { label: "Debug", dependencies: &[] },
269 DeriveCompletion { label: "Default", dependencies: &[] },
270 DeriveCompletion { label: "Hash", dependencies: &[] },
271 DeriveCompletion { label: "PartialEq", dependencies: &[] },
272 DeriveCompletion { label: "Eq", dependencies: &["PartialEq"] },
273 DeriveCompletion { label: "PartialOrd", dependencies: &["PartialEq"] },
274 DeriveCompletion { label: "Ord", dependencies: &["PartialOrd", "Eq", "PartialEq"] },
275];
276 292
277pub(crate) struct LintCompletion { 293 Some(input_derives)
278 pub(crate) label: &'static str,
279 pub(crate) description: &'static str,
280} 294}
281 295
282#[rustfmt::skip]
283const DEFAULT_LINT_COMPLETIONS: &[LintCompletion] = &[
284 LintCompletion { label: "absolute_paths_not_starting_with_crate", description: r#"fully qualified paths that start with a module name instead of `crate`, `self`, or an extern crate name"# },
285 LintCompletion { label: "anonymous_parameters", description: r#"detects anonymous parameters"# },
286 LintCompletion { label: "box_pointers", description: r#"use of owned (Box type) heap memory"# },
287 LintCompletion { label: "deprecated_in_future", description: r#"detects use of items that will be deprecated in a future version"# },
288 LintCompletion { label: "elided_lifetimes_in_paths", description: r#"hidden lifetime parameters in types are deprecated"# },
289 LintCompletion { label: "explicit_outlives_requirements", description: r#"outlives requirements can be inferred"# },
290 LintCompletion { label: "indirect_structural_match", description: r#"pattern with const indirectly referencing non-structural-match type"# },
291 LintCompletion { label: "keyword_idents", description: r#"detects edition keywords being used as an identifier"# },
292 LintCompletion { label: "macro_use_extern_crate", description: r#"the `#[macro_use]` attribute is now deprecated in favor of using macros via the module system"# },
293 LintCompletion { label: "meta_variable_misuse", description: r#"possible meta-variable misuse at macro definition"# },
294 LintCompletion { label: "missing_copy_implementations", description: r#"detects potentially-forgotten implementations of `Copy`"# },
295 LintCompletion { label: "missing_crate_level_docs", description: r#"detects crates with no crate-level documentation"# },
296 LintCompletion { label: "missing_debug_implementations", description: r#"detects missing implementations of Debug"# },
297 LintCompletion { label: "missing_docs", description: r#"detects missing documentation for public members"# },
298 LintCompletion { label: "missing_doc_code_examples", description: r#"detects publicly-exported items without code samples in their documentation"# },
299 LintCompletion { label: "non_ascii_idents", description: r#"detects non-ASCII identifiers"# },
300 LintCompletion { label: "private_doc_tests", description: r#"detects code samples in docs of private items not documented by rustdoc"# },
301 LintCompletion { label: "single_use_lifetimes", description: r#"detects lifetime parameters that are only used once"# },
302 LintCompletion { label: "trivial_casts", description: r#"detects trivial casts which could be removed"# },
303 LintCompletion { label: "trivial_numeric_casts", description: r#"detects trivial casts of numeric types which could be removed"# },
304 LintCompletion { label: "unaligned_references", description: r#"detects unaligned references to fields of packed structs"# },
305 LintCompletion { label: "unreachable_pub", description: r#"`pub` items not reachable from crate root"# },
306 LintCompletion { label: "unsafe_code", description: r#"usage of `unsafe` code"# },
307 LintCompletion { label: "unsafe_op_in_unsafe_fn", description: r#"unsafe operations in unsafe functions without an explicit unsafe block are deprecated"# },
308 LintCompletion { label: "unstable_features", description: r#"enabling unstable features (deprecated. do not use)"# },
309 LintCompletion { label: "unused_crate_dependencies", description: r#"crate dependencies that are never used"# },
310 LintCompletion { label: "unused_extern_crates", description: r#"extern crates that are never used"# },
311 LintCompletion { label: "unused_import_braces", description: r#"unnecessary braces around an imported item"# },
312 LintCompletion { label: "unused_lifetimes", description: r#"detects lifetime parameters that are never used"# },
313 LintCompletion { label: "unused_qualifications", description: r#"detects unnecessarily qualified names"# },
314 LintCompletion { label: "unused_results", description: r#"unused result of an expression in a statement"# },
315 LintCompletion { label: "variant_size_differences", description: r#"detects enums with widely varying variant sizes"# },
316 LintCompletion { label: "array_into_iter", description: r#"detects calling `into_iter` on arrays"# },
317 LintCompletion { label: "asm_sub_register", description: r#"using only a subset of a register for inline asm inputs"# },
318 LintCompletion { label: "bare_trait_objects", description: r#"suggest using `dyn Trait` for trait objects"# },
319 LintCompletion { label: "bindings_with_variant_name", description: r#"detects pattern bindings with the same name as one of the matched variants"# },
320 LintCompletion { label: "cenum_impl_drop_cast", description: r#"a C-like enum implementing Drop is cast"# },
321 LintCompletion { label: "clashing_extern_declarations", description: r#"detects when an extern fn has been declared with the same name but different types"# },
322 LintCompletion { label: "coherence_leak_check", description: r#"distinct impls distinguished only by the leak-check code"# },
323 LintCompletion { label: "confusable_idents", description: r#"detects visually confusable pairs between identifiers"# },
324 LintCompletion { label: "dead_code", description: r#"detect unused, unexported items"# },
325 LintCompletion { label: "deprecated", description: r#"detects use of deprecated items"# },
326 LintCompletion { label: "ellipsis_inclusive_range_patterns", description: r#"`...` range patterns are deprecated"# },
327 LintCompletion { label: "exported_private_dependencies", description: r#"public interface leaks type from a private dependency"# },
328 LintCompletion { label: "illegal_floating_point_literal_pattern", description: r#"floating-point literals cannot be used in patterns"# },
329 LintCompletion { label: "improper_ctypes", description: r#"proper use of libc types in foreign modules"# },
330 LintCompletion { label: "improper_ctypes_definitions", description: r#"proper use of libc types in foreign item definitions"# },
331 LintCompletion { label: "incomplete_features", description: r#"incomplete features that may function improperly in some or all cases"# },
332 LintCompletion { label: "inline_no_sanitize", description: r#"detects incompatible use of `#[inline(always)]` and `#[no_sanitize(...)]`"# },
333 LintCompletion { label: "intra_doc_link_resolution_failure", description: r#"failures in resolving intra-doc link targets"# },
334 LintCompletion { label: "invalid_codeblock_attributes", description: r#"codeblock attribute looks a lot like a known one"# },
335 LintCompletion { label: "invalid_value", description: r#"an invalid value is being created (such as a NULL reference)"# },
336 LintCompletion { label: "irrefutable_let_patterns", description: r#"detects irrefutable patterns in if-let and while-let statements"# },
337 LintCompletion { label: "late_bound_lifetime_arguments", description: r#"detects generic lifetime arguments in path segments with late bound lifetime parameters"# },
338 LintCompletion { label: "mixed_script_confusables", description: r#"detects Unicode scripts whose mixed script confusables codepoints are solely used"# },
339 LintCompletion { label: "mutable_borrow_reservation_conflict", description: r#"reservation of a two-phased borrow conflicts with other shared borrows"# },
340 LintCompletion { label: "non_camel_case_types", description: r#"types, variants, traits and type parameters should have camel case names"# },
341 LintCompletion { label: "non_shorthand_field_patterns", description: r#"using `Struct { x: x }` instead of `Struct { x }` in a pattern"# },
342 LintCompletion { label: "non_snake_case", description: r#"variables, methods, functions, lifetime parameters and modules should have snake case names"# },
343 LintCompletion { label: "non_upper_case_globals", description: r#"static constants should have uppercase identifiers"# },
344 LintCompletion { label: "no_mangle_generic_items", description: r#"generic items must be mangled"# },
345 LintCompletion { label: "overlapping_patterns", description: r#"detects overlapping patterns"# },
346 LintCompletion { label: "path_statements", description: r#"path statements with no effect"# },
347 LintCompletion { label: "private_in_public", description: r#"detect private items in public interfaces not caught by the old implementation"# },
348 LintCompletion { label: "proc_macro_derive_resolution_fallback", description: r#"detects proc macro derives using inaccessible names from parent modules"# },
349 LintCompletion { label: "redundant_semicolons", description: r#"detects unnecessary trailing semicolons"# },
350 LintCompletion { label: "renamed_and_removed_lints", description: r#"lints that have been renamed or removed"# },
351 LintCompletion { label: "safe_packed_borrows", description: r#"safe borrows of fields of packed structs were erroneously allowed"# },
352 LintCompletion { label: "stable_features", description: r#"stable features found in `#[feature]` directive"# },
353 LintCompletion { label: "trivial_bounds", description: r#"these bounds don't depend on an type parameters"# },
354 LintCompletion { label: "type_alias_bounds", description: r#"bounds in type aliases are not enforced"# },
355 LintCompletion { label: "tyvar_behind_raw_pointer", description: r#"raw pointer to an inference variable"# },
356 LintCompletion { label: "uncommon_codepoints", description: r#"detects uncommon Unicode codepoints in identifiers"# },
357 LintCompletion { label: "unconditional_recursion", description: r#"functions that cannot return without calling themselves"# },
358 LintCompletion { label: "unknown_lints", description: r#"unrecognized lint attribute"# },
359 LintCompletion { label: "unnameable_test_items", description: r#"detects an item that cannot be named being marked as `#[test_case]`"# },
360 LintCompletion { label: "unreachable_code", description: r#"detects unreachable code paths"# },
361 LintCompletion { label: "unreachable_patterns", description: r#"detects unreachable patterns"# },
362 LintCompletion { label: "unstable_name_collisions", description: r#"detects name collision with an existing but unstable method"# },
363 LintCompletion { label: "unused_allocation", description: r#"detects unnecessary allocations that can be eliminated"# },
364 LintCompletion { label: "unused_assignments", description: r#"detect assignments that will never be read"# },
365 LintCompletion { label: "unused_attributes", description: r#"detects attributes that were not used by the compiler"# },
366 LintCompletion { label: "unused_braces", description: r#"unnecessary braces around an expression"# },
367 LintCompletion { label: "unused_comparisons", description: r#"comparisons made useless by limits of the types involved"# },
368 LintCompletion { label: "unused_doc_comments", description: r#"detects doc comments that aren't used by rustdoc"# },
369 LintCompletion { label: "unused_features", description: r#"unused features found in crate-level `#[feature]` directives"# },
370 LintCompletion { label: "unused_imports", description: r#"imports that are never used"# },
371 LintCompletion { label: "unused_labels", description: r#"detects labels that are never used"# },
372 LintCompletion { label: "unused_macros", description: r#"detects macros that were not used"# },
373 LintCompletion { label: "unused_must_use", description: r#"unused result of a type flagged as `#[must_use]`"# },
374 LintCompletion { label: "unused_mut", description: r#"detect mut variables which don't need to be mutable"# },
375 LintCompletion { label: "unused_parens", description: r#"`if`, `match`, `while` and `return` do not need parentheses"# },
376 LintCompletion { label: "unused_unsafe", description: r#"unnecessary use of an `unsafe` block"# },
377 LintCompletion { label: "unused_variables", description: r#"detect variables which are not used in any way"# },
378 LintCompletion { label: "warnings", description: r#"mass-change the level for lints which produce warnings"# },
379 LintCompletion { label: "where_clauses_object_safety", description: r#"checks the object safety of where clauses"# },
380 LintCompletion { label: "while_true", description: r#"suggest using `loop { }` instead of `while true { }`"# },
381 LintCompletion { label: "ambiguous_associated_items", description: r#"ambiguous associated items"# },
382 LintCompletion { label: "arithmetic_overflow", description: r#"arithmetic operation overflows"# },
383 LintCompletion { label: "conflicting_repr_hints", description: r#"conflicts between `#[repr(..)]` hints that were previously accepted and used in practice"# },
384 LintCompletion { label: "const_err", description: r#"constant evaluation detected erroneous expression"# },
385 LintCompletion { label: "ill_formed_attribute_input", description: r#"ill-formed attribute inputs that were previously accepted and used in practice"# },
386 LintCompletion { label: "incomplete_include", description: r#"trailing content in included file"# },
387 LintCompletion { label: "invalid_type_param_default", description: r#"type parameter default erroneously allowed in invalid location"# },
388 LintCompletion { label: "macro_expanded_macro_exports_accessed_by_absolute_paths", description: r#"macro-expanded `macro_export` macros from the current crate cannot be referred to by absolute paths"# },
389 LintCompletion { label: "missing_fragment_specifier", description: r#"detects missing fragment specifiers in unused `macro_rules!` patterns"# },
390 LintCompletion { label: "mutable_transmutes", description: r#"mutating transmuted &mut T from &T may cause undefined behavior"# },
391 LintCompletion { label: "no_mangle_const_items", description: r#"const items will not have their symbols exported"# },
392 LintCompletion { label: "order_dependent_trait_objects", description: r#"trait-object types were treated as different depending on marker-trait order"# },
393 LintCompletion { label: "overflowing_literals", description: r#"literal out of range for its type"# },
394 LintCompletion { label: "patterns_in_fns_without_body", description: r#"patterns in functions without body were erroneously allowed"# },
395 LintCompletion { label: "pub_use_of_private_extern_crate", description: r#"detect public re-exports of private extern crates"# },
396 LintCompletion { label: "soft_unstable", description: r#"a feature gate that doesn't break dependent crates"# },
397 LintCompletion { label: "unconditional_panic", description: r#"operation will cause a panic at runtime"# },
398 LintCompletion { label: "unknown_crate_types", description: r#"unknown crate type found in `#[crate_type]` directive"# },
399];
400
401#[cfg(test)] 296#[cfg(test)]
402mod tests { 297mod tests {
298 use super::*;
299
403 use expect_test::{expect, Expect}; 300 use expect_test::{expect, Expect};
404 301
405 use crate::{test_utils::completion_list, CompletionKind}; 302 use crate::{test_utils::completion_list, CompletionKind};
406 303
304 #[test]
305 fn attributes_are_sorted() {
306 let mut attrs = ATTRIBUTES.iter().map(|attr| attr.key());
307 let mut prev = attrs.next().unwrap();
308
309 attrs.for_each(|next| {
310 assert!(
311 prev < next,
312 r#"ATTRIBUTES array is not sorted, "{}" should come after "{}""#,
313 prev,
314 next
315 );
316 prev = next;
317 });
318 }
319
407 fn check(ra_fixture: &str, expect: Expect) { 320 fn check(ra_fixture: &str, expect: Expect) {
408 let actual = completion_list(ra_fixture, CompletionKind::Attribute); 321 let actual = completion_list(ra_fixture, CompletionKind::Attribute);
409 expect.assert_eq(&actual); 322 expect.assert_eq(&actual);
410 } 323 }
411 324
412 #[test] 325 #[test]
413 fn empty_derive_completion() { 326 fn test_attribute_completion_inside_nested_attr() {
327 check(r#"#[cfg($0)]"#, expect![[]])
328 }
329
330 #[test]
331 fn test_attribute_completion_with_existing_attr() {
414 check( 332 check(
415 r#" 333 r#"#[no_mangle] #[$0] mcall!();"#,
416#[derive($0)]
417struct Test {}
418 "#,
419 expect![[r#" 334 expect![[r#"
420 at Clone 335 at allow(…)
421 at Clone, Copy 336 at cfg(…)
422 at Debug 337 at cfg_attr(…)
423 at Default 338 at deny(…)
424 at Hash 339 at forbid(…)
425 at PartialEq 340 at warn(…)
426 at PartialEq, Eq 341 "#]],
427 at PartialEq, PartialOrd 342 )
428 at PartialEq, Eq, PartialOrd, Ord 343 }
344
345 #[test]
346 fn complete_attribute_on_source_file() {
347 check(
348 r#"#![$0]"#,
349 expect![[r#"
350 at allow(…)
351 at cfg(…)
352 at cfg_attr(…)
353 at deny(…)
354 at forbid(…)
355 at warn(…)
356 at deprecated
357 at doc = "…"
358 at doc(hidden)
359 at doc(alias = "…")
360 at must_use
361 at no_mangle
362 at crate_name = ""
363 at feature(…)
364 at no_implicit_prelude
365 at no_main
366 at no_std
367 at recursion_limit = …
368 at type_length_limit = …
369 at windows_subsystem = "…"
429 "#]], 370 "#]],
430 ); 371 );
431 } 372 }
432 373
433 #[test] 374 #[test]
434 fn no_completion_for_incorrect_derive() { 375 fn complete_attribute_on_module() {
435 check( 376 check(
436 r#" 377 r#"#[$0] mod foo;"#,
437#[derive{$0)] 378 expect![[r#"
438struct Test {} 379 at allow(…)
439"#, 380 at cfg(…)
440 expect![[r#""#]], 381 at cfg_attr(…)
441 ) 382 at deny(…)
383 at forbid(…)
384 at warn(…)
385 at deprecated
386 at doc = "…"
387 at doc(hidden)
388 at doc(alias = "…")
389 at must_use
390 at no_mangle
391 at path = "…"
392 "#]],
393 );
394 check(
395 r#"mod foo {#![$0]}"#,
396 expect![[r#"
397 at allow(…)
398 at cfg(…)
399 at cfg_attr(…)
400 at deny(…)
401 at forbid(…)
402 at warn(…)
403 at deprecated
404 at doc = "…"
405 at doc(hidden)
406 at doc(alias = "…")
407 at must_use
408 at no_mangle
409 at no_implicit_prelude
410 "#]],
411 );
442 } 412 }
443 413
444 #[test] 414 #[test]
445 fn derive_with_input_completion() { 415 fn complete_attribute_on_macro_rules() {
446 check( 416 check(
447 r#" 417 r#"#[$0] macro_rules! foo {}"#,
448#[derive(serde::Serialize, PartialEq, $0)]
449struct Test {}
450"#,
451 expect![[r#" 418 expect![[r#"
452 at Clone 419 at allow(…)
453 at Clone, Copy 420 at cfg(…)
454 at Debug 421 at cfg_attr(…)
455 at Default 422 at deny(…)
456 at Hash 423 at forbid(…)
457 at Eq 424 at warn(…)
458 at PartialOrd 425 at deprecated
459 at Eq, PartialOrd, Ord 426 at doc = "…"
427 at doc(hidden)
428 at doc(alias = "…")
429 at must_use
430 at no_mangle
431 at macro_export
432 at macro_use
460 "#]], 433 "#]],
461 ) 434 );
462 } 435 }
463 436
464 #[test] 437 #[test]
465 fn test_attribute_completion() { 438 fn complete_attribute_on_macro_def() {
466 check( 439 check(
467 r#"#[$0]"#, 440 r#"#[$0] macro foo {}"#,
468 expect![[r#" 441 expect![[r#"
469 at allow(…) 442 at allow(…)
470 at automatically_derived 443 at cfg(…)
471 at cfg_attr(…) 444 at cfg_attr(…)
445 at deny(…)
446 at forbid(…)
447 at warn(…)
448 at deprecated
449 at doc = "…"
450 at doc(hidden)
451 at doc(alias = "…")
452 at must_use
453 at no_mangle
454 "#]],
455 );
456 }
457
458 #[test]
459 fn complete_attribute_on_extern_crate() {
460 check(
461 r#"#[$0] extern crate foo;"#,
462 expect![[r#"
463 at allow(…)
472 at cfg(…) 464 at cfg(…)
473 at cold 465 at cfg_attr(…)
474 at deny(…) 466 at deny(…)
467 at forbid(…)
468 at warn(…)
475 at deprecated 469 at deprecated
476 at derive() 470 at doc = ""
477 at export_name = "…" 471 at doc(hidden)
478 at doc(alias = "…") 472 at doc(alias = "…")
473 at must_use
474 at no_mangle
475 at macro_use
476 "#]],
477 );
478 }
479
480 #[test]
481 fn complete_attribute_on_use() {
482 check(
483 r#"#[$0] use foo;"#,
484 expect![[r#"
485 at allow(…)
486 at cfg(…)
487 at cfg_attr(…)
488 at deny(…)
489 at forbid(…)
490 at warn(…)
491 at deprecated
479 at doc = "…" 492 at doc = "…"
480 at doc(hidden) 493 at doc(hidden)
494 at doc(alias = "…")
495 at must_use
496 at no_mangle
497 "#]],
498 );
499 }
500
501 #[test]
502 fn complete_attribute_on_type_alias() {
503 check(
504 r#"#[$0] type foo = ();"#,
505 expect![[r#"
506 at allow(…)
507 at cfg(…)
508 at cfg_attr(…)
509 at deny(…)
481 at forbid(…) 510 at forbid(…)
482 at ignore = "…" 511 at warn(…)
483 at inline 512 at deprecated
484 at link 513 at doc = "…"
485 at link_name = "…" 514 at doc(hidden)
486 at link_section = "…" 515 at doc(alias = "…")
487 at macro_export 516 at must_use
488 at macro_use 517 at no_mangle
518 "#]],
519 );
520 }
521
522 #[test]
523 fn complete_attribute_on_struct() {
524 check(
525 r#"#[$0] struct Foo;"#,
526 expect![[r#"
527 at allow(…)
528 at cfg(…)
529 at cfg_attr(…)
530 at deny(…)
531 at forbid(…)
532 at warn(…)
533 at deprecated
534 at doc = "…"
535 at doc(hidden)
536 at doc(alias = "…")
489 at must_use 537 at must_use
490 at no_mangle 538 at no_mangle
539 at derive(…)
540 at repr(…)
491 at non_exhaustive 541 at non_exhaustive
492 at path = "…" 542 "#]],
493 at proc_macro 543 );
494 at proc_macro_attribute 544 }
495 at proc_macro_derive(…) 545
546 #[test]
547 fn complete_attribute_on_enum() {
548 check(
549 r#"#[$0] enum Foo {}"#,
550 expect![[r#"
551 at allow(…)
552 at cfg(…)
553 at cfg_attr(…)
554 at deny(…)
555 at forbid(…)
556 at warn(…)
557 at deprecated
558 at doc = "…"
559 at doc(hidden)
560 at doc(alias = "…")
561 at must_use
562 at no_mangle
563 at derive(…)
496 at repr(…) 564 at repr(…)
497 at should_panic 565 at non_exhaustive
498 at target_feature = "…" 566 "#]],
499 at test 567 );
500 at track_caller 568 }
501 at used 569
570 #[test]
571 fn complete_attribute_on_const() {
572 check(
573 r#"#[$0] const FOO: () = ();"#,
574 expect![[r#"
575 at allow(…)
576 at cfg(…)
577 at cfg_attr(…)
578 at deny(…)
579 at forbid(…)
502 at warn(…) 580 at warn(…)
581 at deprecated
582 at doc = "…"
583 at doc(hidden)
584 at doc(alias = "…")
585 at must_use
586 at no_mangle
503 "#]], 587 "#]],
504 ) 588 );
505 } 589 }
506 590
507 #[test] 591 #[test]
508 fn test_attribute_completion_inside_nested_attr() { 592 fn complete_attribute_on_static() {
509 check(r#"#[cfg($0)]"#, expect![[]]) 593 check(
594 r#"#[$0] static FOO: () = ()"#,
595 expect![[r#"
596 at allow(…)
597 at cfg(…)
598 at cfg_attr(…)
599 at deny(…)
600 at forbid(…)
601 at warn(…)
602 at deprecated
603 at doc = "…"
604 at doc(hidden)
605 at doc(alias = "…")
606 at must_use
607 at no_mangle
608 at export_name = "…"
609 at link_name = "…"
610 at link_section = "…"
611 at global_allocator
612 at used
613 "#]],
614 );
510 } 615 }
511 616
512 #[test] 617 #[test]
513 fn test_inner_attribute_completion() { 618 fn complete_attribute_on_trait() {
514 check( 619 check(
515 r"#![$0]", 620 r#"#[$0] trait Foo {}"#,
516 expect![[r#" 621 expect![[r#"
517 at allow(…) 622 at allow(…)
518 at automatically_derived 623 at cfg(…)
519 at cfg_attr(…) 624 at cfg_attr(…)
625 at deny(…)
626 at forbid(…)
627 at warn(…)
628 at deprecated
629 at doc = "…"
630 at doc(hidden)
631 at doc(alias = "…")
632 at must_use
633 at no_mangle
634 at must_use
635 "#]],
636 );
637 }
638
639 #[test]
640 fn complete_attribute_on_impl() {
641 check(
642 r#"#[$0] impl () {}"#,
643 expect![[r#"
644 at allow(…)
520 at cfg(…) 645 at cfg(…)
521 at cold 646 at cfg_attr(…)
522 at crate_name = ""
523 at deny(…) 647 at deny(…)
648 at forbid(…)
649 at warn(…)
524 at deprecated 650 at deprecated
525 at derive(…) 651 at doc = "…"
526 at export_name = "…" 652 at doc(hidden)
653 at doc(alias = "…")
654 at must_use
655 at no_mangle
656 at automatically_derived
657 "#]],
658 );
659 check(
660 r#"impl () {#![$0]}"#,
661 expect![[r#"
662 at allow(…)
663 at cfg(…)
664 at cfg_attr(…)
665 at deny(…)
666 at forbid(…)
667 at warn(…)
668 at deprecated
669 at doc = "…"
670 at doc(hidden)
527 at doc(alias = "…") 671 at doc(alias = "…")
672 at must_use
673 at no_mangle
674 "#]],
675 );
676 }
677
678 #[test]
679 fn complete_attribute_on_extern_block() {
680 check(
681 r#"#[$0] extern {}"#,
682 expect![[r#"
683 at allow(…)
684 at cfg(…)
685 at cfg_attr(…)
686 at deny(…)
687 at forbid(…)
688 at warn(…)
689 at deprecated
528 at doc = "…" 690 at doc = "…"
529 at doc(hidden) 691 at doc(hidden)
530 at feature(…) 692 at doc(alias = "…")
693 at must_use
694 at no_mangle
695 at link
696 "#]],
697 );
698 check(
699 r#"extern {#![$0]}"#,
700 expect![[r#"
701 at allow(…)
702 at cfg(…)
703 at cfg_attr(…)
704 at deny(…)
531 at forbid(…) 705 at forbid(…)
532 at global_allocator 706 at warn(…)
533 at ignore = "…" 707 at deprecated
534 at inline 708 at doc = "…"
709 at doc(hidden)
710 at doc(alias = "…")
711 at must_use
712 at no_mangle
535 at link 713 at link
714 "#]],
715 );
716 }
717
718 #[test]
719 fn complete_attribute_on_variant() {
720 check(
721 r#"enum Foo { #[$0] Bar }"#,
722 expect![[r#"
723 at allow(…)
724 at cfg(…)
725 at cfg_attr(…)
726 at deny(…)
727 at forbid(…)
728 at warn(…)
729 at non_exhaustive
730 "#]],
731 );
732 }
733
734 #[test]
735 fn complete_attribute_on_fn() {
736 check(
737 r#"#[$0] fn main() {}"#,
738 expect![[r#"
739 at allow(…)
740 at cfg(…)
741 at cfg_attr(…)
742 at deny(…)
743 at forbid(…)
744 at warn(…)
745 at deprecated
746 at doc = "…"
747 at doc(hidden)
748 at doc(alias = "…")
749 at must_use
750 at no_mangle
751 at export_name = "…"
536 at link_name = "…" 752 at link_name = "…"
537 at link_section = "…" 753 at link_section = "…"
538 at macro_export 754 at cold
539 at macro_use 755 at ignore = "…"
756 at inline
540 at must_use 757 at must_use
541 at no_link
542 at no_implicit_prelude
543 at no_main
544 at no_mangle
545 at no_std
546 at non_exhaustive
547 at panic_handler 758 at panic_handler
548 at path = "…"
549 at proc_macro 759 at proc_macro
550 at proc_macro_attribute
551 at proc_macro_derive(…) 760 at proc_macro_derive(…)
552 at recursion_limit = … 761 at proc_macro_attribute
553 at repr(…)
554 at should_panic 762 at should_panic
555 at target_feature = "…" 763 at target_feature = "…"
556 at test 764 at test
557 at track_caller 765 at track_caller
558 at type_length_limit = … 766 "#]],
559 at used 767 );
768 }
769
770 #[test]
771 fn complete_attribute_on_expr() {
772 check(
773 r#"fn main() { #[$0] foo() }"#,
774 expect![[r#"
775 at allow(…)
776 at cfg(…)
777 at cfg_attr(…)
778 at deny(…)
779 at forbid(…)
560 at warn(…) 780 at warn(…)
561 at windows_subsystem = "…"
562 "#]], 781 "#]],
563 ); 782 );
564 } 783 }