From 508a128e12f5c5bb30182cc0f1b8782ec50c8d8a Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Apr 2026 00:13:52 +0000 Subject: [PATCH 1/4] Fix #1225: Guard IsOutAttribute/IsExtensionAttribute against cross-tree attributes When a parameter is defined in an external assembly or different source file, its AttributeSyntax nodes belong to a different syntax tree than the current semantic model. Calling SemanticModel.GetTypeInfo on such nodes throws "Node is not within syntax tree". Fall back to a name-based check when the attribute's syntax tree differs from the model's tree. https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX --- CodeConverter/CSharp/CommonConversions.cs | 24 ++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/CodeConverter/CSharp/CommonConversions.cs b/CodeConverter/CSharp/CommonConversions.cs index 6647937fe..586bc430a 100644 --- a/CodeConverter/CSharp/CommonConversions.cs +++ b/CodeConverter/CSharp/CommonConversions.cs @@ -664,11 +664,33 @@ public bool HasOutAttribute(VBSyntax.AttributeListSyntax a) public bool IsExtensionAttribute(VBSyntax.AttributeSyntax a) { + // Guard against attributes from a different syntax tree (see IsOutAttribute for details). + if (a.SyntaxTree != SemanticModel.SyntaxTree) { + var name = a.Name.ToString(); + return name.Equals("Extension", StringComparison.Ordinal) || + name.Equals("ExtensionAttribute", StringComparison.Ordinal) || + name.EndsWith(".Extension", StringComparison.Ordinal) || + name.EndsWith(".ExtensionAttribute", StringComparison.Ordinal); + } return (SemanticModel.GetTypeInfo(a).ConvertedType?.GetFullMetadataName()) ?.Equals(ExtensionAttributeType.FullName, StringComparison.Ordinal) == true; } - public bool IsOutAttribute(VBSyntax.AttributeSyntax a) => SemanticModel.GetTypeInfo(a).ConvertedType.IsOutAttribute(); + public bool IsOutAttribute(VBSyntax.AttributeSyntax a) + { + // If the attribute's syntax tree differs from the current semantic model's tree (e.g. the + // parameter is declared in another source file or in a metadata assembly), calling + // SemanticModel.GetTypeInfo on it throws "Knoten ist nicht innerhalb Syntaxbaum" / + // "Node is not within syntax tree". Fall back to a name-based check in that case. + if (a.SyntaxTree != SemanticModel.SyntaxTree) { + var name = a.Name.ToString(); + return name.Equals("Out", StringComparison.Ordinal) || + name.Equals("OutAttribute", StringComparison.Ordinal) || + name.EndsWith(".Out", StringComparison.Ordinal) || + name.EndsWith(".OutAttribute", StringComparison.Ordinal); + } + return SemanticModel.GetTypeInfo(a).ConvertedType.IsOutAttribute(); + } public ISymbol GetCsOriginalSymbolOrNull(ISymbol symbol) { From beed8196e18074fbb7bc6e2920e5d0dd97027df8 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Apr 2026 00:16:13 +0000 Subject: [PATCH 2/4] Add regression test for issue #1225 Adds a unit test in the existing ByRefTests style that verifies conversion of an external-library method call (with metadata parameter attributes) does not throw "Node is not within syntax tree". https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX --- Tests/CSharp/ExpressionTests/ByRefTests.cs | 47 ++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/Tests/CSharp/ExpressionTests/ByRefTests.cs b/Tests/CSharp/ExpressionTests/ByRefTests.cs index a5badfd3c..1f80233d9 100644 --- a/Tests/CSharp/ExpressionTests/ByRefTests.cs +++ b/Tests/CSharp/ExpressionTests/ByRefTests.cs @@ -1,4 +1,6 @@ using System.Threading.Tasks; +using ICSharpCode.CodeConverter.Common; +using ICSharpCode.CodeConverter.CSharp; using ICSharpCode.CodeConverter.Tests.TestRunners; using Xunit; @@ -914,4 +916,49 @@ public static void LogAndReset(ref int arg) }"); } + [Fact] + public async Task Issue1225_OutAttributeOnParameterFromOtherFileDoesNotCrashAsync() + { + // Issue #1225: IsOutAttribute called SemanticModel.GetTypeInfo(attribute) on an attribute node + // that belongs to a different syntax tree (parameter declared in a separate source file). + // This caused ArgumentException "Knoten ist nicht innerhalb Syntaxbaum" / + // "Node is not within syntax tree". The fix falls back to name-based checking + // when the attribute's syntax tree differs from the current semantic model's tree. + var serviceFileContent = @"Imports System.Runtime.InteropServices +Public Class LicenseService + Public Shared ReadOnly Property Instance As LicenseService + Get + Return New LicenseService() + End Get + End Property + + Public Function GetLicenseMaybe( ByRef licenseName As String) As Boolean + licenseName = Nothing + Return False + End Function +End Class"; + + var callerFileContent = @"Public Class Caller + Public Sub Test(licenseName As String) + Dim res = LicenseService.Instance.GetLicenseMaybe(licenseName) + End Sub +End Class"; + + var options = new TextConversionOptions(References); + var languageConversion = new VBToCSConversion { ConversionOptions = options }; + var serviceTree = languageConversion.CreateTree(serviceFileContent); + var callerTree = languageConversion.CreateTree(callerFileContent); + + // Add both files to the same project so that the VB compilation sees both + var serviceDoc = await languageConversion.CreateProjectDocumentFromTreeAsync(serviceTree, options.References); + var callerDoc = serviceDoc.Project.AddDocumentFromTree(callerTree); + + // Convert only the caller document; its parameter info comes from the service file's syntax tree + var result = await ProjectConversion.ConvertSingleAsync(callerDoc, options); + var output = (result.ConvertedCode ?? "") + (result.GetExceptionsAsString() ?? ""); + + Assert.DoesNotContain("#error", output); + Assert.Contains("GetLicenseMaybe", output); + } + } \ No newline at end of file From 9a8c99992a2da03a6c6748fe7cb01bf0708a9fd4 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Apr 2026 00:18:05 +0000 Subject: [PATCH 3/4] Fix Issue1225 regression test: use DefaultReferences instead of private References The previous test commit used `References` which is a `private static` member of ConverterTestBase and not accessible from the derived ByRefTests class. Replace with `DefaultReferences.With()` (equivalent references) and also add `ShowCompilationErrors = true` for consistency with other tests. https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX --- Tests/CSharp/ExpressionTests/ByRefTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tests/CSharp/ExpressionTests/ByRefTests.cs b/Tests/CSharp/ExpressionTests/ByRefTests.cs index 1f80233d9..2d0be02dc 100644 --- a/Tests/CSharp/ExpressionTests/ByRefTests.cs +++ b/Tests/CSharp/ExpressionTests/ByRefTests.cs @@ -944,7 +944,7 @@ Public Sub Test(licenseName As String) End Sub End Class"; - var options = new TextConversionOptions(References); + var options = new TextConversionOptions(DefaultReferences.With()) { ShowCompilationErrors = true }; var languageConversion = new VBToCSConversion { ConversionOptions = options }; var serviceTree = languageConversion.CreateTree(serviceFileContent); var callerTree = languageConversion.CreateTree(callerFileContent); From 8af8bc368d6414ad8ed44da14d6fe685520d2aed Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Apr 2026 22:10:44 +0000 Subject: [PATCH 4/4] Refactor #1225: extract AttributeNameMatches helper, remove duplicate logic https://claude.ai/code/session_01AkwUvu3XuCdj3D4axoX4UX --- CodeConverter/CSharp/CommonConversions.cs | 33 ++++++++++------------- 1 file changed, 14 insertions(+), 19 deletions(-) diff --git a/CodeConverter/CSharp/CommonConversions.cs b/CodeConverter/CSharp/CommonConversions.cs index 586bc430a..8af2c712a 100644 --- a/CodeConverter/CSharp/CommonConversions.cs +++ b/CodeConverter/CSharp/CommonConversions.cs @@ -664,34 +664,29 @@ public bool HasOutAttribute(VBSyntax.AttributeListSyntax a) public bool IsExtensionAttribute(VBSyntax.AttributeSyntax a) { - // Guard against attributes from a different syntax tree (see IsOutAttribute for details). - if (a.SyntaxTree != SemanticModel.SyntaxTree) { - var name = a.Name.ToString(); - return name.Equals("Extension", StringComparison.Ordinal) || - name.Equals("ExtensionAttribute", StringComparison.Ordinal) || - name.EndsWith(".Extension", StringComparison.Ordinal) || - name.EndsWith(".ExtensionAttribute", StringComparison.Ordinal); - } + if (a.SyntaxTree != SemanticModel.SyntaxTree) + return AttributeNameMatches(a, "Extension"); return (SemanticModel.GetTypeInfo(a).ConvertedType?.GetFullMetadataName()) ?.Equals(ExtensionAttributeType.FullName, StringComparison.Ordinal) == true; } public bool IsOutAttribute(VBSyntax.AttributeSyntax a) { - // If the attribute's syntax tree differs from the current semantic model's tree (e.g. the - // parameter is declared in another source file or in a metadata assembly), calling - // SemanticModel.GetTypeInfo on it throws "Knoten ist nicht innerhalb Syntaxbaum" / - // "Node is not within syntax tree". Fall back to a name-based check in that case. - if (a.SyntaxTree != SemanticModel.SyntaxTree) { - var name = a.Name.ToString(); - return name.Equals("Out", StringComparison.Ordinal) || - name.Equals("OutAttribute", StringComparison.Ordinal) || - name.EndsWith(".Out", StringComparison.Ordinal) || - name.EndsWith(".OutAttribute", StringComparison.Ordinal); - } + if (a.SyntaxTree != SemanticModel.SyntaxTree) + return AttributeNameMatches(a, "Out"); return SemanticModel.GetTypeInfo(a).ConvertedType.IsOutAttribute(); } + // SemanticModel.GetTypeInfo throws when the node is not in its syntax tree; fall back to name matching. + private static bool AttributeNameMatches(VBSyntax.AttributeSyntax a, string shortName) + { + var name = a.Name.ToString(); + return name.Equals(shortName, StringComparison.Ordinal) || + name.Equals(shortName + "Attribute", StringComparison.Ordinal) || + name.EndsWith("." + shortName, StringComparison.Ordinal) || + name.EndsWith("." + shortName + "Attribute", StringComparison.Ordinal); + } + public ISymbol GetCsOriginalSymbolOrNull(ISymbol symbol) { if (symbol == null) return null;