Skip to content

Commit 7261d9a

Browse files
antonsyndclaude
andcommitted
fix: verification remediation — spec, declaration position, missing tests, and snapshots (#565, #566)
Update language spec to mark inline out declarations as implemented. Fix inline variable declaration position to point to variable name instead of `out` keyword. Add missing out_inline_type_mismatch error test fixture, LSP tests for `in` modifier and out-inline-with-type token emission, and C# snapshot tests for all out inline declaration fixtures. Fix misleading HoverService fallback comment and null-conditional inconsistency in codegen. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent e18e09a commit 7261d9a

11 files changed

Lines changed: 151 additions & 6 deletions

File tree

docs/language_specification/parameter_modifiers.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ Calculate(in largeData); // or just Calculate(largeData)
243243
- *`out T``out T` parameter — parsed, type-checked, emitted*
244244
- *`in T``in T` parameter — parsed, type-checked, emitted*
245245
- *Call site `ref`/`out`/`in` keywords map directly to C#*
246-
- *Inline `out` declaration (`out value: int`) — not yet implemented*
246+
- *Inline `out` declaration (`out value: int`, `out value: auto`) — parsed, type-checked, emitted*
247247

248248
## See Also
249249

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#nullable enable
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using global::Sharpy;
8+
9+
public static partial class OutInlineAuto
10+
{
11+
public static bool TryParse(string s, out int result)
12+
{
13+
#line 2 "out_inline_auto.spy"
14+
result = global::Sharpy.Builtins.Int(s);
15+
#line 3 "out_inline_auto.spy"
16+
return true;
17+
}
18+
19+
public static void Main()
20+
{
21+
#line 6 "out_inline_auto.spy"
22+
var success = TryParse("42", out var value);
23+
#line 7 "out_inline_auto.spy"
24+
global::Sharpy.Builtins.Print(success, value);
25+
}
26+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
#nullable enable
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using global::Sharpy;
8+
9+
public static partial class OutInlineDeclaration
10+
{
11+
public static bool TryParse(string s, out int result)
12+
{
13+
#line 2 "out_inline_declaration.spy"
14+
result = global::Sharpy.Builtins.Int(s);
15+
#line 3 "out_inline_declaration.spy"
16+
return true;
17+
}
18+
19+
public static void Main()
20+
{
21+
#line 6 "out_inline_declaration.spy"
22+
var success = TryParse("42", out int value);
23+
#line 7 "out_inline_declaration.spy"
24+
global::Sharpy.Builtins.Print(success, value);
25+
}
26+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#nullable enable
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using global::Sharpy;
8+
9+
public static partial class OutInlineInIf
10+
{
11+
public static bool TryParse(string s, out int result)
12+
{
13+
#line 2 "out_inline_in_if.spy"
14+
result = global::Sharpy.Builtins.Int(s);
15+
#line 3 "out_inline_in_if.spy"
16+
return true;
17+
}
18+
19+
public static void Main()
20+
{
21+
#line 6 "out_inline_in_if.spy"
22+
if (TryParse("42", out int value))
23+
{
24+
#line 7 "out_inline_in_if.spy"
25+
global::Sharpy.Builtins.Print(value);
26+
}
27+
}
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#nullable enable
2+
3+
using System;
4+
using System.Collections.Generic;
5+
using System.Linq;
6+
using System.Threading.Tasks;
7+
using global::Sharpy;
8+
9+
public static partial class OutInlineMultiple
10+
{
11+
public static bool ParsePair(string s, out int a, out int b)
12+
{
13+
#line 2 "out_inline_multiple.spy"
14+
a = 10;
15+
#line 3 "out_inline_multiple.spy"
16+
b = 20;
17+
#line 4 "out_inline_multiple.spy"
18+
return true;
19+
}
20+
21+
public static void Main()
22+
{
23+
#line 7 "out_inline_multiple.spy"
24+
var success = ParsePair("x", out int first, out int second);
25+
#line 8 "out_inline_multiple.spy"
26+
global::Sharpy.Builtins.Print(success, first, second);
27+
}
28+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
Cannot pass argument of type 'str' to parameter of type 'int'
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
def try_parse(s: str, result: out int) -> bool:
2+
result = int(s)
3+
return True
4+
5+
def main():
6+
success = try_parse("42", out value: str)
7+
print(success, value)

src/Sharpy.Compiler/CodeGen/RoslynEmitter.Expressions.Access.Calls.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -868,7 +868,7 @@ private IEnumerable<ArgumentSyntax> GeneratePositionalArguments(
868868
{
869869
// Map the type: "auto" → var, otherwise use TypeSyntaxMapper
870870
TypeSyntax typeSyntax;
871-
if (modArg.InlineType?.Name == "auto")
871+
if (modArg.InlineType!.Name == "auto")
872872
{
873873
typeSyntax = IdentifierName("var");
874874
}

src/Sharpy.Compiler/Semantic/TypeChecker.Expressions.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,8 @@ private SemanticType CheckModifiedArgument(ModifiedArgument modArg)
125125
Kind = SymbolKind.Variable,
126126
Type = resolvedType,
127127
IsConstant = false,
128-
DeclarationLine = modArg.LineStart,
129-
DeclarationColumn = modArg.ColumnStart
128+
DeclarationLine = modArg.Argument.LineStart,
129+
DeclarationColumn = modArg.Argument.ColumnStart
130130
};
131131
_symbolTable.Define(newSymbol);
132132
SemanticBinding.SetVariableType(newSymbol, resolvedType);

src/Sharpy.Lsp.Tests/SemanticTokensTests.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -628,6 +628,33 @@ public void ModifiedArgument_OutModifier_EmitsKeywordToken()
628628
"'out' at the call site should produce a keyword token with length 3");
629629
}
630630

631+
[Fact]
632+
public void ModifiedArgument_InModifier_EmitsKeywordToken()
633+
{
634+
var tokens = CollectTokensFrom(
635+
"def process(x: in int):\n print(x)\ndef main():\n a: int = 5\n process(in a)");
636+
var keywords = tokens.Where(t => t.TokenType == TKeyword && t.Line == 4).ToList();
637+
keywords.Should().Contain(t => t.Length == 2,
638+
"'in' at the call site should produce a keyword token with length 2");
639+
}
640+
641+
[Fact]
642+
public void ModifiedArgument_OutInlineDeclaration_EmitsKeywordAndTypeTokens()
643+
{
644+
var tokens = CollectTokensFrom(
645+
"def try_parse(s: str, result: out int) -> bool:\n" +
646+
" result = int(s)\n" +
647+
" return True\n" +
648+
"def main():\n" +
649+
" try_parse(\"42\", out value: int)");
650+
var keywordsOnCallLine = tokens.Where(t => t.TokenType == TKeyword && t.Line == 4).ToList();
651+
keywordsOnCallLine.Should().Contain(t => t.Length == 3,
652+
"'out' at the call site should produce a keyword token with length 3");
653+
var typeTokensOnCallLine = tokens.Where(t => t.TokenType == TType && t.Line == 4).ToList();
654+
typeTokensOnCallLine.Should().Contain(t => t.Length == 3,
655+
"'int' inline type annotation should produce a type token");
656+
}
657+
631658
#endregion
632659

633660
#region LambdaExpression tokens

0 commit comments

Comments
 (0)