Skip to content

Commit e449c55

Browse files
Merge pull request #46357 from CyrusNajmabadi/genericErrorType
Fix roundtripping of generic error types.
2 parents 6f47f3d + 2e5cc7f commit e449c55

File tree

2 files changed

+56
-14
lines changed

2 files changed

+56
-14
lines changed

src/Workspaces/Core/Portable/SymbolKey/SymbolKey.ErrorTypeSymbolKey.cs

Lines changed: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,11 @@ public static void Create(INamedTypeSymbol symbol, SymbolKeyWriter visitor)
3030
break;
3131
}
3232

33+
var isConstructed = !symbol.Equals(symbol.ConstructedFrom);
3334
visitor.WriteInteger(symbol.Arity);
34-
if (!symbol.Equals(symbol.ConstructedFrom))
35+
visitor.WriteBoolean(isConstructed);
36+
37+
if (isConstructed)
3538
{
3639
visitor.WriteSymbolKeyArray(symbol.TypeArguments);
3740
}
@@ -63,6 +66,7 @@ public static SymbolKeyResolution Resolve(SymbolKeyReader reader, out string fai
6366

6467
var containingSymbolResolution = ResolveContainer(reader, out var containingSymbolFailureReason);
6568
var arity = reader.ReadInteger();
69+
var isConstructed = reader.ReadBoolean();
6670

6771
using var typeArguments = reader.ReadSymbolKeyArray<ITypeSymbol>(out var typeArgumentsFailureReason);
6872

@@ -86,19 +90,17 @@ public static SymbolKeyResolution Resolve(SymbolKeyReader reader, out string fai
8690

8791
using var result = PooledArrayBuilder<INamedTypeSymbol>.GetInstance();
8892

89-
var typeArgumentsArray = arity > 0 ? typeArguments.Builder.ToArray() : null;
93+
var typeArgumentsArray = isConstructed ? typeArguments.Builder.ToArray() : null;
9094
foreach (var container in containingSymbolResolution.OfType<INamespaceOrTypeSymbol>())
9195
{
92-
result.AddIfNotNull(Construct(
93-
reader, container, name, arity, typeArgumentsArray));
96+
var originalType = reader.Compilation.CreateErrorTypeSymbol(container, name, arity);
97+
var errorType = isConstructed ? originalType.Construct(typeArgumentsArray) : originalType;
98+
result.AddIfNotNull(errorType);
9499
}
95100

96101
// Always ensure at least one error type was created.
97102
if (result.Count == 0)
98-
{
99-
result.AddIfNotNull(Construct(
100-
reader, container: null, name, arity, typeArgumentsArray));
101-
}
103+
result.AddIfNotNull(reader.Compilation.CreateErrorTypeSymbol(container: null, name, arity));
102104

103105
return CreateResolution(result, $"({nameof(ErrorTypeSymbolKey)} failed)", out failureReason);
104106
}
@@ -131,12 +133,6 @@ private static SymbolKeyResolution ResolveContainer(SymbolKeyReader reader, out
131133

132134
throw ExceptionUtilities.UnexpectedValue(type);
133135
}
134-
135-
private static INamedTypeSymbol Construct(SymbolKeyReader reader, INamespaceOrTypeSymbol container, string name, int arity, ITypeSymbol[] typeArguments)
136-
{
137-
var result = reader.Compilation.CreateErrorTypeSymbol(container, name, arity);
138-
return typeArguments != null ? result.Construct(typeArguments) : result;
139-
}
140136
}
141137
}
142138
}

src/Workspaces/CoreTest/SymbolKeyTests.cs

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1016,6 +1016,52 @@ class C
10161016
TestRoundTrip(fields, comp);
10171017
}
10181018

1019+
[Fact]
1020+
public void TestGenericErrorType()
1021+
{
1022+
var source1 = @"
1023+
public class C
1024+
{
1025+
public Goo<X> G() { }
1026+
}";
1027+
1028+
// We don't add metadata to the second compilation, so even `System.Collections.IEnumerable` will be an
1029+
// error type.
1030+
var compilation1 = GetCompilation(source1, LanguageNames.CSharp, "File1.cs");
1031+
var tree = compilation1.SyntaxTrees.Single();
1032+
var root = tree.GetRoot();
1033+
var node = root.DescendantNodes().OfType<CSharp.Syntax.GenericNameSyntax>().Single();
1034+
1035+
var semanticModel = compilation1.GetSemanticModel(tree);
1036+
var symbol = semanticModel.GetTypeInfo(node).Type;
1037+
1038+
{
1039+
// Ensure we don't crash getting these symbol keys.
1040+
var id = SymbolKey.CreateString(symbol);
1041+
Assert.NotNull(id);
1042+
1043+
// Validate that if the client does ask to resolve locations that we
1044+
// do not crash if those locations cannot be found.
1045+
var found = SymbolKey.ResolveString(id, compilation1).GetAnySymbol();
1046+
Assert.NotNull(found);
1047+
1048+
Assert.Equal(symbol.Name, found.Name);
1049+
Assert.Equal(symbol.Kind, found.Kind);
1050+
}
1051+
1052+
{
1053+
// Ensure we don't crash getting these symbol keys.
1054+
var id = SymbolKey.CreateString(symbol.OriginalDefinition);
1055+
Assert.NotNull(id);
1056+
1057+
var found = SymbolKey.ResolveString(id, compilation1).GetAnySymbol();
1058+
Assert.NotNull(found);
1059+
1060+
Assert.Equal(symbol.Name, found.Name);
1061+
Assert.Equal(symbol.Kind, found.Kind);
1062+
}
1063+
}
1064+
10191065
private static void TestRoundTrip(IEnumerable<ISymbol> symbols, Compilation compilation, Func<ISymbol, object> fnId = null)
10201066
{
10211067
foreach (var symbol in symbols)

0 commit comments

Comments
 (0)