diff --git a/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs b/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs index 6a1dd783477c9..35ede5f6be75e 100644 --- a/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs +++ b/src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs @@ -50,11 +50,12 @@ internal abstract class AliasSymbol : Symbol private readonly ImmutableArray _locations; // NOTE: can be empty for the "global" alias. private readonly string _aliasName; private readonly bool _isExtern; - private readonly Symbol? _containingSymbol; + private readonly Symbol _containingSymbol; - protected AliasSymbol(string aliasName, Symbol? containingSymbol, ImmutableArray locations, bool isExtern) + protected AliasSymbol(string aliasName, Symbol containingSymbol, ImmutableArray locations, bool isExtern) { Debug.Assert(locations.Length == 1 || (locations.IsEmpty && aliasName == "global")); // It looks like equality implementation depends on this condition. + Debug.Assert(containingSymbol is not null); _locations = locations; _aliasName = aliasName; @@ -69,7 +70,7 @@ internal static AliasSymbol CreateGlobalNamespaceAlias(NamespaceSymbol globalNam return new AliasSymbolFromResolvedTarget(globalNamespace, "global", globalNamespace, ImmutableArray.Empty, isExtern: false); } - internal static AliasSymbol CreateCustomDebugInfoAlias(NamespaceOrTypeSymbol targetSymbol, SyntaxToken aliasToken, Symbol? containingSymbol, bool isExtern) + internal static AliasSymbol CreateCustomDebugInfoAlias(NamespaceOrTypeSymbol targetSymbol, SyntaxToken aliasToken, Symbol containingSymbol, bool isExtern) { return new AliasSymbolFromResolvedTarget(targetSymbol, aliasToken.ValueText, containingSymbol, ImmutableArray.Create(aliasToken.GetLocation()), isExtern); } @@ -200,7 +201,7 @@ public override Accessibility DeclaredAccessibility /// return that as the "containing" symbol, even though the alias isn't a member of the /// namespace as such. /// - public sealed override Symbol? ContainingSymbol + public sealed override Symbol ContainingSymbol { get { @@ -253,7 +254,7 @@ public override bool Equals(Symbol? obj, TypeCompareKind compareKind) return (object?)other != null && Equals(this.Locations.FirstOrDefault(), other.Locations.FirstOrDefault()) && - this.ContainingAssembly.Equals(other.ContainingAssembly, compareKind); + Equals(this.ContainingSymbol, other.ContainingSymbol, compareKind); } public override int GetHashCode() @@ -358,7 +359,7 @@ internal BindingDiagnosticBag AliasTargetDiagnostics private NamespaceSymbol ResolveExternAliasTarget(BindingDiagnosticBag diagnostics) { NamespaceSymbol? target; - if (!ContainingSymbol!.DeclaringCompilation.GetExternAliasTarget(Name, out target)) + if (!ContainingSymbol.DeclaringCompilation.GetExternAliasTarget(Name, out target)) { diagnostics.Add(ErrorCode.ERR_BadExternAlias, Locations[0], Name); } @@ -371,7 +372,7 @@ private NamespaceSymbol ResolveExternAliasTarget(BindingDiagnosticBag diagnostic private NamespaceOrTypeSymbol ResolveAliasTarget(NameSyntax syntax, BindingDiagnosticBag diagnostics, ConsList? basesBeingResolved) { - var declarationBinder = ContainingSymbol!.DeclaringCompilation.GetBinderFactory(syntax.SyntaxTree).GetBinder(syntax).WithAdditionalFlags(BinderFlags.SuppressConstraintChecks | BinderFlags.SuppressObsoleteChecks); + var declarationBinder = ContainingSymbol.DeclaringCompilation.GetBinderFactory(syntax.SyntaxTree).GetBinder(syntax).WithAdditionalFlags(BinderFlags.SuppressConstraintChecks | BinderFlags.SuppressObsoleteChecks); return declarationBinder.BindNamespaceOrTypeSymbol(syntax, diagnostics, basesBeingResolved).NamespaceOrTypeSymbol; } @@ -385,7 +386,7 @@ internal sealed class AliasSymbolFromResolvedTarget : AliasSymbol { private readonly NamespaceOrTypeSymbol _aliasTarget; - internal AliasSymbolFromResolvedTarget(NamespaceOrTypeSymbol target, string aliasName, Symbol? containingSymbol, ImmutableArray locations, bool isExtern) + internal AliasSymbolFromResolvedTarget(NamespaceOrTypeSymbol target, string aliasName, Symbol containingSymbol, ImmutableArray locations, bool isExtern) : base(aliasName, containingSymbol, locations, isExtern) { _aliasTarget = target; diff --git a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs index 975370bae1e53..6387a4000b808 100644 --- a/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs +++ b/src/Compilers/CSharp/Test/Symbol/Compilation/SemanticModelAPITests.cs @@ -4450,6 +4450,46 @@ void M() { } Assert.Equal("DEBUG", model.GetConstantValue(root.DescendantNodes().OfType().Single())); } + [Fact] + public void EqualsOnAliasSymbolWithNullContainingAssembly_NotEquals() + { + var text = @" +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute("".NETCoreApp, Version = v6.0"", FrameworkDisplayName = """")] +"; + var alias1 = getGlobalAlias(CreateCompilation(text)); + var alias2 = getGlobalAlias(CreateCompilation(text)); + + Assert.Equal("", alias1.ContainingSymbol.ToTestDisplayString()); + Assert.Null(alias1.ContainingAssembly); + Assert.False(alias1.Equals(alias2)); + + static IAliasSymbol getGlobalAlias(CSharpCompilation compilation) + { + var tree = compilation.SyntaxTrees.Single(); + var node = tree.GetRoot().DescendantNodes().OfType().Where(ident => ident.Identifier.Text == "global").Single(); + return compilation.GetSemanticModel(tree).GetAliasInfo(node); + } + } + + [Fact] + public void EqualsOnAliasSymbolWithNullContainingAssembly_Equals() + { + var text = @" +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute("".NETCoreApp, Version = v6.0"", FrameworkDisplayName = """")] +[assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute("".NETCoreApp, Version = v6.0"", FrameworkDisplayName = """")] +"; + var compilation = CreateCompilation(text); + var tree = compilation.SyntaxTrees.Single(); + var nodes = tree.GetRoot().DescendantNodes().OfType().Where(ident => ident.Identifier.Text == "global").ToArray(); + var model = compilation.GetSemanticModel(tree); + var alias1 = model.GetAliasInfo(nodes[0]); + var alias2 = model.GetAliasInfo(nodes[1]); + + Assert.Equal("", alias1.ContainingSymbol.ToTestDisplayString()); + Assert.Null(alias1.ContainingAssembly); + Assert.True(alias1.Equals(alias2)); + } + #region "regression helper" private void Regression(string text) {