Skip to content

Commit

Permalink
feat: Add deprecated attribute (#2041)
Browse files Browse the repository at this point in the history
* impl deprecated attribute

* add note

* add tests

* simplify

* use secondary_message
  • Loading branch information
kek kek kek authored Aug 1, 2023
1 parent 9b417da commit 9e2cf6f
Show file tree
Hide file tree
Showing 5 changed files with 96 additions and 25 deletions.
2 changes: 1 addition & 1 deletion crates/noirc_frontend/src/ast/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ impl From<FunctionDefinition> for NoirFunction {
Some(Attribute::Foreign(_)) => FunctionKind::LowLevel,
Some(Attribute::Test) => FunctionKind::Normal,
Some(Attribute::Oracle(_)) => FunctionKind::Oracle,
None => FunctionKind::Normal,
Some(Attribute::Deprecated(_)) | None => FunctionKind::Normal,
};

NoirFunction { def: fd, kind }
Expand Down
8 changes: 8 additions & 0 deletions crates/noirc_frontend/src/hir/type_check/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,8 @@ pub enum TypeCheckError {
},
#[error("Cannot infer type of expression, type annotations needed before this point")]
TypeAnnotationsNeeded { span: Span },
#[error("use of deprecated function {name}")]
CallDeprecated { name: String, note: Option<String>, span: Span },
#[error("{0}")]
ResolverError(ResolverError),
}
Expand Down Expand Up @@ -205,6 +207,12 @@ impl From<TypeCheckError> for Diagnostic {

Diagnostic::simple_error(message, String::new(), span)
}
TypeCheckError::CallDeprecated { span, ref note, .. } => {
let primary_message = error.to_string();
let secondary_message = note.clone().unwrap_or_default();

Diagnostic::simple_warning(primary_message, secondary_message, span)
}
}
}
}
23 changes: 22 additions & 1 deletion crates/noirc_frontend/src/hir/type_check/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,32 @@ use crate::{
},
types::Type,
},
node_interner::{ExprId, FuncId},
node_interner::{DefinitionKind, ExprId, FuncId},
token::Attribute::Deprecated,
CompTime, Shared, TypeBinding, TypeVariableKind, UnaryOp,
};

use super::{errors::TypeCheckError, TypeChecker};

impl<'interner> TypeChecker<'interner> {
fn check_if_deprecated(&mut self, expr: &ExprId) {
if let HirExpression::Ident(expr::HirIdent { location, id }) =
self.interner.expression(expr)
{
if let Some(DefinitionKind::Function(func_id)) =
self.interner.try_definition(id).map(|def| &def.kind)
{
let meta = self.interner.function_meta(func_id);
if let Some(Deprecated(note)) = meta.attributes {
self.errors.push(TypeCheckError::CallDeprecated {
name: self.interner.definition_name(id).to_string(),
note,
span: location.span,
});
}
}
}
}
/// Infers a type for a given expression, and return this type.
/// As a side-effect, this function will also remember this type in the NodeInterner
/// for the given expr_id key.
Expand Down Expand Up @@ -112,6 +131,8 @@ impl<'interner> TypeChecker<'interner> {
}
HirExpression::Index(index_expr) => self.check_index_expression(index_expr),
HirExpression::Call(call_expr) => {
self.check_if_deprecated(&call_expr.func);

let function = self.check_expression(&call_expr.func);
let args = vecmap(&call_expr.arguments, |arg| {
let typ = self.check_expression(arg);
Expand Down
23 changes: 19 additions & 4 deletions crates/noirc_frontend/src/lexer/lexer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -244,10 +244,7 @@ impl<'a> Lexer<'a> {
}
self.next_char();

let (word, start, end) = self.eat_while(None, |ch| {
(ch.is_ascii_alphabetic() || ch.is_numeric() || ch == '_' || ch == '(' || ch == ')')
&& (ch != ']')
});
let (word, start, end) = self.eat_while(None, |ch| ch != ']');

if !self.peek_char_is(']') {
return Err(LexerErrorKind::UnexpectedCharacter {
Expand Down Expand Up @@ -427,6 +424,24 @@ fn invalid_attribute() {
assert!(token.is_err());
}

#[test]
fn deprecated_attribute() {
let input = r#"#[deprecated]"#;
let mut lexer = Lexer::new(input);

let token = lexer.next().unwrap().unwrap();
assert_eq!(token.token(), &Token::Attribute(Attribute::Deprecated(None)));
}

#[test]
fn deprecated_attribute_with_note() {
let input = r#"#[deprecated("hello")]"#;
let mut lexer = Lexer::new(input);

let token = lexer.next().unwrap().unwrap();
assert_eq!(token.token(), &Token::Attribute(Attribute::Deprecated("hello".to_string().into())));
}

#[test]
fn test_custom_gate_syntax() {
let input = "#[foreign(sha256)]#[foreign(blake2s)]#[builtin(sum)]";
Expand Down
65 changes: 46 additions & 19 deletions crates/noirc_frontend/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -322,6 +322,7 @@ pub enum Attribute {
Foreign(String),
Builtin(String),
Oracle(String),
Deprecated(Option<String>),
Test,
}

Expand All @@ -332,6 +333,8 @@ impl fmt::Display for Attribute {
Attribute::Builtin(ref k) => write!(f, "#[builtin({k})]"),
Attribute::Oracle(ref k) => write!(f, "#[oracle({k})]"),
Attribute::Test => write!(f, "#[test]"),
Attribute::Deprecated(None) => write!(f, "#[deprecated]"),
Attribute::Deprecated(Some(ref note)) => write!(f, r#"#[deprecated("{note}")]"#),
}
}
}
Expand All @@ -345,29 +348,52 @@ impl Attribute {
.filter(|string_segment| !string_segment.is_empty())
.collect();

if word_segments.len() != 2 {
if word_segments.len() == 1 && word_segments[0] == "test" {
return Ok(Token::Attribute(Attribute::Test));
} else {
return Err(LexerErrorKind::MalformedFuncAttribute {
span,
found: word.to_owned(),
});
}
}

let attribute_type = word_segments[0];
let attribute_name = word_segments[1];
let validate = |slice: &str| {
let is_valid = slice
.chars()
.all(|ch| {
ch.is_ascii_alphabetic()
|| ch.is_numeric()
|| ch == '_'
|| ch == '('
|| ch == ')'
})
.then_some(());

is_valid.ok_or(LexerErrorKind::MalformedFuncAttribute { span, found: word.to_owned() })
};

let tok = match attribute_type {
"foreign" => Token::Attribute(Attribute::Foreign(attribute_name.to_string())),
"builtin" => Token::Attribute(Attribute::Builtin(attribute_name.to_string())),
"oracle" => Token::Attribute(Attribute::Oracle(attribute_name.to_string())),
let attribute = match &word_segments[..] {
["foreign", name] => {
validate(name)?;
Attribute::Foreign(name.to_string())
}
["builtin", name] => {
validate(name)?;
Attribute::Builtin(name.to_string())
}
["oracle", name] => {
validate(name)?;
Attribute::Oracle(name.to_string())
}
["deprecated"] => Attribute::Deprecated(None),
["deprecated", name] => {
if !name.starts_with('"') && !name.ends_with('"') {
return Err(LexerErrorKind::MalformedFuncAttribute {
span,
found: word.to_owned(),
});
}

Attribute::Deprecated(name.trim_matches('"').to_string().into())
}
["test"] => Attribute::Test,
_ => {
return Err(LexerErrorKind::MalformedFuncAttribute { span, found: word.to_owned() })
}
};
Ok(tok)

Ok(Token::Attribute(attribute))
}

pub fn builtin(self) -> Option<String> {
Expand Down Expand Up @@ -399,7 +425,8 @@ impl AsRef<str> for Attribute {
Attribute::Foreign(string) => string,
Attribute::Builtin(string) => string,
Attribute::Oracle(string) => string,
Attribute::Test => "",
Attribute::Deprecated(Some(string)) => string,
Attribute::Test | Attribute::Deprecated(None) => "",
}
}
}
Expand Down

0 comments on commit 9e2cf6f

Please sign in to comment.