Skip to content

Commit a2bc059

Browse files
authored
feat: allow silencing an unused variable defined via let (#6149)
# Description ## Problem Resolves #6148 ## Summary ![lsp-allow-unused](https://github.com/user-attachments/assets/738168ab-cfd6-468e-8abc-fbb0e30e60b6) ## Additional Context This is only for `let`, which is what we currently need. Should this be documented? I guess so, but I couldn't find where in our docs we explain `let`. (on the other hand we have `deprecated` but it seems to also be undocumented) ## Documentation Check one: - [ ] No documentation needed. - [ ] Documentation included in this PR. - [ ] **[For Experimental Features]** Documentation to be submitted in a separate PR. # PR Checklist - [x] I have tested the changes locally. - [x] I have formatted the changes with [Prettier](https://prettier.io/) and/or `cargo fmt` on default settings.
1 parent acdfbbc commit a2bc059

15 files changed

Lines changed: 120 additions & 22 deletions

File tree

compiler/noirc_frontend/src/ast/statement.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -141,13 +141,14 @@ impl StatementKind {
141141
pattern: Pattern,
142142
r#type: UnresolvedType,
143143
expression: Expression,
144+
attributes: Vec<SecondaryAttribute>,
144145
) -> StatementKind {
145146
StatementKind::Let(LetStatement {
146147
pattern,
147148
r#type,
148149
expression,
149150
comptime: false,
150-
attributes: vec![],
151+
attributes,
151152
})
152153
}
153154

@@ -814,6 +815,7 @@ impl ForRange {
814815
Pattern::Identifier(array_ident.clone()),
815816
UnresolvedTypeData::Unspecified.with_span(Default::default()),
816817
array,
818+
vec![],
817819
),
818820
span: array_span,
819821
};
@@ -858,6 +860,7 @@ impl ForRange {
858860
Pattern::Identifier(identifier),
859861
UnresolvedTypeData::Unspecified.with_span(Default::default()),
860862
Expression::new(loop_element, array_span),
863+
vec![],
861864
),
862865
span: array_span,
863866
};

compiler/noirc_frontend/src/ast/visitor.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ pub enum AttributeTarget {
3232
Struct,
3333
Trait,
3434
Function,
35+
Let,
3536
}
3637

3738
/// Implements the [Visitor pattern](https://en.wikipedia.org/wiki/Visitor_pattern) for Noir's AST.
@@ -1097,6 +1098,10 @@ impl Statement {
10971098

10981099
impl LetStatement {
10991100
pub fn accept(&self, visitor: &mut impl Visitor) {
1101+
for attribute in &self.attributes {
1102+
attribute.accept(AttributeTarget::Let, visitor);
1103+
}
1104+
11001105
if visitor.visit_let_statement(self) {
11011106
self.accept_children(visitor);
11021107
}

compiler/noirc_frontend/src/debug/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ impl DebugInstrumenter {
146146
ast::Pattern::Identifier(ident("__debug_expr", ret_expr.span)),
147147
ast::UnresolvedTypeData::Unspecified.with_span(Default::default()),
148148
ret_expr.clone(),
149+
vec![],
149150
),
150151
span: ret_expr.span,
151152
};
@@ -249,6 +250,7 @@ impl DebugInstrumenter {
249250
}),
250251
span: let_stmt.expression.span,
251252
},
253+
vec![],
252254
),
253255
span: *span,
254256
}
@@ -274,6 +276,7 @@ impl DebugInstrumenter {
274276
ast::Pattern::Identifier(ident("__debug_expr", assign_stmt.expression.span)),
275277
ast::UnresolvedTypeData::Unspecified.with_span(Default::default()),
276278
assign_stmt.expression.clone(),
279+
vec![],
277280
);
278281
let expression_span = assign_stmt.expression.span;
279282
let new_assign_stmt = match &assign_stmt.lvalue {

compiler/noirc_frontend/src/elaborator/expressions.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -790,7 +790,7 @@ impl<'context> Elaborator<'context> {
790790
let parameter = DefinitionKind::Local(None);
791791
let typ = self.resolve_inferred_type(typ);
792792
arg_types.push(typ.clone());
793-
(self.elaborate_pattern(pattern, typ.clone(), parameter), typ)
793+
(self.elaborate_pattern(pattern, typ.clone(), parameter, true), typ)
794794
});
795795

796796
let return_type = self.resolve_inferred_type(lambda.return_type);

compiler/noirc_frontend/src/elaborator/mod.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -362,8 +362,12 @@ impl<'context> Elaborator<'context> {
362362
if let Kind::Numeric(typ) = &generic.kind {
363363
let definition = DefinitionKind::GenericType(generic.type_var.clone());
364364
let ident = Ident::new(generic.name.to_string(), generic.span);
365-
let hir_ident =
366-
self.add_variable_decl_inner(ident, false, false, false, definition);
365+
let hir_ident = self.add_variable_decl(
366+
ident, false, // mutable
367+
false, // allow_shadowing
368+
false, // warn_if_unused
369+
definition,
370+
);
367371
self.interner.push_definition_type(hir_ident.id, *typ.clone());
368372
}
369373
}
@@ -760,6 +764,7 @@ impl<'context> Elaborator<'context> {
760764
typ.clone(),
761765
DefinitionKind::Local(None),
762766
&mut parameter_idents,
767+
true, // warn_if_unused
763768
None,
764769
);
765770

compiler/noirc_frontend/src/elaborator/patterns.rs

Lines changed: 18 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -26,13 +26,15 @@ impl<'context> Elaborator<'context> {
2626
pattern: Pattern,
2727
expected_type: Type,
2828
definition_kind: DefinitionKind,
29+
warn_if_unused: bool,
2930
) -> HirPattern {
3031
self.elaborate_pattern_mut(
3132
pattern,
3233
expected_type,
3334
definition_kind,
3435
None,
3536
&mut Vec::new(),
37+
warn_if_unused,
3638
None,
3739
)
3840
}
@@ -45,6 +47,7 @@ impl<'context> Elaborator<'context> {
4547
expected_type: Type,
4648
definition_kind: DefinitionKind,
4749
created_ids: &mut Vec<HirIdent>,
50+
warn_if_unused: bool,
4851
global_id: Option<GlobalId>,
4952
) -> HirPattern {
5053
self.elaborate_pattern_mut(
@@ -53,17 +56,20 @@ impl<'context> Elaborator<'context> {
5356
definition_kind,
5457
None,
5558
created_ids,
59+
warn_if_unused,
5660
global_id,
5761
)
5862
}
5963

64+
#[allow(clippy::too_many_arguments)]
6065
fn elaborate_pattern_mut(
6166
&mut self,
6267
pattern: Pattern,
6368
expected_type: Type,
6469
definition: DefinitionKind,
6570
mutable: Option<Span>,
6671
new_definitions: &mut Vec<HirIdent>,
72+
warn_if_unused: bool,
6773
global_id: Option<GlobalId>,
6874
) -> HirPattern {
6975
match pattern {
@@ -80,7 +86,13 @@ impl<'context> Elaborator<'context> {
8086
let location = Location::new(name.span(), self.file);
8187
HirIdent::non_trait_method(id, location)
8288
} else {
83-
self.add_variable_decl(name, mutable.is_some(), true, definition)
89+
self.add_variable_decl(
90+
name,
91+
mutable.is_some(),
92+
true, // allow_shadowing
93+
warn_if_unused,
94+
definition,
95+
)
8496
};
8597
self.interner.push_definition_type(ident.id, expected_type);
8698
new_definitions.push(ident.clone());
@@ -97,6 +109,7 @@ impl<'context> Elaborator<'context> {
97109
definition,
98110
Some(span),
99111
new_definitions,
112+
warn_if_unused,
100113
global_id,
101114
);
102115
let location = Location::new(span, self.file);
@@ -128,6 +141,7 @@ impl<'context> Elaborator<'context> {
128141
definition.clone(),
129142
mutable,
130143
new_definitions,
144+
warn_if_unused,
131145
global_id,
132146
)
133147
});
@@ -151,6 +165,7 @@ impl<'context> Elaborator<'context> {
151165
definition,
152166
mutable,
153167
new_definitions,
168+
warn_if_unused,
154169
global_id,
155170
)
156171
}
@@ -180,7 +195,7 @@ impl<'context> Elaborator<'context> {
180195
// shadowing here lets us avoid further errors if we define ERROR_IDENT
181196
// multiple times.
182197
let name = ERROR_IDENT.into();
183-
let identifier = this.add_variable_decl(name, false, true, definition.clone());
198+
let identifier = this.add_variable_decl(name, false, true, true, definition.clone());
184199
HirPattern::Identifier(identifier)
185200
};
186201

@@ -263,6 +278,7 @@ impl<'context> Elaborator<'context> {
263278
definition.clone(),
264279
mutable,
265280
new_definitions,
281+
true, // warn_if_unused
266282
None,
267283
);
268284

@@ -295,16 +311,6 @@ impl<'context> Elaborator<'context> {
295311
}
296312

297313
pub(super) fn add_variable_decl(
298-
&mut self,
299-
name: Ident,
300-
mutable: bool,
301-
allow_shadowing: bool,
302-
definition: DefinitionKind,
303-
) -> HirIdent {
304-
self.add_variable_decl_inner(name, mutable, allow_shadowing, true, definition)
305-
}
306-
307-
pub fn add_variable_decl_inner(
308314
&mut self,
309315
name: Ident,
310316
mutable: bool,

compiler/noirc_frontend/src/elaborator/statements.rs

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,12 +98,16 @@ impl<'context> Elaborator<'context> {
9898
}
9999
}
100100

101+
let warn_if_unused =
102+
!let_stmt.attributes.iter().any(|attr| attr.is_allow_unused_variables());
103+
101104
let r#type = annotated_type;
102105
let pattern = self.elaborate_pattern_and_store_ids(
103106
let_stmt.pattern,
104107
r#type.clone(),
105108
definition,
106109
&mut Vec::new(),
110+
warn_if_unused,
107111
global_id,
108112
);
109113

@@ -215,7 +219,12 @@ impl<'context> Elaborator<'context> {
215219
// TODO: For loop variables are currently mutable by default since we haven't
216220
// yet implemented syntax for them to be optionally mutable.
217221
let kind = DefinitionKind::Local(None);
218-
let identifier = self.add_variable_decl(identifier, false, true, kind);
222+
let identifier = self.add_variable_decl(
223+
identifier, false, // mutable
224+
true, // allow_shadowing
225+
true, // warn_if_unused
226+
kind,
227+
);
219228

220229
// Check that start range and end range have the same types
221230
let range_span = start_span.merge(end_span);

compiler/noirc_frontend/src/hir/comptime/hir_to_display_ast.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ impl HirStatement {
2929
let pattern = let_stmt.pattern.to_display_ast(interner);
3030
let r#type = interner.id_type(let_stmt.expression).to_display_ast();
3131
let expression = let_stmt.expression.to_display_ast(interner);
32-
StatementKind::new_let(pattern, r#type, expression)
32+
StatementKind::new_let(pattern, r#type, expression, let_stmt.attributes.clone())
3333
}
3434
HirStatement::Constrain(constrain) => {
3535
let expr = constrain.0.to_display_ast(interner);

compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2338,6 +2338,7 @@ fn function_def_set_parameters(
23382338
parameter_type.clone(),
23392339
DefinitionKind::Local(None),
23402340
&mut parameter_idents,
2341+
true, // warn_if_unused
23412342
None,
23422343
)
23432344
});

compiler/noirc_frontend/src/lexer/token.rs

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -804,6 +804,7 @@ impl Attribute {
804804
}
805805
["varargs"] => Attribute::Secondary(SecondaryAttribute::Varargs),
806806
["use_callers_scope"] => Attribute::Secondary(SecondaryAttribute::UseCallersScope),
807+
["allow", tag] => Attribute::Secondary(SecondaryAttribute::Allow(tag.to_string())),
807808
tokens => {
808809
tokens.iter().try_for_each(|token| validate(token))?;
809810
Attribute::Secondary(SecondaryAttribute::Custom(CustomAttribute {
@@ -925,6 +926,9 @@ pub enum SecondaryAttribute {
925926
/// within the scope of the calling function/module rather than this one.
926927
/// This affects functions such as `Expression::resolve` or `Quoted::as_type`.
927928
UseCallersScope,
929+
930+
/// Allow chosen warnings to happen so they are silenced.
931+
Allow(String),
928932
}
929933

930934
impl SecondaryAttribute {
@@ -948,6 +952,14 @@ impl SecondaryAttribute {
948952
SecondaryAttribute::Abi(_) => Some("abi".to_string()),
949953
SecondaryAttribute::Varargs => Some("varargs".to_string()),
950954
SecondaryAttribute::UseCallersScope => Some("use_callers_scope".to_string()),
955+
SecondaryAttribute::Allow(_) => Some("allow".to_string()),
956+
}
957+
}
958+
959+
pub(crate) fn is_allow_unused_variables(&self) -> bool {
960+
match self {
961+
SecondaryAttribute::Allow(string) => string == "unused_variables",
962+
_ => false,
951963
}
952964
}
953965
}
@@ -966,6 +978,7 @@ impl fmt::Display for SecondaryAttribute {
966978
SecondaryAttribute::Abi(ref k) => write!(f, "#[abi({k})]"),
967979
SecondaryAttribute::Varargs => write!(f, "#[varargs]"),
968980
SecondaryAttribute::UseCallersScope => write!(f, "#[use_callers_scope]"),
981+
SecondaryAttribute::Allow(ref k) => write!(f, "#[allow(#{k})]"),
969982
}
970983
}
971984
}
@@ -1011,7 +1024,9 @@ impl AsRef<str> for SecondaryAttribute {
10111024
SecondaryAttribute::Deprecated(Some(string)) => string,
10121025
SecondaryAttribute::Deprecated(None) => "",
10131026
SecondaryAttribute::Custom(attribute) => &attribute.contents,
1014-
SecondaryAttribute::Field(string) | SecondaryAttribute::Abi(string) => string,
1027+
SecondaryAttribute::Field(string)
1028+
| SecondaryAttribute::Abi(string)
1029+
| SecondaryAttribute::Allow(string) => string,
10151030
SecondaryAttribute::ContractLibraryMethod => "",
10161031
SecondaryAttribute::Export => "",
10171032
SecondaryAttribute::Varargs => "",

0 commit comments

Comments
 (0)