diff --git a/tests/CodeIndex.Tests/SymbolExtractorTests.cs b/tests/CodeIndex.Tests/SymbolExtractorTests.cs index e1a401d7ba..1a37a4081c 100644 --- a/tests/CodeIndex.Tests/SymbolExtractorTests.cs +++ b/tests/CodeIndex.Tests/SymbolExtractorTests.cs @@ -5690,6 +5690,85 @@ public int WrappedMethod() Assert.DoesNotContain(symbols, s => s.Kind == "property" && s.Name == "WrappedMethod"); } + [Fact] + public void Extract_CSharp_SplitReturnTypeLine_StillCapturesMethodPropertyAndIndexer() + { + // issue #361: when a long C# return type wraps onto the previous line, the + // method/property/indexer must still be emitted instead of being silently + // dropped by a per-line-only regex pass. + // issue #361: C# の長い戻り値型が前行へ折り返されても、method/property/indexer は + // per-line 前提の regex で silent drop されず、引き続き抽出される必要がある。 + var content = """ + using System.Collections.Generic; + + namespace CsMultilineSig; + + public class Svc + { + public Dictionary> + GetMapping( + string key, + int defaultValue) => new(); + + public Dictionary + Cache { get; } = new(); + + public T Create(string name) + where T : class, new() + => default!; + + public int + this[string key] { get => 0; set { } } + + public int Simple() => 0; + } + """; + var symbols = SymbolExtractor.Extract(1, "csharp", content); + + var getMapping = Assert.Single(symbols.Where(s => s.Kind == "function" && s.Name == "GetMapping")); + Assert.Equal("public", getMapping.Visibility); + Assert.Equal("Dictionary>", getMapping.ReturnType); + Assert.Equal(7, getMapping.StartLine); + Assert.Equal(10, getMapping.EndLine); + + var cache = Assert.Single(symbols.Where(s => s.Kind == "property" && s.Name == "Cache")); + Assert.Equal("public", cache.Visibility); + Assert.Equal("Dictionary", cache.ReturnType); + Assert.Equal(12, cache.StartLine); + Assert.Equal(13, cache.EndLine); + + var create = Assert.Single(symbols.Where(s => s.Kind == "function" && s.Name == "Create")); + Assert.Equal("public", create.Visibility); + Assert.Equal("T", create.ReturnType); + Assert.Equal(15, create.StartLine); + Assert.Equal(17, create.EndLine); + + var indexer = Assert.Single(symbols.Where(s => s.Kind == "function" && s.Name == "Item")); + Assert.Equal("public", indexer.Visibility); + Assert.Equal("int", indexer.ReturnType); + Assert.Equal(19, indexer.StartLine); + Assert.Equal(20, indexer.EndLine); + + Assert.Contains(symbols, s => s.Kind == "function" && s.Name == "Simple" && s.ReturnType == "int"); + Assert.Equal( + new[] + { + ("class", "Svc"), + ("function", "Create"), + ("function", "GetMapping"), + ("function", "Item"), + ("function", "Simple"), + ("namespace", "CsMultilineSig"), + ("property", "Cache"), + }, + symbols + .Where(s => s.Kind != "import") + .Select(s => (s.Kind, s.Name)) + .OrderBy(x => x.Kind, StringComparer.Ordinal) + .ThenBy(x => x.Name, StringComparer.Ordinal) + .ToArray()); + } + [Fact] public void Extract_CSharp_AllmanBlockBodiedProperty_WithIntermediateBlockComment_IsExtracted() {