Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
29 changes: 27 additions & 2 deletions src/Features/CSharpTest/EditAndContinue/TopLevelEditingTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8496,6 +8496,21 @@ public void Namespace_Insert_NewType_Qualified(string src2)
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}

[Fact]
public void Namespace_Insert_NewType_HidingMetadataType()
{
var srcA1 = "";
var srcA2 = """
namespace Microsoft.CodeAnalysis;
public readonly partial class EmbeddedAttribute;
""";

EditAndContinueValidation.VerifySemantics(
GetTopEdits(srcA1, srcA2),
[SemanticEdit(SemanticEditKind.Insert, c => c.Assembly.GlobalNamespace.GetMember<INamespaceSymbol>("Microsoft").GetMember<INamespaceSymbol>("CodeAnalysis").GetMember("EmbeddedAttribute"))],
capabilities: EditAndContinueCapabilities.NewTypeDefinition);
}

[Theory]
[InlineData("class")]
[InlineData("interface")]
Expand Down Expand Up @@ -8759,7 +8774,12 @@ public void Namespace_Update_FileScoped()
[Fact]
public void Namespace_Update_MultiplePartials1()
=> EditAndContinueValidation.VerifySemantics(
[GetTopEdits(@"namespace N { partial class/*1*/C {} } namespace N { partial class/*2*/C {} }", @"namespace N { partial class/*1*/C {} } namespace M { partial class/*2*/C {} }"), GetTopEdits(@"namespace N { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }", @"namespace M { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }")],
[GetTopEdits(
@"namespace N { partial class/*1*/C {} } namespace N { partial class/*2*/C {} }",
@"namespace N { partial class/*1*/C {} } namespace M { partial class/*2*/C {} }"),
GetTopEdits(
@"namespace N { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }",
@"namespace M { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }")],
[
DocumentResults(
semanticEdits:
Expand All @@ -8777,7 +8797,12 @@ public void Namespace_Update_MultiplePartials1()
[Fact]
public void Namespace_Update_MultiplePartials2()
=> EditAndContinueValidation.VerifySemantics(
[GetTopEdits(@"namespace N { partial class/*1*/C {} } namespace N { partial class/*2*/C {} }", @"namespace M { partial class/*1*/C {} } namespace M { partial class/*2*/C {} }"), GetTopEdits(@"namespace N { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }", @"namespace M { partial class/*3*/C {} } namespace M { partial class/*4*/C {} }")],
[GetTopEdits(
@"namespace N { partial class/*1*/C {} } namespace N { partial class/*2*/C {} }",
@"namespace M { partial class/*1*/C {} } namespace M { partial class/*2*/C {} }"),
GetTopEdits(
@"namespace N { partial class/*3*/C {} } namespace N { partial class/*4*/C {} }",
@"namespace M { partial class/*3*/C {} } namespace M { partial class/*4*/C {} }")],
[
DocumentResults(diagnostics:
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2754,8 +2754,8 @@ private async Task<ImmutableArray<SemanticEditInfo>> AnalyzeSemanticsAsync(
continue;
}

var oldSymbolInNewCompilation = symbolCache.GetKey(oldSymbol, cancellationToken).Resolve(newModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol;
var newSymbolInOldCompilation = symbolCache.GetKey(newSymbol, cancellationToken).Resolve(oldModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol;
var oldSymbolInNewCompilation = symbolCache.GetKey(oldSymbol, cancellationToken).Resolve(newModel.Compilation, cancellationToken: cancellationToken).Symbol;
var newSymbolInOldCompilation = symbolCache.GetKey(newSymbol, cancellationToken).Resolve(oldModel.Compilation, cancellationToken: cancellationToken).Symbol;

if (oldSymbolInNewCompilation == null || newSymbolInOldCompilation == null)
{
Expand Down Expand Up @@ -2823,8 +2823,8 @@ private async Task<ImmutableArray<SemanticEditInfo>> AnalyzeSemanticsAsync(
if (containingType != null && (syntacticEditKind != EditKind.Delete || newSymbol == null))
{
var containingTypeSymbolKey = symbolCache.GetKey(containingType, cancellationToken);
oldContainingType ??= (INamedTypeSymbol?)containingTypeSymbolKey.Resolve(oldModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol;
newContainingType ??= (INamedTypeSymbol?)containingTypeSymbolKey.Resolve(newModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol;
oldContainingType ??= (INamedTypeSymbol?)containingTypeSymbolKey.Resolve(oldModel.Compilation, cancellationToken: cancellationToken).Symbol;
newContainingType ??= (INamedTypeSymbol?)containingTypeSymbolKey.Resolve(newModel.Compilation, cancellationToken: cancellationToken).Symbol;

if (oldContainingType != null && newContainingType != null && IsReloadable(oldContainingType))
{
Expand Down Expand Up @@ -2967,7 +2967,7 @@ newSymbol is IPropertySymbol newProperty &&
// If so, skip the member deletion and only report the containing symbol deletion.
var oldContainingType = oldSymbol.ContainingType;
var containingTypeKey = symbolCache.GetKey(oldContainingType, cancellationToken);
var newContainingType = (INamedTypeSymbol?)containingTypeKey.Resolve(newModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol;
var newContainingType = (INamedTypeSymbol?)containingTypeKey.Resolve(newModel.Compilation, cancellationToken: cancellationToken).Symbol;
if (newContainingType == null)
{
// If a type parameter is deleted from the parameter list of a type declaration, the symbol key won't be resolved (because the arities do not match).
Expand Down Expand Up @@ -3092,7 +3092,7 @@ newSymbol is IPropertySymbol newProperty &&
HasEdit(editMap, GetSymbolDeclarationSyntax(newAssociatedMember, cancellationToken), EditKind.Insert);

var containingTypeKey = symbolCache.GetKey(newContainingType, cancellationToken);
oldContainingType = containingTypeKey.Resolve(oldModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol as INamedTypeSymbol;
oldContainingType = containingTypeKey.Resolve(oldModel.Compilation, cancellationToken: cancellationToken).Symbol as INamedTypeSymbol;

// Check rude edits for each member even if it is inserted into a new type.
if (!hasAssociatedSymbolInsert && IsMember(newSymbol))
Expand Down Expand Up @@ -3629,7 +3629,7 @@ bool PreprocessSymbolEdit(ref ISymbol? oldSymbol, ref ISymbol? newSymbol)
static ISymbol? Resolve(ISymbol symbol, SymbolKey symbolKey, Compilation compilation, CancellationToken cancellationToken)
{
// Ignore ambiguous resolution result - it may happen if there are semantic errors in the compilation.
var result = symbolKey.Resolve(compilation, ignoreAssemblyKey: true, cancellationToken).Symbol;
var result = symbolKey.Resolve(compilation, cancellationToken: cancellationToken).Symbol;

// If we were looking for a definition and an implementation is returned the definition does not exist.
return symbol.IsPartialImplementation() && result?.IsPartialDefinition() == true ? null : result;
Expand Down Expand Up @@ -3709,7 +3709,7 @@ protected static bool IsMemberOrDelegate(ISymbol symbol)
protected static ISymbol? GetSemanticallyMatchingNewSymbol(ISymbol? oldSymbol, ISymbol? newSymbol, Compilation newCompilation, SymbolInfoCache symbolCache, CancellationToken cancellationToken)
=> oldSymbol != null && IsMember(oldSymbol) &&
newSymbol != null && IsMember(newSymbol) &&
symbolCache.GetKey(oldSymbol, cancellationToken).Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol is { } matchingNewSymbol &&
symbolCache.GetKey(oldSymbol, cancellationToken).Resolve(newCompilation, cancellationToken: cancellationToken).Symbol is { } matchingNewSymbol &&
!matchingNewSymbol.IsSynthesized() &&
matchingNewSymbol != newSymbol
? matchingNewSymbol
Expand Down Expand Up @@ -3805,7 +3805,7 @@ void AddEdits(IMethodSymbol? constructor, Compilation otherCompilation, bool isD
IsPrimaryConstructor(constructor, cancellationToken) &&
constructor.GetMatchingDeconstructor() is { IsImplicitlyDeclared: true } deconstructor)
{
if (SymbolKey.Create(deconstructor, cancellationToken).Resolve(otherCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol != null)
if (SymbolKey.Create(deconstructor, cancellationToken).Resolve(otherCompilation, cancellationToken: cancellationToken).Symbol != null)
{
// Update for transition from synthesized to declared deconstructor
AddUpdateEditsForMemberAndAccessors(semanticEdits, deconstructor, cancellationToken);
Expand Down Expand Up @@ -4053,7 +4053,7 @@ void AddDelete(ISymbol? symbol)
continue;
}

var newType = SymbolKey.Create(oldType, cancellationToken).Resolve(newModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol;
var newType = SymbolKey.Create(oldType, cancellationToken).Resolve(newModel.Compilation, cancellationToken: cancellationToken).Symbol;
if (newType == null)
{
var key = (oldType.Name, oldType.Arity);
Expand Down Expand Up @@ -4084,7 +4084,7 @@ void AddDelete(ISymbol? symbol)
continue;
}

var oldType = SymbolKey.Create(newType, cancellationToken).Resolve(oldModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol;
var oldType = SymbolKey.Create(newType, cancellationToken).Resolve(oldModel.Compilation, cancellationToken: cancellationToken).Symbol;
if (oldType == null)
{
// Check if a type with the same name and arity was also removed. If so treat it as a move.
Expand Down Expand Up @@ -5193,7 +5193,7 @@ private SyntaxNode GetDiagnosticNode(out int distance, CancellationToken cancell
while (oldContainer is not null and not INamespaceSymbol { IsGlobalNamespace: true })
{
var symbolKey = SymbolKey.Create(oldSymbol, cancellationToken);
if (symbolKey.Resolve(newModel.Compilation, ignoreAssemblyKey: true, cancellationToken).Symbol is { } newSymbol)
if (symbolKey.Resolve(newModel.Compilation, cancellationToken: cancellationToken).Symbol is { } newSymbol)
{
return newSymbol;
}
Expand Down Expand Up @@ -5600,7 +5600,7 @@ private void AddConstructorEdits(
}
else
{
var resolution = newCtorKey.Resolve(oldModel.Compilation, ignoreAssemblyKey: true, cancellationToken);
var resolution = newCtorKey.Resolve(oldModel.Compilation, cancellationToken: cancellationToken);

// There may be semantic errors in the compilation that result in multiple candidates.
// Pick the first candidate.
Expand Down Expand Up @@ -7054,7 +7054,7 @@ private bool DeleteEditImpliesInsertEdit(ISymbol oldSymbol, ISymbol newSymbol, C
GetPrimaryConstructor(newSymbol.ContainingType, cancellationToken) is { } newPrimaryConstructor &&
method.HasDeconstructorSignature(newPrimaryConstructor))
{
var oldConstructor = SymbolKey.Create(newPrimaryConstructor, cancellationToken).Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol;
var oldConstructor = SymbolKey.Create(newPrimaryConstructor, cancellationToken).Resolve(oldCompilation, cancellationToken: cancellationToken).Symbol;

// An insert exists if the new primary constructor is explicitly declared and
// the old one doesn't exist, is synthesized, or is not a primary constructor parameter.
Expand All @@ -7067,7 +7067,7 @@ private bool DeleteEditImpliesInsertEdit(ISymbol oldSymbol, ISymbol newSymbol, C
GetPrimaryConstructor(newSymbol.ContainingType, cancellationToken)?.Parameters.FirstOrDefault(
static (parameter, name) => parameter.Name == name, newSymbol.Name) is { } newPrimaryParameter)
{
var oldParameter = SymbolKey.Create(newPrimaryParameter, cancellationToken).Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken).Symbol;
var oldParameter = SymbolKey.Create(newPrimaryParameter, cancellationToken).Resolve(oldCompilation, cancellationToken: cancellationToken).Symbol;
var oldProperty = (IPropertySymbol)oldSymbol;

// An insert exists if the new primary parameter is explicitly declared and
Expand Down
6 changes: 3 additions & 3 deletions src/Features/Core/Portable/EditAndContinue/EditSession.cs
Original file line number Diff line number Diff line change
Expand Up @@ -841,7 +841,7 @@ internal static void MergePartialEdits(
SymbolKeyResolution oldResolution;
if (edit.Kind is SemanticEditKind.Update or SemanticEditKind.Delete)
{
oldResolution = edit.Symbol.Resolve(oldCompilation, ignoreAssemblyKey: true, cancellationToken);
oldResolution = edit.Symbol.Resolve(oldCompilation, cancellationToken: cancellationToken);
Contract.ThrowIfNull(oldResolution.Symbol);
}
else
Expand All @@ -852,13 +852,13 @@ internal static void MergePartialEdits(
SymbolKeyResolution newResolution;
if (edit.Kind is SemanticEditKind.Update or SemanticEditKind.Insert or SemanticEditKind.Replace)
{
newResolution = edit.Symbol.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken);
newResolution = edit.Symbol.Resolve(newCompilation, cancellationToken: cancellationToken);
Contract.ThrowIfNull(newResolution.Symbol);
}
else if (edit.Kind == SemanticEditKind.Delete && edit.DeletedSymbolContainer is not null)
{
// For deletes, we use NewSymbol to reference the containing type of the deleted member
newResolution = edit.DeletedSymbolContainer.Value.Resolve(newCompilation, ignoreAssemblyKey: true, cancellationToken);
newResolution = edit.DeletedSymbolContainer.Value.Resolve(newCompilation, cancellationToken: cancellationToken);
Contract.ThrowIfNull(newResolution.Symbol);
}
else
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -342,8 +342,8 @@ SymbolKey CreateSymbolKey(SemanticEditDescription edit)
expectedOldSymbol = expectedSemanticEdit.SymbolProvider(oldCompilation);
expectedNewSymbol = expectedSemanticEdit.SymbolProvider(newCompilation);

Assert.Equal(expectedOldSymbol, symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true).Symbol);
Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol);
Assert.Equal(expectedOldSymbol, symbolKey.Resolve(oldCompilation).Symbol);
Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation).Symbol);
break;

case SemanticEditKind.Delete:
Expand All @@ -352,25 +352,25 @@ SymbolKey CreateSymbolKey(SemanticEditDescription edit)
// Symbol key will happily resolve to a definition part that has no implementation, so we validate that
// differently
if (expectedOldSymbol.IsPartialDefinition() &&
symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true).Symbol is ISymbol resolvedSymbol)
symbolKey.Resolve(oldCompilation).Symbol is ISymbol resolvedSymbol)
{
Assert.Equal(expectedOldSymbol, resolvedSymbol.PartialDefinitionPart());
Assert.Equal(null, resolvedSymbol.PartialImplementationPart());
}
else
{
Assert.Equal(expectedOldSymbol, symbolKey.Resolve(oldCompilation, ignoreAssemblyKey: true).Symbol);
Assert.Equal(expectedOldSymbol, symbolKey.Resolve(oldCompilation).Symbol);

// When we're deleting a symbol, and have a deleted symbol container, it means the symbol wasn't really deleted,
// but rather had its signature changed in some way. Some of those ways, like changing the return type, are not
// represented in the symbol key, so the check below would fail, so we skip it.
if (expectedSemanticEdit.DeletedSymbolContainerProvider is null)
{
Assert.Equal(null, symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol);
Assert.Equal(null, symbolKey.Resolve(newCompilation).Symbol);
}
}

var deletedSymbolContainer = actualSemanticEdit.DeletedSymbolContainer?.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol;
var deletedSymbolContainer = actualSemanticEdit.DeletedSymbolContainer?.Resolve(newCompilation).Symbol;
AssertEx.AreEqual(
deletedSymbolContainer,
expectedSemanticEdit.DeletedSymbolContainerProvider?.Invoke(newCompilation),
Expand All @@ -380,7 +380,7 @@ SymbolKey CreateSymbolKey(SemanticEditDescription edit)

case SemanticEditKind.Insert or SemanticEditKind.Replace:
expectedNewSymbol = expectedSemanticEdit.SymbolProvider(newCompilation);
Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol);
Assert.Equal(expectedNewSymbol, symbolKey.Resolve(newCompilation).Symbol);
break;

default:
Expand All @@ -390,7 +390,7 @@ SymbolKey CreateSymbolKey(SemanticEditDescription edit)
// Partial types must match:
AssertEx.AreEqual(
expectedSemanticEdit.PartialType?.Invoke(newCompilation),
actualSemanticEdit.PartialType?.Resolve(newCompilation, ignoreAssemblyKey: true).Symbol,
actualSemanticEdit.PartialType?.Resolve(newCompilation).Symbol,
message: $"{message}, {editKind}({expectedNewSymbol ?? expectedOldSymbol}): Partial types do not match");

var expectedSyntaxMap = expectedSemanticEdit.GetSyntaxMap();
Expand Down
Loading