diff --git a/compiler/noirc_frontend/src/parser/parser/path.rs b/compiler/noirc_frontend/src/parser/parser/path.rs index 3e964d271a0..1c9c24f5376 100644 --- a/compiler/noirc_frontend/src/parser/parser/path.rs +++ b/compiler/noirc_frontend/src/parser/parser/path.rs @@ -1,10 +1,11 @@ -use crate::ast::{AsTraitPath, Path, PathKind, PathSegment, TypePath, UnresolvedType}; +use crate::ast::{AsTraitPath, Ident, Path, PathKind, PathSegment, TypePath, UnresolvedType}; use crate::macros_api::ExpressionKind; use crate::parser::{NoirParser, ParserError, ParserErrorReason}; use crate::token::{Keyword, Token}; use chumsky::prelude::*; +use noirc_errors::Span; use super::keyword; use super::primitives::{ident, path_segment, path_segment_no_turbofish, turbofish}; @@ -78,10 +79,19 @@ pub(super) fn type_path<'a>( ) -> impl NoirParser + 'a { primitive_type() .then_ignore(just(Token::DoubleColon)) - .then(ident()) + .then(ident().or_not()) .then(turbofish(type_parser)) - .map(|((typ, item), turbofish)| { + .validate(|((typ, item), turbofish), span, emit| { let turbofish = turbofish.unwrap_or_default(); + let item = if let Some(item) = item { + item + } else { + emit(ParserError::with_reason( + ParserErrorReason::ExpectedIdentifierAfterColons, + span, + )); + Ident::new(String::new(), Span::from(span.end()..span.end())) + }; ExpressionKind::TypePath(TypePath { typ, item, turbofish }) }) } diff --git a/tooling/lsp/src/requests/completion.rs b/tooling/lsp/src/requests/completion.rs index 5fe6f2cfebf..49608c2e06a 100644 --- a/tooling/lsp/src/requests/completion.rs +++ b/tooling/lsp/src/requests/completion.rs @@ -17,10 +17,11 @@ use noirc_frontend::{ ast::{ AsTraitPath, AttributeTarget, BlockExpression, CallExpression, ConstructorExpression, Expression, ExpressionKind, ForLoopStatement, GenericTypeArgs, Ident, IfExpression, - ItemVisibility, LValue, Lambda, LetStatement, MemberAccessExpression, MethodCallExpression, - NoirFunction, NoirStruct, NoirTraitImpl, Path, PathKind, Pattern, Statement, - TraitImplItemKind, TypeImpl, UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, - UnresolvedTypeData, UseTree, UseTreeKind, Visitor, + IntegerBitSize, ItemVisibility, LValue, Lambda, LetStatement, MemberAccessExpression, + MethodCallExpression, NoirFunction, NoirStruct, NoirTraitImpl, Path, PathKind, Pattern, + Signedness, Statement, TraitImplItemKind, TypeImpl, TypePath, UnresolvedGeneric, + UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UnresolvedTypeExpression, UseTree, + UseTreeKind, Visitor, }, graph::{CrateId, Dependency}, hir::def_map::{CrateDefMap, LocalModuleId, ModuleId}, @@ -29,7 +30,7 @@ use noirc_frontend::{ node_interner::ReferenceId, parser::{Item, ItemKind, ParsedSubModule}, token::{CustomAttribute, Token, Tokens}, - ParsedModule, StructType, Type, TypeBinding, + Kind, ParsedModule, StructType, Type, TypeBinding, }; use sort_text::underscore_sort_text; @@ -362,6 +363,8 @@ impl<'a> NodeFinder<'a> { self.local_variables_completion(&prefix); self.builtin_functions_completion(&prefix, function_completion_kind); self.builtin_values_completion(&prefix); + self.builtin_types_completion(&prefix); + self.type_parameters_completion(&prefix); if let Some(self_type) = &self.self_type { let self_prefix = true; self.complete_type_fields_and_methods( @@ -627,7 +630,15 @@ impl<'a> NodeFinder<'a> { }; for (name, methods) in methods_by_name { - for (func_id, _method_type) in methods.iter() { + for (func_id, method_type) in methods.iter() { + if function_kind == FunctionKind::Any { + if let Some(method_type) = method_type { + if method_type.unify(typ).is_err() { + continue; + } + } + } + if name_matches(name, prefix) { let completion_items = self.function_completion_items( name, @@ -1553,6 +1564,44 @@ impl<'a> Visitor for NodeFinder<'a> { false } + fn visit_type_path(&mut self, type_path: &TypePath, _: Span) -> bool { + if type_path.item.span().end() as usize != self.byte_index { + return true; + } + + let typ = match &type_path.typ.typ { + UnresolvedTypeData::FieldElement => Some(Type::FieldElement), + UnresolvedTypeData::Integer(signedness, integer_bit_size) => { + Some(Type::Integer(*signedness, *integer_bit_size)) + } + UnresolvedTypeData::Bool => Some(Type::Bool), + UnresolvedTypeData::String(UnresolvedTypeExpression::Constant(value, _)) => { + Some(Type::String(Box::new(Type::Constant( + *value, + Kind::Numeric(Box::new(Type::Integer( + Signedness::Unsigned, + IntegerBitSize::ThirtyTwo, + ))), + )))) + } + UnresolvedTypeData::Quoted(quoted_type) => Some(Type::Quoted(*quoted_type)), + _ => None, + }; + + if let Some(typ) = typ { + let prefix = &type_path.item.0.contents; + self.complete_type_methods( + &typ, + prefix, + FunctionKind::Any, + FunctionCompletionKind::NameAndParameters, + false, // self_prefix + ); + } + + false + } + fn visit_custom_attribute(&mut self, attribute: &CustomAttribute, target: AttributeTarget) { if self.byte_index != attribute.contents_span.end() as usize { return; diff --git a/tooling/lsp/src/requests/completion/tests.rs b/tooling/lsp/src/requests/completion/tests.rs index 41fc899b7e8..45eb79bd1c2 100644 --- a/tooling/lsp/src/requests/completion/tests.rs +++ b/tooling/lsp/src/requests/completion/tests.rs @@ -431,6 +431,27 @@ mod completion_tests { .await; } + #[test] + async fn test_complete_type_path_with_non_empty_name() { + let src = r#" + trait One { + fn one() -> Self; + } + + impl One for Field { + fn one() -> Self { + 1 + } + } + + fn main() { + Field::o>|< + } + "#; + assert_completion(src, vec![function_completion_item("one()", "one()", "fn() -> Field")]) + .await; + } + #[test] async fn test_complete_function_without_arguments() { let src = r#" @@ -756,6 +777,18 @@ mod completion_tests { ); } + #[test] + async fn test_suggest_builtin_types_in_any_position() { + let src = r#" + fn foo() { + i>|< + } + "#; + + let items = get_completions(src).await; + assert!(items.iter().any(|item| item.label == "i8")); + } + #[test] async fn test_suggest_true() { let src = r#" @@ -763,15 +796,11 @@ mod completion_tests { let x = t>|< } "#; - assert_completion_excluding_auto_import( - src, - vec![simple_completion_item( - "true", - CompletionItemKind::KEYWORD, - Some("bool".to_string()), - )], - ) - .await; + + let items = get_completions(src).await; + assert!(items + .iter() + .any(|item| item.label == "true" && item.kind == Some(CompletionItemKind::KEYWORD))); } #[test]