From 55a6f486c4a2e4273fbec70f4b7ebeff10338c47 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 3 May 2026 19:37:29 +0000 Subject: [PATCH 1/2] test: add crash-reproduction tests for XRefHrefFixer.FixPackage Three tests that crash before the fix is applied: - Fix_PackageMethodWithNoParenthesesInName_DoesNotThrow: name has no '(' so MethodNameRegex fails to match, methodName becomes "", uid.IndexOf("") == 0, uid.Substring(0, -1) throws. - Fix_PackageMethodNameAbsentFromUid_DoesNotThrow: methodName is not found in uid, IndexOf returns -1, uid.Substring(0, -2) throws. - Fix_PackagePropertyNameAbsentFromUid_DoesNotThrow: property name is not found in uid, IndexOf returns -1, uid.Substring(0, -2) throws. https://claude.ai/code/session_01WNaTJnpDwNjqyfL1uhYqJ4 --- UnityXrefMaps.Tests/XRefHrefFixerTests.cs | 71 +++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/UnityXrefMaps.Tests/XRefHrefFixerTests.cs b/UnityXrefMaps.Tests/XRefHrefFixerTests.cs index c6602a6..80dafbb 100644 --- a/UnityXrefMaps.Tests/XRefHrefFixerTests.cs +++ b/UnityXrefMaps.Tests/XRefHrefFixerTests.cs @@ -72,5 +72,76 @@ public void Package_Fix(string apiUrl, string name, string commentId, string exp Assert.Equal(expected, XRefHrefFixer.Fix(apiUrl, xrefMapReference, Array.Empty(), true)); } + + /// + /// The regex that extracts the bare method name requires a '(' character to match. + /// When the display name has no parentheses — e.g. Name = "MyMethod" — the regex + /// fails, producing an empty string. The code then called uid.Substring(0, -1), + /// which throws ArgumentOutOfRangeException. + /// + [Fact] + public void Fix_PackageMethodWithNoParenthesesInName_DoesNotThrow() + { + XrefMapReference xrefMapReference = new() + { + CommentId = "M:MyNamespace.MyClass.MyMethod", + Name = "MyMethod", + }; + + string result = XRefHrefFixer.Fix( + "https://docs.unity3d.com/Packages/test@1.0/api/", + xrefMapReference, + Array.Empty(), + isPackage: true); + + Assert.StartsWith("https://docs.unity3d.com/Packages/test@1.0/api/", result); + } + + /// + /// When the method name extracted from the display name — e.g. "DifferentMethod" from + /// "DifferentMethod()" — does not appear in the uid, e.g. + /// "MyNamespace.MyClass.ActualMethod", IndexOf returns -1. The code then called + /// uid.Substring(0, -2), which throws ArgumentOutOfRangeException. + /// + [Fact] + public void Fix_PackageMethodNameAbsentFromUid_DoesNotThrow() + { + XrefMapReference xrefMapReference = new() + { + CommentId = "M:MyNamespace.MyClass.ActualMethod", + Name = "DifferentMethod()", + }; + + string result = XRefHrefFixer.Fix( + "https://docs.unity3d.com/Packages/test@1.0/api/", + xrefMapReference, + Array.Empty(), + isPackage: true); + + Assert.StartsWith("https://docs.unity3d.com/Packages/test@1.0/api/", result); + } + + /// + /// When the property name — e.g. "DifferentProperty" — does not appear in the uid, + /// e.g. "MyNamespace.MyClass.ActualProperty", IndexOf returns -1. The code then called + /// uid.Substring(0, -2), which throws ArgumentOutOfRangeException. + /// + [Fact] + public void Fix_PackagePropertyNameAbsentFromUid_DoesNotThrow() + { + XrefMapReference xrefMapReference = new() + { + CommentId = "P:MyNamespace.MyClass.ActualProperty", + Name = "DifferentProperty", + }; + + string result = XRefHrefFixer.Fix( + "https://docs.unity3d.com/Packages/test@1.0/api/", + xrefMapReference, + Array.Empty(), + isPackage: true); + + Assert.StartsWith("https://docs.unity3d.com/Packages/test@1.0/api/", result); + } } } From 999e18bd3f4e161b39f94e63b3ffb79146ef1603 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 9 May 2026 17:52:24 +0000 Subject: [PATCH 2/2] fix: prevent crash in XRefHrefFixer.FixPackage on unexpected inputs When the display name had no parentheses, MethodNameRegex failed and returned an empty string, causing Substring(0, -1) to throw. When the extracted method or property name was absent from the uid, IndexOf returned -1, causing Substring(0, -2) to throw. Guard both paths and return a safe fallback URL instead. https://claude.ai/code/session_01WNaTJnpDwNjqyfL1uhYqJ4 --- UnityXrefMaps/XRefHrefFixer.cs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/UnityXrefMaps/XRefHrefFixer.cs b/UnityXrefMaps/XRefHrefFixer.cs index 8bdfab1..ca43ef1 100644 --- a/UnityXrefMaps/XRefHrefFixer.cs +++ b/UnityXrefMaps/XRefHrefFixer.cs @@ -39,15 +39,30 @@ private static string FixPackage(string apiUrl, string name, string type, string case "M": Match methodNameMatch = MethodNameRegex().Match(name); - string methodName = name.Substring(0, methodNameMatch.Groups[1].Value.Length); + // regex requires '(' — fall back to the full name when it does not match + string methodName = methodNameMatch.Success + ? name.Substring(0, methodNameMatch.Groups[1].Value.Length) + : name; - classFullName = uid.Substring(0, uid.IndexOf(methodName) - 1); + int methodIdx = uid.IndexOf(methodName); + if (methodIdx < 0) + { + return $"{apiUrl}{NonWordCharRegex().Replace(uid, "_")}.html"; + } + + classFullName = uid.Substring(0, methodIdx - 1); return $"{apiUrl}{classFullName}.html#{NonWordCharRegex().Replace(uid, "_")}"; case "P": case "F": default: - classFullName = uid.Substring(0, uid.IndexOf(name) - 1); + int nameIdx = uid.IndexOf(name); + if (nameIdx < 0) + { + return $"{apiUrl}{NonWordCharRegex().Replace(uid, "_")}.html"; + } + + classFullName = uid.Substring(0, nameIdx - 1); return $"{apiUrl}{classFullName}.html#{NonWordCharRegex().Replace(uid, "_")}"; }