Skip to content
Merged
Show file tree
Hide file tree
Changes from 6 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
2 changes: 2 additions & 0 deletions compiler/noirc_frontend/src/elaborator/patterns.rs
Original file line number Diff line number Diff line change
Expand Up @@ -877,6 +877,8 @@ impl Elaborator<'_> {
let typ = self.resolve_type(path.typ);
let check_self_param = false;

self.interner.push_type_ref_location(&typ, object_location);

let Some(method) = self.lookup_method(
&typ,
path.item.as_str(),
Expand Down
2 changes: 1 addition & 1 deletion compiler/noirc_frontend/src/elaborator/types.rs
Original file line number Diff line number Diff line change
Expand Up @@ -164,7 +164,7 @@ impl Elaborator<'_> {
match resolved_type {
Type::DataType(ref data_type, _) => {
// Record the location of the type reference
self.interner.push_type_ref_location(resolved_type.clone(), location);
self.interner.push_type_ref_location(&resolved_type, location);
if !is_synthetic {
self.interner.add_type_reference(
data_type.borrow().id,
Expand Down
8 changes: 6 additions & 2 deletions compiler/noirc_frontend/src/node_interner.rs
Original file line number Diff line number Diff line change
Expand Up @@ -876,8 +876,12 @@ impl NodeInterner {
}

/// Store [Location] of [Type] reference
pub fn push_type_ref_location(&mut self, typ: Type, location: Location) {
self.type_ref_locations.push((typ, location));
pub fn push_type_ref_location(&mut self, typ: &Type, location: Location) {
if !self.is_in_lsp_mode() {
return;
}

self.type_ref_locations.push((typ.clone(), location));
}

#[allow(clippy::too_many_arguments)]
Expand Down
54 changes: 51 additions & 3 deletions compiler/noirc_frontend/src/parser/parser/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -286,6 +286,7 @@ impl Parser<'_> {
/// | ComptimeExpression
/// | UnquoteExpression
/// | TypePathExpression
/// | NamelessTypePathExpression
/// | AsTraitPath
/// | ResolvedExpression
/// | InternedExpression
Expand Down Expand Up @@ -353,8 +354,8 @@ impl Parser<'_> {
return Some(kind);
}

if let Some(as_trait_path) = self.parse_as_trait_path() {
return Some(ExpressionKind::AsTraitPath(as_trait_path));
if let Some(kind) = self.parse_nameless_type_path_or_as_trait_path_type_expression() {
return Some(kind);
}

if let Some(kind) = self.parse_resolved_expr() {
Expand All @@ -372,6 +373,25 @@ impl Parser<'_> {
None
}

/// NamelessTypePathExpression = '<' Type '>' '::' identifier ( '::' GenericTypeArgs )?
fn parse_nameless_type_path_or_as_trait_path_type_expression(
&mut self,
) -> Option<ExpressionKind> {
if !self.eat_less() {
return None;
}

let typ = self.parse_type_or_error();
if self.eat_keyword(Keyword::As) {
let as_trait_path = self.parse_as_trait_path_for_type_after_as_keyword(typ);
Some(ExpressionKind::AsTraitPath(as_trait_path))
} else {
self.eat_or_error(Token::Greater);
let type_path = self.parse_type_path_expr_for_type(typ);
Some(ExpressionKind::TypePath(type_path))
}
}

/// ResolvedExpression = unquote_marker
fn parse_resolved_expr(&mut self) -> Option<ExpressionKind> {
if let Some(token) = self.eat_kind(TokenKind::UnquoteMarker) {
Expand Down Expand Up @@ -628,6 +648,10 @@ impl Parser<'_> {
let typ = self.parse_primitive_type()?;
let typ = UnresolvedType { typ, location: self.location_since(start_location) };

Some(ExpressionKind::TypePath(self.parse_type_path_expr_for_type(typ)))
}

fn parse_type_path_expr_for_type(&mut self, typ: UnresolvedType) -> TypePath {
self.eat_or_error(Token::DoubleColon);

let item = if let Some(ident) = self.eat_ident() {
Expand All @@ -645,7 +669,7 @@ impl Parser<'_> {
generics
});

Some(ExpressionKind::TypePath(TypePath { typ, item, turbofish }))
TypePath { typ, item, turbofish }
}

/// Literal
Expand Down Expand Up @@ -1827,6 +1851,30 @@ mod tests {
assert!(type_path.turbofish.is_some());
}

#[test]
fn parses_type_path_with_tuple() {
let src = "<()>::foo";
let expr = parse_expression_no_errors(src);
let ExpressionKind::TypePath(type_path) = expr.kind else {
panic!("Expected type_path");
};
assert_eq!(type_path.typ.to_string(), "()");
assert_eq!(type_path.item.to_string(), "foo");
assert!(type_path.turbofish.is_none());
}

#[test]
fn parses_type_path_with_array_type() {
let src = "<[i32; 3]>::foo";
let expr = parse_expression_no_errors(src);
let ExpressionKind::TypePath(type_path) = expr.kind else {
panic!("Expected type_path");
};
assert_eq!(type_path.typ.to_string(), "[i32; 3]");
assert_eq!(type_path.item.to_string(), "foo");
assert!(type_path.turbofish.is_none());
}

#[test]
fn parses_unquote_var() {
let src = "$foo::bar";
Expand Down
10 changes: 9 additions & 1 deletion compiler/noirc_frontend/src/parser/parser/path.rs
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,14 @@ impl Parser<'_> {

let typ = self.parse_type_or_error();
self.eat_keyword_or_error(Keyword::As);

Some(self.parse_as_trait_path_for_type_after_as_keyword(typ))
}

pub(super) fn parse_as_trait_path_for_type_after_as_keyword(
&mut self,
typ: UnresolvedType,
) -> AsTraitPath {
let trait_path = self.parse_path_no_turbofish_or_error();
let trait_generics = self.parse_generic_type_args();
self.eat_or_error(Token::Greater);
Expand All @@ -204,7 +212,7 @@ impl Parser<'_> {
Ident::new(String::new(), self.location_at_previous_token_end())
};

Some(AsTraitPath { typ, trait_path, trait_generics, impl_item })
AsTraitPath { typ, trait_path, trait_generics, impl_item }
}
}

Expand Down
12 changes: 8 additions & 4 deletions compiler/noirc_frontend/src/resolve_locations.rs
Original file line number Diff line number Diff line change
Expand Up @@ -228,13 +228,17 @@ impl NodeInterner {

/// Attempts to resolve [Location] of [Type] based on [Location] of reference in code
pub(crate) fn try_resolve_type_ref(&self, location: Location) -> Option<Location> {
self.try_type_ref_at_location(location).and_then(|typ| match typ {
Type::DataType(struct_typ, _) => Some(struct_typ.borrow().location),
_ => None,
})
}

pub fn try_type_ref_at_location(&self, location: Location) -> Option<Type> {
self.type_ref_locations
.iter()
.find(|(_typ, type_ref_location)| type_ref_location.contains(&location))
.and_then(|(typ, _)| match typ {
Type::DataType(struct_typ, _) => Some(struct_typ.borrow().location),
_ => None,
})
.map(|(typ, _type_ref_location)| typ.clone())
}

fn try_resolve_type_alias(&self, location: Location) -> Option<Location> {
Expand Down
20 changes: 20 additions & 0 deletions test_programs/compile_success_empty/type_path/src/main.nr
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,30 @@ fn main() {
// whether a TypePath had generics or not, always resolved them, filling them
// up with Type::Error, and eventually leading to an ICE.
let _ = Field::from_be_bytes([1]);

// Make sure `<...>::name` compiles
let _: () = <()>::method();
let _: [i32; 3] = <[i32; 3]>::method();
}

pub struct Foo {}

impl Foo {
fn static() {}
}

trait Trait {
fn method() -> Self;
}

impl Trait for () {
fn method() -> () {
()
}
}

impl Trait for [i32; 3] {
fn method() -> [i32; 3] {
[1, 2, 3]
}
}
50 changes: 16 additions & 34 deletions tooling/lsp/src/requests/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,14 @@ use kinds::{FunctionCompletionKind, FunctionKind, RequestedItems};
use lsp_types::{CompletionItem, CompletionItemKind, CompletionParams, CompletionResponse};
use noirc_errors::{Location, Span};
use noirc_frontend::{
DataType, Kind, ParsedModule, Type, TypeBinding,
DataType, ParsedModule, Type, TypeBinding,
ast::{
AsTraitPath, AttributeTarget, BlockExpression, CallExpression, ConstructorExpression,
Expression, ExpressionKind, ForLoopStatement, GenericTypeArgs, Ident, IfExpression,
IntegerBitSize, ItemVisibility, LValue, Lambda, LetStatement, MemberAccessExpression,
MethodCallExpression, ModuleDeclaration, NoirFunction, NoirStruct, NoirTraitImpl, Path,
PathKind, Pattern, Statement, TraitBound, TraitImplItemKind, TypeImpl, TypePath,
UnresolvedGeneric, UnresolvedGenerics, UnresolvedType, UnresolvedTypeData,
UnresolvedTypeExpression, UseTree, UseTreeKind, Visitor,
ItemVisibility, LValue, Lambda, LetStatement, MemberAccessExpression, MethodCallExpression,
ModuleDeclaration, NoirFunction, NoirStruct, NoirTraitImpl, Path, PathKind, Pattern,
Statement, TraitBound, TraitImplItemKind, TypeImpl, TypePath, UnresolvedGeneric,
UnresolvedGenerics, UnresolvedType, UnresolvedTypeData, UseTree, UseTreeKind, Visitor,
},
graph::{CrateId, Dependency},
hir::{
Expand All @@ -35,7 +34,6 @@ use noirc_frontend::{
hir_def::traits::Trait,
node_interner::{FuncId, NodeInterner, ReferenceId, TypeId},
parser::{Item, ItemKind, ParsedSubModule},
shared::Signedness,
token::{MetaAttribute, Token, Tokens},
};
use sort_text::underscore_sort_text;
Expand Down Expand Up @@ -1802,35 +1800,19 @@ impl Visitor for NodeFinder<'_> {
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,
let location = type_path.typ.location;
let Some(typ) = self.interner.try_type_ref_at_location(location) else {
return true;
};

if let Some(typ) = typ {
let prefix = type_path.item.as_str();
self.complete_type_methods(
&typ,
prefix,
FunctionKind::Any,
FunctionCompletionKind::NameAndParameters,
false, // self_prefix
);
}
let prefix = type_path.item.as_str();
self.complete_type_methods(
&typ,
prefix,
FunctionKind::Any,
FunctionCompletionKind::NameAndParameters,
false, // self_prefix
);

false
}
Expand Down
24 changes: 24 additions & 0 deletions tooling/lsp/src/requests/completion/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -445,6 +445,30 @@ mod completion_tests {
.await;
}

#[test]
async fn test_complete_type_path_for_nameless_type() {
let src = r#"
trait One {
fn some_method() -> Self;
}

impl One for () {
fn some_method() -> Self {
1
}
}

fn main() {
<()>::some_meth>|<
}
"#;
assert_completion(
src,
vec![function_completion_item("some_method()", "some_method()", "fn()")],
)
.await;
}

#[test]
async fn test_complete_function_without_arguments() {
let src = r#"
Expand Down
17 changes: 17 additions & 0 deletions tooling/nargo_fmt/src/formatter/expression.rs
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,17 @@ impl ChunkFormatter<'_, '_> {
pub(super) fn format_type_path(&mut self, type_path: TypePath) -> ChunkGroup {
let mut group = ChunkGroup::new();
group.text(self.chunk(|formatter| {
let nameless = formatter.is_at(Token::Less);
if nameless {
formatter.write_token(Token::Less);
}

formatter.format_type(type_path.typ);

if nameless {
formatter.write_token(Token::Greater);
}

formatter.write_token(Token::DoubleColon);
formatter.write_identifier(type_path.item);
if let Some(turbofish) = type_path.turbofish {
Expand Down Expand Up @@ -2132,6 +2142,13 @@ global y = 1;
assert_format(src, expected);
}

#[test]
fn format_type_path_with_array_type() {
let src = "global x = < [ i32 ; 3 ] > :: max ;";
let expected = "global x = <[i32; 3]>::max;\n";
assert_format(src, expected);
}

#[test]
fn format_if_expression_without_else_one_expression() {
let src = "global x = if 1 { 2 } ;";
Expand Down