Skip to content

Commit

Permalink
Parse trait function signatures
Browse files Browse the repository at this point in the history
  • Loading branch information
cburgdorf committed Jun 8, 2022
1 parent dee378a commit d0ba7c0
Show file tree
Hide file tree
Showing 22 changed files with 899 additions and 826 deletions.
4 changes: 2 additions & 2 deletions crates/analyzer/src/db/queries/contracts.rs
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ pub fn contract_function_map(
def_name,
&NamedThing::Item(Item::Event(event)),
Some(event.name_span(db)),
def.kind.name.span,
def.kind.sig.name.span,
);
continue;
}
Expand All @@ -64,7 +64,7 @@ pub fn contract_function_map(
def_name,
&named_item,
named_item.name_span(db),
def.kind.name.span,
def.kind.sig.name.span,
);
continue;
}
Expand Down
18 changes: 10 additions & 8 deletions crates/analyzer/src/db/queries/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,14 +36,15 @@ pub fn function_signature(
scope.fancy_error(
"generic function parameters aren't yet supported in contract functions",
vec![Label::primary(
function.data(db).ast.kind.generic_params.span,
function.data(db).ast.kind.sig.generic_params.span,
"This can not appear here",
)],
vec!["Hint: Struct functions can have generic parameters".into()],
);
}

let params = def
.sig
.args
.iter()
.enumerate()
Expand Down Expand Up @@ -114,9 +115,9 @@ pub fn function_signature(
if label.kind != "_";
if let Some(dup_idx) = labels.get(&label.kind);
then {
let dup_arg: &Node<ast::FunctionArg> = &def.args[*dup_idx];
let dup_arg: &Node<ast::FunctionArg> = &def.sig.args[*dup_idx];
scope.fancy_error(
&format!("duplicate parameter labels in function `{}`", def.name.kind),
&format!("duplicate parameter labels in function `{}`", def.sig.name.kind),
vec![
Label::primary(dup_arg.span, "the label `{}` was first used here"),
Label::primary(label.span, "label `{}` used again here"),
Expand All @@ -138,9 +139,9 @@ pub fn function_signature(
);
None
} else if let Some(dup_idx) = names.get(&reg.name.kind) {
let dup_arg: &Node<ast::FunctionArg> = &def.args[*dup_idx];
let dup_arg: &Node<ast::FunctionArg> = &def.sig.args[*dup_idx];
scope.duplicate_name_error(
&format!("duplicate parameter names in function `{}`", def.name.kind),
&format!("duplicate parameter names in function `{}`", def.sig.name.kind),
&reg.name.kind,
dup_arg.span,
arg.span,
Expand All @@ -159,10 +160,11 @@ pub fn function_signature(
.collect();

let return_type = def
.sig
.return_type
.as_ref()
.map(|type_node| {
let fn_name = &def.name.kind;
let fn_name = &def.sig.name.kind;
if fn_name == "__init__" || fn_name == "__call__" {
// `__init__` and `__call__` must not return any type other than `()`.
if type_node.kind != ast::TypeDesc::Unit {
Expand Down Expand Up @@ -254,11 +256,11 @@ pub fn function_body(db: &dyn AnalyzerDb, function: FunctionId) -> Analysis<Rc<F
"function body is missing a return or revert statement",
vec![
Label::primary(
def.name.span,
def.sig.name.span,
"all paths of this function must `return` or `revert`",
),
Label::secondary(
def.return_type.as_ref().unwrap().span,
def.sig.return_type.as_ref().unwrap().span,
format!("expected function to return `{}`", return_type),
),
],
Expand Down
4 changes: 2 additions & 2 deletions crates/analyzer/src/db/queries/structs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,7 +150,7 @@ pub fn struct_function_map(
def_name,
&named_item,
named_item.name_span(db),
def.kind.name.span,
def.kind.sig.name.span,
);
continue;
}
Expand All @@ -161,7 +161,7 @@ pub fn struct_function_map(
"function name `{}` conflicts with built-in function",
def_name
),
def.kind.name.span,
def.kind.sig.name.span,
&format!("`{}` is a built-in function", def_name),
);
continue;
Expand Down
11 changes: 6 additions & 5 deletions crates/analyzer/src/namespace/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,7 @@ impl FunctionId {
self.data(db).ast.name().into()
}
pub fn name_span(&self, db: &dyn AnalyzerDb) -> Span {
self.data(db).ast.kind.name.span
self.data(db).ast.kind.sig.name.span
}

// This should probably be scrapped in favor of `parent(`
Expand Down Expand Up @@ -1130,6 +1130,7 @@ impl FunctionId {
self.data(db)
.ast
.kind
.sig
.args
.iter()
.find_map(|arg| matches!(arg.kind, ast::FunctionArg::Self_).then(|| arg.span))
Expand All @@ -1139,11 +1140,11 @@ impl FunctionId {
}

pub fn is_generic(&self, db: &dyn AnalyzerDb) -> bool {
!self.data(db).ast.kind.generic_params.kind.is_empty()
!self.data(db).ast.kind.sig.generic_params.kind.is_empty()
}

pub fn generic_params(&self, db: &dyn AnalyzerDb) -> Vec<GenericParameter> {
self.data(db).ast.kind.generic_params.kind.clone()
self.data(db).ast.kind.sig.generic_params.kind.clone()
}

pub fn generic_param(&self, db: &dyn AnalyzerDb, param_name: &str) -> Option<GenericParameter> {
Expand All @@ -1164,13 +1165,13 @@ impl FunctionId {
self.name(db) == "__init__"
}
pub fn pub_span(&self, db: &dyn AnalyzerDb) -> Option<Span> {
self.data(db).ast.kind.pub_
self.data(db).ast.kind.sig.pub_
}
pub fn is_unsafe(&self, db: &dyn AnalyzerDb) -> bool {
self.unsafe_span(db).is_some()
}
pub fn unsafe_span(&self, db: &dyn AnalyzerDb) -> Option<Span> {
self.data(db).ast.kind.unsafe_
self.data(db).ast.kind.sig.unsafe_
}
pub fn signature(&self, db: &dyn AnalyzerDb) -> Rc<types::FunctionSignature> {
db.function_signature(*self).value
Expand Down
1 change: 1 addition & 0 deletions crates/analyzer/src/namespace/scopes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,7 @@ impl<'a> AnalyzerContext for FunctionScope<'a> {
.data(self.db)
.ast
.kind
.sig
.args
.iter()
.find_map(|param| (param.name() == name).then(|| param.name_span()))
Expand Down
2 changes: 2 additions & 0 deletions crates/mir/src/lower/function.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1191,6 +1191,7 @@ fn self_arg_source(db: &dyn MirDb, func: analyzer_items::FunctionId) -> SourceIn
func.data(db.upcast())
.ast
.kind
.sig
.args
.iter()
.find(|arg| matches!(arg.kind, ast::FunctionArg::Self_))
Expand All @@ -1202,6 +1203,7 @@ fn arg_source(db: &dyn MirDb, func: analyzer_items::FunctionId, arg_name: &str)
func.data(db.upcast())
.ast
.kind
.sig
.args
.iter()
.find_map(|arg| match &arg.kind {
Expand Down
25 changes: 17 additions & 8 deletions crates/parser/src/ast.rs
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,7 @@ pub struct Struct {
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Trait {
pub name: Node<SmolStr>,
pub functions: Vec<Node<FunctionSignature>>,
pub pub_qual: Option<Span>,
}

Expand Down Expand Up @@ -180,14 +181,19 @@ pub struct Event {
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Function {
pub struct FunctionSignature {
// qualifier order: `pub unsafe fn`
pub pub_: Option<Span>,
pub unsafe_: Option<Span>,
pub name: Node<SmolStr>,
pub generic_params: Node<Vec<GenericParameter>>,
pub args: Vec<Node<FunctionArg>>,
pub return_type: Option<Node<TypeDesc>>,
}

#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
pub struct Function {
pub sig: FunctionSignature,
pub body: Vec<Node<FuncStmt>>,
}

Expand Down Expand Up @@ -404,7 +410,7 @@ impl Node<Field> {

impl Node<Function> {
pub fn name(&self) -> &str {
&self.kind.name.kind
&self.kind.sig.name.kind
}
}

Expand Down Expand Up @@ -730,12 +736,15 @@ impl fmt::Display for Node<Function> {
impl fmt::Display for Function {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
let Function {
pub_,
unsafe_,
name,
generic_params,
args,
return_type,
sig:
FunctionSignature {
pub_,
unsafe_,
name,
generic_params,
args,
return_type,
},
body,
} = self;

Expand Down
38 changes: 27 additions & 11 deletions crates/parser/src/grammar/functions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,18 @@ use super::expressions::{parse_call_args, parse_expr};
use super::types::parse_type_desc;

use crate::ast::{
BinOperator, Expr, FuncStmt, Function, FunctionArg, GenericParameter, RegularFunctionArg,
TypeDesc, VarDeclTarget,
BinOperator, Expr, FuncStmt, Function, FunctionArg, FunctionSignature, GenericParameter,
RegularFunctionArg, TypeDesc, VarDeclTarget,
};
use crate::node::{Node, Span};
use crate::{Label, ParseFailed, ParseResult, Parser, TokenKind};

/// Parse a function definition. The optional `pub` qualifier must be parsed by
/// Parse a function definition without a body. The optional `pub` qualifier must be parsed by
/// the caller, and passed in. Next token must be `unsafe` or `fn`.
pub fn parse_fn_def(par: &mut Parser, mut pub_qual: Option<Span>) -> ParseResult<Node<Function>> {
pub fn parse_fn_sig(
par: &mut Parser,
mut pub_qual: Option<Span>,
) -> ParseResult<Node<FunctionSignature>> {
let unsafe_qual = par.optional(TokenKind::Unsafe).map(|tok| tok.span);
if let Some(pub_) = par.optional(TokenKind::Pub) {
let unsafe_span =
Expand Down Expand Up @@ -84,22 +87,35 @@ pub fn parse_fn_def(par: &mut Parser, mut pub_qual: Option<Span>) -> ParseResult
None
};

// TODO: allow multi-line return type? `fn f()\n ->\n u8`
par.enter_block(span, "function definition")?;
let body = parse_block_stmts(par)?;
let rbrace = par.expect(TokenKind::BraceClose, "missing `}` in fn definition")?;

Ok(Node::new(
Function {
FunctionSignature {
pub_: pub_qual,
unsafe_: unsafe_qual,
name: name.into(),
args,
generic_params,
return_type,
},
span,
))
}

/// Parse a function definition. The optional `pub` qualifier must be parsed by
/// the caller, and passed in. Next token must be `unsafe` or `fn`.
pub fn parse_fn_def(par: &mut Parser, pub_qual: Option<Span>) -> ParseResult<Node<Function>> {
let sig = parse_fn_sig(par, pub_qual)?;

// TODO: allow multi-line return type? `fn f()\n ->\n u8`
par.enter_block(sig.span, "function definition")?;
let body = parse_block_stmts(par)?;
let rbrace = par.expect(TokenKind::BraceClose, "missing `}` in fn definition")?;

Ok(Node::new(
Function {
sig: sig.kind,
body,
},
span + rbrace.span,
sig.span + rbrace.span,
))
}

Expand Down
13 changes: 11 additions & 2 deletions crates/parser/src/grammar/types.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::ast::{self, EventField, Field, GenericArg, Impl, Path, Trait, TypeAlias, TypeDesc};
use crate::grammar::expressions::parse_expr;
use crate::grammar::functions::parse_fn_def;
use crate::grammar::functions::{parse_fn_def, parse_fn_sig};
use crate::node::{Node, Span};
use crate::Token;
use crate::{ParseFailed, ParseResult, Parser, TokenKind};
Expand Down Expand Up @@ -78,12 +78,20 @@ pub fn parse_trait_def(par: &mut Parser, trait_pub_qual: Option<Span>) -> ParseR
)?;

let header_span = trait_tok.span + trait_name.span;
let mut functions = vec![];
par.enter_block(header_span, "trait definition")?;

loop {
match par.peek_or_err()? {
TokenKind::Fn => {
// TODO: parse trait functions
// TODO: Traits should also be allowed to have functions that do contain a body.
functions.push(parse_fn_sig(par, None)?);
par.expect_with_notes(
TokenKind::Semi,
"failed to parse trait definition",
|_| vec!["Note: trait functions must appear without body and followed by a semicolon.".into()],
)?;
par.eat_newlines();
}
TokenKind::BraceClose => {
par.next()?;
Expand All @@ -101,6 +109,7 @@ pub fn parse_trait_def(par: &mut Parser, trait_pub_qual: Option<Span>) -> ParseR
Ok(Node::new(
Trait {
name: Node::new(trait_name.text.into(), trait_name.span),
functions,
pub_qual: trait_pub_qual,
},
span,
Expand Down
2 changes: 1 addition & 1 deletion crates/parser/src/lexer/token.rs
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,7 @@ impl TokenKind {
Colon => "symbol `:`",
ColonColon => "symbol `::`",
Comma => "symbol `,`",
Semi => "symbol ``",
Semi => "symbol `;`",
Plus => "symbol `+`",
Minus => "symbol `-`",
Star => "symbol `*`",
Expand Down
Loading

0 comments on commit d0ba7c0

Please sign in to comment.