Skip to content

Commit 93c68c1

Browse files
committed
Parse trait function signatures
1 parent dee378a commit 93c68c1

File tree

11 files changed

+84
-39
lines changed

11 files changed

+84
-39
lines changed

crates/analyzer/src/db/queries/contracts.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ pub fn contract_function_map(
5353
def_name,
5454
&NamedThing::Item(Item::Event(event)),
5555
Some(event.name_span(db)),
56-
def.kind.name.span,
56+
def.kind.sig.name.span,
5757
);
5858
continue;
5959
}
@@ -64,7 +64,7 @@ pub fn contract_function_map(
6464
def_name,
6565
&named_item,
6666
named_item.name_span(db),
67-
def.kind.name.span,
67+
def.kind.sig.name.span,
6868
);
6969
continue;
7070
}

crates/analyzer/src/db/queries/functions.rs

+10-8
Original file line numberDiff line numberDiff line change
@@ -36,14 +36,15 @@ pub fn function_signature(
3636
scope.fancy_error(
3737
"generic function parameters aren't yet supported in contract functions",
3838
vec![Label::primary(
39-
function.data(db).ast.kind.generic_params.span,
39+
function.data(db).ast.kind.sig.generic_params.span,
4040
"This can not appear here",
4141
)],
4242
vec!["Hint: Struct functions can have generic parameters".into()],
4343
);
4444
}
4545

4646
let params = def
47+
.sig
4748
.args
4849
.iter()
4950
.enumerate()
@@ -114,9 +115,9 @@ pub fn function_signature(
114115
if label.kind != "_";
115116
if let Some(dup_idx) = labels.get(&label.kind);
116117
then {
117-
let dup_arg: &Node<ast::FunctionArg> = &def.args[*dup_idx];
118+
let dup_arg: &Node<ast::FunctionArg> = &def.sig.args[*dup_idx];
118119
scope.fancy_error(
119-
&format!("duplicate parameter labels in function `{}`", def.name.kind),
120+
&format!("duplicate parameter labels in function `{}`", def.sig.name.kind),
120121
vec![
121122
Label::primary(dup_arg.span, "the label `{}` was first used here"),
122123
Label::primary(label.span, "label `{}` used again here"),
@@ -138,9 +139,9 @@ pub fn function_signature(
138139
);
139140
None
140141
} else if let Some(dup_idx) = names.get(&reg.name.kind) {
141-
let dup_arg: &Node<ast::FunctionArg> = &def.args[*dup_idx];
142+
let dup_arg: &Node<ast::FunctionArg> = &def.sig.args[*dup_idx];
142143
scope.duplicate_name_error(
143-
&format!("duplicate parameter names in function `{}`", def.name.kind),
144+
&format!("duplicate parameter names in function `{}`", def.sig.name.kind),
144145
&reg.name.kind,
145146
dup_arg.span,
146147
arg.span,
@@ -159,10 +160,11 @@ pub fn function_signature(
159160
.collect();
160161

161162
let return_type = def
163+
.sig
162164
.return_type
163165
.as_ref()
164166
.map(|type_node| {
165-
let fn_name = &def.name.kind;
167+
let fn_name = &def.sig.name.kind;
166168
if fn_name == "__init__" || fn_name == "__call__" {
167169
// `__init__` and `__call__` must not return any type other than `()`.
168170
if type_node.kind != ast::TypeDesc::Unit {
@@ -254,11 +256,11 @@ pub fn function_body(db: &dyn AnalyzerDb, function: FunctionId) -> Analysis<Rc<F
254256
"function body is missing a return or revert statement",
255257
vec![
256258
Label::primary(
257-
def.name.span,
259+
def.sig.name.span,
258260
"all paths of this function must `return` or `revert`",
259261
),
260262
Label::secondary(
261-
def.return_type.as_ref().unwrap().span,
263+
def.sig.return_type.as_ref().unwrap().span,
262264
format!("expected function to return `{}`", return_type),
263265
),
264266
],

crates/analyzer/src/db/queries/structs.rs

+2-2
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ pub fn struct_function_map(
150150
def_name,
151151
&named_item,
152152
named_item.name_span(db),
153-
def.kind.name.span,
153+
def.kind.sig.name.span,
154154
);
155155
continue;
156156
}
@@ -161,7 +161,7 @@ pub fn struct_function_map(
161161
"function name `{}` conflicts with built-in function",
162162
def_name
163163
),
164-
def.kind.name.span,
164+
def.kind.sig.name.span,
165165
&format!("`{}` is a built-in function", def_name),
166166
);
167167
continue;

crates/analyzer/src/namespace/items.rs

+6-5
Original file line numberDiff line numberDiff line change
@@ -1092,7 +1092,7 @@ impl FunctionId {
10921092
self.data(db).ast.name().into()
10931093
}
10941094
pub fn name_span(&self, db: &dyn AnalyzerDb) -> Span {
1095-
self.data(db).ast.kind.name.span
1095+
self.data(db).ast.kind.sig.name.span
10961096
}
10971097

10981098
// This should probably be scrapped in favor of `parent(`
@@ -1130,6 +1130,7 @@ impl FunctionId {
11301130
self.data(db)
11311131
.ast
11321132
.kind
1133+
.sig
11331134
.args
11341135
.iter()
11351136
.find_map(|arg| matches!(arg.kind, ast::FunctionArg::Self_).then(|| arg.span))
@@ -1139,11 +1140,11 @@ impl FunctionId {
11391140
}
11401141

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

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

11491150
pub fn generic_param(&self, db: &dyn AnalyzerDb, param_name: &str) -> Option<GenericParameter> {
@@ -1164,13 +1165,13 @@ impl FunctionId {
11641165
self.name(db) == "__init__"
11651166
}
11661167
pub fn pub_span(&self, db: &dyn AnalyzerDb) -> Option<Span> {
1167-
self.data(db).ast.kind.pub_
1168+
self.data(db).ast.kind.sig.pub_
11681169
}
11691170
pub fn is_unsafe(&self, db: &dyn AnalyzerDb) -> bool {
11701171
self.unsafe_span(db).is_some()
11711172
}
11721173
pub fn unsafe_span(&self, db: &dyn AnalyzerDb) -> Option<Span> {
1173-
self.data(db).ast.kind.unsafe_
1174+
self.data(db).ast.kind.sig.unsafe_
11741175
}
11751176
pub fn signature(&self, db: &dyn AnalyzerDb) -> Rc<types::FunctionSignature> {
11761177
db.function_signature(*self).value

crates/analyzer/src/namespace/scopes.rs

+1
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,7 @@ impl<'a> AnalyzerContext for FunctionScope<'a> {
309309
.data(self.db)
310310
.ast
311311
.kind
312+
.sig
312313
.args
313314
.iter()
314315
.find_map(|param| (param.name() == name).then(|| param.name_span()))

crates/mir/src/lower/function.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1191,6 +1191,7 @@ fn self_arg_source(db: &dyn MirDb, func: analyzer_items::FunctionId) -> SourceIn
11911191
func.data(db.upcast())
11921192
.ast
11931193
.kind
1194+
.sig
11941195
.args
11951196
.iter()
11961197
.find(|arg| matches!(arg.kind, ast::FunctionArg::Self_))
@@ -1202,6 +1203,7 @@ fn arg_source(db: &dyn MirDb, func: analyzer_items::FunctionId, arg_name: &str)
12021203
func.data(db.upcast())
12031204
.ast
12041205
.kind
1206+
.sig
12051207
.args
12061208
.iter()
12071209
.find_map(|arg| match &arg.kind {

crates/parser/src/ast.rs

+17-8
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,7 @@ pub struct Struct {
9292
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
9393
pub struct Trait {
9494
pub name: Node<SmolStr>,
95+
pub functions: Vec<Node<FunctionSignature>>,
9596
pub pub_qual: Option<Span>,
9697
}
9798

@@ -180,14 +181,19 @@ pub struct Event {
180181
}
181182

182183
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
183-
pub struct Function {
184+
pub struct FunctionSignature {
184185
// qualifier order: `pub unsafe fn`
185186
pub pub_: Option<Span>,
186187
pub unsafe_: Option<Span>,
187188
pub name: Node<SmolStr>,
188189
pub generic_params: Node<Vec<GenericParameter>>,
189190
pub args: Vec<Node<FunctionArg>>,
190191
pub return_type: Option<Node<TypeDesc>>,
192+
}
193+
194+
#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Hash, Clone)]
195+
pub struct Function {
196+
pub sig: FunctionSignature,
191197
pub body: Vec<Node<FuncStmt>>,
192198
}
193199

@@ -404,7 +410,7 @@ impl Node<Field> {
404410

405411
impl Node<Function> {
406412
pub fn name(&self) -> &str {
407-
&self.kind.name.kind
413+
&self.kind.sig.name.kind
408414
}
409415
}
410416

@@ -730,12 +736,15 @@ impl fmt::Display for Node<Function> {
730736
impl fmt::Display for Function {
731737
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
732738
let Function {
733-
pub_,
734-
unsafe_,
735-
name,
736-
generic_params,
737-
args,
738-
return_type,
739+
sig:
740+
FunctionSignature {
741+
pub_,
742+
unsafe_,
743+
name,
744+
generic_params,
745+
args,
746+
return_type,
747+
},
739748
body,
740749
} = self;
741750

crates/parser/src/grammar/functions.rs

+27-11
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,18 @@ use super::expressions::{parse_call_args, parse_expr};
22
use super::types::parse_type_desc;
33

44
use crate::ast::{
5-
BinOperator, Expr, FuncStmt, Function, FunctionArg, GenericParameter, RegularFunctionArg,
6-
TypeDesc, VarDeclTarget,
5+
BinOperator, Expr, FuncStmt, Function, FunctionArg, FunctionSignature, GenericParameter,
6+
RegularFunctionArg, TypeDesc, VarDeclTarget,
77
};
88
use crate::node::{Node, Span};
99
use crate::{Label, ParseFailed, ParseResult, Parser, TokenKind};
1010

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

87-
// TODO: allow multi-line return type? `fn f()\n ->\n u8`
88-
par.enter_block(span, "function definition")?;
89-
let body = parse_block_stmts(par)?;
90-
let rbrace = par.expect(TokenKind::BraceClose, "missing `}` in fn definition")?;
91-
9290
Ok(Node::new(
93-
Function {
91+
FunctionSignature {
9492
pub_: pub_qual,
9593
unsafe_: unsafe_qual,
9694
name: name.into(),
9795
args,
9896
generic_params,
9997
return_type,
98+
},
99+
span,
100+
))
101+
}
102+
103+
/// Parse a function definition. The optional `pub` qualifier must be parsed by
104+
/// the caller, and passed in. Next token must be `unsafe` or `fn`.
105+
pub fn parse_fn_def(par: &mut Parser, pub_qual: Option<Span>) -> ParseResult<Node<Function>> {
106+
let sig = parse_fn_sig(par, pub_qual)?;
107+
108+
// TODO: allow multi-line return type? `fn f()\n ->\n u8`
109+
par.enter_block(sig.span, "function definition")?;
110+
let body = parse_block_stmts(par)?;
111+
let rbrace = par.expect(TokenKind::BraceClose, "missing `}` in fn definition")?;
112+
113+
Ok(Node::new(
114+
Function {
115+
sig: sig.kind,
100116
body,
101117
},
102-
span + rbrace.span,
118+
sig.span + rbrace.span,
103119
))
104120
}
105121

crates/parser/src/grammar/types.rs

+11-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use crate::ast::{self, EventField, Field, GenericArg, Impl, Path, Trait, TypeAlias, TypeDesc};
22
use crate::grammar::expressions::parse_expr;
3-
use crate::grammar::functions::parse_fn_def;
3+
use crate::grammar::functions::{parse_fn_def, parse_fn_sig};
44
use crate::node::{Node, Span};
55
use crate::Token;
66
use crate::{ParseFailed, ParseResult, Parser, TokenKind};
@@ -78,12 +78,20 @@ pub fn parse_trait_def(par: &mut Parser, trait_pub_qual: Option<Span>) -> ParseR
7878
)?;
7979

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

8384
loop {
8485
match par.peek_or_err()? {
8586
TokenKind::Fn => {
86-
// TODO: parse trait functions
87+
// TODO: Traits should also be allowed to have functions that do contain a body.
88+
functions.push(parse_fn_sig(par, None)?);
89+
par.expect_with_notes(
90+
TokenKind::Semi,
91+
"failed to parse trait definition",
92+
|_| vec!["Note: trait functions must appear without body and followed by a semicolon.".into()],
93+
)?;
94+
par.eat_newlines();
8795
}
8896
TokenKind::BraceClose => {
8997
par.next()?;
@@ -101,6 +109,7 @@ pub fn parse_trait_def(par: &mut Parser, trait_pub_qual: Option<Span>) -> ParseR
101109
Ok(Node::new(
102110
Trait {
103111
name: Node::new(trait_name.text.into(), trait_name.span),
112+
functions,
104113
pub_qual: trait_pub_qual,
105114
},
106115
span,

crates/parser/src/lexer/token.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -261,7 +261,7 @@ impl TokenKind {
261261
Colon => "symbol `:`",
262262
ColonColon => "symbol `::`",
263263
Comma => "symbol `,`",
264-
Semi => "symbol ``",
264+
Semi => "symbol `;`",
265265
Plus => "symbol `+`",
266266
Minus => "symbol `-`",
267267
Star => "symbol `*`",

crates/test-files/fixtures/features/generic_functions.fe

+5
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
use std::traits::IsNumeric
22

3+
trait SomeTrait {
4+
fn do_this() -> bool;
5+
fn do_that();
6+
}
7+
38
struct Bar {
49

510
pub fn bar<T: IsNumeric>(self, x: T, y: u256) -> bool {

0 commit comments

Comments
 (0)