Skip to content

Commit

Permalink
Merge pull request #66819 from CyrusNajmabadi/nameSuggestionError
Browse files Browse the repository at this point in the history
Provide smart name suggestions, even when the type of the declaration cannot be bound
  • Loading branch information
CyrusNajmabadi authored Feb 10, 2023
2 parents 58d1f45 + 754c0ac commit 2a186e3
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -2883,6 +2883,22 @@ void Main()
await VerifyItemExistsAsync(markup, "goo");
}

[Fact, WorkItem(36352, "https://github.com/dotnet/roslyn/issues/36352")]
public async Task InferCollectionInErrorCase1()
{
var markup = @"
class Customer { }
class V
{
void M(IEnumerable<Customer> $$)
{
}
}
";
await VerifyItemExistsAsync(markup, "customers");
}

private static NamingStylePreferences MultipleCamelCaseLocalRules()
{
var styles = new[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#nullable disable

using System;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Completion;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,12 @@ private static bool IsValidType([NotNullWhen(true)] ITypeSymbol? type)
return UnwrapType(arrayType.ElementType, compilation, wasPlural: true, seenTypes: seenTypes);
}

if (type is IErrorTypeSymbol { TypeArguments: [var typeArgument] } &&
LooksLikeWellKnownCollectionType(compilation, type.Name))
{
return UnwrapType(typeArgument, compilation, wasPlural: true, seenTypes);
}

if (type is INamedTypeSymbol namedType && namedType.OriginalDefinition != null)
{
// if namedType contains a valid GetEnumerator method, we want collectionType to be the type of
Expand Down Expand Up @@ -169,6 +175,34 @@ private static bool IsValidType([NotNullWhen(true)] ITypeSymbol? type)
return (type, wasPlural);
}

private bool LooksLikeWellKnownCollectionType(Compilation compilation, string name)
{
// see if the user has something like `IEnumerable<Customer>` (where IEnumerable doesn't bind). Weak
// heuristic. If there's a matching type under System.Collections with that name, then assume it's a
// collection and attempt to create a name from the type arg.
var system = compilation.GlobalNamespace.GetMembers(nameof(System)).OfType<INamespaceSymbol>().FirstOrDefault();
var systemCollections = system?.GetMembers(nameof(System.Collections)).OfType<INamespaceSymbol>().FirstOrDefault();

// just check System.Collections, and it's immediate namespace children. This covers all the common cases
// like "Concurrent/Generic/Immutable/Specialized", and prevents having to worry about huge trees to walk.
if (systemCollections is not null)
{
if (Check(systemCollections, name))
return true;

foreach (var childNamespace in systemCollections.GetNamespaceMembers())
{
if (Check(childNamespace, name))
return true;
}
}

return false;

static bool Check(INamespaceSymbol? namespaceSymbol, string name)
=> namespaceSymbol != null && namespaceSymbol.GetTypeMembers(name).Any(static t => t.DeclaredAccessibility == Accessibility.Public);
}

private static void GetRecommendedNames(
ImmutableArray<ImmutableArray<string>> baseNames,
NameDeclarationInfo declarationInfo,
Expand Down

0 comments on commit 2a186e3

Please sign in to comment.