0

Suppose I created two Compilation objects, initialized both with the same SyntaxTree, obtained two SemanticModel objects and used each to obtain a symbol to the same declaration.

Here is the complete unit test.

using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using NUnit.Framework;
using System.Linq;

namespace CSTool.UnitTests
{
    public class RoslynScratch
    {
        private const string CODE = @"namespace N
{
    public class T
    {
    }
}";
        [Test]
        public void SameSymbols()
        {
            var syntaxTree = CSharpSyntaxTree.ParseText(CODE);
            var node = syntaxTree.GetCompilationUnitRoot().DescendantNodes().OfType<TypeDeclarationSyntax>().FirstOrDefault();
            Assert.IsNotNull(node);
            var s1 = GetDeclSymbol(node);
            var s2 = GetDeclSymbol(node);
            Assert.IsFalse(SymbolEqualityComparer.Default.Equals(s1, s2));
            Assert.IsNotNull(GetDeclNode(s1));
            Assert.AreEqual(GetDeclNode(s1), GetDeclNode(s2));
        }

        private static SyntaxNode GetDeclNode(ISymbol s) => s.DeclaringSyntaxReferences.FirstOrDefault().GetSyntax();

        private static ISymbol GetDeclSymbol(TypeDeclarationSyntax node)
        {
            var compilation = CSharpCompilation.Create("test", new[] { node.SyntaxTree });
            var model = compilation.GetSemanticModel(node.SyntaxTree);
            return model.GetDeclaredSymbol(node);
        }
    }
}

Notice the two symbols are not equal, but we can get to their respective declarations and because both symbols are associated with the same SyntaxTree object we can figure out they are equivalent.

How can one figure out that these symbols are equivalent, if the syntax trees are different, but the code is the same?

EDIT 1

I have slightly modified the question - I am looking for the way to establish equivalence if the syntax trees are different, but the source code is the exactly the same for both trees.

EDIT 2

I have just realised that if the source code is the same, then instead of comparing the nodes we can:

  1. Compare the spans of the respective SyntaxNode objects associated with the symbols.
  2. If equal, compare the two SourceText objects associated with the syntax trees

I wonder if there is a better way.

mark
  • 59,016
  • 79
  • 296
  • 580
  • "syntax trees are different, but the source code is the exactly the same for both trees" --> This is a little perplexing. If the source code is identical, the compiler frontend produces identical syntax trees. – momo Nov 06 '21 at 21:13
  • What do you mean? If I create two syntax trees by running `CSharpSyntaxTree.ParseText(CODE)` the generated syntax trees fail the `Assert.AreEqual` test, even though the source code is identical. Could you elaborate? – mark Nov 06 '21 at 22:38
  • You can compare two syntax trees by using the [IsEquivalentTo](https://learn.microsoft.com/en-us/dotnet/api/microsoft.codeanalysis.syntaxtree.isequivalentto?view=roslyn-dotnet-3.11.0) method: `syntaxTree1.IsEquivalentTo(syntaxTree2)` – momo Nov 07 '21 at 07:23
  • Works as well for objects of type `SyntaxNode`, e.g. `node1.IsEquivalentTo(node2)`. – momo Nov 07 '21 at 07:29
  • I missed the existence of `IsEquivalentTo`. Could you arrange your reply as an answer, so I could credit you? – mark Nov 08 '21 at 17:44
  • To answer the original question: symbols from different compilations are not equal. They are simply not the same symbol from the API point of view even when exactly equivalent source code or metadata references were used to produce them. So it's necessary to come up with a criteria to compare the symbols by to decide if they're "equivalent". One such criteria exists internally in Roslyn in [SymbolEquivalenceComparer](https://github.com/dotnet/roslyn/blob/main/src/Workspaces/SharedUtilitiesAndExtensions/Compiler/Core/Utilities/SymbolEquivalenceComparer.cs). – Rikki Gibson Nov 13 '21 at 20:27
  • I have noticed there are a lot of internal goodies in Roslyn. Alas, they are internal. – mark Nov 15 '21 at 15:26

1 Answers1

1

The IsEquivalentTo(...) method can be used to compare the equality of two trees, nodes, tokens or trivias. For example

var syntaxTree1 = CSharpSyntaxTree.ParseText(CODE);
var syntaxTree2 = CSharpSyntaxTree.ParseText(CODE);

Assert.IsTrue(syntaxTree1.IsEquivalentTo(syntaxTree2));
momo
  • 3,313
  • 2
  • 19
  • 37