Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 2 additions & 4 deletions src/Compilers/CSharp/Portable/Symbols/AssemblySymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,8 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Reflection;
using System.Reflection.PortableExecutable;
using Microsoft.CodeAnalysis.Collections;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;
Expand Down Expand Up @@ -855,7 +853,7 @@ internal NamedTypeSymbol GetPrimitiveType(Microsoft.Cci.PrimitiveTypeCode type)
DiagnosticBag? warnings = null, // this is set to collect ambiguity warning for well-known types before C# 7
bool ignoreCorLibraryDuplicatedTypes = false)
{
// Type from this assembly always wins.
// Type from this assembly usually wins unless we search for well-known type and find file-local declaration in source.
// After that we look in references, which may yield ambiguities. If `ignoreCorLibraryDuplicatedTypes` is set,
// corlib does not contribute to ambiguities (corlib loses over other references).
// For well-known types before C# 7, ambiguities are reported as a warning and the first candidate wins.
Expand Down Expand Up @@ -994,7 +992,7 @@ private bool IsValidWellKnownType(NamedTypeSymbol? result)
return false;
}

Debug.Assert((object)result.ContainingType == null || IsValidWellKnownType(result.ContainingType),
Debug.Assert(result.ContainingType is null || IsValidWellKnownType(result.ContainingType),
"Checking the containing type is the caller's responsibility.");

return result.DeclaredAccessibility == Accessibility.Public || IsSymbolAccessible(result, this);
Expand Down
14 changes: 8 additions & 6 deletions src/Compilers/CSharp/Portable/Symbols/NamespaceOrTypeSymbol.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,7 @@
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using Microsoft.CodeAnalysis.CSharp.Symbols.Metadata.PE;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Symbols;
using Roslyn.Utilities;

Expand Down Expand Up @@ -271,9 +269,11 @@ public virtual ImmutableArray<NamedTypeSymbol> GetTypeMembers(string name, int a

foreach (var named in namespaceOrTypeMembers)
{
if (emittedTypeName.InferredArity == named.Arity && named.MangleName)
if (emittedTypeName.InferredArity == named.Arity &&
named.MangleName &&
named.MetadataName == emittedTypeName.TypeName)
{
if ((object?)namedType != null)
if (namedType is not null)
{
namedType = null;
break;
Expand Down Expand Up @@ -317,9 +317,11 @@ public virtual ImmutableArray<NamedTypeSymbol> GetTypeMembers(string name, int a

foreach (var named in namespaceOrTypeMembers)
{
if (!named.MangleName && (forcedArity == -1 || forcedArity == named.Arity))
if (!named.MangleName &&
(forcedArity == -1 || forcedArity == named.Arity) &&
named.MetadataName == emittedTypeName.TypeName)
{
if ((object?)namedType != null)
if (namedType is not null)
{
namedType = null;
break;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4762,5 +4762,68 @@ static void verify(CSharpCompilation comp, string expectedAssemblyName)
Assert.Equal(expectedAssemblyName, comp.GetTypeByMetadataName("System.Runtime.CompilerServices.IsExternalInit").ContainingAssembly.Name);
}
}

[Fact, WorkItem(67079, "https://github.com/dotnet/roslyn/issues/67079")]
public void DoNotPickTypeFromSourceWithFileModifier()
{
var corlib_cs = """
namespace System
{
public class Object { }
public struct Int32 { }
public struct Boolean { }
public class String { }
public class ValueType { }
public struct Void { }
public class Attribute { }
public class AttributeUsageAttribute : Attribute
{
public AttributeUsageAttribute(AttributeTargets t) { }
public bool AllowMultiple { get; set; }
public bool Inherited { get; set; }
}
public struct Enum { }
public enum AttributeTargets { }
}
""";

var source = """
namespace System.Runtime.CompilerServices
{
file class IsExternalInit {}
}

public class C
{
public string Property { get; init; }
}
""";

var corlibWithoutIsExternalInitRef = CreateEmptyCompilation(corlib_cs)
.EmitToImageReference();

var corlibWithIsExternalInitRef = CreateEmptyCompilation(corlib_cs + IsExternalInitTypeDefinition)
.EmitToImageReference();

{
// proper type in corlib and file type in source
var comp = CreateEmptyCompilation(source, references: new[] { corlibWithIsExternalInitRef });
comp.VerifyEmitDiagnostics();
var modifier = ((SourcePropertySymbol)comp.GlobalNamespace.GetMember("C.Property")).SetMethod.ReturnTypeWithAnnotations.CustomModifiers.Single();
Assert.False(modifier.Modifier.IsFileLocal);
}

{
// no type in corlib and file type in source
var comp = CreateEmptyCompilation(source, references: new[] { corlibWithoutIsExternalInitRef });
comp.VerifyEmitDiagnostics(
// (8,35): error CS018: Predefined type 'System.Runtime.CompilerServices.IsExternalInit' is not defined or imported
// public int Property { get; init; }
Diagnostic(ErrorCode.ERR_PredefinedTypeNotFound, "init").WithArguments("System.Runtime.CompilerServices.IsExternalInit").WithLocation(8, 35)
);
var modifier = ((SourcePropertySymbol)comp.GlobalNamespace.GetMember("C.Property")).SetMethod.ReturnTypeWithAnnotations.CustomModifiers.Single();
Assert.False(modifier.Modifier.IsFileLocal);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3576,6 +3576,7 @@ file class C { }
Assert.Null(comp.GetTypeByMetadataName("<>F1__C"));
Assert.Null(comp.GetTypeByMetadataName("F0__C"));
Assert.Null(comp.GetTypeByMetadataName("<file>F0__C"));
Assert.Null(comp.GetTypeByMetadataName("C"));

// from metadata
var comp2 = CreateCompilation("", references: new[] { comp.EmitToImageReference() });
Expand All @@ -3585,6 +3586,8 @@ file class C { }

var metadataType = comp2.GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C");
Assert.Equal(metadataMember, metadataType);

Assert.Null(comp2.GetTypeByMetadataName("C"));
}

[Fact]
Expand All @@ -3603,6 +3606,7 @@ file class C<T> { }
var sourceType = comp.GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C`1");
Assert.Equal(sourceMember, sourceType);
Assert.Null(comp.GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C"));
Assert.Null(comp.GetTypeByMetadataName("C`1"));

// from metadata
var comp2 = CreateCompilation("", references: new[] { comp.EmitToImageReference() });
Expand All @@ -3613,6 +3617,8 @@ file class C<T> { }

var metadataType = comp2.GetTypeByMetadataName("<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C`1");
Assert.Equal(metadataMember, metadataType);

Assert.Null(comp2.GetTypeByMetadataName("C`1"));
}

[Fact]
Expand All @@ -3639,6 +3645,7 @@ file class C { } // 1
// However, since we don't actually support nested file types, we don't think we need the API to do the additional lookup
// when the requested type is nested, and so we end up giving a null here.
Assert.Null(sourceType);
Assert.Null(comp.GetTypeByMetadataName("Outer.C"));
}

[Fact]
Expand All @@ -3661,6 +3668,10 @@ class C { }
var sourceType = comp.GetTypeByMetadataName("<file1>F96B1D9CB33A43D51528FE81EDAFE5AE31358FE749929AC76B76C64B60DEF129D__C");
Assert.Equal(sourceMember, sourceType);

var sourceTypeCByMetadataName = comp.GetTypeByMetadataName("C");
Assert.Equal("C", sourceTypeCByMetadataName.MetadataName);
Assert.False(sourceTypeCByMetadataName is SourceMemberContainerTypeSymbol { IsFileLocal: true });

// from metadata
var comp2 = CreateCompilation("", references: new[] { comp.EmitToImageReference() });
comp2.VerifyDiagnostics();
Expand All @@ -3670,6 +3681,9 @@ class C { }

var metadataType = comp2.GetTypeByMetadataName("<file1>F96B1D9CB33A43D51528FE81EDAFE5AE31358FE749929AC76B76C64B60DEF129D__C");
Assert.Equal(metadataMember, metadataType);

var metadataTypeCByMetadataName = comp2.GetTypeByMetadataName("C");
Assert.Equal("C", metadataTypeCByMetadataName.MetadataName);
}

[CombinatorialData]
Expand All @@ -3694,12 +3708,15 @@ file class C { }
const string metadataName = "<>FE3B0C44298FC1C149AFBF4C8996FB92427AE41E4649B934CA495991B7852B855__C";
var sourceType = comp.GetTypeByMetadataName(metadataName);
Assert.Null(sourceType);
Assert.Null(comp.GetTypeByMetadataName("C"));

var types = comp.GetTypesByMetadataName(metadataName);
Assert.Equal(2, types.Length);
Assert.Equal(firstIsMetadataReference ? "C@<tree 0>" : "C@<unknown>", types[0].ToTestDisplayString());
Assert.Equal(secondIsMetadataReference ? "C@<tree 0>" : "C@<unknown>", types[1].ToTestDisplayString());
Assert.NotEqual(types[0], types[1]);

Assert.Empty(comp.GetTypesByMetadataName("C"));
}

[Fact]
Expand All @@ -3720,9 +3737,13 @@ file class C { }
var sourceType = ((Compilation)comp).GetTypeByMetadataName(metadataName);
Assert.Equal("C@<tree 0>", sourceType.ToTestDisplayString());

Assert.Null(((Compilation)comp).GetTypeByMetadataName("C"));

var types = comp.GetTypesByMetadataName(metadataName);
Assert.Equal(1, types.Length);
Assert.Same(sourceType, types[0]);

Assert.Empty(comp.GetTypesByMetadataName("C"));
}

[Fact]
Expand Down