From eba1489b6ffa6db7f0eaefce6e06d53cd9066f7b Mon Sep 17 00:00:00 2001 From: Techatrix Date: Sun, 28 Jul 2024 17:09:56 +0200 Subject: [PATCH] functions inside namespaces can't have self parameters --- src/analysis.zig | 1 + src/features/completions.zig | 9 +-- tests/lsp_features/completion.zig | 88 ++++++++++++++++++++++++++ tests/lsp_features/semantic_tokens.zig | 14 ++-- tests/lsp_features/signature_help.zig | 7 ++ 5 files changed, 110 insertions(+), 9 deletions(-) diff --git a/src/analysis.zig b/src/analysis.zig index c560dae8a..ff396e21e 100644 --- a/src/analysis.zig +++ b/src/analysis.zig @@ -348,6 +348,7 @@ pub fn hasSelfParam(analyser: *Analyser, func_ty: Type) error{OutOfMemory}!bool const fn_token = func_node.handle.tree.nodes.items(.main_token)[func_node.node]; const in_container = try innermostContainer(func_node.handle, func_node.handle.tree.tokens.items(.start)[fn_token]); std.debug.assert(in_container.is_type_val); + if (in_container.isNamespace()) return false; return analyser.firstParamIs(func_ty, in_container); } diff --git a/src/features/completions.zig b/src/features/completions.zig index 136374fb9..452a9c7ae 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -321,10 +321,11 @@ fn functionTypeCompletion( const use_snippets = builder.server.config.enable_snippets and builder.server.client_capabilities.supports_snippets; - const has_self_param = if (parent_container_ty) |container_ty| - if (container_ty.is_type_val) false else try builder.analyser.firstParamIs(func_ty, container_ty.typeOf(builder.analyser)) - else - false; + const has_self_param = if (parent_container_ty) |container_ty| blk: { + if (container_ty.is_type_val) break :blk false; + if (container_ty.isNamespace()) break :blk false; + break :blk try builder.analyser.firstParamIs(func_ty, container_ty.typeOf(builder.analyser)); + } else false; const insert_range, const replace_range, const new_text_format = prepareFunctionCompletion(builder); diff --git a/tests/lsp_features/completion.zig b/tests/lsp_features/completion.zig index fc1a8ce51..b50642d2a 100644 --- a/tests/lsp_features/completion.zig +++ b/tests/lsp_features/completion.zig @@ -1076,6 +1076,50 @@ test "switch capture by ref" { }); } +test "namespace" { + try testCompletion( + \\const namespace = struct {}; + \\const bar = namespace. + , &.{}); + try testCompletion( + \\const namespace = struct { + \\ fn alpha() void {} + \\ fn beta(_: anytype) void {} + \\ fn gamma(_: @This()) void {} + \\}; + \\const bar = namespace. + , &.{ + .{ .label = "alpha", .kind = .Function, .detail = "fn () void" }, + .{ .label = "beta", .kind = .Function, .detail = "fn (_: anytype) void" }, + .{ .label = "gamma", .kind = .Function, .detail = "fn (_: @This()) void" }, + }); + try testCompletion( + \\const namespace = struct { + \\ fn alpha() void {} + \\ fn beta(_: anytype) void {} + \\ fn gamma(_: @This()) void {} + \\}; + \\const instance: namespace = undefined; + \\const bar = instance. + , &.{ + .{ .label = "alpha", .kind = .Function, .detail = "fn () void" }, + .{ .label = "beta", .kind = .Function, .detail = "fn (_: anytype) void" }, + .{ .label = "gamma", .kind = .Function, .detail = "fn (_: @This()) void" }, + }); + try testCompletion( + \\fn alpha() void {} + \\fn beta(_: anytype) void {} + \\fn gamma(_: @This()) void {} + \\ + \\const foo: @This() = undefined; + \\const bar = foo.; + , &.{ + .{ .label = "alpha", .kind = .Function, .detail = "fn () void" }, + .{ .label = "beta", .kind = .Function, .detail = "fn (_: anytype) void" }, + .{ .label = "gamma", .kind = .Function, .detail = "fn (_: @This()) void" }, + }); +} + test "struct" { try testCompletion( \\const S = struct { @@ -1103,6 +1147,7 @@ test "struct" { try testCompletion( \\const Foo = struct { + \\ alpha: u32, \\ fn add(foo: Foo) Foo {} \\}; \\test { @@ -1113,6 +1158,7 @@ test "struct" { \\ . \\} , &.{ + .{ .label = "alpha", .kind = .Field, .detail = "u32" }, .{ .label = "add", .kind = .Method, .detail = "fn (foo: Foo) Foo" }, }); @@ -1146,6 +1192,7 @@ test "struct" { \\fn barImpl(_: *const Foo) void {} \\fn bazImpl(_: u32) void {} \\const Foo = struct { + \\ alpha: u32, \\ pub const foo = fooImpl; \\ pub const bar = barImpl; \\ pub const baz = bazImpl; @@ -1153,9 +1200,26 @@ test "struct" { \\const foo = Foo{}; \\const baz = foo.; , &.{ + .{ .label = "alpha", .kind = .Field, .detail = "u32" }, .{ .label = "foo", .kind = .Method, .detail = "fn (_: Foo) void" }, .{ .label = "bar", .kind = .Method, .detail = "fn (_: *const Foo) void" }, }); + try testCompletion( + \\alpha: u32, + \\ + \\fn alpha() void {} + \\fn beta(_: anytype) void {} + \\fn gamma(_: @This()) void {} + \\ + \\const Self = @This(); + \\const bar = Self.; + , &.{ + .{ .label = "alpha", .kind = .Function, .detail = "fn () void" }, + .{ .label = "beta", .kind = .Function, .detail = "fn (_: anytype) void" }, + .{ .label = "gamma", .kind = .Function, .detail = "fn (_: @This()) void" }, + .{ .label = "Self", .kind = .Struct }, + .{ .label = "bar", .kind = .Struct }, + }); } test "union" { @@ -1364,6 +1428,7 @@ test "enum" { \\ sef2, \\}; \\const S = struct { + \\ alpha: u32, \\ const Self = @This(); \\ pub fn f(_: *Self, _: SomeEnum) void {} \\}; @@ -1387,6 +1452,7 @@ test "enum" { \\ se: SomeEnum, \\}; \\const S = struct { + \\ alpha: u32, \\ const Self = @This(); \\ pub fn f(_: *Self, _: SCE) void {} \\}; @@ -2226,12 +2292,14 @@ test "usingnamespace" { \\ }; \\} \\const Foo = struct { + \\ alpha: u32, \\ pub usingnamespace Bar(Foo); \\ fn deinit(self: Foo) void { _ = self; } \\}; \\const foo: Foo = undefined; \\const bar = foo. , &.{ + .{ .label = "alpha", .kind = .Field, .detail = "u32" }, .{ .label = "inner", .kind = .Method, .detail = "fn (self: Self) void" }, .{ .label = "deinit", .kind = .Method, .detail = "fn (self: Foo) void" }, }); @@ -2404,11 +2472,13 @@ test "either" { \\ fn alpha() void {} \\}; \\const Beta = struct { + \\ field: u32, \\ fn beta(_: @This()) void {} \\}; \\const foo: if (undefined) Alpha else Beta = undefined; \\const bar = foo. , &.{ + .{ .label = "field", .kind = .Field, .detail = "u32" }, .{ .label = "alpha", .kind = .Function, .detail = "fn () void" }, .{ .label = "beta", .kind = .Method, .detail = "fn (_: @This()) void" }, }); @@ -2417,6 +2487,7 @@ test "either" { \\ fn alpha() void {} \\}; \\const Beta = struct { + \\ field: u32, \\ fn beta(_: @This()) void {} \\}; \\const alpha: Alpha = undefined; @@ -2424,6 +2495,7 @@ test "either" { \\const gamma = if (undefined) alpha else beta; \\const foo = gamma. , &.{ + .{ .label = "field", .kind = .Field, .detail = "u32" }, .{ .label = "alpha", .kind = .Function, .detail = "fn () void" }, .{ .label = "beta", .kind = .Method, .detail = "fn (_: @This()) void" }, }); @@ -2537,11 +2609,13 @@ test "filesystem" { test "label details disabled" { try testCompletionWithOptions( \\const S = struct { + \\ alpha: u32, \\ fn f(self: S) void {} \\}; \\const s = S{}; \\s. , &.{ + .{ .label = "alpha", .kind = .Field, .detail = "u32" }, .{ .label = "f", .labelDetails = .{ @@ -2556,11 +2630,13 @@ test "label details disabled" { }); try testCompletionWithOptions( \\const S = struct { + \\ alpha: u32, \\ fn f(self: S, value: u32) !void {} \\}; \\const s = S{}; \\s. , &.{ + .{ .label = "alpha", .kind = .Field, .detail = "u32" }, .{ .label = "f", .labelDetails = .{ @@ -2825,6 +2901,7 @@ test "insert replace behaviour - function 'self parameter' detection" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f(self: S) void {} \\}; \\const s = S{}; @@ -2837,6 +2914,7 @@ test "insert replace behaviour - function 'self parameter' detection" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f(self: S) void {} \\}; \\S. @@ -2848,6 +2926,7 @@ test "insert replace behaviour - function 'self parameter' detection" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f() void {} \\}; \\S. @@ -2859,6 +2938,7 @@ test "insert replace behaviour - function 'self parameter' detection" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f(self: S) void {} \\}; \\const s = S{}; @@ -2871,6 +2951,7 @@ test "insert replace behaviour - function 'self parameter' detection" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f(self: @This()) void {} \\}; \\const s = S{}; @@ -2883,6 +2964,7 @@ test "insert replace behaviour - function 'self parameter' detection" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f(self: anytype) void {} \\}; \\const s = S{}; @@ -2895,6 +2977,7 @@ test "insert replace behaviour - function 'self parameter' detection" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f(self: S) void {} \\}; \\const s = S{}; @@ -2907,6 +2990,7 @@ test "insert replace behaviour - function 'self parameter' detection" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f(self: S, number: u32) void {} \\}; \\const s = S{}; @@ -2955,6 +3039,7 @@ test "insert replace behaviour - function with snippets" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f(self: S) void {} \\}; \\S. @@ -2968,6 +3053,7 @@ test "insert replace behaviour - function with snippets" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f(self: S, number: u32) void {} \\}; \\var s = S{}; @@ -2982,6 +3068,7 @@ test "insert replace behaviour - function with snippets" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f(self: S) void {} \\}; \\const s = S{}; @@ -2996,6 +3083,7 @@ test "insert replace behaviour - function with snippets" { try testCompletionTextEdit(.{ .source = \\const S = struct { + \\ alpha: u32, \\ fn f(self: S) void {} \\}; \\S. diff --git a/tests/lsp_features/semantic_tokens.zig b/tests/lsp_features/semantic_tokens.zig index e8a48b19c..3e97af18e 100644 --- a/tests/lsp_features/semantic_tokens.zig +++ b/tests/lsp_features/semantic_tokens.zig @@ -419,7 +419,7 @@ test "call" { \\const alpha = foo(0); , &.{ .{ "fn", .keyword, .{} }, - .{ "foo", .method, .{ .declaration = true, .generic = true } }, + .{ "foo", .function, .{ .declaration = true, .generic = true } }, .{ "a", .parameter, .{ .declaration = true } }, .{ "anytype", .type, .{} }, .{ "void", .type, .{} }, @@ -430,7 +430,7 @@ test "call" { .{ "const", .keyword, .{} }, .{ "alpha", .variable, .{ .declaration = true } }, .{ "=", .operator, .{} }, - .{ "foo", .method, .{ .generic = true } }, + .{ "foo", .function, .{ .generic = true } }, .{ "0", .number, .{} }, }); } @@ -1233,23 +1233,27 @@ test "function" { test "method" { try testSemanticTokens( \\const S = struct { + \\ alpha: u32, \\ fn create() S {} \\ fn doTheThing(self: S) void {} \\}; , &.{ .{ "const", .keyword, .{} }, - .{ "S", .namespace, .{ .declaration = true } }, + .{ "S", .@"struct", .{ .declaration = true } }, .{ "=", .operator, .{} }, .{ "struct", .keyword, .{} }, + .{ "alpha", .property, .{ .declaration = true } }, + .{ "u32", .type, .{} }, + .{ "fn", .keyword, .{} }, .{ "create", .function, .{ .declaration = true } }, - .{ "S", .namespace, .{} }, + .{ "S", .@"struct", .{} }, .{ "fn", .keyword, .{} }, .{ "doTheThing", .method, .{ .declaration = true } }, .{ "self", .parameter, .{ .declaration = true } }, - .{ "S", .namespace, .{} }, + .{ "S", .@"struct", .{} }, .{ "void", .type, .{} }, }); } diff --git a/tests/lsp_features/signature_help.zig b/tests/lsp_features/signature_help.zig index dc1c59895..4979af2a7 100644 --- a/tests/lsp_features/signature_help.zig +++ b/tests/lsp_features/signature_help.zig @@ -152,6 +152,7 @@ test "self parameter" { // argument: S try testSignatureHelp( \\const S = struct { + \\ alpha: u32, \\ fn foo(self: @This(), a: u32, b: void) bool {} \\}; \\const s: S = undefined; @@ -159,6 +160,7 @@ test "self parameter" { , "fn foo(self: @This(), a: u32, b: void) bool", 2); try testSignatureHelp( \\const S = struct { + \\ alpha: u32, \\ fn foo(self: @This(), a: u32, b: void) bool {} \\}; \\const foo = S.foo(undefined,); @@ -168,6 +170,7 @@ test "self parameter" { // argument: S try testSignatureHelp( \\const S = struct { + \\ alpha: u32, \\ fn foo(self: *@This(), a: u32, b: void) bool {} \\}; \\const s: S = undefined; @@ -175,6 +178,7 @@ test "self parameter" { , "fn foo(self: *@This(), a: u32, b: void) bool", 2); try testSignatureHelp( \\const S = struct { + \\ alpha: u32, \\ fn foo(self: *@This(), a: u32, b: void) bool {} \\}; \\const foo = S.foo(undefined,); @@ -184,6 +188,7 @@ test "self parameter" { // argument: *S try testSignatureHelp( \\const S = struct { + \\ alpha: u32, \\ fn foo(self: @This(), a: u32, b: void) bool {} \\}; \\const s: *S = undefined; @@ -194,6 +199,7 @@ test "self parameter" { // argument: *S try testSignatureHelp( \\const S = struct { + \\ alpha: u32, \\ fn foo(self: *@This(), a: u32, b: void) bool {} \\}; \\const s: *S = undefined; @@ -204,6 +210,7 @@ test "self parameter" { test "self parameter is anytype" { try testSignatureHelp( \\const S = struct { + \\ alpha: u32, \\ fn foo(self: anytype, a: u32, b: void) bool {} \\}; \\const s: S = undefined;