Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement trait associated functions #805

Merged
merged 3 commits into from
Nov 23, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions crates/analyzer/src/context.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,8 +52,13 @@ impl<T> Analysis<T> {

pub trait AnalyzerContext {
fn resolve_name(&self, name: &str, span: Span) -> Result<Option<NamedThing>, IncompleteItem>;
/// Resolves the given path and registers all errors
fn resolve_path(&self, path: &ast::Path, span: Span) -> Result<NamedThing, FatalError>;
fn maybe_resolve_path(&self, path: &ast::Path) -> Option<NamedThing>;
/// Resolves the given path only if it is visible. Does not register any errors
fn resolve_visible_path(&self, path: &ast::Path) -> Option<NamedThing>;
/// Resolves the given path. Does not register any errors
fn resolve_any_path(&self, path: &ast::Path) -> Option<NamedThing>;

fn add_diagnostic(&self, diag: Diagnostic);
fn db(&self) -> &dyn AnalyzerDb;

Expand Down Expand Up @@ -319,7 +324,11 @@ impl AnalyzerContext for TempContext {
panic!("TempContext can't resolve paths")
}

fn maybe_resolve_path(&self, _path: &ast::Path) -> Option<NamedThing> {
fn resolve_visible_path(&self, _path: &ast::Path) -> Option<NamedThing> {
panic!("TempContext can't resolve paths")
}

fn resolve_any_path(&self, _path: &ast::Path) -> Option<NamedThing> {
panic!("TempContext can't resolve paths")
}

Expand Down
8 changes: 8 additions & 0 deletions crates/analyzer/src/db/queries/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -441,6 +441,14 @@ pub fn module_used_item_map(
diagnostics.extend(items.diagnostics.iter().cloned());

for (name, (name_span, item)) in items.value.iter() {
if !item.is_public(db) {
diagnostics.push(errors::error(
&format!("{} {} is private", item.item_kind_display_name(), name,),
*name_span,
name.as_str(),
));
}

if let Some((other_name_span, other_item)) =
accum.insert(name.clone(), (*name_span, *item))
{
Expand Down
52 changes: 38 additions & 14 deletions crates/analyzer/src/namespace/items.rs
Original file line number Diff line number Diff line change
Expand Up @@ -824,7 +824,7 @@ impl TypeDef {
}
TypeDef::Enum(val) => val.pure_functions_as_items(db),
TypeDef::Contract(val) => val.pure_functions_as_items(db),
_ => todo!("cannot access items in types yet"),
_ => Rc::new(indexmap! {}),
}
}

Expand Down Expand Up @@ -1803,6 +1803,40 @@ impl ImplId {
vec![],
));
}

if impl_fn.takes_self(db) != trait_fn.takes_self(db) {
let ((selfy_thing, selfy_span), (non_selfy_thing, non_selfy_span)) =
if impl_fn.takes_self(db) {
(
("impl", impl_fn.name_span(db)),
("trait", trait_fn.name_span(db)),
)
} else {
(
("trait", trait_fn.name_span(db)),
("impl", impl_fn.name_span(db)),
)
};
sink.push(&errors::fancy_error(
format!(
"method `{}` has a `self` declaration in the {}, but not in the `{}`",
impl_fn.name(db),
selfy_thing,
non_selfy_thing
),
vec![
Label::primary(
selfy_span,
format!("`self` declared on the `{}`", selfy_thing),
),
Label::primary(
non_selfy_span,
format!("no `self` declared on the `{}`", non_selfy_thing),
),
],
vec![],
));
}
} else {
sink.push(&errors::fancy_error(
format!(
Expand Down Expand Up @@ -1903,19 +1937,9 @@ impl TraitId {
}

pub fn sink_diagnostics(&self, db: &dyn AnalyzerDb, sink: &mut impl DiagnosticSink) {
db.trait_all_functions(*self).iter().for_each(|id| {
if !id.takes_self(db) {
sink.push(&errors::fancy_error(
"associated functions aren't yet supported in traits",
vec![Label::primary(
id.data(db).ast.span,
"function doesn't take `self`",
)],
vec!["Hint: add a `self` param to this function".into()],
));
}
id.sink_diagnostics(db, sink)
});
db.trait_all_functions(*self)
.iter()
.for_each(|id| id.sink_diagnostics(db, sink));
}
}

Expand Down
37 changes: 32 additions & 5 deletions crates/analyzer/src/namespace/scopes.rs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ impl<'a> AnalyzerContext for ItemScope<'a> {
}
}

fn maybe_resolve_path(&self, path: &ast::Path) -> Option<NamedThing> {
fn resolve_visible_path(&self, path: &ast::Path) -> Option<NamedThing> {
let resolved = self.module.resolve_path_internal(self.db(), path);

if resolved.diagnostics.len() > 0 {
Expand All @@ -156,6 +156,16 @@ impl<'a> AnalyzerContext for ItemScope<'a> {
}
}

fn resolve_any_path(&self, path: &ast::Path) -> Option<NamedThing> {
let resolved = self.module.resolve_path_internal(self.db(), path);

if resolved.diagnostics.len() > 0 {
return None;
}

resolved.value
}

fn add_diagnostic(&self, diag: Diagnostic) {
self.diagnostics.borrow_mut().push(diag)
}
Expand Down Expand Up @@ -384,7 +394,7 @@ impl<'a> AnalyzerContext for FunctionScope<'a> {
}
}

fn maybe_resolve_path(&self, path: &ast::Path) -> Option<NamedThing> {
fn resolve_visible_path(&self, path: &ast::Path) -> Option<NamedThing> {
let resolved = self
.function
.module(self.db())
Expand All @@ -402,6 +412,19 @@ impl<'a> AnalyzerContext for FunctionScope<'a> {
}
}

fn resolve_any_path(&self, path: &ast::Path) -> Option<NamedThing> {
let resolved = self
.function
.module(self.db())
.resolve_path_internal(self.db(), path);

if resolved.diagnostics.len() > 0 {
return None;
}

resolved.value
}

fn get_context_type(&self) -> Option<TypeId> {
if let Ok(Some(NamedThing::Item(Item::Type(TypeDef::Struct(id))))) =
self.resolve_name("Context", Span::dummy())
Expand Down Expand Up @@ -530,8 +553,12 @@ impl AnalyzerContext for BlockScope<'_, '_> {
self.root.resolve_path(path, span)
}

fn maybe_resolve_path(&self, path: &ast::Path) -> Option<NamedThing> {
self.root.maybe_resolve_path(path)
fn resolve_visible_path(&self, path: &ast::Path) -> Option<NamedThing> {
self.root.resolve_visible_path(path)
}

fn resolve_any_path(&self, path: &ast::Path) -> Option<NamedThing> {
self.root.resolve_any_path(path)
}

fn add_diagnostic(&self, diag: Diagnostic) {
Expand Down Expand Up @@ -634,7 +661,7 @@ impl<T> OptionExt for Option<T> {

/// Check an item visibility and sink diagnostics if an item is invisible from
/// the scope.
fn check_visibility(context: &dyn AnalyzerContext, named_thing: &NamedThing, span: Span) {
pub fn check_visibility(context: &dyn AnalyzerContext, named_thing: &NamedThing, span: Span) {
if let NamedThing::Item(item) = named_thing {
let item_module = item
.module(context.db())
Expand Down
44 changes: 41 additions & 3 deletions crates/analyzer/src/namespace/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ use crate::context::AnalyzerContext;
use crate::display::DisplayWithDb;
use crate::display::Displayable;
use crate::errors::TypeError;
use crate::namespace::items::{ContractId, EnumId, FunctionSigId, ImplId, StructId, TraitId};
use crate::namespace::items::{
ContractId, EnumId, FunctionId, FunctionSigId, ImplId, Item, StructId, TraitId,
};
use crate::AnalyzerDb;

use fe_common::impl_intern_key;
Expand Down Expand Up @@ -55,6 +57,9 @@ pub enum Type {
SPtr(TypeId),
Mut(TypeId),
}

type TraitFunctionLookup = (Vec<(FunctionId, ImplId)>, Vec<(FunctionId, ImplId)>);

#[derive(Default, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Copy, Clone)]
pub struct TypeId(pub(crate) u32);
impl_intern_key!(TypeId);
Expand Down Expand Up @@ -162,8 +167,41 @@ impl TypeId {
db.impl_for(*self, trait_)
}

// Signature for the function with the given name defined directly on the type.
// Does not consider trait impls.
/// Looks up all possible candidates of the given function name that are implemented via traits.
/// Groups results in two lists, the first contains all theoretical possible candidates and
/// the second contains only those that are actually callable because the trait is in scope.
pub fn trait_function_candidates(
&self,
context: &mut dyn AnalyzerContext,
fn_name: &str,
) -> TraitFunctionLookup {
let candidates = context
.db()
.all_impls(*self)
.iter()
.cloned()
.filter_map(|_impl| {
_impl
.function(context.db(), fn_name)
.map(|fun| (fun, _impl))
})
.collect::<Vec<_>>();

let in_scope_candidates = candidates
.iter()
.cloned()
.filter(|(_, _impl)| {
context
.module()
.is_in_scope(context.db(), Item::Trait(_impl.trait_id(context.db())))
})
.collect::<Vec<_>>();

(candidates, in_scope_candidates)
}

/// Signature for the function with the given name defined directly on the type.
/// Does not consider trait impls.
pub fn function_sig(&self, db: &dyn AnalyzerDb, name: &str) -> Option<FunctionSigId> {
match self.typ(db) {
Type::SPtr(inner) => inner.function_sig(db, name),
Expand Down
Loading