Skip to content

Commit

Permalink
Fix null-ref in AliasSymbol.Equals (#59649)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcouv authored Feb 19, 2022
1 parent fab4fb3 commit abd4cae
Show file tree
Hide file tree
Showing 2 changed files with 49 additions and 8 deletions.
17 changes: 9 additions & 8 deletions src/Compilers/CSharp/Portable/Symbols/AliasSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,12 @@ internal abstract class AliasSymbol : Symbol
private readonly ImmutableArray<Location> _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<Location> locations, bool isExtern)
protected AliasSymbol(string aliasName, Symbol containingSymbol, ImmutableArray<Location> 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;
Expand All @@ -69,7 +70,7 @@ internal static AliasSymbol CreateGlobalNamespaceAlias(NamespaceSymbol globalNam
return new AliasSymbolFromResolvedTarget(globalNamespace, "global", globalNamespace, ImmutableArray<Location>.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);
}
Expand Down Expand Up @@ -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.
/// </summary>
public sealed override Symbol? ContainingSymbol
public sealed override Symbol ContainingSymbol
{
get
{
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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);
}
Expand All @@ -371,7 +372,7 @@ private NamespaceSymbol ResolveExternAliasTarget(BindingDiagnosticBag diagnostic

private NamespaceOrTypeSymbol ResolveAliasTarget(NameSyntax syntax, BindingDiagnosticBag diagnostics, ConsList<TypeSymbol>? 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;
}

Expand All @@ -385,7 +386,7 @@ internal sealed class AliasSymbolFromResolvedTarget : AliasSymbol
{
private readonly NamespaceOrTypeSymbol _aliasTarget;

internal AliasSymbolFromResolvedTarget(NamespaceOrTypeSymbol target, string aliasName, Symbol? containingSymbol, ImmutableArray<Location> locations, bool isExtern)
internal AliasSymbolFromResolvedTarget(NamespaceOrTypeSymbol target, string aliasName, Symbol containingSymbol, ImmutableArray<Location> locations, bool isExtern)
: base(aliasName, containingSymbol, locations, isExtern)
{
_aliasTarget = target;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4450,6 +4450,46 @@ void M() { }
Assert.Equal("DEBUG", model.GetConstantValue(root.DescendantNodes().OfType<InvocationExpressionSyntax>().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("<global namespace>", 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<IdentifierNameSyntax>().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<IdentifierNameSyntax>().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("<global namespace>", alias1.ContainingSymbol.ToTestDisplayString());
Assert.Null(alias1.ContainingAssembly);
Assert.True(alias1.Equals(alias2));
}

#region "regression helper"
private void Regression(string text)
{
Expand Down

0 comments on commit abd4cae

Please sign in to comment.