diff --git a/compiler/noirc_driver/src/lib.rs b/compiler/noirc_driver/src/lib.rs index bb0bee4ee62..97c532b9dfb 100644 --- a/compiler/noirc_driver/src/lib.rs +++ b/compiler/noirc_driver/src/lib.rs @@ -23,7 +23,7 @@ use noirc_frontend::monomorphization::{ errors::MonomorphizationError, monomorphize, monomorphize_debug, }; use noirc_frontend::node_interner::{FuncId, GlobalId, TypeId}; -use noirc_frontend::token::SecondaryAttribute; +use noirc_frontend::token::SecondaryAttributeKind; use std::collections::HashMap; use std::path::Path; use tracing::info; @@ -507,7 +507,7 @@ fn read_contract(context: &Context, module_id: ModuleId, name: String) -> Contra context.def_interner.get_all_globals().iter().for_each(|global_info| { context.def_interner.global_attributes(&global_info.id).iter().for_each(|attr| { - if let SecondaryAttribute::Abi(tag) = attr { + if let SecondaryAttributeKind::Abi(tag) = &attr.kind { if let Some(tagged) = outputs.globals.get_mut(tag) { tagged.push(global_info.id); } else { @@ -520,7 +520,7 @@ fn read_contract(context: &Context, module_id: ModuleId, name: String) -> Contra module.type_definitions().for_each(|id| { if let ModuleDefId::TypeId(struct_id) = id { context.def_interner.type_attributes(&struct_id).iter().for_each(|attr| { - if let SecondaryAttribute::Abi(tag) = attr { + if let SecondaryAttributeKind::Abi(tag) = &attr.kind { if let Some(tagged) = outputs.structs.get_mut(tag) { tagged.push(struct_id); } else { @@ -584,9 +584,9 @@ fn compile_contract_inner( .attributes .secondary .iter() - .filter_map(|attr| match attr { - SecondaryAttribute::Tag(attribute) => Some(attribute.contents.clone()), - SecondaryAttribute::Meta(attribute) => Some(attribute.to_string()), + .filter_map(|attr| match &attr.kind { + SecondaryAttributeKind::Tag(contents) => Some(contents.clone()), + SecondaryAttributeKind::Meta(attribute) => Some(attribute.to_string()), _ => None, }) .collect(); diff --git a/compiler/noirc_frontend/src/ast/enumeration.rs b/compiler/noirc_frontend/src/ast/enumeration.rs index 5ac60be5fba..5bb012a3b06 100644 --- a/compiler/noirc_frontend/src/ast/enumeration.rs +++ b/compiler/noirc_frontend/src/ast/enumeration.rs @@ -21,7 +21,7 @@ pub struct NoirEnumeration { impl NoirEnumeration { pub fn is_abi(&self) -> bool { - self.attributes.iter().any(|attr| attr.is_abi()) + self.attributes.iter().any(|attr| attr.kind.is_abi()) } } diff --git a/compiler/noirc_frontend/src/ast/expression.rs b/compiler/noirc_frontend/src/ast/expression.rs index cbf0b321bdc..66e02603f37 100644 --- a/compiler/noirc_frontend/src/ast/expression.rs +++ b/compiler/noirc_frontend/src/ast/expression.rs @@ -10,7 +10,7 @@ use crate::ast::{ use crate::node_interner::{ExprId, InternedExpressionKind, InternedStatementKind, QuotedTypeId}; use crate::shared::Visibility; use crate::signed_field::SignedField; -use crate::token::{Attributes, FmtStrFragment, FunctionAttribute, Token, Tokens}; +use crate::token::{Attributes, FmtStrFragment, FunctionAttributeKind, Token, Tokens}; use crate::{Kind, Type}; use acvm::FieldElement; use iter_extended::vecmap; @@ -531,7 +531,7 @@ impl FunctionDefinition { pub fn is_test(&self) -> bool { if let Some(attribute) = self.attributes.function() { - matches!(attribute, FunctionAttribute::Test(..)) + matches!(attribute.kind, FunctionAttributeKind::Test(..)) } else { false } diff --git a/compiler/noirc_frontend/src/ast/function.rs b/compiler/noirc_frontend/src/ast/function.rs index cbb1c4c1d18..e935bcd4521 100644 --- a/compiler/noirc_frontend/src/ast/function.rs +++ b/compiler/noirc_frontend/src/ast/function.rs @@ -5,7 +5,7 @@ use noirc_errors::{Location, Span}; use crate::{ ast::{FunctionReturnType, Ident, Param}, shared::Visibility, - token::{Attributes, FunctionAttribute, SecondaryAttribute}, + token::{Attributes, FunctionAttribute, FunctionAttributeKind, SecondaryAttribute}, }; use super::{FunctionDefinition, UnresolvedType, UnresolvedTypeData}; @@ -105,29 +105,20 @@ impl NoirFunction { pub fn span(&self) -> Span { self.location().span } - - pub fn foreign(&self) -> Option<&FunctionDefinition> { - match &self.kind { - FunctionKind::LowLevel => {} - _ => return None, - } - assert!(self.function_attribute().unwrap().is_foreign()); - Some(&self.def) - } } impl From for NoirFunction { fn from(fd: FunctionDefinition) -> Self { // The function type is determined by the existence of a function attribute - let kind = match fd.attributes.function() { - Some(FunctionAttribute::Builtin(_)) => FunctionKind::Builtin, - Some(FunctionAttribute::Foreign(_)) => FunctionKind::LowLevel, - Some(FunctionAttribute::Test { .. }) => FunctionKind::Normal, - Some(FunctionAttribute::FuzzingHarness { .. }) => FunctionKind::Normal, - Some(FunctionAttribute::Oracle(_)) => FunctionKind::Oracle, - Some(FunctionAttribute::Fold) => FunctionKind::Normal, - Some(FunctionAttribute::NoPredicates) => FunctionKind::Normal, - Some(FunctionAttribute::InlineAlways) => FunctionKind::Normal, + let kind = match fd.attributes.function().map(|attr| &attr.kind) { + Some(FunctionAttributeKind::Builtin(_)) => FunctionKind::Builtin, + Some(FunctionAttributeKind::Foreign(_)) => FunctionKind::LowLevel, + Some(FunctionAttributeKind::Test { .. }) => FunctionKind::Normal, + Some(FunctionAttributeKind::FuzzingHarness { .. }) => FunctionKind::Normal, + Some(FunctionAttributeKind::Oracle(_)) => FunctionKind::Oracle, + Some(FunctionAttributeKind::Fold) => FunctionKind::Normal, + Some(FunctionAttributeKind::NoPredicates) => FunctionKind::Normal, + Some(FunctionAttributeKind::InlineAlways) => FunctionKind::Normal, None => FunctionKind::Normal, }; diff --git a/compiler/noirc_frontend/src/ast/structure.rs b/compiler/noirc_frontend/src/ast/structure.rs index 7162a2d3ba6..fe9292fa1dc 100644 --- a/compiler/noirc_frontend/src/ast/structure.rs +++ b/compiler/noirc_frontend/src/ast/structure.rs @@ -21,7 +21,7 @@ pub struct NoirStruct { impl NoirStruct { pub fn is_abi(&self) -> bool { - self.attributes.iter().any(|attr| attr.is_abi()) + self.attributes.iter().any(|attr| attr.kind.is_abi()) } } diff --git a/compiler/noirc_frontend/src/ast/visitor.rs b/compiler/noirc_frontend/src/ast/visitor.rs index 38128b192aa..456068e22fa 100644 --- a/compiler/noirc_frontend/src/ast/visitor.rs +++ b/compiler/noirc_frontend/src/ast/visitor.rs @@ -18,7 +18,10 @@ use crate::{ }, parser::{Item, ItemKind, ParsedSubModule}, signed_field::SignedField, - token::{FmtStrFragment, MetaAttribute, MetaAttributeName, SecondaryAttribute, Tokens}, + token::{ + FmtStrFragment, MetaAttribute, MetaAttributeName, SecondaryAttribute, + SecondaryAttributeKind, Tokens, + }, }; use super::{ @@ -526,7 +529,21 @@ pub trait Visitor { true } - fn visit_meta_attribute(&mut self, _: &MetaAttribute, _target: AttributeTarget) -> bool { + fn visit_secondary_attribute_kind( + &mut self, + _: &SecondaryAttributeKind, + _target: AttributeTarget, + _span: Span, + ) -> bool { + true + } + + fn visit_meta_attribute( + &mut self, + _: &MetaAttribute, + _target: AttributeTarget, + _span: Span, + ) -> bool { true } } @@ -1644,15 +1661,27 @@ impl SecondaryAttribute { } pub fn accept_children(&self, target: AttributeTarget, visitor: &mut impl Visitor) { - if let SecondaryAttribute::Meta(meta_attribute) = self { - meta_attribute.accept(target, visitor); + self.kind.accept(target, self.location.span, visitor); + } +} + +impl SecondaryAttributeKind { + pub fn accept(&self, target: AttributeTarget, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_secondary_attribute_kind(self, target, span) { + self.accept_children(target, span, visitor); + } + } + + pub fn accept_children(&self, target: AttributeTarget, span: Span, visitor: &mut impl Visitor) { + if let SecondaryAttributeKind::Meta(meta_attribute) = self { + meta_attribute.accept(target, span, visitor); } } } impl MetaAttribute { - pub fn accept(&self, target: AttributeTarget, visitor: &mut impl Visitor) { - if visitor.visit_meta_attribute(self, target) { + pub fn accept(&self, target: AttributeTarget, span: Span, visitor: &mut impl Visitor) { + if visitor.visit_meta_attribute(self, target, span) { self.accept_children(visitor); } } diff --git a/compiler/noirc_frontend/src/elaborator/comptime.rs b/compiler/noirc_frontend/src/elaborator/comptime.rs index 9d8f5dc1250..35dde85c41c 100644 --- a/compiler/noirc_frontend/src/elaborator/comptime.rs +++ b/compiler/noirc_frontend/src/elaborator/comptime.rs @@ -21,7 +21,7 @@ use crate::{ hir_def::expr::{HirExpression, HirIdent}, node_interner::{DefinitionKind, DependencyId, FuncId, NodeInterner, TraitId, TypeId}, parser::{Item, ItemKind}, - token::{MetaAttribute, MetaAttributeName, SecondaryAttribute}, + token::{MetaAttribute, MetaAttributeName, SecondaryAttribute, SecondaryAttributeKind}, }; use super::{ElaborateReason, Elaborator, FunctionContext, ResolverMeta}; @@ -160,10 +160,11 @@ impl<'context> Elaborator<'context> { attribute_context: AttributeContext, attributes_to_run: &mut CollectedAttributes, ) { - if let SecondaryAttribute::Meta(attribute) = attribute { + if let SecondaryAttributeKind::Meta(meta) = &attribute.kind { self.elaborate_in_comptime_context(|this| { if let Err(error) = this.collect_comptime_attribute_name_on_item( - attribute, + meta, + attribute.location, item.clone(), attribute_context, attributes_to_run, @@ -178,12 +179,12 @@ impl<'context> Elaborator<'context> { fn collect_comptime_attribute_name_on_item( &mut self, attribute: &MetaAttribute, + location: Location, item: Value, attribute_context: AttributeContext, attributes_to_run: &mut CollectedAttributes, ) -> Result<(), CompilationError> { self.local_module = attribute_context.attribute_module; - let location = attribute.location; let kind = match &attribute.name { MetaAttributeName::Path(path) => ExpressionKind::Variable(path.clone()), diff --git a/compiler/noirc_frontend/src/elaborator/lints.rs b/compiler/noirc_frontend/src/elaborator/lints.rs index 3856f488f16..fe330f24515 100644 --- a/compiler/noirc_frontend/src/elaborator/lints.rs +++ b/compiler/noirc_frontend/src/elaborator/lints.rs @@ -15,6 +15,7 @@ use crate::{ DefinitionId, DefinitionKind, ExprId, FuncId, FunctionModifiers, NodeInterner, }, shared::{Signedness, Visibility}, + token::FunctionAttributeKind, }; use noirc_errors::Location; @@ -46,32 +47,42 @@ pub(super) fn inlining_attributes( func: &FuncMeta, modifiers: &FunctionModifiers, ) -> Option { - if modifiers.is_unconstrained { - if modifiers.attributes.is_no_predicates() { + if !modifiers.is_unconstrained { + return None; + } + + let attribute = modifiers.attributes.function()?; + let location = attribute.location; + match &attribute.kind { + FunctionAttributeKind::NoPredicates => { let ident = func_meta_name_ident(func, modifiers); - Some(ResolverError::NoPredicatesAttributeOnUnconstrained { ident }) - } else if modifiers.attributes.is_foldable() { + Some(ResolverError::NoPredicatesAttributeOnUnconstrained { ident, location }) + } + FunctionAttributeKind::Fold => { let ident = func_meta_name_ident(func, modifiers); - Some(ResolverError::FoldAttributeOnUnconstrained { ident }) - } else { - None + Some(ResolverError::FoldAttributeOnUnconstrained { ident, location }) } - } else { - None + FunctionAttributeKind::Foreign(_) + | FunctionAttributeKind::Builtin(_) + | FunctionAttributeKind::Oracle(_) + | FunctionAttributeKind::Test(_) + | FunctionAttributeKind::InlineAlways + | FunctionAttributeKind::FuzzingHarness(_) => None, } } /// Attempting to define new low level (`#[builtin]` or `#[foreign]`) functions outside of the stdlib is disallowed. pub(super) fn low_level_function_outside_stdlib( - func: &FuncMeta, modifiers: &FunctionModifiers, crate_id: CrateId, ) -> Option { - let is_low_level_function = - modifiers.attributes.function().is_some_and(|func| func.is_low_level()); - if !crate_id.is_stdlib() && is_low_level_function { - let ident = func_meta_name_ident(func, modifiers); - Some(ResolverError::LowLevelFunctionOutsideOfStdlib { ident }) + if crate_id.is_stdlib() { + return None; + } + + let attribute = modifiers.attributes.function()?; + if attribute.kind.is_low_level() { + Some(ResolverError::LowLevelFunctionOutsideOfStdlib { location: attribute.location }) } else { None } @@ -82,10 +93,15 @@ pub(super) fn oracle_not_marked_unconstrained( func: &FuncMeta, modifiers: &FunctionModifiers, ) -> Option { - let is_oracle_function = modifiers.attributes.function().is_some_and(|func| func.is_oracle()); - if is_oracle_function && !modifiers.is_unconstrained { + if modifiers.is_unconstrained { + return None; + } + + let attribute = modifiers.attributes.function()?; + if matches!(attribute.kind, FunctionAttributeKind::Oracle(_)) { let ident = func_meta_name_ident(func, modifiers); - Some(ResolverError::OracleMarkedAsConstrained { ident }) + let location = attribute.location; + Some(ResolverError::OracleMarkedAsConstrained { ident, location }) } else { None } @@ -105,7 +121,7 @@ pub(super) fn oracle_called_from_constrained_function( } let function_attributes = interner.function_attributes(called_func); - let is_oracle_call = function_attributes.function().is_some_and(|func| func.is_oracle()); + let is_oracle_call = function_attributes.function().is_some_and(|func| func.kind.is_oracle()); if is_oracle_call { Some(ResolverError::UnconstrainedOracleReturnToConstrained { location }) } else { diff --git a/compiler/noirc_frontend/src/elaborator/mod.rs b/compiler/noirc_frontend/src/elaborator/mod.rs index 23b776e4b18..31567d00991 100644 --- a/compiler/noirc_frontend/src/elaborator/mod.rs +++ b/compiler/noirc_frontend/src/elaborator/mod.rs @@ -10,6 +10,7 @@ use crate::{ hir_def::traits::ResolvedTraitBound, node_interner::GlobalValue, shared::Signedness, + token::SecondaryAttributeKind, usage_tracker::UsageTracker, }; use crate::{ @@ -47,7 +48,6 @@ use crate::{ ReferenceId, TraitId, TraitImplId, TypeAliasId, TypeId, }, parser::{ParserError, ParserErrorReason}, - token::SecondaryAttribute, }; mod comptime; @@ -1162,8 +1162,7 @@ impl<'context> Elaborator<'context> { }); self.run_lint(|_| lints::oracle_not_marked_unconstrained(func, modifiers).map(Into::into)); self.run_lint(|elaborator| { - lints::low_level_function_outside_stdlib(func, modifiers, elaborator.crate_id) - .map(Into::into) + lints::low_level_function_outside_stdlib(modifiers, elaborator.crate_id).map(Into::into) }); } @@ -1995,10 +1994,14 @@ impl<'context> Elaborator<'context> { let location = let_stmt.pattern.location(); - if !self.in_contract() - && let_stmt.attributes.iter().any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) - { - self.push_err(ResolverError::AbiAttributeOutsideContract { location }); + if !self.in_contract() { + for attr in &let_stmt.attributes { + if matches!(attr.kind, SecondaryAttributeKind::Abi(_)) { + self.push_err(ResolverError::AbiAttributeOutsideContract { + location: attr.location, + }); + } + } } if !let_stmt.comptime && matches!(let_stmt.pattern, Pattern::Mutable(..)) { diff --git a/compiler/noirc_frontend/src/elaborator/statements.rs b/compiler/noirc_frontend/src/elaborator/statements.rs index 7cb25aeeb6d..3ebefa7fd0f 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.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/elaborator/types.rs b/compiler/noirc_frontend/src/elaborator/types.rs index 27f5bbd2459..a1ca171e95a 100644 --- a/compiler/noirc_frontend/src/elaborator/types.rs +++ b/compiler/noirc_frontend/src/elaborator/types.rs @@ -37,7 +37,7 @@ use crate::{ }, shared::Signedness, signed_field::SignedField, - token::SecondaryAttribute, + token::SecondaryAttributeKind, }; use super::{ @@ -318,7 +318,7 @@ impl Elaborator<'_> { .interner .type_attributes(&data_type.borrow().id) .iter() - .any(|attr| matches!(attr, SecondaryAttribute::Abi(_))) + .any(|attr| matches!(attr.kind, SecondaryAttributeKind::Abi(_))) { self.push_err(ResolverError::AbiAttributeOutsideContract { location: data_type.borrow().name.location(), diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs index 9d620d68677..4610a5a595d 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter.rs @@ -237,8 +237,8 @@ impl<'local, 'interner> Interpreter<'local, 'interner> { location: Location, ) -> IResult { let attributes = self.elaborator.interner.function_attributes(&function); - let func_attrs = attributes.function() - .expect("all builtin functions must contain a function attribute which contains the opcode which it links to"); + let func_attrs = &attributes.function() + .expect("all builtin functions must contain a function attribute which contains the opcode which it links to").kind; if let Some(builtin) = func_attrs.builtin() { self.call_builtin(builtin.clone().as_str(), arguments, return_type, location) diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs index c591baf9141..688be1eddcc 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin.rs @@ -2500,7 +2500,7 @@ fn function_def_has_named_attribute( let modifiers = interner.function_modifiers(&func_id); if let Some(attribute) = modifiers.attributes.function() { - if name == attribute.name() { + if name == attribute.kind.name() { return Ok(Value::Bool(true)); } } diff --git a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs index 4da6be3d818..3a37bf03aba 100644 --- a/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs +++ b/compiler/noirc_frontend/src/hir/comptime/interpreter/builtin/builtin_helpers.rs @@ -12,7 +12,7 @@ use crate::hir::def_collector::dc_crate::CompilationError; use crate::hir_def::expr::HirExpression; use crate::lexer::Lexer; use crate::parser::{Parser, ParserError}; -use crate::token::{LocatedToken, MetaAttributeName}; +use crate::token::{LocatedToken, MetaAttributeName, SecondaryAttributeKind}; use crate::{DataType, Kind, Shared}; use crate::{ QuotedType, Type, @@ -596,13 +596,19 @@ fn secondary_attribute_name( attribute: &SecondaryAttribute, interner: &NodeInterner, ) -> Option { - match attribute { - SecondaryAttribute::Deprecated(_) => Some("deprecated".to_string()), - SecondaryAttribute::ContractLibraryMethod => Some("contract_library_method".to_string()), - SecondaryAttribute::Export => Some("export".to_string()), - SecondaryAttribute::Field(_) => Some("field".to_string()), - SecondaryAttribute::Tag(custom) => custom.name(), - SecondaryAttribute::Meta(meta) => match &meta.name { + match &attribute.kind { + SecondaryAttributeKind::Deprecated(_) => Some("deprecated".to_string()), + SecondaryAttributeKind::ContractLibraryMethod => { + Some("contract_library_method".to_string()) + } + SecondaryAttributeKind::Export => Some("export".to_string()), + SecondaryAttributeKind::Field(_) => Some("field".to_string()), + SecondaryAttributeKind::Tag(contents) => { + let mut lexer = Lexer::new_with_dummy_file(contents); + let token = lexer.next()?.ok()?; + if let Token::Ident(ident) = token.into_token() { Some(ident) } else { None } + } + SecondaryAttributeKind::Meta(meta) => match &meta.name { MetaAttributeName::Path(path) => Some(path.last_name().to_string()), MetaAttributeName::Resolved(expr_id) => { let HirExpression::Ident(ident, _) = interner.expression(expr_id) else { @@ -611,10 +617,10 @@ fn secondary_attribute_name( interner.try_definition(ident.id).map(|def| def.name.clone()) } }, - SecondaryAttribute::Abi(_) => Some("abi".to_string()), - SecondaryAttribute::Varargs => Some("varargs".to_string()), - SecondaryAttribute::UseCallersScope => Some("use_callers_scope".to_string()), - SecondaryAttribute::Allow(_) => Some("allow".to_string()), + SecondaryAttributeKind::Abi(_) => Some("abi".to_string()), + SecondaryAttributeKind::Varargs => Some("varargs".to_string()), + SecondaryAttributeKind::UseCallersScope => Some("use_callers_scope".to_string()), + SecondaryAttributeKind::Allow(_) => Some("allow".to_string()), } } 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 ba74efef93e..c05cd203ad6 100644 --- a/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs +++ b/compiler/noirc_frontend/src/hir/def_collector/dc_mod.rs @@ -1414,7 +1414,7 @@ pub(crate) fn collect_global( let global = global.item; let name = global.pattern.name_ident().clone(); - let is_abi = global.attributes.iter().any(|attribute| attribute.is_abi()); + let is_abi = global.attributes.iter().any(|attribute| attribute.kind.is_abi()); let global_id = interner.push_empty_global( name.clone(), diff --git a/compiler/noirc_frontend/src/hir/def_map/mod.rs b/compiler/noirc_frontend/src/hir/def_map/mod.rs index 58b87628f11..6014ba8d2ee 100644 --- a/compiler/noirc_frontend/src/hir/def_map/mod.rs +++ b/compiler/noirc_frontend/src/hir/def_map/mod.rs @@ -5,7 +5,7 @@ use crate::hir::def_collector::dc_crate::{CompilationError, DefCollector}; use crate::node_interner::{FuncId, NodeInterner}; use crate::parse_program; use crate::parser::{ParsedModule, ParserError}; -use crate::token::{FunctionAttribute, FuzzingScope, TestScope}; +use crate::token::{FunctionAttributeKind, FuzzingScope, TestScope}; use fm::{FileId, FileManager}; use noirc_arena::{Arena, Index}; use noirc_errors::Location; @@ -202,8 +202,8 @@ impl CrateDefMap { module.value_definitions().filter_map(|id| { if let Some(func_id) = id.as_function() { let attributes = interner.function_attributes(&func_id); - match attributes.function() { - Some(FunctionAttribute::Test(scope)) => { + match attributes.function().map(|attr| &attr.kind) { + Some(FunctionAttributeKind::Test(scope)) => { let location = interner.function_meta(&func_id).name.location; Some(TestFunction::new(func_id, scope.clone(), location)) } @@ -226,8 +226,8 @@ impl CrateDefMap { module.value_definitions().filter_map(|id| { if let Some(func_id) = id.as_function() { let attributes = interner.function_attributes(&func_id); - match attributes.function() { - Some(FunctionAttribute::FuzzingHarness(scope)) => { + match attributes.function().map(|attr| &attr.kind) { + Some(FunctionAttributeKind::FuzzingHarness(scope)) => { let location = interner.function_meta(&func_id).name.location; Some(FuzzingHarness::new(func_id, scope.clone(), location)) } diff --git a/compiler/noirc_frontend/src/hir/resolution/errors.rs b/compiler/noirc_frontend/src/hir/resolution/errors.rs index f58b82e19a0..00b3c08cf21 100644 --- a/compiler/noirc_frontend/src/hir/resolution/errors.rs +++ b/compiler/noirc_frontend/src/hir/resolution/errors.rs @@ -85,9 +85,9 @@ pub enum ResolverError { #[error( "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library" )] - LowLevelFunctionOutsideOfStdlib { ident: Ident }, + LowLevelFunctionOutsideOfStdlib { location: Location }, #[error("Usage of the `#[oracle]` function attribute is only valid on unconstrained functions")] - OracleMarkedAsConstrained { ident: Ident }, + OracleMarkedAsConstrained { ident: Ident, location: Location }, #[error("Oracle functions cannot be called directly from constrained functions")] UnconstrainedOracleReturnToConstrained { location: Location }, #[error("Dependency cycle found, '{item}' recursively depends on itself: {cycle} ")] @@ -121,9 +121,9 @@ pub enum ResolverError { #[error("Self-referential types are not supported")] SelfReferentialType { location: Location }, #[error("#[no_predicates] attribute is only allowed on constrained functions")] - NoPredicatesAttributeOnUnconstrained { ident: Ident }, + NoPredicatesAttributeOnUnconstrained { ident: Ident, location: Location }, #[error("#[fold] attribute is only allowed on constrained functions")] - FoldAttributeOnUnconstrained { ident: Ident }, + FoldAttributeOnUnconstrained { ident: Ident, location: Location }, #[error("expected type, found numeric generic parameter")] NumericGenericUsedForType { name: String, location: Location }, #[error("Invalid array length construction")] @@ -264,17 +264,17 @@ impl ResolverError { | ResolverError::UnexpectedItemInPattern { location, .. } | ResolverError::NoSuchMethodInTrait { location, .. } | ResolverError::VariableAlreadyDefinedInPattern { new_location: location, .. } - | ResolverError::NonU32Index { location } => *location, + | ResolverError::NonU32Index { location } + | ResolverError::NoPredicatesAttributeOnUnconstrained { location, .. } + | ResolverError::FoldAttributeOnUnconstrained { location, .. } + | ResolverError::OracleMarkedAsConstrained { location, .. } + | ResolverError::LowLevelFunctionOutsideOfStdlib { location } => *location, ResolverError::UnusedVariable { ident } | ResolverError::UnusedItem { ident, .. } | ResolverError::DuplicateField { field: ident } | ResolverError::NoSuchField { field: ident, .. } | ResolverError::UnnecessaryPub { ident, .. } | ResolverError::NecessaryPub { ident } - | ResolverError::LowLevelFunctionOutsideOfStdlib { ident } - | ResolverError::OracleMarkedAsConstrained { ident } - | ResolverError::NoPredicatesAttributeOnUnconstrained { ident } - | ResolverError::FoldAttributeOnUnconstrained { ident } | ResolverError::UnconstrainedTypeParameter { ident } => ident.location(), ResolverError::ArrayLengthInterpreter { error } => error.location(), ResolverError::PathResolutionError(path_resolution_error) => { @@ -500,16 +500,20 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *location, ) }, - ResolverError::LowLevelFunctionOutsideOfStdlib { ident } => Diagnostic::simple_error( + ResolverError::LowLevelFunctionOutsideOfStdlib { location } => Diagnostic::simple_error( "Definition of low-level function outside of standard library".into(), "Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library".into(), - ident.location(), - ), - ResolverError::OracleMarkedAsConstrained { ident } => Diagnostic::simple_error( - error.to_string(), - "Oracle functions must have the `unconstrained` keyword applied".into(), - ident.location(), + *location, ), + ResolverError::OracleMarkedAsConstrained { ident, location } => { + let mut diagnostic = Diagnostic::simple_error( + error.to_string(), + String::new(), + *location, + ); + diagnostic.add_secondary("Oracle functions must have the `unconstrained` keyword applied".into(), ident.location()); + diagnostic + }, ResolverError::UnconstrainedOracleReturnToConstrained { location } => Diagnostic::simple_error( error.to_string(), "This oracle call must be wrapped in a call to another unconstrained function before being returned to a constrained runtime".into(), @@ -610,21 +614,21 @@ impl<'a> From<&'a ResolverError> for Diagnostic { *location, ) }, - ResolverError::NoPredicatesAttributeOnUnconstrained { ident } => { + ResolverError::NoPredicatesAttributeOnUnconstrained { ident, location } => { let mut diag = Diagnostic::simple_error( format!("misplaced #[no_predicates] attribute on unconstrained function {ident}. Only allowed on constrained functions"), "misplaced #[no_predicates] attribute".to_string(), - ident.location(), + *location, ); diag.add_note("The `#[no_predicates]` attribute specifies to the compiler whether it should diverge from auto-inlining constrained functions".to_owned()); diag } - ResolverError::FoldAttributeOnUnconstrained { ident } => { + ResolverError::FoldAttributeOnUnconstrained { ident, location } => { let mut diag = Diagnostic::simple_error( format!("misplaced #[fold] attribute on unconstrained function {ident}. Only allowed on constrained functions"), "misplaced #[fold] attribute".to_string(), - ident.location(), + *location, ); diag.add_note("The `#[fold]` attribute specifies whether a constrained function should be treated as a separate circuit rather than inlined into the program entry point".to_owned()); diff --git a/compiler/noirc_frontend/src/lexer/token.rs b/compiler/noirc_frontend/src/lexer/token.rs index c24c2268f0b..3208c7ccad5 100644 --- a/compiler/noirc_frontend/src/lexer/token.rs +++ b/compiler/noirc_frontend/src/lexer/token.rs @@ -10,8 +10,6 @@ use crate::{ }, }; -use super::Lexer; - /// Represents a token in noir's grammar - a word, number, /// or symbol that can be used in noir's syntax. This is the /// smallest unit of grammar. A parser may (will) decide to parse @@ -800,15 +798,18 @@ impl Attributes { /// This is useful for finding out if we should compile a contract method /// as an entry point or not. pub fn has_contract_library_method(&self) -> bool { - self.has_secondary_attr(&SecondaryAttribute::ContractLibraryMethod) + self.has_secondary_attr(&SecondaryAttributeKind::ContractLibraryMethod) } pub fn is_test_function(&self) -> bool { - matches!(self.function(), Some(FunctionAttribute::Test(_))) + matches!(self.function().map(|attr| &attr.kind), Some(FunctionAttributeKind::Test(_))) } pub fn is_fuzzing_harness(&self) -> bool { - matches!(self.function(), Some(FunctionAttribute::FuzzingHarness(_))) + matches!( + self.function().map(|attr| &attr.kind), + Some(FunctionAttributeKind::FuzzingHarness(_)) + ) } /// True if these attributes mean the given function is an entry point function if it was @@ -822,15 +823,15 @@ impl Attributes { /// Returns note if a deprecated secondary attribute is found pub fn get_deprecated_note(&self) -> Option> { - self.secondary.iter().find_map(|attr| match attr { - SecondaryAttribute::Deprecated(note) => Some(note.clone()), + self.secondary.iter().find_map(|attr| match &attr.kind { + SecondaryAttributeKind::Deprecated(note) => Some(note.clone()), _ => None, }) } pub fn get_field_attribute(&self) -> Option { for secondary in &self.secondary { - if let SecondaryAttribute::Field(field) = secondary { + if let SecondaryAttributeKind::Field(field) = &secondary.kind { return Some(field.to_lowercase()); } } @@ -838,29 +839,29 @@ impl Attributes { } pub fn is_foldable(&self) -> bool { - self.function().is_some_and(|func_attribute| func_attribute.is_foldable()) + self.function().is_some_and(|func_attribute| func_attribute.kind.is_foldable()) } pub fn is_no_predicates(&self) -> bool { - self.function().is_some_and(|func_attribute| func_attribute.is_no_predicates()) + self.function().is_some_and(|func_attribute| func_attribute.kind.is_no_predicates()) } pub fn has_varargs(&self) -> bool { - self.has_secondary_attr(&SecondaryAttribute::Varargs) + self.has_secondary_attr(&SecondaryAttributeKind::Varargs) } pub fn has_use_callers_scope(&self) -> bool { - self.has_secondary_attr(&SecondaryAttribute::UseCallersScope) + self.has_secondary_attr(&SecondaryAttributeKind::UseCallersScope) } /// True if the function is marked with an `#[export]` attribute. pub fn has_export(&self) -> bool { - self.has_secondary_attr(&SecondaryAttribute::Export) + self.has_secondary_attr(&SecondaryAttributeKind::Export) } /// Check if secondary attributes contain a specific instance. - pub fn has_secondary_attr(&self, attr: &SecondaryAttribute) -> bool { - self.secondary.contains(attr) + pub fn has_secondary_attr(&self, kind: &SecondaryAttributeKind) -> bool { + self.secondary.iter().any(|attr| &attr.kind == kind) } } @@ -885,7 +886,15 @@ impl fmt::Display for Attribute { /// 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)] -pub enum FunctionAttribute { +pub struct FunctionAttribute { + pub kind: FunctionAttributeKind, + pub location: Location, +} + +/// 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)] +pub enum FunctionAttributeKind { Foreign(String), Builtin(String), Oracle(String), @@ -896,83 +905,85 @@ pub enum FunctionAttribute { FuzzingHarness(FuzzingScope), } -impl FunctionAttribute { +impl FunctionAttributeKind { pub fn builtin(&self) -> Option<&String> { match self { - FunctionAttribute::Builtin(name) => Some(name), + FunctionAttributeKind::Builtin(name) => Some(name), _ => None, } } pub fn foreign(&self) -> Option<&String> { match self { - FunctionAttribute::Foreign(name) => Some(name), + FunctionAttributeKind::Foreign(name) => Some(name), _ => None, } } pub fn oracle(&self) -> Option<&String> { match self { - FunctionAttribute::Oracle(name) => Some(name), + FunctionAttributeKind::Oracle(name) => Some(name), _ => None, } } - pub fn is_foreign(&self) -> bool { - matches!(self, FunctionAttribute::Foreign(_)) - } - pub fn is_oracle(&self) -> bool { - matches!(self, FunctionAttribute::Oracle(_)) + matches!(self, FunctionAttributeKind::Oracle(_)) } pub fn is_low_level(&self) -> bool { - matches!(self, FunctionAttribute::Foreign(_) | FunctionAttribute::Builtin(_)) + matches!(self, FunctionAttributeKind::Foreign(_) | FunctionAttributeKind::Builtin(_)) } pub fn is_foldable(&self) -> bool { - matches!(self, FunctionAttribute::Fold) + matches!(self, FunctionAttributeKind::Fold) } /// Check whether we have an `inline` attribute /// Although we also do not want to inline foldable functions, /// we keep the two attributes distinct for clarity. pub fn is_no_predicates(&self) -> bool { - matches!(self, FunctionAttribute::NoPredicates) + matches!(self, FunctionAttributeKind::NoPredicates) } /// Check whether we have an `inline_always` attribute /// This is used to indicate that a function should always be inlined /// regardless of the target runtime. pub fn is_inline_always(&self) -> bool { - matches!(self, FunctionAttribute::InlineAlways) + matches!(self, FunctionAttributeKind::InlineAlways) } pub fn name(&self) -> &'static str { match self { - FunctionAttribute::Foreign(_) => "foreign", - FunctionAttribute::Builtin(_) => "builtin", - FunctionAttribute::Oracle(_) => "oracle", - FunctionAttribute::Test(_) => "test", - FunctionAttribute::Fold => "fold", - FunctionAttribute::NoPredicates => "no_predicates", - FunctionAttribute::InlineAlways => "inline_always", - FunctionAttribute::FuzzingHarness(_) => "fuzz", + FunctionAttributeKind::Foreign(_) => "foreign", + FunctionAttributeKind::Builtin(_) => "builtin", + FunctionAttributeKind::Oracle(_) => "oracle", + FunctionAttributeKind::Test(_) => "test", + FunctionAttributeKind::Fold => "fold", + FunctionAttributeKind::NoPredicates => "no_predicates", + FunctionAttributeKind::InlineAlways => "inline_always", + FunctionAttributeKind::FuzzingHarness(_) => "fuzz", } } } impl fmt::Display for FunctionAttribute { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.fmt(f) + } +} + +impl fmt::Display for FunctionAttributeKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { - FunctionAttribute::Test(scope) => write!(f, "#[test{scope}]"), - FunctionAttribute::Foreign(k) => write!(f, "#[foreign({k})]"), - FunctionAttribute::Builtin(k) => write!(f, "#[builtin({k})]"), - FunctionAttribute::Oracle(k) => write!(f, "#[oracle({k})]"), - FunctionAttribute::Fold => write!(f, "#[fold]"), - FunctionAttribute::NoPredicates => write!(f, "#[no_predicates]"), - FunctionAttribute::InlineAlways => write!(f, "#[inline_always]"), - FunctionAttribute::FuzzingHarness(scope) => write!(f, "#[fuzz{scope}]"), + FunctionAttributeKind::Test(scope) => write!(f, "#[test{scope}]"), + FunctionAttributeKind::Foreign(k) => write!(f, "#[foreign({k})]"), + FunctionAttributeKind::Builtin(k) => write!(f, "#[builtin({k})]"), + FunctionAttributeKind::Oracle(k) => write!(f, "#[oracle({k})]"), + FunctionAttributeKind::Fold => write!(f, "#[fold]"), + FunctionAttributeKind::NoPredicates => write!(f, "#[no_predicates]"), + FunctionAttributeKind::InlineAlways => write!(f, "#[inline_always]"), + FunctionAttributeKind::FuzzingHarness(scope) => write!(f, "#[fuzz{scope}]"), } } } @@ -981,7 +992,13 @@ impl fmt::Display for FunctionAttribute { /// They are not able to change the `FunctionKind` and thus do not have direct impact on the IR output /// They are often consumed by libraries or used as notices for the developer #[derive(PartialEq, Eq, Debug, Clone)] -pub enum SecondaryAttribute { +pub struct SecondaryAttribute { + pub kind: SecondaryAttributeKind, + pub location: Location, +} + +#[derive(PartialEq, Eq, Debug, Clone)] +pub enum SecondaryAttributeKind { Deprecated(Option), // This is an attribute to specify that a function // is a helper method for a contract and should not be seen as @@ -991,7 +1008,7 @@ pub enum SecondaryAttribute { Field(String), /// A custom tag attribute: `#['foo]` - Tag(CustomAttribute), + Tag(String), /// An attribute expected to run a comptime function of the same name: `#[foo]` Meta(MetaAttribute), @@ -1010,38 +1027,44 @@ pub enum SecondaryAttribute { Allow(String), } -impl SecondaryAttribute { +impl SecondaryAttributeKind { pub(crate) fn is_allow_unused_variables(&self) -> bool { match self { - SecondaryAttribute::Allow(string) => string == "unused_variables", + SecondaryAttributeKind::Allow(string) => string == "unused_variables", _ => false, } } pub(crate) fn is_abi(&self) -> bool { - matches!(self, SecondaryAttribute::Abi(_)) + matches!(self, SecondaryAttributeKind::Abi(_)) } pub(crate) fn contents(&self) -> String { match self { - SecondaryAttribute::Deprecated(None) => "deprecated".to_string(), - SecondaryAttribute::Deprecated(Some(note)) => { + SecondaryAttributeKind::Deprecated(None) => "deprecated".to_string(), + SecondaryAttributeKind::Deprecated(Some(note)) => { format!("deprecated({note:?})") } - SecondaryAttribute::Tag(attribute) => format!("'{}", attribute.contents), - SecondaryAttribute::Meta(meta) => meta.to_string(), - SecondaryAttribute::ContractLibraryMethod => "contract_library_method".to_string(), - SecondaryAttribute::Export => "export".to_string(), - SecondaryAttribute::Field(k) => format!("field({k})"), - SecondaryAttribute::Abi(k) => format!("abi({k})"), - SecondaryAttribute::Varargs => "varargs".to_string(), - SecondaryAttribute::UseCallersScope => "use_callers_scope".to_string(), - SecondaryAttribute::Allow(k) => format!("allow({k})"), + SecondaryAttributeKind::Tag(contents) => format!("'{}", contents), + SecondaryAttributeKind::Meta(meta) => meta.to_string(), + SecondaryAttributeKind::ContractLibraryMethod => "contract_library_method".to_string(), + SecondaryAttributeKind::Export => "export".to_string(), + SecondaryAttributeKind::Field(k) => format!("field({k})"), + SecondaryAttributeKind::Abi(k) => format!("abi({k})"), + SecondaryAttributeKind::Varargs => "varargs".to_string(), + SecondaryAttributeKind::UseCallersScope => "use_callers_scope".to_string(), + SecondaryAttributeKind::Allow(k) => format!("allow({k})"), } } } impl fmt::Display for SecondaryAttribute { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.kind.fmt(f) + } +} + +impl fmt::Display for SecondaryAttributeKind { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { write!(f, "#[{}]", self.contents()) } @@ -1051,7 +1074,6 @@ impl fmt::Display for SecondaryAttribute { pub struct MetaAttribute { pub name: MetaAttributeName, pub arguments: Vec, - pub location: Location, } impl Display for MetaAttribute { @@ -1082,22 +1104,6 @@ impl Display for MetaAttributeName { } } } -#[derive(PartialEq, Eq, Hash, Debug, Clone, PartialOrd, Ord)] -pub struct CustomAttribute { - pub contents: String, - // The span of the entire attribute, including leading `#[` and trailing `]` - pub span: Span, - // The span for the attribute contents (what's inside `#[...]`) - pub contents_span: Span, -} - -impl CustomAttribute { - pub(crate) fn name(&self) -> Option { - let mut lexer = Lexer::new_with_dummy_file(&self.contents); - let token = lexer.next()?.ok()?; - if let Token::Ident(ident) = token.into_token() { Some(ident) } else { None } - } -} /// Note that `self` is not present - it is a contextual keyword rather than a true one as it is /// only special within `impl`s. Otherwise `self` functions as a normal identifier. diff --git a/compiler/noirc_frontend/src/monomorphization/ast.rs b/compiler/noirc_frontend/src/monomorphization/ast.rs index c897333f30c..02b9347af4b 100644 --- a/compiler/noirc_frontend/src/monomorphization/ast.rs +++ b/compiler/noirc_frontend/src/monomorphization/ast.rs @@ -6,15 +6,15 @@ use noirc_errors::{ debug_info::{DebugFunctions, DebugTypes, DebugVariables}, }; -use crate::shared::Visibility; use crate::{ ast::{BinaryOpKind, IntegerBitSize}, hir_def::expr::Constructor, shared::Signedness, signed_field::SignedField, - token::{Attributes, FunctionAttribute}, + token::Attributes, }; use crate::{hir_def::function::FunctionSignature, token::FmtStrFragment}; +use crate::{shared::Visibility, token::FunctionAttributeKind}; use serde::{Deserialize, Serialize}; use super::HirType; @@ -273,10 +273,12 @@ pub enum InlineType { impl From<&Attributes> for InlineType { fn from(attributes: &Attributes) -> Self { - attributes.function().map_or(InlineType::default(), |func_attribute| match func_attribute { - FunctionAttribute::Fold => InlineType::Fold, - FunctionAttribute::NoPredicates => InlineType::NoPredicates, - FunctionAttribute::InlineAlways => InlineType::InlineAlways, + attributes.function().map_or(InlineType::default(), |func_attribute| match &func_attribute + .kind + { + FunctionAttributeKind::Fold => InlineType::Fold, + FunctionAttributeKind::NoPredicates => InlineType::NoPredicates, + FunctionAttributeKind::InlineAlways => InlineType::InlineAlways, _ => InlineType::default(), }) } diff --git a/compiler/noirc_frontend/src/monomorphization/mod.rs b/compiler/noirc_frontend/src/monomorphization/mod.rs index 3a118094043..db379ae91a5 100644 --- a/compiler/noirc_frontend/src/monomorphization/mod.rs +++ b/compiler/noirc_frontend/src/monomorphization/mod.rs @@ -273,14 +273,14 @@ impl<'interner> Monomorphizer<'interner> { match self.interner.function_meta(&id).kind { FunctionKind::LowLevel => { let attribute = attributes.function().expect("all low level functions must contain a function attribute which contains the opcode which it links to"); - let opcode = attribute.foreign().expect( + let opcode = attribute.kind.foreign().expect( "ice: function marked as foreign, but attribute kind does not match this", ); Definition::LowLevel(opcode.to_string()) } FunctionKind::Builtin => { let attribute = attributes.function().expect("all builtin functions must contain a function attribute which contains the opcode which it links to"); - let opcode = attribute.builtin().expect( + let opcode = attribute.kind.builtin().expect( "ice: function marked as builtin, but attribute kind does not match this", ); Definition::Builtin(opcode.to_string()) @@ -292,7 +292,7 @@ impl<'interner> Monomorphizer<'interner> { } FunctionKind::Oracle => { let attribute = attributes.function().expect("all oracle functions must contain a function attribute which contains the opcode which it links to"); - let opcode = attribute.oracle().expect( + let opcode = attribute.kind.oracle().expect( "ice: function marked as builtin, but attribute kind does not match this", ); Definition::Oracle(opcode.to_string()) diff --git a/compiler/noirc_frontend/src/parser/parser/attributes.rs b/compiler/noirc_frontend/src/parser/parser/attributes.rs index 34c069c50bf..e86cec93497 100644 --- a/compiler/noirc_frontend/src/parser/parser/attributes.rs +++ b/compiler/noirc_frontend/src/parser/parser/attributes.rs @@ -5,9 +5,9 @@ use crate::lexer::errors::LexerErrorKind; use crate::parser::ParserErrorReason; use crate::parser::labels::ParsingRuleLabel; use crate::token::{ - Attribute, FunctionAttribute, FuzzingScope, MetaAttribute, MetaAttributeName, TestScope, Token, + Attribute, FunctionAttribute, FunctionAttributeKind, FuzzingScope, MetaAttribute, + MetaAttributeName, SecondaryAttribute, SecondaryAttributeKind, TestScope, Token, }; -use crate::token::{CustomAttribute, SecondaryAttribute}; use super::Parser; use super::parse_many::without_separator; @@ -105,8 +105,6 @@ impl Parser<'_> { } fn parse_tag_attribute(&mut self, start_location: Location) -> Attribute { - let contents_start_location = self.current_token_location; - let mut contents_location = contents_start_location; let mut contents = String::new(); let mut brackets_count = 1; // 1 because of the starting `#[` @@ -117,7 +115,6 @@ impl Parser<'_> { } else if self.at(Token::RightBracket) { brackets_count -= 1; if brackets_count == 0 { - contents_location = self.location_since(contents_start_location); self.bump(); break; } @@ -127,11 +124,10 @@ impl Parser<'_> { self.bump(); } - Attribute::Secondary(SecondaryAttribute::Tag(CustomAttribute { - contents, - span: self.location_since(start_location).span, - contents_span: contents_location.span, - })) + let location = self.location_since(start_location); + let kind = SecondaryAttributeKind::Tag(contents); + let attr = SecondaryAttribute { kind, location }; + Attribute::Secondary(attr) } fn parse_non_tag_attribute(&mut self, start_location: Location) -> Attribute { @@ -179,11 +175,10 @@ impl Parser<'_> { ) -> Attribute { let arguments = self.parse_arguments().unwrap_or_default(); self.skip_until_right_bracket(); - Attribute::Secondary(SecondaryAttribute::Meta(MetaAttribute { - name, - arguments, - location: self.location_since(start_location), - })) + let location = self.location_since(start_location); + let kind = SecondaryAttributeKind::Meta(MetaAttribute { name, arguments }); + let attr = SecondaryAttribute { kind, location }; + Attribute::Secondary(attr) } fn parse_ident_attribute_other_than_test_and_fuzz( @@ -193,65 +188,99 @@ impl Parser<'_> { ) -> Attribute { let arguments = self.parse_arguments().unwrap_or_default(); self.skip_until_right_bracket(); + let location = self.location_since(start_location); match ident.as_str() { "abi" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { - Attribute::Secondary(SecondaryAttribute::Abi(name)) + let kind = SecondaryAttributeKind::Abi(name); + let attr = SecondaryAttribute { kind, location }; + Attribute::Secondary(attr) }), "allow" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { - Attribute::Secondary(SecondaryAttribute::Allow(name)) + let kind = SecondaryAttributeKind::Allow(name); + let attr = SecondaryAttribute { kind, location }; + Attribute::Secondary(attr) }), "builtin" => { self.parse_single_name_attribute(ident, arguments, start_location, |name| { - Attribute::Function(FunctionAttribute::Builtin(name)) + let kind = FunctionAttributeKind::Builtin(name); + let attr = FunctionAttribute { kind, location }; + Attribute::Function(attr) }) } - "deprecated" => self.parse_deprecated_attribute(ident, arguments), + "deprecated" => { + let kind = self.parse_deprecated_attribute(ident, arguments); + let attr = SecondaryAttribute { kind, location }; + Attribute::Secondary(attr) + } "contract_library_method" => { - let attr = Attribute::Secondary(SecondaryAttribute::ContractLibraryMethod); + let kind = SecondaryAttributeKind::ContractLibraryMethod; + let attr = SecondaryAttribute { kind, location }; + let attr = Attribute::Secondary(attr); self.parse_no_args_attribute(ident, arguments, attr) } "export" => { - let attr = Attribute::Secondary(SecondaryAttribute::Export); + let kind = SecondaryAttributeKind::Export; + let attr = SecondaryAttribute { kind, location }; + let attr = Attribute::Secondary(attr); self.parse_no_args_attribute(ident, arguments, attr) } "field" => self.parse_single_name_attribute(ident, arguments, start_location, |name| { - Attribute::Secondary(SecondaryAttribute::Field(name)) + let kind = SecondaryAttributeKind::Field(name); + let attr = SecondaryAttribute { kind, location }; + Attribute::Secondary(attr) }), "fold" => { - let attr = Attribute::Function(FunctionAttribute::Fold); + let kind = FunctionAttributeKind::Fold; + let attr = FunctionAttribute { kind, location }; + let attr = Attribute::Function(attr); self.parse_no_args_attribute(ident, arguments, attr) } "foreign" => { self.parse_single_name_attribute(ident, arguments, start_location, |name| { - Attribute::Function(FunctionAttribute::Foreign(name)) + let kind = FunctionAttributeKind::Foreign(name); + let attr = FunctionAttribute { kind, location }; + Attribute::Function(attr) }) } "inline_always" => { - let attr = Attribute::Function(FunctionAttribute::InlineAlways); + let kind = FunctionAttributeKind::InlineAlways; + let attr = FunctionAttribute { kind, location }; + let attr = Attribute::Function(attr); self.parse_no_args_attribute(ident, arguments, attr) } "no_predicates" => { - let attr = Attribute::Function(FunctionAttribute::NoPredicates); + let kind = FunctionAttributeKind::NoPredicates; + let attr = FunctionAttribute { kind, location }; + let attr = Attribute::Function(attr); self.parse_no_args_attribute(ident, arguments, attr) } "oracle" => { self.parse_single_name_attribute(ident, arguments, start_location, |name| { - Attribute::Function(FunctionAttribute::Oracle(name)) + let kind = FunctionAttributeKind::Oracle(name); + let attr = FunctionAttribute { kind, location }; + Attribute::Function(attr) }) } "use_callers_scope" => { - let attr = Attribute::Secondary(SecondaryAttribute::UseCallersScope); + let kind = SecondaryAttributeKind::UseCallersScope; + let attr = SecondaryAttribute { kind, location }; + let attr = Attribute::Secondary(attr); self.parse_no_args_attribute(ident, arguments, attr) } "varargs" => { - let attr = Attribute::Secondary(SecondaryAttribute::Varargs); + let kind = SecondaryAttributeKind::Varargs; + let attr = SecondaryAttribute { kind, location }; + let attr = Attribute::Secondary(attr); self.parse_no_args_attribute(ident, arguments, attr) } - _ => Attribute::Secondary(SecondaryAttribute::Meta(MetaAttribute { - name: MetaAttributeName::Path(Path::from_ident(ident.clone())), - arguments, - location: self.location_since(start_location), - })), + _ => { + let kind = SecondaryAttributeKind::Meta(MetaAttribute { + name: MetaAttributeName::Path(Path::from_ident(ident.clone())), + arguments, + }); + let attr = SecondaryAttribute { kind, location }; + Attribute::Secondary(attr) + } } } @@ -259,9 +288,9 @@ impl Parser<'_> { &mut self, ident: &Ident, mut arguments: Vec, - ) -> Attribute { + ) -> SecondaryAttributeKind { if arguments.is_empty() { - return Attribute::Secondary(SecondaryAttribute::Deprecated(None)); + return SecondaryAttributeKind::Deprecated(None); } if arguments.len() > 1 { @@ -274,7 +303,7 @@ impl Parser<'_> { }, ident.location(), ); - return Attribute::Secondary(SecondaryAttribute::Deprecated(None)); + return SecondaryAttributeKind::Deprecated(None); } let argument = arguments.remove(0); @@ -283,10 +312,10 @@ impl Parser<'_> { ParserErrorReason::DeprecatedAttributeExpectsAStringArgument, argument.location, ); - return Attribute::Secondary(SecondaryAttribute::Deprecated(None)); + return SecondaryAttributeKind::Deprecated(None); }; - Attribute::Secondary(SecondaryAttribute::Deprecated(Some(message))) + SecondaryAttributeKind::Deprecated(Some(message)) } fn parse_test_attribute(&mut self, start_location: Location) -> Attribute { @@ -327,7 +356,10 @@ impl Parser<'_> { TestScope::None }; - Attribute::Function(FunctionAttribute::Test(scope)) + let location = self.location_since(start_location); + let kind = FunctionAttributeKind::Test(scope); + let attr = FunctionAttribute { kind, location }; + Attribute::Function(attr) } fn parse_fuzz_attribute(&mut self, start_location: Location) -> Attribute { @@ -363,7 +395,10 @@ impl Parser<'_> { FuzzingScope::None }; - Attribute::Function(FunctionAttribute::FuzzingHarness(scope)) + let location = self.location_since(start_location); + let kind = FunctionAttributeKind::FuzzingHarness(scope); + let attr = FunctionAttribute { kind, location }; + Attribute::Function(attr) } fn parse_single_name_attribute( @@ -451,186 +486,194 @@ impl Parser<'_> { #[cfg(test)] mod tests { - use noirc_errors::Span; - use crate::{ parser::{Parser, parser::tests::expect_no_errors}, - token::{Attribute, FunctionAttribute, SecondaryAttribute, TestScope}, + token::{Attribute, FunctionAttributeKind, SecondaryAttributeKind, TestScope}, }; - fn parse_inner_secondary_attribute_no_errors(src: &str, expected: SecondaryAttribute) { + fn parse_inner_secondary_attribute_no_errors(src: &str, expected: SecondaryAttributeKind) { let mut parser = Parser::for_str_with_dummy_file(src); let attribute = parser.parse_inner_attribute(); expect_no_errors(&parser.errors); - assert_eq!(attribute.unwrap(), expected); + assert_eq!(attribute.unwrap().kind, expected); + } + + fn parse_function_attribute_no_errors(src: &str, expected: FunctionAttributeKind) { + let mut parser = Parser::for_str_with_dummy_file(src); + let (attribute, _span) = parser.parse_attribute().unwrap(); + expect_no_errors(&parser.errors); + let Attribute::Function(attribute) = attribute else { + panic!("Expected function attribute"); + }; + assert_eq!(attribute.kind, expected); } - fn parse_attribute_no_errors(src: &str, expected: Attribute) { + fn parse_secondary_attribute_no_errors(src: &str, expected: SecondaryAttributeKind) { let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); - assert_eq!(attribute, expected); + let Attribute::Secondary(attribute) = attribute else { + panic!("Expected secondary attribute"); + }; + assert_eq!(attribute.kind, expected); } #[test] fn parses_inner_attribute_as_tag() { let src = "#!['hello]"; let mut parser = Parser::for_str_with_dummy_file(src); - let Some(SecondaryAttribute::Tag(custom)) = parser.parse_inner_attribute() else { + let SecondaryAttributeKind::Tag(contents) = parser.parse_inner_attribute().unwrap().kind + else { panic!("Expected inner tag attribute"); }; expect_no_errors(&parser.errors); - assert_eq!(custom.contents, "hello"); - assert_eq!(custom.span, Span::from(0..src.len() as u32)); - assert_eq!(custom.contents_span, Span::from(4..src.len() as u32 - 1)); + assert_eq!(contents, "hello"); } #[test] fn parses_inner_attribute_as_tag_with_nested_brackets() { let src = "#!['hello[1]]"; let mut parser = Parser::for_str_with_dummy_file(src); - let Some(SecondaryAttribute::Tag(custom)) = parser.parse_inner_attribute() else { + let SecondaryAttributeKind::Tag(contents) = parser.parse_inner_attribute().unwrap().kind + else { panic!("Expected inner tag attribute"); }; expect_no_errors(&parser.errors); - assert_eq!(custom.contents, "hello[1]"); + assert_eq!(contents, "hello[1]"); } #[test] fn parses_inner_attribute_deprecated() { let src = "#![deprecated]"; - let expected = SecondaryAttribute::Deprecated(None); + let expected = SecondaryAttributeKind::Deprecated(None); parse_inner_secondary_attribute_no_errors(src, expected); } #[test] fn parses_inner_attribute_deprecated_with_message() { let src = "#![deprecated(\"use something else\")]"; - let expected = SecondaryAttribute::Deprecated(Some("use something else".to_string())); + let expected = SecondaryAttributeKind::Deprecated(Some("use something else".to_string())); parse_inner_secondary_attribute_no_errors(src, expected); } #[test] fn parses_inner_attribute_contract_library_method() { let src = "#![contract_library_method]"; - let expected = SecondaryAttribute::ContractLibraryMethod; + let expected = SecondaryAttributeKind::ContractLibraryMethod; parse_inner_secondary_attribute_no_errors(src, expected); } #[test] fn parses_inner_attribute_export() { let src = "#![export]"; - let expected = SecondaryAttribute::Export; + let expected = SecondaryAttributeKind::Export; parse_inner_secondary_attribute_no_errors(src, expected); } #[test] fn parses_inner_attribute_varargs() { let src = "#![varargs]"; - let expected = SecondaryAttribute::Varargs; + let expected = SecondaryAttributeKind::Varargs; parse_inner_secondary_attribute_no_errors(src, expected); } #[test] fn parses_inner_attribute_use_callers_scope() { let src = "#![use_callers_scope]"; - let expected = SecondaryAttribute::UseCallersScope; + let expected = SecondaryAttributeKind::UseCallersScope; parse_inner_secondary_attribute_no_errors(src, expected); } #[test] fn parses_attribute_abi() { let src = "#[abi(foo)]"; - let expected = Attribute::Secondary(SecondaryAttribute::Abi("foo".to_string())); - parse_attribute_no_errors(src, expected); + let expected = SecondaryAttributeKind::Abi("foo".to_string()); + parse_secondary_attribute_no_errors(src, expected); } #[test] fn parses_attribute_foreign() { let src = "#[foreign(foo)]"; - let expected = Attribute::Function(FunctionAttribute::Foreign("foo".to_string())); - parse_attribute_no_errors(src, expected); + let expected = FunctionAttributeKind::Foreign("foo".to_string()); + parse_function_attribute_no_errors(src, expected); } #[test] fn parses_attribute_builtin() { let src = "#[builtin(foo)]"; - let expected = Attribute::Function(FunctionAttribute::Builtin("foo".to_string())); - parse_attribute_no_errors(src, expected); + let expected = FunctionAttributeKind::Builtin("foo".to_string()); + parse_function_attribute_no_errors(src, expected); } #[test] fn parses_attribute_oracle() { let src = "#[oracle(foo)]"; - let expected = Attribute::Function(FunctionAttribute::Oracle("foo".to_string())); - parse_attribute_no_errors(src, expected); + let expected = FunctionAttributeKind::Oracle("foo".to_string()); + parse_function_attribute_no_errors(src, expected); } #[test] fn parses_attribute_fold() { let src = "#[fold]"; - let expected = Attribute::Function(FunctionAttribute::Fold); - parse_attribute_no_errors(src, expected); + let expected = FunctionAttributeKind::Fold; + parse_function_attribute_no_errors(src, expected); } #[test] fn parses_attribute_no_predicates() { let src = "#[no_predicates]"; - let expected = Attribute::Function(FunctionAttribute::NoPredicates); - parse_attribute_no_errors(src, expected); + let expected = FunctionAttributeKind::NoPredicates; + parse_function_attribute_no_errors(src, expected); } #[test] fn parses_attribute_inline_always() { let src = "#[inline_always]"; - let expected = Attribute::Function(FunctionAttribute::InlineAlways); - parse_attribute_no_errors(src, expected); + let expected = FunctionAttributeKind::InlineAlways; + parse_function_attribute_no_errors(src, expected); } #[test] fn parses_attribute_field() { let src = "#[field(bn254)]"; - let expected = Attribute::Secondary(SecondaryAttribute::Field("bn254".to_string())); - parse_attribute_no_errors(src, expected); + let expected = SecondaryAttributeKind::Field("bn254".to_string()); + parse_secondary_attribute_no_errors(src, expected); } #[test] fn parses_attribute_field_with_integer() { let src = "#[field(23)]"; - let expected = Attribute::Secondary(SecondaryAttribute::Field("23".to_string())); - parse_attribute_no_errors(src, expected); + let expected = SecondaryAttributeKind::Field("23".to_string()); + parse_secondary_attribute_no_errors(src, expected); } #[test] fn parses_attribute_allow() { let src = "#[allow(unused_vars)]"; - let expected = Attribute::Secondary(SecondaryAttribute::Allow("unused_vars".to_string())); - parse_attribute_no_errors(src, expected); + let expected = SecondaryAttributeKind::Allow("unused_vars".to_string()); + parse_secondary_attribute_no_errors(src, expected); } #[test] fn parses_attribute_test_no_scope() { let src = "#[test]"; - let expected = Attribute::Function(FunctionAttribute::Test(TestScope::None)); - parse_attribute_no_errors(src, expected); + let expected = FunctionAttributeKind::Test(TestScope::None); + parse_function_attribute_no_errors(src, expected); } #[test] fn parses_attribute_test_should_fail() { let src = "#[test(should_fail)]"; - let expected = Attribute::Function(FunctionAttribute::Test(TestScope::ShouldFailWith { - reason: None, - })); - parse_attribute_no_errors(src, expected); + let expected = FunctionAttributeKind::Test(TestScope::ShouldFailWith { reason: None }); + parse_function_attribute_no_errors(src, expected); } #[test] fn parses_attribute_test_should_fail_with() { let src = "#[test(should_fail_with = \"reason\")]"; - let expected = Attribute::Function(FunctionAttribute::Test(TestScope::ShouldFailWith { - reason: Some("reason".to_string()), - })); - parse_attribute_no_errors(src, expected); + let reason = Some("reason".to_string()); + let expected = FunctionAttributeKind::Test(TestScope::ShouldFailWith { reason }); + parse_function_attribute_no_errors(src, expected); } #[test] @@ -639,7 +682,10 @@ mod tests { let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); - let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { + let Attribute::Secondary(attribute) = attribute else { + panic!("Expected secondary attribute"); + }; + let SecondaryAttributeKind::Meta(meta) = attribute.kind else { panic!("Expected meta attribute"); }; assert_eq!(meta.name.to_string(), "foo"); @@ -652,7 +698,10 @@ mod tests { let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); - let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { + let Attribute::Secondary(attribute) = attribute else { + panic!("Expected secondary attribute"); + }; + let SecondaryAttributeKind::Meta(meta) = attribute.kind else { panic!("Expected meta attribute"); }; assert_eq!(meta.name.to_string(), "dep"); @@ -665,7 +714,10 @@ mod tests { let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); - let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { + let Attribute::Secondary(attribute) = attribute else { + panic!("Expected secondary attribute"); + }; + let SecondaryAttributeKind::Meta(meta) = attribute.kind else { panic!("Expected meta attribute"); }; assert_eq!(meta.name.to_string(), "foo"); @@ -679,7 +731,10 @@ mod tests { let mut parser = Parser::for_str_with_dummy_file(src); let (attribute, _span) = parser.parse_attribute().unwrap(); expect_no_errors(&parser.errors); - let Attribute::Secondary(SecondaryAttribute::Meta(meta)) = attribute else { + let Attribute::Secondary(attribute) = attribute else { + panic!("Expected secondary attribute"); + }; + let SecondaryAttributeKind::Meta(meta) = attribute.kind else { panic!("Expected meta attribute"); }; assert_eq!(meta.name.to_string(), "foo::bar"); @@ -696,9 +751,15 @@ mod tests { assert_eq!(attributes.len(), 2); let (attr, _) = attributes.remove(0); - assert!(matches!(attr, Attribute::Function(FunctionAttribute::Test(TestScope::None)))); + let Attribute::Function(attr) = attr else { + panic!("Expected function attribute"); + }; + assert!(matches!(attr.kind, FunctionAttributeKind::Test(TestScope::None))); let (attr, _) = attributes.remove(0); - assert!(matches!(attr, Attribute::Secondary(SecondaryAttribute::Deprecated(None)))); + let Attribute::Secondary(attr) = attr else { + panic!("Expected secondary attribute"); + }; + assert!(matches!(attr.kind, SecondaryAttributeKind::Deprecated(None))); } } diff --git a/compiler/noirc_frontend/src/tests.rs b/compiler/noirc_frontend/src/tests.rs index 00a06b0f750..65ea464d34a 100644 --- a/compiler/noirc_frontend/src/tests.rs +++ b/compiler/noirc_frontend/src/tests.rs @@ -1398,12 +1398,11 @@ fn ban_mutable_globals() { #[named] #[test] fn deny_inline_attribute_on_unconstrained() { - // TODO: improve the error location let src = r#" #[no_predicates] + ^^^^^^^^^^^^^^^^ misplaced #[no_predicates] attribute on unconstrained function foo. Only allowed on constrained functions + ~~~~~~~~~~~~~~~~ misplaced #[no_predicates] attribute unconstrained pub fn foo(x: Field, y: Field) { - ^^^ misplaced #[no_predicates] attribute on unconstrained function foo. Only allowed on constrained functions - ~~~ misplaced #[no_predicates] attribute assert(x != y); } "#; @@ -1413,18 +1412,44 @@ fn deny_inline_attribute_on_unconstrained() { #[named] #[test] fn deny_fold_attribute_on_unconstrained() { - // TODO: improve the error location let src = r#" #[fold] + ^^^^^^^ misplaced #[fold] attribute on unconstrained function foo. Only allowed on constrained functions + ~~~~~~~ misplaced #[fold] attribute unconstrained pub fn foo(x: Field, y: Field) { - ^^^ misplaced #[fold] attribute on unconstrained function foo. Only allowed on constrained functions - ~~~ misplaced #[fold] attribute assert(x != y); } "#; check_errors!(src); } +#[named] +#[test] +fn deny_oracle_attribute_on_non_unconstrained() { + let src = r#" + #[oracle(foo)] + ^^^^^^^^^^^^^^ Usage of the `#[oracle]` function attribute is only valid on unconstrained functions + pub fn foo(x: Field, y: Field) { + ~~~ Oracle functions must have the `unconstrained` keyword applied + assert(x != y); + } + "#; + check_errors!(src); +} + +#[named] +#[test] +fn deny_abi_attribute_outside_of_contract() { + let src = r#" + + #[abi(foo)] + ^^^^^^^^^^^ #[abi(tag)] attributes can only be used in contracts + ~~~~~~~~~~~ misplaced #[abi(tag)] attribute + global foo: Field = 1; + "#; + check_errors!(src); +} + #[named] #[test] fn specify_function_types_with_turbofish() { diff --git a/test_programs/compile_failure/builtin_function_declaration/stderr.txt b/test_programs/compile_failure/builtin_function_declaration/stderr.txt index 3bbff4e21ad..f64dec37615 100644 --- a/test_programs/compile_failure/builtin_function_declaration/stderr.txt +++ b/test_programs/compile_failure/builtin_function_declaration/stderr.txt @@ -1,8 +1,8 @@ error: Definition of low-level function outside of standard library - ┌─ src/main.nr:5:4 + ┌─ src/main.nr:4:1 │ -5 │ fn to_le_bits(_x: Field) -> [u1; N] {} - │ ---------- Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library +4 │ #[builtin(to_le_bits)] + │ ---------------------- Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library │ Aborting due to 1 previous error diff --git a/test_programs/compile_failure/foreign_function_declaration/stderr.txt b/test_programs/compile_failure/foreign_function_declaration/stderr.txt index dfc0b786c99..ebfb6ccaeea 100644 --- a/test_programs/compile_failure/foreign_function_declaration/stderr.txt +++ b/test_programs/compile_failure/foreign_function_declaration/stderr.txt @@ -6,10 +6,10 @@ error: Type provided when a numeric generic was expected │ error: Definition of low-level function outside of standard library - ┌─ src/main.nr:6:4 + ┌─ src/main.nr:5:1 │ -6 │ fn my_pedersen_hash(_input: [Field; N]) -> Field {} - │ ---------------- Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library +5 │ #[foreign(pedersen_hash)] + │ ------------------------- Usage of the `#[foreign]` or `#[builtin]` function attributes are not allowed outside of the Noir standard library │ -Aborting due to 2 previous errors \ No newline at end of file +Aborting due to 2 previous errors diff --git a/test_programs/compile_failure/multiple_primary_attributes_fail/stderr.txt b/test_programs/compile_failure/multiple_primary_attributes_fail/stderr.txt index 3a02690879b..b737573b8c0 100644 --- a/test_programs/compile_failure/multiple_primary_attributes_fail/stderr.txt +++ b/test_programs/compile_failure/multiple_primary_attributes_fail/stderr.txt @@ -1,6 +1,9 @@ error: Usage of the `#[oracle]` function attribute is only valid on unconstrained functions - ┌─ src/main.nr:4:4 + ┌─ src/main.nr:2:1 │ +2 │ #[oracle(oracleName)] + │ --------------------- +3 │ #[builtin(builtinName)] 4 │ fn main(x: Field) -> pub Field { │ ---- Oracle functions must have the `unconstrained` keyword applied │ @@ -12,4 +15,4 @@ error: Multiple primary attributes found. Only one function attribute is allowed │ ----------------------- │ -Aborting due to 2 previous errors \ No newline at end of file +Aborting due to 2 previous errors diff --git a/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/Nargo.toml b/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/Nargo.toml new file mode 100644 index 00000000000..f2324491f69 --- /dev/null +++ b/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_deny_abi_attribute_outside_of_contract" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/src/main.nr b/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/src/main.nr new file mode 100644 index 00000000000..9bae055408a --- /dev/null +++ b/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/src/main.nr @@ -0,0 +1,5 @@ + + + #[abi(foo)] + global foo: Field = 1; + \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/src_hash.txt b/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/src_hash.txt new file mode 100644 index 00000000000..7e80b21bc3a --- /dev/null +++ b/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/src_hash.txt @@ -0,0 +1 @@ +619298329797893628 \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/stderr.txt b/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/stderr.txt new file mode 100644 index 00000000000..667ec2d0ae1 --- /dev/null +++ b/test_programs/compile_failure/noirc_frontend_tests_deny_abi_attribute_outside_of_contract/stderr.txt @@ -0,0 +1,8 @@ +error: #[abi(tag)] attributes can only be used in contracts + ┌─ src/main.nr:3:9 + │ +3 │ #[abi(foo)] + │ ----------- misplaced #[abi(tag)] attribute + │ + +Aborting due to 1 previous error diff --git a/test_programs/compile_failure/noirc_frontend_tests_deny_fold_attribute_on_unconstrained/stderr.txt b/test_programs/compile_failure/noirc_frontend_tests_deny_fold_attribute_on_unconstrained/stderr.txt index 80f96723530..90528879c80 100644 --- a/test_programs/compile_failure/noirc_frontend_tests_deny_fold_attribute_on_unconstrained/stderr.txt +++ b/test_programs/compile_failure/noirc_frontend_tests_deny_fold_attribute_on_unconstrained/stderr.txt @@ -1,8 +1,8 @@ error: misplaced #[fold] attribute on unconstrained function foo. Only allowed on constrained functions - ┌─ src/main.nr:3:30 + ┌─ src/main.nr:2:9 │ -3 │ unconstrained pub fn foo(x: Field, y: Field) { - │ --- misplaced #[fold] attribute +2 │ #[fold] + │ ------- misplaced #[fold] attribute │ = The `#[fold]` attribute specifies whether a constrained function should be treated as a separate circuit rather than inlined into the program entry point diff --git a/test_programs/compile_failure/noirc_frontend_tests_deny_inline_attribute_on_unconstrained/stderr.txt b/test_programs/compile_failure/noirc_frontend_tests_deny_inline_attribute_on_unconstrained/stderr.txt index 9cc73a9ef90..1094d628d3c 100644 --- a/test_programs/compile_failure/noirc_frontend_tests_deny_inline_attribute_on_unconstrained/stderr.txt +++ b/test_programs/compile_failure/noirc_frontend_tests_deny_inline_attribute_on_unconstrained/stderr.txt @@ -1,8 +1,8 @@ error: misplaced #[no_predicates] attribute on unconstrained function foo. Only allowed on constrained functions - ┌─ src/main.nr:3:30 + ┌─ src/main.nr:2:9 │ -3 │ unconstrained pub fn foo(x: Field, y: Field) { - │ --- misplaced #[no_predicates] attribute +2 │ #[no_predicates] + │ ---------------- misplaced #[no_predicates] attribute │ = The `#[no_predicates]` attribute specifies to the compiler whether it should diverge from auto-inlining constrained functions diff --git a/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/Nargo.toml b/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/Nargo.toml new file mode 100644 index 00000000000..f1959a67a76 --- /dev/null +++ b/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/Nargo.toml @@ -0,0 +1,7 @@ + + [package] + name = "noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained" + type = "bin" + authors = [""] + + [dependencies] \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/src/main.nr b/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/src/main.nr new file mode 100644 index 00000000000..ca62de7d1da --- /dev/null +++ b/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/src/main.nr @@ -0,0 +1,6 @@ + + #[oracle(foo)] + pub fn foo(x: Field, y: Field) { + assert(x != y); + } + \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/src_hash.txt b/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/src_hash.txt new file mode 100644 index 00000000000..5c6acabe625 --- /dev/null +++ b/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/src_hash.txt @@ -0,0 +1 @@ +9365656274238483297 \ No newline at end of file diff --git a/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/stderr.txt b/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/stderr.txt new file mode 100644 index 00000000000..909065cbf64 --- /dev/null +++ b/test_programs/compile_failure/noirc_frontend_tests_deny_oracle_attribute_on_non_unconstrained/stderr.txt @@ -0,0 +1,10 @@ +error: Usage of the `#[oracle]` function attribute is only valid on unconstrained functions + ┌─ src/main.nr:2:9 + │ +2 │ #[oracle(foo)] + │ -------------- +3 │ pub fn foo(x: Field, y: Field) { + │ --- Oracle functions must have the `unconstrained` keyword applied + │ + +Aborting due to 1 previous error diff --git a/tooling/lsp/src/attribute_reference_finder.rs b/tooling/lsp/src/attribute_reference_finder.rs index 377734f5639..9f40d5cb3d2 100644 --- a/tooling/lsp/src/attribute_reference_finder.rs +++ b/tooling/lsp/src/attribute_reference_finder.rs @@ -89,8 +89,9 @@ impl Visitor for AttributeReferenceFinder<'_> { &mut self, attribute: &MetaAttribute, _target: AttributeTarget, + span: Span, ) -> bool { - if !self.includes_span(attribute.location.span) { + if !self.includes_span(span) { return false; } diff --git a/tooling/lsp/src/requests/completion.rs b/tooling/lsp/src/requests/completion.rs index 6d7cbc47237..7662ca884ba 100644 --- a/tooling/lsp/src/requests/completion.rs +++ b/tooling/lsp/src/requests/completion.rs @@ -1829,7 +1829,12 @@ impl Visitor for NodeFinder<'_> { false } - fn visit_meta_attribute(&mut self, attribute: &MetaAttribute, target: AttributeTarget) -> bool { + fn visit_meta_attribute( + &mut self, + attribute: &MetaAttribute, + target: AttributeTarget, + _span: Span, + ) -> bool { let MetaAttributeName::Path(path) = &attribute.name else { return true; }; diff --git a/tooling/lsp/src/with_file.rs b/tooling/lsp/src/with_file.rs index 3625de465c1..1edde48265c 100644 --- a/tooling/lsp/src/with_file.rs +++ b/tooling/lsp/src/with_file.rs @@ -19,7 +19,7 @@ use noirc_frontend::{ parser::{Item, ItemKind, ParsedSubModule}, token::{ Attributes, FmtStrFragment, LocatedToken, MetaAttribute, MetaAttributeName, - SecondaryAttribute, Token, Tokens, + SecondaryAttribute, SecondaryAttributeKind, Token, Tokens, }, }; @@ -578,20 +578,21 @@ fn secondary_attribute_with_file( secondary_attribute: SecondaryAttribute, file: FileId, ) -> SecondaryAttribute { - match secondary_attribute { - SecondaryAttribute::Meta(meta_attribute) => { - SecondaryAttribute::Meta(meta_attribute_with_file(meta_attribute, file)) + let kind = match secondary_attribute.kind { + SecondaryAttributeKind::Meta(meta_attribute) => { + SecondaryAttributeKind::Meta(meta_attribute_with_file(meta_attribute, file)) } - SecondaryAttribute::Deprecated(_) - | SecondaryAttribute::ContractLibraryMethod - | SecondaryAttribute::Export - | SecondaryAttribute::Field(_) - | SecondaryAttribute::Tag(..) - | SecondaryAttribute::Abi(_) - | SecondaryAttribute::Varargs - | SecondaryAttribute::UseCallersScope - | SecondaryAttribute::Allow(_) => secondary_attribute, - } + SecondaryAttributeKind::Deprecated(_) + | SecondaryAttributeKind::ContractLibraryMethod + | SecondaryAttributeKind::Export + | SecondaryAttributeKind::Field(_) + | SecondaryAttributeKind::Tag(..) + | SecondaryAttributeKind::Abi(_) + | SecondaryAttributeKind::Varargs + | SecondaryAttributeKind::UseCallersScope + | SecondaryAttributeKind::Allow(_) => secondary_attribute.kind, + }; + SecondaryAttribute { kind, location: location_with_file(secondary_attribute.location, file) } } fn meta_attribute_with_file(meta_attribute: MetaAttribute, file: FileId) -> MetaAttribute { @@ -599,11 +600,7 @@ fn meta_attribute_with_file(meta_attribute: MetaAttribute, file: FileId) -> Meta MetaAttributeName::Path(path) => MetaAttributeName::Path(path_with_file(path, file)), MetaAttributeName::Resolved(expr_id) => MetaAttributeName::Resolved(expr_id), }; - MetaAttribute { - name, - arguments: expressions_with_file(meta_attribute.arguments, file), - location: location_with_file(meta_attribute.location, file), - } + MetaAttribute { name, arguments: expressions_with_file(meta_attribute.arguments, file) } } fn expressions_with_file(expressions: Vec, file: FileId) -> Vec { diff --git a/tooling/nargo_fmt/src/formatter/attribute.rs b/tooling/nargo_fmt/src/formatter/attribute.rs index 08990f32988..3a8c0d616c4 100644 --- a/tooling/nargo_fmt/src/formatter/attribute.rs +++ b/tooling/nargo_fmt/src/formatter/attribute.rs @@ -1,6 +1,6 @@ use noirc_frontend::token::{ - Attribute, Attributes, FunctionAttribute, FuzzingScope, MetaAttribute, MetaAttributeName, - SecondaryAttribute, TestScope, Token, + Attribute, Attributes, FunctionAttribute, FunctionAttributeKind, FuzzingScope, MetaAttribute, + MetaAttributeName, SecondaryAttribute, SecondaryAttributeKind, TestScope, Token, }; use crate::chunks::ChunkGroup; @@ -51,15 +51,17 @@ impl Formatter<'_> { panic!("Expected attribute start, got: {:?}", self.token); } - match attribute { - FunctionAttribute::Foreign(_) - | FunctionAttribute::Builtin(_) - | FunctionAttribute::Oracle(_) => self.format_one_arg_attribute(), - FunctionAttribute::Test(test_scope) => self.format_test_attribute(test_scope), - FunctionAttribute::FuzzingHarness(fuzz_scope) => self.format_fuzz_attribute(fuzz_scope), - FunctionAttribute::Fold - | FunctionAttribute::NoPredicates - | FunctionAttribute::InlineAlways => { + match attribute.kind { + FunctionAttributeKind::Foreign(_) + | FunctionAttributeKind::Builtin(_) + | FunctionAttributeKind::Oracle(_) => self.format_one_arg_attribute(), + FunctionAttributeKind::Test(test_scope) => self.format_test_attribute(test_scope), + FunctionAttributeKind::FuzzingHarness(fuzz_scope) => { + self.format_fuzz_attribute(fuzz_scope); + } + FunctionAttributeKind::Fold + | FunctionAttributeKind::NoPredicates + | FunctionAttributeKind::InlineAlways => { self.format_no_args_attribute(); } } @@ -75,25 +77,25 @@ impl Formatter<'_> { panic!("Expected attribute start, got: {:?}", self.token); } - match attribute { - SecondaryAttribute::Deprecated(message) => { + match attribute.kind { + SecondaryAttributeKind::Deprecated(message) => { self.format_deprecated_attribute(message); } - SecondaryAttribute::ContractLibraryMethod - | SecondaryAttribute::Export - | SecondaryAttribute::Varargs - | SecondaryAttribute::UseCallersScope => { + SecondaryAttributeKind::ContractLibraryMethod + | SecondaryAttributeKind::Export + | SecondaryAttributeKind::Varargs + | SecondaryAttributeKind::UseCallersScope => { self.format_no_args_attribute(); } - SecondaryAttribute::Field(_) - | SecondaryAttribute::Abi(_) - | SecondaryAttribute::Allow(_) => { + SecondaryAttributeKind::Field(_) + | SecondaryAttributeKind::Abi(_) + | SecondaryAttributeKind::Allow(_) => { self.format_one_arg_attribute(); } - SecondaryAttribute::Tag(custom_attribute) => { - self.write_and_skip_span_without_formatting(custom_attribute.span); + SecondaryAttributeKind::Tag(_) => { + self.write_and_skip_span_without_formatting(attribute.location.span); } - SecondaryAttribute::Meta(meta_attribute) => { + SecondaryAttributeKind::Meta(meta_attribute) => { self.format_meta_attribute(meta_attribute); } }