I was working on a project and I found that the operator I used and the one I declared were not equal.
I made a minimum reproducible example:
var tree = CSharpSyntaxTree.ParseText(@"
bool a = 3 > 5;
namespace System{
public struct Int32
{
public static extern bool operator > (int a, int b);
}
public struct Boolean { }
}");
var compilation = CSharpCompilation.Create("bla").AddSyntaxTrees(tree);
var model = compilation.GetSemanticModel(tree);
var usedSymbol = model.GetSymbolInfo(tree.GetRoot().DescendantNodes().OfType<BinaryExpressionSyntax>().Single()).Symbol;
var declaredSymbol = model.GetDeclaredSymbol(tree.GetRoot().DescendantNodes().OfType<OperatorDeclarationSyntax>().Single());
Console.WriteLine(
$"{declaredSymbol} and {usedSymbol} are {(declaredSymbol.Equals(usedSymbol) ? "" : "not ")}equal.");
// int.operator >(int, int) and int.operator >(int, int) are not equal.
Why aren't these operators that seem the same showing that they are equal?

I modified your code, and using Reflection together with a peek at the Roslyn source, have found that
usedSymbolanddeclaredSymbolend up as two distinct Symbol types.The types for the two representations of the symbol do not match. One is SynthesizedIntrinsicOperatorSymbol, and the other is SourceUserDefinedOperatorSymbol. Ultimately, this is why equality doesn't work - it seems to not have been implemented for these two types.
For example, equality for SynthesizedIntrinsicOperatorSymbol does a type check, which would fail in this use case:
Looking into the other type, SourceUserDefinedOperatorSymbol, reveals that equality is implemented on a base class many layers deep: Symbols.MethodSymbol. Nothing in the inheritance chain for SourceUserDefinedOperatorSymbol overrides equality and implements a special equality check.
In looking at the source for
MethodSymbol, it does not overrideObject.Equals(object). (It does override a related method; more on that later.)MethodSymbolis derived fromSymbol. The source ofSymbolshows that it does overrideObject.Equals(object), which in turn calls another Equals function. Note the implementation and the comments:So it seems this class just returns reference equality by design.
The
Equals(Symbol, TypeCompareKind)method isvirtual, and theMethodSymbolclass overrides it, but only to check specific types. Because nothing in the inheritance chain for this type (SourceUserDefinedOperatorSymbol) overrides the equality methods, your code would still end up calling thebaseversion that uses reference equality: