Skip to content

Commit dabb93a

Browse files
Merge pull request #1249 from GrahamTheCoder/claude/fix-issue-1225-invocation-node-not-in-tree
Claude/fix issue 1225 invocation node not in tree
2 parents 0e1ce42 + 8af8bc3 commit dabb93a

File tree

2 files changed

+65
-1
lines changed

2 files changed

+65
-1
lines changed

CodeConverter/CSharp/CommonConversions.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -664,11 +664,28 @@ public bool HasOutAttribute(VBSyntax.AttributeListSyntax a)
664664

665665
public bool IsExtensionAttribute(VBSyntax.AttributeSyntax a)
666666
{
667+
if (a.SyntaxTree != SemanticModel.SyntaxTree)
668+
return AttributeNameMatches(a, "Extension");
667669
return (SemanticModel.GetTypeInfo(a).ConvertedType?.GetFullMetadataName())
668670
?.Equals(ExtensionAttributeType.FullName, StringComparison.Ordinal) == true;
669671
}
670672

671-
public bool IsOutAttribute(VBSyntax.AttributeSyntax a) => SemanticModel.GetTypeInfo(a).ConvertedType.IsOutAttribute();
673+
public bool IsOutAttribute(VBSyntax.AttributeSyntax a)
674+
{
675+
if (a.SyntaxTree != SemanticModel.SyntaxTree)
676+
return AttributeNameMatches(a, "Out");
677+
return SemanticModel.GetTypeInfo(a).ConvertedType.IsOutAttribute();
678+
}
679+
680+
// SemanticModel.GetTypeInfo throws when the node is not in its syntax tree; fall back to name matching.
681+
private static bool AttributeNameMatches(VBSyntax.AttributeSyntax a, string shortName)
682+
{
683+
var name = a.Name.ToString();
684+
return name.Equals(shortName, StringComparison.Ordinal) ||
685+
name.Equals(shortName + "Attribute", StringComparison.Ordinal) ||
686+
name.EndsWith("." + shortName, StringComparison.Ordinal) ||
687+
name.EndsWith("." + shortName + "Attribute", StringComparison.Ordinal);
688+
}
672689

673690
public ISymbol GetCsOriginalSymbolOrNull(ISymbol symbol)
674691
{

Tests/CSharp/ExpressionTests/ByRefTests.cs

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System.Threading.Tasks;
2+
using ICSharpCode.CodeConverter.Common;
3+
using ICSharpCode.CodeConverter.CSharp;
24
using ICSharpCode.CodeConverter.Tests.TestRunners;
35
using Xunit;
46

@@ -914,4 +916,49 @@ public static void LogAndReset(ref int arg)
914916
}");
915917
}
916918

919+
[Fact]
920+
public async Task Issue1225_OutAttributeOnParameterFromOtherFileDoesNotCrashAsync()
921+
{
922+
// Issue #1225: IsOutAttribute called SemanticModel.GetTypeInfo(attribute) on an attribute node
923+
// that belongs to a different syntax tree (parameter declared in a separate source file).
924+
// This caused ArgumentException "Knoten ist nicht innerhalb Syntaxbaum" /
925+
// "Node is not within syntax tree". The fix falls back to name-based checking
926+
// when the attribute's syntax tree differs from the current semantic model's tree.
927+
var serviceFileContent = @"Imports System.Runtime.InteropServices
928+
Public Class LicenseService
929+
Public Shared ReadOnly Property Instance As LicenseService
930+
Get
931+
Return New LicenseService()
932+
End Get
933+
End Property
934+
935+
Public Function GetLicenseMaybe(<Out> ByRef licenseName As String) As Boolean
936+
licenseName = Nothing
937+
Return False
938+
End Function
939+
End Class";
940+
941+
var callerFileContent = @"Public Class Caller
942+
Public Sub Test(licenseName As String)
943+
Dim res = LicenseService.Instance.GetLicenseMaybe(licenseName)
944+
End Sub
945+
End Class";
946+
947+
var options = new TextConversionOptions(DefaultReferences.With()) { ShowCompilationErrors = true };
948+
var languageConversion = new VBToCSConversion { ConversionOptions = options };
949+
var serviceTree = languageConversion.CreateTree(serviceFileContent);
950+
var callerTree = languageConversion.CreateTree(callerFileContent);
951+
952+
// Add both files to the same project so that the VB compilation sees both
953+
var serviceDoc = await languageConversion.CreateProjectDocumentFromTreeAsync(serviceTree, options.References);
954+
var callerDoc = serviceDoc.Project.AddDocumentFromTree(callerTree);
955+
956+
// Convert only the caller document; its parameter info comes from the service file's syntax tree
957+
var result = await ProjectConversion.ConvertSingleAsync<VBToCSConversion>(callerDoc, options);
958+
var output = (result.ConvertedCode ?? "") + (result.GetExceptionsAsString() ?? "");
959+
960+
Assert.DoesNotContain("#error", output);
961+
Assert.Contains("GetLicenseMaybe", output);
962+
}
963+
917964
}

0 commit comments

Comments
 (0)