From 748f894b76964ec2dc594ae89c9ab73a9134b418 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 14 Apr 2025 17:25:52 -0300 Subject: [PATCH 1/2] feat: allow(dead_code) --- .../src/elaborator/statements.rs | 2 +- .../src/hir/def_collector/dc_mod.rs | 34 ++++++++---- compiler/noirc_frontend/src/lexer/token.rs | 8 ++- .../noirc_frontend/src/tests/unused_items.rs | 52 +++++++++++++++++++ .../Nargo.toml | 7 +++ .../src/main.nr | 7 +++ .../src_hash.txt | 1 + .../Nargo.toml | 7 +++ .../src/main.nr | 7 +++ .../src_hash.txt | 1 + .../Nargo.toml | 7 +++ .../src/main.nr | 7 +++ .../src_hash.txt | 1 + .../Nargo.toml | 7 +++ .../src/main.nr | 7 +++ .../src_hash.txt | 1 + .../Nargo.toml | 7 +++ .../src/main.nr | 7 +++ .../src_hash.txt | 1 + .../lsp/src/requests/completion/builtins.rs | 10 +++- 20 files changed, 168 insertions(+), 13 deletions(-) create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/Nargo.toml create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/src/main.nr create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/src_hash.txt create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/Nargo.toml create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/src/main.nr create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/src_hash.txt create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/Nargo.toml create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/src/main.nr create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/src_hash.txt create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/Nargo.toml create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/src/main.nr create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/src_hash.txt create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/Nargo.toml create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/src/main.nr create mode 100644 test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/src_hash.txt diff --git a/compiler/noirc_frontend/src/elaborator/statements.rs b/compiler/noirc_frontend/src/elaborator/statements.rs index 3ebefa7fd0f..921b79ca2a9 100644 --- a/compiler/noirc_frontend/src/elaborator/statements.rs +++ b/compiler/noirc_frontend/src/elaborator/statements.rs @@ -130,7 +130,7 @@ impl Elaborator<'_> { } let warn_if_unused = - !let_stmt.attributes.iter().any(|attr| attr.kind.is_allow_unused_variables()); + !let_stmt.attributes.iter().any(|attr| attr.kind.is_allow("unused_variables")); let r#type = annotated_type; let pattern = self.elaborate_pattern_and_store_ids( diff --git a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs index c05cd203ad6..49b8eee1755 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -450,6 +450,8 @@ impl ModCollector<'_> { let mut trait_definition = trait_definition.item; let name = trait_definition.name.clone(); let location = trait_definition.location; + let has_allow_dead_code = + trait_definition.attributes.iter().any(|attr| attr.kind.is_allow("dead_code")); // Create the corresponding module for the trait namespace let trait_id = match self.push_child_module( @@ -481,12 +483,14 @@ impl ModCollector<'_> { ); let parent_module_id = ModuleId { krate, local_id: self.module_id }; - context.usage_tracker.add_unused_item( - parent_module_id, - name.clone(), - UnusedItem::Trait(trait_id), - visibility, - ); + if !has_allow_dead_code { + context.usage_tracker.add_unused_item( + parent_module_id, + name.clone(), + UnusedItem::Trait(trait_id), + visibility, + ); + } if let Err((first_def, second_def)) = result { let error = DefCollectorErrorKind::Duplicate { @@ -1013,6 +1017,7 @@ pub fn collect_function( function.name() == MAIN_FUNCTION }; let has_export = function.def.attributes.has_export(); + let has_allow_dead_code = function.def.attributes.has_allow("dead_code"); let name = function.name_ident().clone(); let func_id = interner.push_empty_fn(); @@ -1023,7 +1028,12 @@ pub fn collect_function( interner.register_function(func_id, &function.def); } - if !is_test && !is_fuzzing_harness && !is_entry_point_function && !has_export { + if !is_test + && !is_fuzzing_harness + && !is_entry_point_function + && !has_export + && !has_allow_dead_code + { let item = UnusedItem::Function(func_id); usage_tracker.add_unused_item(module, name.clone(), item, visibility); } @@ -1109,7 +1119,10 @@ pub fn collect_struct( let parent_module_id = ModuleId { krate, local_id: module_id }; - if !unresolved.struct_def.is_abi() { + let has_allow_dead_code = + unresolved.struct_def.attributes.iter().any(|attr| attr.kind.is_allow("dead_code")); + + if !unresolved.struct_def.is_abi() && !has_allow_dead_code { usage_tracker.add_unused_item( parent_module_id, name.clone(), @@ -1200,7 +1213,10 @@ pub fn collect_enum( let parent_module_id = ModuleId { krate, local_id: module_id }; - if !unresolved.enum_def.is_abi() { + let has_allow_dead_code = + unresolved.enum_def.attributes.iter().any(|attr| attr.kind.is_allow("dead_code")); + + if !unresolved.enum_def.is_abi() && !has_allow_dead_code { usage_tracker.add_unused_item( parent_module_id, name.clone(), diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 3208c7ccad5..dad9feaf31c 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -859,6 +859,10 @@ impl Attributes { self.has_secondary_attr(&SecondaryAttributeKind::Export) } + pub fn has_allow(&self, name: &'static str) -> bool { + self.secondary.iter().any(|attr| attr.kind.is_allow(name)) + } + /// Check if secondary attributes contain a specific instance. pub fn has_secondary_attr(&self, kind: &SecondaryAttributeKind) -> bool { self.secondary.iter().any(|attr| &attr.kind == kind) @@ -1028,9 +1032,9 @@ pub enum SecondaryAttributeKind { } impl SecondaryAttributeKind { - pub(crate) fn is_allow_unused_variables(&self) -> bool { + pub(crate) fn is_allow(&self, name: &'static str) -> bool { match self { - SecondaryAttributeKind::Allow(string) => string == "unused_variables", + SecondaryAttributeKind::Allow(string) => string == name, _ => false, } } diff --git a/compiler/noirc_frontend/src/tests/unused_items.rs b/compiler/noirc_frontend/src/tests/unused_items.rs index 2b0077b4889..0dfe02e39cd 100644 --- a/compiler/noirc_frontend/src/tests/unused_items.rs +++ b/compiler/noirc_frontend/src/tests/unused_items.rs @@ -409,3 +409,55 @@ fn does_not_consider_struct_as_constructed_if_mentioned_in_function_argument() { "; check_errors!(src); } + +#[named] +#[test] +fn allow_dead_code_on_unused_function() { + let src = " + #[allow(dead_code)] + fn foo() {} + + fn main() { + } + "; + assert_no_errors!(src); +} + +#[named] +#[test] +fn allow_dead_code_on_unused_struct() { + let src = " + #[allow(dead_code)] + struct Foo {} + + fn main() { + } + "; + assert_no_errors!(src); +} + +#[named] +#[test] +fn allow_dead_code_on_unused_trait() { + let src = " + #[allow(dead_code)] + trait Foo {} + + fn main() { + } + "; + assert_no_errors!(src); +} + +#[named] +#[test] +fn allow_dead_code_on_unused_enum() { + let src = " + #[allow(dead_code)] + enum Foo {} + + fn main() { + } + "; + assert_no_errors!(src); +} diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/Nargo.toml new file mode 100644 index 00000000000..0ff48d08a7a --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/src/main.nr new file mode 100644 index 00000000000..4bda1c2008b --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/src/main.nr @@ -0,0 +1,7 @@ + + #[allow(dead_code)] + enum Foo {} + + fn main() { + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/src_hash.txt new file mode 100644 index 00000000000..0cd9cc4faf4 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_enum/src_hash.txt @@ -0,0 +1 @@ +2912787966510288424 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/Nargo.toml new file mode 100644 index 00000000000..cc9f113c4aa --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/src/main.nr new file mode 100644 index 00000000000..21479290676 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/src/main.nr @@ -0,0 +1,7 @@ + + #[allow(dead_code)] + fn foo() {} + + fn main() { + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/src_hash.txt new file mode 100644 index 00000000000..61bd7239002 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_function/src_hash.txt @@ -0,0 +1 @@ +8138537160766711355 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/Nargo.toml new file mode 100644 index 00000000000..1580a97e3b7 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/src/main.nr new file mode 100644 index 00000000000..e2c788b0e77 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/src/main.nr @@ -0,0 +1,7 @@ + + #[allow(dead_code)] + struct Foo {} + + fn main() { + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/src_hash.txt new file mode 100644 index 00000000000..6722c447cd6 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_struct/src_hash.txt @@ -0,0 +1 @@ +14645872963190054969 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/Nargo.toml new file mode 100644 index 00000000000..8544ffdf348 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/src/main.nr new file mode 100644 index 00000000000..ebd17ee77e0 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/src/main.nr @@ -0,0 +1,7 @@ + + #[allow(dead_code)] + trait Foo {} + + fn main() { + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/src_hash.txt new file mode 100644 index 00000000000..70b7fda86d5 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_trait/src_hash.txt @@ -0,0 +1 @@ +14851478487121828974 \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/Nargo.toml b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/Nargo.toml new file mode 100644 index 00000000000..cd33b004c3a --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/src/main.nr b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/src/main.nr new file mode 100644 index 00000000000..eaf659eccf6 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/src/main.nr @@ -0,0 +1,7 @@ + + #[allow(dead_code)] + type Foo = Field; + + fn main() { + } + \ No newline at end of file diff --git a/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/src_hash.txt b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/src_hash.txt new file mode 100644 index 00000000000..851a17c5da1 --- /dev/null +++ b/test_programs/compile_success_no_bug/noirc_frontend_tests_unused_items_allow_dead_code_on_unused_type_alias/src_hash.txt @@ -0,0 +1 @@ +10281445062708207909 \ No newline at end of file diff --git a/tooling/lsp/src/requests/completion/builtins.rs b/tooling/lsp/src/requests/completion/builtins.rs index 8dd09522691..19dcc8c3f59 100644 --- a/tooling/lsp/src/requests/completion/builtins.rs +++ b/tooling/lsp/src/requests/completion/builtins.rs @@ -149,13 +149,21 @@ impl NodeFinder<'_> { } } AttributeTarget::Let => { - if name_matches("allow", prefix) || name_matches("unused_variables", prefix) { + let is_allow = name_matches("allow", prefix); + if is_allow || name_matches("unused_variables", prefix) { self.completion_items.push(simple_completion_item( "allow(unused_variables)", CompletionItemKind::METHOD, None, )); } + if is_allow || name_matches("dead_code", prefix) { + self.completion_items.push(simple_completion_item( + "allow(dead_code)", + CompletionItemKind::METHOD, + None, + )); + } } } } From b6c8c5ba4754ab9b63f0239b3ad5ab9c89d02e31 Mon Sep 17 00:00:00 2001 From: Ary Borenszweig Date: Mon, 14 Apr 2025 20:33:08 -0300 Subject: [PATCH 2/2] Fix LSP --- .../lsp/src/requests/completion/builtins.rs | 35 ++++++++++--------- tooling/lsp/src/requests/completion/tests.rs | 14 ++++++++ 2 files changed, 33 insertions(+), 16 deletions(-) diff --git a/tooling/lsp/src/requests/completion/builtins.rs b/tooling/lsp/src/requests/completion/builtins.rs index 19dcc8c3f59..e966b4513b3 100644 --- a/tooling/lsp/src/requests/completion/builtins.rs +++ b/tooling/lsp/src/requests/completion/builtins.rs @@ -88,12 +88,17 @@ impl NodeFinder<'_> { pub(super) fn suggest_builtin_attributes(&mut self, prefix: &str, target: AttributeTarget) { match target { - AttributeTarget::Module | AttributeTarget::Trait => (), + AttributeTarget::Module => (), + AttributeTarget::Trait => { + self.suggest_allow("dead_code", prefix); + } AttributeTarget::Struct => { self.suggest_one_argument_attributes(prefix, &["abi"]); + self.suggest_allow("dead_code", prefix); } AttributeTarget::Enum => { self.suggest_one_argument_attributes(prefix, &["abi"]); + self.suggest_allow("dead_code", prefix); } AttributeTarget::Function => { let no_arguments_attributes = &[ @@ -147,26 +152,24 @@ impl NodeFinder<'_> { None, )); } + + self.suggest_allow("dead_code", prefix); } AttributeTarget::Let => { - let is_allow = name_matches("allow", prefix); - if is_allow || name_matches("unused_variables", prefix) { - self.completion_items.push(simple_completion_item( - "allow(unused_variables)", - CompletionItemKind::METHOD, - None, - )); - } - if is_allow || name_matches("dead_code", prefix) { - self.completion_items.push(simple_completion_item( - "allow(dead_code)", - CompletionItemKind::METHOD, - None, - )); - } + self.suggest_allow("unused_variables", prefix); } } } + + fn suggest_allow(&mut self, name: &'static str, prefix: &str) { + if name_matches("allow", prefix) || name_matches(name, prefix) { + self.completion_items.push(simple_completion_item( + format!("allow({name})"), + CompletionItemKind::METHOD, + None, + )); + } + } } pub(super) fn builtin_integer_types() -> [&'static str; 9] { diff --git a/tooling/lsp/src/requests/completion/tests.rs b/tooling/lsp/src/requests/completion/tests.rs index a7f5e945bfc..b0a0d56b56a 100644 --- a/tooling/lsp/src/requests/completion/tests.rs +++ b/tooling/lsp/src/requests/completion/tests.rs @@ -2414,6 +2414,20 @@ fn main() { .await; } + #[test] + async fn test_suggests_built_in_allow_function_attribute() { + let src = r#" + #[dead_c>|<] + fn foo() {} + "#; + + assert_completion_excluding_auto_import( + src, + vec![simple_completion_item("allow(dead_code)", CompletionItemKind::METHOD, None)], + ) + .await; + } + #[test] async fn test_suggests_built_in_let_attribute() { let src = r#"