From d33e171b4e94897ce9961c659f1432d08c065f05 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 16 Jan 2025 15:50:12 -0500 Subject: [PATCH 1/3] feat: support cfg(feature = "name") attributes, add new secondary attribute kind, add enum for cfg attributes w/ 'Feature' variant, add 'cfg'/'feature' keywords, add attribute parsing for 'cfg(..)', test parsing 'cfg(..)', ensure parsing 'cfg' always gives non-empty feature name, add frontend tests for (disabled) 'cfg' attributes on functions/globals/statements/use, wip implementing --- compiler/noirc_frontend/src/lexer/token.rs | 30 +++ .../src/parser/parser/attributes.rs | 90 ++++++- .../src/parser/parser/function.rs | 11 + compiler/noirc_frontend/src/tests.rs | 234 ++++++++++++++++++ 4 files changed, 359 insertions(+), 6 deletions(-) diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 8c136f5e45d..5cb878049c3 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -904,6 +904,9 @@ pub enum SecondaryAttribute { /// Allow chosen warnings to happen so they are silenced. Allow(String), + + // A #[cfg(..)] attribute + Cfg(CfgAttribute), } impl SecondaryAttribute { @@ -921,6 +924,7 @@ impl SecondaryAttribute { SecondaryAttribute::Varargs => Some("varargs".to_string()), SecondaryAttribute::UseCallersScope => Some("use_callers_scope".to_string()), SecondaryAttribute::Allow(_) => Some("allow".to_string()), + SecondaryAttribute::Cfg(_) => Some("cfg".to_string()), } } @@ -950,6 +954,7 @@ impl SecondaryAttribute { SecondaryAttribute::Varargs => "varargs".to_string(), SecondaryAttribute::UseCallersScope => "use_callers_scope".to_string(), SecondaryAttribute::Allow(ref k) => format!("allow({k})"), + SecondaryAttribute::Cfg(ref cfg_attribute) => format!("cfg({cfg_attribute})"), } } } @@ -979,6 +984,25 @@ impl Display for MetaAttribute { } } +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum CfgAttribute { + // feature = "{name}" + Feature { + name: String, + span: Span, + }, +} + +impl Display for CfgAttribute { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + match self { + CfgAttribute::Feature { name, span: _ } => { + write!(f, "feature = {:?}", name) + } + } + } +} + #[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)] pub struct CustomAttribute { pub contents: String, @@ -1010,6 +1034,7 @@ pub enum Keyword { Bool, Break, CallData, + Cfg, Char, Comptime, Constrain, @@ -1021,6 +1046,7 @@ pub enum Keyword { Else, Enum, Expr, + Feature, Field, Fn, For, @@ -1069,6 +1095,7 @@ impl fmt::Display for Keyword { Keyword::AssertEq => write!(f, "assert_eq"), Keyword::Bool => write!(f, "bool"), Keyword::Break => write!(f, "break"), + Keyword::Cfg => write!(f, "cfg"), Keyword::Char => write!(f, "char"), Keyword::CallData => write!(f, "call_data"), Keyword::Comptime => write!(f, "comptime"), @@ -1081,6 +1108,7 @@ impl fmt::Display for Keyword { Keyword::Else => write!(f, "else"), Keyword::Enum => write!(f, "enum"), Keyword::Expr => write!(f, "Expr"), + Keyword::Feature => write!(f, "feature"), Keyword::Field => write!(f, "Field"), Keyword::Fn => write!(f, "fn"), Keyword::For => write!(f, "for"), @@ -1133,6 +1161,7 @@ impl Keyword { "bool" => Keyword::Bool, "break" => Keyword::Break, "call_data" => Keyword::CallData, + "cfg" => Keyword::Cfg, "char" => Keyword::Char, "comptime" => Keyword::Comptime, "constrain" => Keyword::Constrain, @@ -1144,6 +1173,7 @@ impl Keyword { "else" => Keyword::Else, "enum" => Keyword::Enum, "Expr" => Keyword::Expr, + "feature" => Keyword::Feature, "Field" => Keyword::Field, "fn" => Keyword::Fn, "for" => Keyword::For, diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 12cb37edb4b..bd96d7cb3d0 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -1,10 +1,11 @@ use noirc_errors::Span; use crate::ast::{Expression, ExpressionKind, Ident, Literal, Path}; +use crate::lexer::token::Keyword; use crate::lexer::errors::LexerErrorKind; use crate::parser::labels::ParsingRuleLabel; use crate::parser::ParserErrorReason; -use crate::token::{Attribute, FunctionAttribute, MetaAttribute, TestScope, Token}; +use crate::token::{Attribute, CfgAttribute, FunctionAttribute, MetaAttribute, TestScope, Token}; use crate::token::{CustomAttribute, SecondaryAttribute}; use super::parse_many::without_separator; @@ -60,6 +61,7 @@ impl<'a> Parser<'a> { /// | 'allow' '(' AttributeValue ')' /// | 'deprecated' /// | 'deprecated' '(' string ')' + /// | 'cfg' '(' CfgAttribute ')' /// | 'contract_library_method' /// | 'export' /// | 'field' '(' AttributeValue ')' @@ -67,6 +69,10 @@ impl<'a> Parser<'a> { /// | 'varargs' /// | MetaAttribute /// + /// TODO: ?? + /// CfgAttribute + /// = Arguments + /// /// MetaAttribute /// = Path Arguments? /// @@ -100,7 +106,11 @@ impl<'a> Parser<'a> { .collect() } + // TODO: make issue unless this panic triggers later fn parse_tag_attribute(&mut self, start_span: Span) -> Attribute { + // TODO: make issue unless this panic triggers later + panic!("parsing tag attr: {:?}", &start_span); + let contents_start_span = self.current_token_span; let mut contents_span = contents_start_span; let mut contents = String::new(); @@ -134,10 +144,16 @@ impl<'a> Parser<'a> { if matches!(&self.token.token(), Token::Keyword(..)) && (self.next_is(Token::LeftParen) || self.next_is(Token::RightBracket)) { - // This is a Meta attribute with the syntax `keyword(arg1, arg2, .., argN)` - let path = Path::from_single(self.token.to_string(), self.current_token_span); - self.bump(); - self.parse_meta_attribute(path, start_span) + if self.token.token() == &Token::Keyword(Keyword::Cfg) { + // This is a Cfg attribute with the syntax `cfg(arg1)` + self.bump(); + self.parse_cfg_attribute(start_span) + } else { + // This is a Meta attribute with the syntax `keyword(arg1, arg2, .., argN)` + let path = Path::from_single(self.token.to_string(), self.current_token_span); + self.bump(); + self.parse_meta_attribute(path, start_span) + } } else if let Some(path) = self.parse_path_no_turbofish() { if let Some(ident) = path.as_ident() { if ident.0.contents == "test" { @@ -148,6 +164,9 @@ impl<'a> Parser<'a> { // Every other attribute has the form `name(arg1, arg2, .., argN)` self.parse_ident_attribute_other_than_test(ident, start_span) } + } else if path.to_string() == "cfg" { + // This is a Cfg attribute with the syntax `cfg(arg1)` + self.parse_cfg_attribute(start_span) } else { // This is a Meta attribute with the syntax `path(arg1, arg2, .., argN)` self.parse_meta_attribute(path, start_span) @@ -158,6 +177,36 @@ impl<'a> Parser<'a> { } } + fn parse_cfg_attribute(&mut self, start_span: Span) -> Attribute { + // `[cfg` <- already parsed + // `(feature = "{str}"` + self.eat_or_error(Token::LeftParen); + self.eat_or_error(Token::Keyword(Keyword::Feature)); + self.eat_or_error(Token::Assign); + let name = self.eat_str().unwrap_or_else(|| { + self.push_error( + ParserErrorReason::WrongNumberOfAttributeArguments { + // name: "cfg".to_string(), + // TODO: revert name after debugging + name: format!("cfg: {:?}", (self.token.token(), self.next_token.token())), + min: 1, + max: 1, + found: 0, + }, + self.span_since(start_span), + ); + String::new() + }); + + // `)]` + self.eat_or_error(Token::RightParen); + self.eat_or_error(Token::RightBracket); + Attribute::Secondary(SecondaryAttribute::Cfg(CfgAttribute::Feature { + name, + span: self.span_since(start_span), + })) + } + fn parse_meta_attribute(&mut self, name: Path, start_span: Span) -> Attribute { let arguments = self.parse_arguments().unwrap_or_default(); self.skip_until_right_bracket(); @@ -389,7 +438,7 @@ mod tests { use crate::{ parser::{parser::tests::expect_no_errors, Parser}, - token::{Attribute, FunctionAttribute, SecondaryAttribute, TestScope}, + token::{Attribute, CfgAttribute, FunctionAttribute, SecondaryAttribute, TestScope}, }; fn parse_inner_secondary_attribute_no_errors(src: &str, expected: SecondaryAttribute) { @@ -621,6 +670,35 @@ mod tests { assert_eq!(meta.arguments[0].to_string(), "1"); } + #[test] + fn parses_cfg_feature() { + let src = "#[cfg(feature = \"foo\")]"; + let mut parser = Parser::for_str(src); + let (attribute, _span) = parser.parse_attribute().unwrap(); + + // TODO cleanup + dbg!(&parser.errors); + + expect_no_errors(&parser.errors); + let Attribute::Secondary(SecondaryAttribute::Cfg(cfg_attribute)) = attribute else { + panic!("Expected cfg attribute"); + }; + let CfgAttribute::Feature { name: feature_name, span: _ } = cfg_attribute; + assert_eq!(feature_name, "foo"); + } + + #[test] + fn parsing_cfg_feature_requires_nonempty_string() { + let src = "#[cfg(feature = \"\")]"; + let mut parser = Parser::for_str(src); + let (attribute, _span) = parser.parse_attribute().unwrap(); + + // TODO cleanup + dbg!(&parser.errors); + + expect_no_errors(&parser.errors); + } + #[test] fn parses_attributes() { let src = "#[test] #[deprecated]"; diff --git a/compiler/noirc_frontend/src/parser/parser/function.rs b/compiler/noirc_frontend/src/parser/parser/function.rs index 29e864200f3..da95b8a037b 100644 --- a/compiler/noirc_frontend/src/parser/parser/function.rs +++ b/compiler/noirc_frontend/src/parser/parser/function.rs @@ -415,6 +415,17 @@ mod tests { assert_eq!("foo", noir_function.def.name.to_string()); } + #[test] + fn parse_single_function_attribute() { + let src = "#[foreign(foo)] fn foo() {}"; + let (_, errors) = parse_program(&src); + + // TODO: cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 0); + } + #[test] fn parse_error_multiple_function_attributes_found() { let src = " diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 637b15e7197..682351b00b3 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -3844,6 +3844,240 @@ fn disallows_export_attribute_on_trait_impl_method() { }); } +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// TODO: move these to their own test sub-directory +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +#[test] +fn cfg_attribute_on_function() { + let src = r#" + #[cfg(feature = "foo")] + fn foo() { } + + fn main() { + foo(); + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 0); +} + +#[test] +fn cfg_disabled_attribute_on_function() { + let src = r#" + #[cfg(feature = "foo")] + fn foo() { + let result = unresolved_function(unresolved_variable) + .unresolved_method::(); + result + } + + fn main() { } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 0); +} + +#[test] +fn cfg_disabled_attribute_on_function_rejects_parse_error() { + let src = r#" + #[cfg(feature = "foo")] + fn foo() { + unmatched parentheses -> ) + } + + fn main() { } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 1); +} + +#[test] +fn cfg_disabled_attribute_on_global() { + let src = r#" + #[cfg(feature = "foo")] + global FOO: bool = true; + + fn main() { } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 0); +} + +#[test] +fn cfg_attribute_on_global() { + let src = r#" + #[cfg(feature = "foo")] + global FOO: bool = true; + + fn main() { + let _ = FOO; + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 0); +} + +#[test] +fn cfg_disabled_attribute_on_statement_block() { + let src = r#" + fn foo() -> Field { + let mut result = 0; + + #[cfg(feature = "bar")] + { + result = 1; + } + + result + } + + fn main() { + let _ = foo() == 0; + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 0); +} + +#[test] +fn cfg_attribute_on_statement_block() { + let src = r#" + fn foo() -> Field { + let mut result = 0; + + #[cfg(feature = "bar")] + { + result = 1; + } + + result + } + + fn main() { + let _ = foo() == 1; + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 0); +} + +#[test] +fn cfg_attribute_on_module() { + let src = r#" + #[cfg(feature = "foo")] + mod foo_module { + pub global FOO: bool = true; + } + + fn main() { + let _ = foo_module::FOO; + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 0); +} + +#[test] +fn cfg_disabled_attribute_on_module() { + let src = r#" + #[cfg(feature = "foo")] + mod foo_module { + pub global FOO: bool = true; + } + + fn main() { + let _ = foo_module::FOO; + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 1); +} + +#[test] +fn cfg_attribute_on_use() { + let src = r#" + mod foo_module { + pub global FOO: bool = true; + } + + #[cfg(feature = "foo")] + use foo_module::FOO; + + fn main() { + let _ = FOO; + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 0); +} + +#[test] +fn cfg_disabled_attribute_on_use() { + let src = r#" + mod foo_module { + pub global FOO: bool = true; + } + + #[cfg(feature = "foo")] + use foo_module::FOO; + + fn main() { + let _ = FOO; + } + "#; + let errors = get_program_errors(src); + + // TODO cleanup + dbg!(&errors); + + assert_eq!(errors.len(), 1); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// +// END cfg tests +///////////////////////////////////////////////////////////////////////////////////////////////////////////////// + #[test] fn allows_multiple_underscore_parameters() { let src = r#" From d179653c5e14286cbbd941c1bc946e2cd7c7b4bb Mon Sep 17 00:00:00 2001 From: Michael Klein Date: Wed, 22 Jan 2025 18:05:48 -0500 Subject: [PATCH 2/3] add wip methods is_disabled(_cfg) to (Secondary|Cfg|)Attribute, enable 'default' feature by default for testing, checking for disabled cfg when parsing items and returning vec![] when disabled (partially working) --- compiler/noirc_frontend/src/lexer/token.rs | 38 +++++++++++ .../src/parser/parser/attributes.rs | 16 ++--- .../noirc_frontend/src/parser/parser/item.rs | 63 ++++++++++++++----- compiler/noirc_frontend/src/tests.rs | 42 +++++++------ 4 files changed, 115 insertions(+), 44 deletions(-) diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index 93fca508c13..36c6dd4e07c 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -781,6 +781,15 @@ impl fmt::Display for Attribute { } } +impl Attribute { + pub(crate) fn is_disabled_cfg(&self) -> bool { + match self { + Attribute::Function(_) => false, + Attribute::Secondary(secondary) => secondary.is_disabled_cfg(), + } + } +} + /// Primary Attributes are those which a function can only have one of. /// They change the FunctionKind and thus have direct impact on the IR output #[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)] @@ -939,6 +948,22 @@ impl SecondaryAttribute { matches!(self, SecondaryAttribute::Abi(_)) } + pub(crate) fn is_disabled_cfg(&self) -> bool { + match self { + SecondaryAttribute::Deprecated(..) => false, + SecondaryAttribute::Tag(..) => false, + SecondaryAttribute::Meta(..) => false, + SecondaryAttribute::ContractLibraryMethod => false, + SecondaryAttribute::Export => false, + SecondaryAttribute::Field(..) => false, + SecondaryAttribute::Abi(..) => false, + SecondaryAttribute::Varargs => false, + SecondaryAttribute::UseCallersScope => false, + SecondaryAttribute::Allow(..) => false, + SecondaryAttribute::Cfg(ref cfg_attribute) => cfg_attribute.is_disabled(), + } + } + pub(crate) fn contents(&self) -> String { match self { SecondaryAttribute::Deprecated(None) => "deprecated".to_string(), @@ -1003,6 +1028,19 @@ impl Display for CfgAttribute { } } +// TODO: enable more features once working +impl CfgAttribute { + pub(crate) fn is_disabled(&self) -> bool { + match self { + CfgAttribute::Feature { name, .. } => { + name != &"default" + } + } + } + +} + + #[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)] pub struct CustomAttribute { pub contents: String, diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 21c99f1a41c..dc72d7cd369 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -106,11 +106,7 @@ impl<'a> Parser<'a> { .collect() } - // TODO: make issue unless this panic triggers later fn parse_tag_attribute(&mut self, start_span: Span) -> Attribute { - // TODO: make issue unless this panic triggers later - panic!("parsing tag attr: {:?}", &start_span); - let contents_start_span = self.current_token_span; let mut contents_span = contents_start_span; let mut contents = String::new(); @@ -675,10 +671,6 @@ mod tests { let src = "#[cfg(feature = \"foo\")]"; let mut parser = Parser::for_str(src); let (attribute, _span) = parser.parse_attribute().unwrap(); - - // TODO cleanup - dbg!(&parser.errors); - expect_no_errors(&parser.errors); let Attribute::Secondary(SecondaryAttribute::Cfg(cfg_attribute)) = attribute else { panic!("Expected cfg attribute"); @@ -692,10 +684,10 @@ mod tests { let src = "#[cfg(feature = \"\")]"; let mut parser = Parser::for_str(src); let (attribute, _span) = parser.parse_attribute().unwrap(); - - // TODO cleanup - dbg!(&parser.errors); - + match attribute { + Attribute::Secondary(SecondaryAttribute::Cfg(CfgAttribute::Feature { name, .. })) => assert_eq!(name, String::new()), + other => panic!("expected an CfgAttribute::Feature, but found {other:?}"), + } expect_no_errors(&parser.errors); } diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index d928d8e82d3..df24ad2c933 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -121,6 +121,7 @@ impl<'a> Parser<'a> { let start_span = self.current_token_span; let attributes = self.parse_attributes(); + let cfg_feature_disabled = attributes.iter().any(|attribute| attribute.0.is_disabled_cfg()); let modifiers = self.parse_modifiers( true, // allow mut @@ -130,39 +131,58 @@ impl<'a> Parser<'a> { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); let use_tree = self.parse_use_tree(); + if cfg_feature_disabled { + return vec![]; + } return vec![ItemKind::Import(use_tree, modifiers.visibility)]; } if let Some(is_contract) = self.eat_mod_or_contract() { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); - return vec![self.parse_mod_or_contract(attributes, is_contract, modifiers.visibility)]; + let parsed_mod_or_contract = self.parse_mod_or_contract(attributes, is_contract, modifiers.visibility); + if cfg_feature_disabled { + return vec![]; + } + return vec![parsed_mod_or_contract]; } if self.eat_keyword(Keyword::Struct) { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); - return vec![ItemKind::Struct(self.parse_struct( + let parsed_struct = self.parse_struct( attributes, modifiers.visibility, start_span, - ))]; + ); + if cfg_feature_disabled { + return vec![]; + } + return vec![ItemKind::Struct(parsed_struct)]; } if self.eat_keyword(Keyword::Enum) { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); - return vec![ItemKind::Enum(self.parse_enum( + let parsed_enum = self.parse_enum( attributes, modifiers.visibility, start_span, - ))]; + ); + if cfg_feature_disabled { + return vec![]; + } + return vec![ItemKind::Enum(parsed_enum)]; } if self.eat_keyword(Keyword::Impl) { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); - return vec![match self.parse_impl() { + let parsed_impl = self.parse_impl(); + if cfg_feature_disabled { + return vec![]; + } + return vec![match parsed_impl { Impl::Impl(type_impl) => ItemKind::Impl(type_impl), Impl::TraitImpl(noir_trait_impl) => ItemKind::TraitImpl(noir_trait_impl), }]; @@ -173,6 +193,9 @@ impl<'a> Parser<'a> { let (noir_trait, noir_impl) = self.parse_trait(attributes, modifiers.visibility, start_span); + if cfg_feature_disabled { + return vec![]; + } let mut output = vec![ItemKind::Trait(noir_trait)]; if let Some(noir_impl) = noir_impl { output.push(ItemKind::TraitImpl(noir_impl)); @@ -184,12 +207,16 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Global) { self.unconstrained_not_applicable(modifiers); + let parsed_global = self.parse_global( + attributes, + modifiers.comptime.is_some(), + modifiers.mutable.is_some(), + ); + if cfg_feature_disabled { + return vec![]; + } return vec![ItemKind::Global( - self.parse_global( - attributes, - modifiers.comptime.is_some(), - modifiers.mutable.is_some(), - ), + parsed_global, modifiers.visibility, )]; } @@ -197,21 +224,29 @@ impl<'a> Parser<'a> { if self.eat_keyword(Keyword::Type) { self.comptime_mutable_and_unconstrained_not_applicable(modifiers); + let parsed_type_alias = self.parse_type_alias(modifiers.visibility, start_span); + if cfg_feature_disabled { + return vec![]; + } return vec![ItemKind::TypeAlias( - self.parse_type_alias(modifiers.visibility, start_span), + parsed_type_alias )]; } if self.eat_keyword(Keyword::Fn) { self.mutable_not_applicable(modifiers); - return vec![ItemKind::Function(self.parse_function( + let parsed_function = self.parse_function( attributes, modifiers.visibility, modifiers.comptime.is_some(), modifiers.unconstrained.is_some(), false, // allow_self - ))]; + ); + if cfg_feature_disabled { + return vec![]; + } + return vec![ItemKind::Function(parsed_function)]; } vec![] diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 05b87bdff8a..e05fcb9160a 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -3858,7 +3858,7 @@ fn disallows_export_attribute_on_trait_impl_method() { #[test] fn cfg_attribute_on_function() { let src = r#" - #[cfg(feature = "foo")] + #[cfg(feature = "default")] fn foo() { } fn main() { @@ -3866,10 +3866,6 @@ fn cfg_attribute_on_function() { } "#; let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); - assert_eq!(errors.len(), 0); } @@ -3886,9 +3882,12 @@ fn cfg_disabled_attribute_on_function() { fn main() { } "#; let errors = get_program_errors(src); - + // TODO cleanup dbg!(&errors); + for error in &errors { + println!("{}", error.0); + } assert_eq!(errors.len(), 0); } @@ -3923,6 +3922,9 @@ fn cfg_disabled_attribute_on_global() { // TODO cleanup dbg!(&errors); + for error in &errors { + println!("{}", error.0); + } assert_eq!(errors.len(), 0); } @@ -3930,7 +3932,7 @@ fn cfg_disabled_attribute_on_global() { #[test] fn cfg_attribute_on_global() { let src = r#" - #[cfg(feature = "foo")] + #[cfg(feature = "default")] global FOO: bool = true; fn main() { @@ -3938,10 +3940,6 @@ fn cfg_attribute_on_global() { } "#; let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); - assert_eq!(errors.len(), 0); } @@ -3967,6 +3965,9 @@ fn cfg_disabled_attribute_on_statement_block() { // TODO cleanup dbg!(&errors); + for error in &errors { + println!("{}", error.0); + } assert_eq!(errors.len(), 0); } @@ -3977,7 +3978,7 @@ fn cfg_attribute_on_statement_block() { fn foo() -> Field { let mut result = 0; - #[cfg(feature = "bar")] + #[cfg(feature = "default")] { result = 1; } @@ -4000,7 +4001,7 @@ fn cfg_attribute_on_statement_block() { #[test] fn cfg_attribute_on_module() { let src = r#" - #[cfg(feature = "foo")] + #[cfg(feature = "default")] mod foo_module { pub global FOO: bool = true; } @@ -4033,6 +4034,9 @@ fn cfg_disabled_attribute_on_module() { // TODO cleanup dbg!(&errors); + for error in &errors { + println!("{}", error.0); + } assert_eq!(errors.len(), 1); } @@ -4044,7 +4048,7 @@ fn cfg_attribute_on_use() { pub global FOO: bool = true; } - #[cfg(feature = "foo")] + #[cfg(feature = "default")] use foo_module::FOO; fn main() { @@ -4052,10 +4056,6 @@ fn cfg_attribute_on_use() { } "#; let errors = get_program_errors(src); - - // TODO cleanup - dbg!(&errors); - assert_eq!(errors.len(), 0); } @@ -4077,10 +4077,16 @@ fn cfg_disabled_attribute_on_use() { // TODO cleanup dbg!(&errors); + for error in &errors { + println!("{}", error.0); + } assert_eq!(errors.len(), 1); } +// TODO: test cfg on trait, impl, contract, struct, enum, type + + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// // END cfg tests ///////////////////////////////////////////////////////////////////////////////////////////////////////////////// From 668426551fb73e8a676f0ea8689bfc5b8b451de0 Mon Sep 17 00:00:00 2001 From: "Michael J. Klein" Date: Thu, 30 Jan 2025 15:16:10 -0500 Subject: [PATCH 3/3] add wip is_cfg_attribute_enabled functions, return optional CfgAttribute from parsing, drop statements later using optional CfgAttribute --- compiler/noirc_frontend/src/elaborator/mod.rs | 7 +- .../src/hir/comptime/interpreter/builtin.rs | 20 ++-- compiler/noirc_frontend/src/parser/errors.rs | 7 +- compiler/noirc_frontend/src/parser/parser.rs | 7 +- .../src/parser/parser/attributes.rs | 5 +- .../src/parser/parser/expression.rs | 7 +- .../src/parser/parser/statement.rs | 108 +++++++++++------- .../statement_or_expression_or_lvalue.rs | 16 +-- 8 files changed, 113 insertions(+), 64 deletions(-) diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 1e792ada677..2472a2853bb 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -34,7 +34,7 @@ use crate::{ DefinitionKind, DependencyId, ExprId, FuncId, FunctionModifiers, GlobalId, NodeInterner, ReferenceId, TraitId, TraitImplId, TypeAliasId, TypeId, }, - token::SecondaryAttribute, + token::{CfgAttribute, SecondaryAttribute}, Shared, Type, TypeVariable, }; use crate::{ @@ -2003,4 +2003,9 @@ impl<'context> Elaborator<'context> { _ => true, }) } + + pub(crate) fn is_cfg_attribute_enabled(&self, _opt_cfg_attribute: Option) -> bool { + // TODO + true + } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index 7909a423bef..c0081b54af9 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -797,14 +797,20 @@ fn quoted_as_expr( ); let value = - result.ok().map( - |statement_or_expression_or_lvalue| match statement_or_expression_or_lvalue { - StatementOrExpressionOrLValue::Expression(expr) => Value::expression(expr.kind), - StatementOrExpressionOrLValue::Statement(statement) => { - Value::statement(statement.kind) + result.ok().and_then( + |(statement_or_expression_or_lvalue, opt_cfg_attribute)| { + if elaborator.is_cfg_attribute_enabled(opt_cfg_attribute) { + Some(match statement_or_expression_or_lvalue { + StatementOrExpressionOrLValue::Expression(expr) => Value::expression(expr.kind), + StatementOrExpressionOrLValue::Statement(statement) => { + Value::statement(statement.kind) + } + StatementOrExpressionOrLValue::LValue(lvalue) => Value::lvalue(lvalue), + }) + } else { + None } - StatementOrExpressionOrLValue::LValue(lvalue) => Value::lvalue(lvalue), - }, + } ); option(return_type, value, location.span) diff --git a/compiler/noirc_frontend/src/parser/errors.rs b/compiler/noirc_frontend/src/parser/errors.rs index 508ed33857e..1742237532f 100644 --- a/compiler/noirc_frontend/src/parser/errors.rs +++ b/compiler/noirc_frontend/src/parser/errors.rs @@ -1,6 +1,6 @@ use crate::ast::{Expression, IntegerBitSize, ItemVisibility}; use crate::lexer::errors::LexerErrorKind; -use crate::lexer::token::Token; +use crate::lexer::token::{CfgAttribute, Token}; use crate::token::TokenKind; use small_ord_set::SmallOrdSet; use thiserror::Error; @@ -79,6 +79,11 @@ pub enum ParserErrorReason { ComptimeDeprecated, #[error("{0} are experimental and aren't fully supported yet")] ExperimentalFeature(&'static str), + #[error("Only one 'cfg' attribute is allowed, but found {cfg_attribute} and {second_cfg_attribute}")] + MultipleCfgAttributesFound { + cfg_attribute: CfgAttribute, + second_cfg_attribute: CfgAttribute, + }, #[error( "Multiple primary attributes found. Only one function attribute is allowed per function" )] diff --git a/compiler/noirc_frontend/src/parser/parser.rs b/compiler/noirc_frontend/src/parser/parser.rs index e554248fb03..945271ec08a 100644 --- a/compiler/noirc_frontend/src/parser/parser.rs +++ b/compiler/noirc_frontend/src/parser/parser.rs @@ -5,7 +5,7 @@ use noirc_errors::Span; use crate::{ ast::{Ident, ItemVisibility}, lexer::{Lexer, SpannedTokenResult}, - token::{FmtStrFragment, IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, + token::{CfgAttribute, FmtStrFragment, IntType, Keyword, SpannedToken, Token, TokenKind, Tokens}, }; use super::{labels::ParsingRuleLabel, ParsedModule, ParserError, ParserErrorReason}; @@ -571,6 +571,11 @@ impl<'a> Parser<'a> { fn push_error(&mut self, reason: ParserErrorReason, span: Span) { self.errors.push(ParserError::with_reason(reason, span)); } + + pub(crate) fn is_enabled_cfg(&self, _opt_cfg_attribute: Option) -> bool { + // TODO + true + } } fn eof_spanned_token() -> SpannedToken { diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index dc72d7cd369..1698d474b92 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -69,7 +69,6 @@ impl<'a> Parser<'a> { /// | 'varargs' /// | MetaAttribute /// - /// TODO: ?? /// CfgAttribute /// = Arguments /// @@ -182,9 +181,7 @@ impl<'a> Parser<'a> { let name = self.eat_str().unwrap_or_else(|| { self.push_error( ParserErrorReason::WrongNumberOfAttributeArguments { - // name: "cfg".to_string(), - // TODO: revert name after debugging - name: format!("cfg: {:?}", (self.token.token(), self.next_token.token())), + name: "cfg".to_string(), min: 1, max: 1, found: 0, diff --git a/compiler/noirc_frontend/src/parser/parser/expression.rs b/compiler/noirc_frontend/src/parser/parser/expression.rs index 15be2155a15..b7b707c327b 100644 --- a/compiler/noirc_frontend/src/parser/parser/expression.rs +++ b/compiler/noirc_frontend/src/parser/parser/expression.rs @@ -770,8 +770,11 @@ impl<'a> Parser<'a> { } fn parse_statement_in_block(&mut self) -> Option<(Statement, (Option, Span))> { - if let Some(statement) = self.parse_statement() { - Some(statement) + if let Some((statement, semicolon, opt_cfg_attribute)) = self.parse_statement() { + if !self.is_enabled_cfg(opt_cfg_attribute) { + return None; + } + Some((statement, semicolon)) } else { self.expected_label(ParsingRuleLabel::Statement); None diff --git a/compiler/noirc_frontend/src/parser/parser/statement.rs b/compiler/noirc_frontend/src/parser/parser/statement.rs index 005216b1deb..87a78d4bfa5 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement.rs @@ -7,23 +7,23 @@ use crate::{ LetStatement, Statement, StatementKind, }, parser::{labels::ParsingRuleLabel, ParserErrorReason}, - token::{Attribute, Keyword, Token, TokenKind}, + token::{Attribute, CfgAttribute, Keyword, SecondaryAttribute, Token, TokenKind}, }; use super::{Parser, StatementDocComments}; impl<'a> Parser<'a> { - pub(crate) fn parse_statement_or_error(&mut self) -> Statement { - if let Some((statement, (_token, _span))) = self.parse_statement() { - statement + pub(crate) fn parse_statement_or_error(&mut self) -> (Statement, Option) { + if let Some((statement, (_token, _span), cfg_attribute)) = self.parse_statement() { + (statement, cfg_attribute) } else { self.expected_label(ParsingRuleLabel::Statement); - Statement { kind: StatementKind::Error, span: self.span_at_previous_token_end() } + (Statement { kind: StatementKind::Error, span: self.span_at_previous_token_end() }, None) } } /// Statement = Attributes StatementKind ';'? - pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Span))> { + pub(crate) fn parse_statement(&mut self) -> Option<(Statement, (Option, Span), Option)> { loop { let span_before_doc_comments = self.current_token_span; let doc_comments = self.parse_outer_doc_comments(); @@ -41,6 +41,7 @@ impl<'a> Parser<'a> { let attributes = self.parse_attributes(); let start_span = self.current_token_span; + let cfg_attribute = self.cfg_attribute(&attributes); let kind = self.parse_statement_kind(attributes); if let Some(statement_doc_comments) = &self.statement_doc_comments { @@ -71,7 +72,7 @@ impl<'a> Parser<'a> { if let Some(kind) = kind { let statement = Statement { kind, span }; - return Some((statement, (semicolon_token, semicolon_span))); + return Some((statement, (semicolon_token, semicolon_span), cfg_attribute)); } self.expected_label(ParsingRuleLabel::Statement); @@ -84,6 +85,35 @@ impl<'a> Parser<'a> { } } + // TODO relocate + // + /// Return the unique `CfgAttribute` in the given `attributes` list along + /// with its `Span`, if it exists + fn cfg_attribute( + &mut self, + attributes: &Vec<(Attribute, Span)>, + ) -> Option { + let mut found_cfg_attribute: Option = None; + for (attribute, attribute_span) in attributes { + if let Attribute::Secondary(SecondaryAttribute::Cfg(cfg_attribute)) = attribute { + if let Some(ref second_cfg_attribute) = found_cfg_attribute { + let cfg_attribute = cfg_attribute.clone(); + let second_cfg_attribute = second_cfg_attribute.clone(); + self.push_error( + ParserErrorReason::MultipleCfgAttributesFound { + cfg_attribute, + second_cfg_attribute, + }, + *attribute_span, + ); + } else { + found_cfg_attribute = Some(cfg_attribute.clone()); + } + } + } + found_cfg_attribute + } + /// StatementKind /// = BreakStatement /// | ContinueStatement @@ -482,15 +512,15 @@ mod tests { UnresolvedTypeData, }, parser::{ - parser::tests::{ + parser::{CfgAttribute, tests::{ expect_no_errors, get_single_error, get_single_error_reason, get_source_with_error_span, - }, + }}, Parser, ParserErrorReason, }, }; - fn parse_statement_no_errors(src: &str) -> Statement { + fn parse_statement_no_errors(src: &str) -> (Statement, Option) { let mut parser = Parser::for_str(src); let statement = parser.parse_statement_or_error(); expect_no_errors(&parser.errors); @@ -500,21 +530,21 @@ mod tests { #[test] fn parses_break() { let src = "break"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; assert!(matches!(statement.kind, StatementKind::Break)); } #[test] fn parses_continue() { let src = "continue"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; assert!(matches!(statement.kind, StatementKind::Continue)); } #[test] fn parses_let_statement_no_type() { let src = "let x = 1;"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); }; @@ -527,7 +557,7 @@ mod tests { #[test] fn parses_let_statement_with_type() { let src = "let x: Field = 1;"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); }; @@ -541,7 +571,7 @@ mod tests { fn parses_let_statement_with_unsafe() { let src = "/// Safety: doc comment let x = unsafe { 1 };"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Let(let_statement) = statement.kind else { panic!("Expected let statement"); }; @@ -551,7 +581,7 @@ mod tests { #[test] fn parses_assert() { let src = "assert(true, \"good\")"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Constrain(constrain) = statement.kind else { panic!("Expected constrain statement"); }; @@ -562,7 +592,7 @@ mod tests { #[test] fn parses_assert_eq() { let src = "assert_eq(1, 2, \"bad\")"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Constrain(constrain) = statement.kind else { panic!("Expected constrain statement"); }; @@ -578,7 +608,7 @@ mod tests { "; let (src, span) = get_source_with_error_span(src); let mut parser = Parser::for_str(&src); - let statement = parser.parse_statement_or_error(); + let (statement, None) = parser.parse_statement_or_error() else { panic!("cfg found") }; let StatementKind::Constrain(constrain) = statement.kind else { panic!("Expected constrain statement"); }; @@ -592,7 +622,7 @@ mod tests { #[test] fn parses_comptime_block() { let src = "comptime { 1 }"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Comptime(statement) = statement.kind else { panic!("Expected comptime statement"); }; @@ -608,7 +638,7 @@ mod tests { #[test] fn parses_comptime_let() { let src = "comptime let x = 1;"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Comptime(statement) = statement.kind else { panic!("Expected comptime statement"); }; @@ -620,7 +650,7 @@ mod tests { #[test] fn parses_for_array() { let src = "for i in x { }"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::For(for_loop) = statement.kind else { panic!("Expected for loop"); }; @@ -634,7 +664,7 @@ mod tests { #[test] fn parses_for_range() { let src = "for i in 0..10 { }"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::For(for_loop) = statement.kind else { panic!("Expected for loop"); }; @@ -650,7 +680,7 @@ mod tests { #[test] fn parses_for_range_inclusive() { let src = "for i in 0..=10 { }"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::For(for_loop) = statement.kind else { panic!("Expected for loop"); }; @@ -666,7 +696,7 @@ mod tests { #[test] fn parses_comptime_for() { let src = "comptime for i in x { }"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Comptime(statement) = statement.kind else { panic!("Expected comptime"); }; @@ -680,7 +710,7 @@ mod tests { #[test] fn parses_assignment() { let src = "x = 1"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Assign(assign) = statement.kind else { panic!("Expected assign"); }; @@ -694,7 +724,7 @@ mod tests { #[test] fn parses_assignment_with_parentheses() { let src = "(x)[0] = 1"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Assign(..) = statement.kind else { panic!("Expected assign"); }; @@ -704,7 +734,7 @@ mod tests { fn parses_assignment_with_unsafe() { let src = "/// Safety: test x = unsafe { 1 }"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Assign(assign) = statement.kind else { panic!("Expected assign"); }; @@ -717,7 +747,7 @@ mod tests { #[test] fn parses_op_assignment() { let src = "x += 1"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Assign(assign) = statement.kind else { panic!("Expected assign"); }; @@ -727,7 +757,7 @@ mod tests { #[test] fn parses_op_assignment_with_shift_right() { let src = "x >>= 1"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Assign(assign) = statement.kind else { panic!("Expected assign"); }; @@ -738,7 +768,7 @@ mod tests { fn parses_op_assignment_with_unsafe() { let src = "/// Safety: comment x += unsafe { 1 }"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Assign(_) = statement.kind else { panic!("Expected assign"); }; @@ -748,7 +778,7 @@ mod tests { fn parses_if_statement_followed_by_tuple() { // This shouldn't be parsed as a call let src = "{ if 1 { 2 } (3, 4) }"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Expression(expr) = statement.kind else { panic!("Expected expr"); }; @@ -762,7 +792,7 @@ mod tests { fn parses_block_followed_by_tuple() { // This shouldn't be parsed as a call let src = "{ { 2 } (3, 4) }"; - let statement = parse_statement_no_errors(src); + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; let StatementKind::Expression(expr) = statement.kind else { panic!("Expected expr"); }; @@ -781,7 +811,7 @@ mod tests { "; let (src, span) = get_source_with_error_span(src); let mut parser = Parser::for_str(&src); - let statement = parser.parse_statement_or_error(); + let (statement, None) = parser.parse_statement_or_error() else { panic!("cfg found") }; assert!(matches!(statement.kind, StatementKind::Error)); let reason = get_single_error_reason(&parser.errors, span); assert!(matches!(reason, ParserErrorReason::EarlyReturn)); @@ -795,7 +825,7 @@ mod tests { "; let (src, span) = get_source_with_error_span(src); let mut parser = Parser::for_str(&src); - let statement = parser.parse_statement_or_error(); + let (statement, None) = parser.parse_statement_or_error() else { panic!("cfg found") }; assert!(matches!(statement.kind, StatementKind::Let(..))); let error = get_single_error(&parser.errors, span); assert_eq!(error.to_string(), "Expected a statement but found ']'"); @@ -822,9 +852,8 @@ mod tests { #[test] fn parses_empty_loop() { let src = "loop { }"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - let StatementKind::Loop(block, span) = statement.kind else { + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; + let StatementKind::Loop(block) = statement.kind else { panic!("Expected loop"); }; let ExpressionKind::Block(block) = block.kind else { @@ -838,9 +867,8 @@ mod tests { #[test] fn parses_loop_with_statements() { let src = "loop { 1; 2 }"; - let mut parser = Parser::for_str(src); - let statement = parser.parse_statement_or_error(); - let StatementKind::Loop(block, _) = statement.kind else { + let (statement, None) = parse_statement_no_errors(src) else { panic!("cfg found") }; + let StatementKind::Loop(block) = statement.kind else { panic!("Expected loop"); }; let ExpressionKind::Block(block) = block.kind else { diff --git a/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs b/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs index fdc187f3fb2..a2e5cab3796 100644 --- a/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs +++ b/compiler/noirc_frontend/src/parser/parser/statement_or_expression_or_lvalue.rs @@ -1,6 +1,6 @@ use crate::{ ast::{AssignStatement, Expression, LValue, Statement, StatementKind}, - token::{Token, TokenKind}, + token::{CfgAttribute, Token, TokenKind}, }; use super::Parser; @@ -19,7 +19,7 @@ impl<'a> Parser<'a> { /// This method is only used in `Quoted::as_expr`. pub(crate) fn parse_statement_or_expression_or_lvalue( &mut self, - ) -> StatementOrExpressionOrLValue { + ) -> (StatementOrExpressionOrLValue, Option) { let start_span = self.current_token_span; // First check if it's an interned LValue @@ -32,12 +32,12 @@ impl<'a> Parser<'a> { if self.eat(Token::Assign) { let expression = self.parse_expression_or_error(); let kind = StatementKind::Assign(AssignStatement { lvalue, expression }); - return StatementOrExpressionOrLValue::Statement(Statement { + return (StatementOrExpressionOrLValue::Statement(Statement { kind, span: self.span_since(start_span), - }); + }), None); } else { - return StatementOrExpressionOrLValue::LValue(lvalue); + return (StatementOrExpressionOrLValue::LValue(lvalue), None); } } _ => unreachable!(), @@ -45,11 +45,11 @@ impl<'a> Parser<'a> { } // Otherwise, check if it's a statement (which in turn checks if it's an expression) - let statement = self.parse_statement_or_error(); + let (statement, opt_cfg_attribute) = self.parse_statement_or_error(); if let StatementKind::Expression(expr) = statement.kind { - StatementOrExpressionOrLValue::Expression(expr) + (StatementOrExpressionOrLValue::Expression(expr), opt_cfg_attribute) } else { - StatementOrExpressionOrLValue::Statement(statement) + (StatementOrExpressionOrLValue::Statement(statement), opt_cfg_attribute) } } }