diff --git a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs index c98b9e085ab6b..6ce761efe6ec2 100644 --- a/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs +++ b/src/EditorFeatures/CSharpTest/Completion/CompletionProviders/SymbolCompletionProviderTests.cs @@ -6,6 +6,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Threading.Tasks; using Microsoft.CodeAnalysis.Completion.Providers; using Microsoft.CodeAnalysis.CSharp; @@ -12558,18 +12559,17 @@ void M(int parameter) { } await VerifyItemExistsAsync(MakeMarkup(source, languageVersion: "10"), "parameter"); } - [Fact] + [Fact, WorkItem("https://github.com/dotnet/roslyn/issues/60812")] public async Task ParameterNotAvailableInMethodAttributeNameofWithNoArgument() { - var source = @" -class C -{ - [Some(nameof($$))] - void M(int parameter) { } -} -"; - // Tracked by https://github.com/dotnet/roslyn/issues/60812 - await VerifyItemIsAbsentAsync(MakeMarkup(source), "parameter"); + var source = """ + class C + { + [Some(nameof($$))] + void M(int parameter) { } + } + """; + await VerifyItemExistsAsync(MakeMarkup(source), "parameter"); } [Fact] @@ -14075,7 +14075,7 @@ IEnumerable M() => [string.Empty, .. ($$ #endregion - private static string MakeMarkup(string source, string languageVersion = "Preview") + private static string MakeMarkup([StringSyntax(PredefinedEmbeddedLanguageNames.CSharpTest)] string source, string languageVersion = "Preview") { return $$""" diff --git a/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs b/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs index ac22c1d582b72..cbf581d5a70c9 100644 --- a/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs +++ b/src/Features/CSharp/Portable/Completion/CompletionProviders/SymbolCompletionProvider.cs @@ -24,7 +24,9 @@ namespace Microsoft.CodeAnalysis.CSharp.Completion.Providers; [ExportCompletionProvider(nameof(SymbolCompletionProvider), LanguageNames.CSharp)] [ExtensionOrder(After = nameof(SpeculativeTCompletionProvider))] [Shared] -internal sealed class SymbolCompletionProvider : AbstractRecommendationServiceBasedCompletionProvider +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal sealed class SymbolCompletionProvider() : AbstractRecommendationServiceBasedCompletionProvider { private static readonly Dictionary<(bool importDirective, bool preselect, bool tupleLiteral), CompletionItemRules> s_cachedRules = []; @@ -65,12 +67,6 @@ static CompletionItemRules MakeRule((bool importDirective, bool preselect, bool } } - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public SymbolCompletionProvider() - { - } - public override ImmutableHashSet TriggerCharacters { get; } = CompletionUtilities.CommonTriggerCharactersWithArgumentList; internal override string Language => LanguageNames.CSharp; diff --git a/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs b/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs index 86ca4124a8c4e..e72b55575a075 100644 --- a/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs +++ b/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs @@ -13,14 +13,10 @@ namespace Microsoft.CodeAnalysis.CSharp.Recommendations; [ExportLanguageService(typeof(IRecommendationService), LanguageNames.CSharp), Shared] -internal partial class CSharpRecommendationService : AbstractRecommendationService +[method: ImportingConstructor] +[method: Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] +internal partial class CSharpRecommendationService() : AbstractRecommendationService { - [ImportingConstructor] - [Obsolete(MefConstruction.ImportingConstructorMessage, error: true)] - public CSharpRecommendationService() - { - } - protected override AbstractRecommendationServiceRunner CreateRunner(CSharpSyntaxContext context, bool filterOutOfScopeLocals, CancellationToken cancellationToken) => new CSharpRecommendationServiceRunner(context, filterOutOfScopeLocals, cancellationToken); } diff --git a/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs b/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs index 8a4a7fc4208ec..9590c55a7d66c 100644 --- a/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs +++ b/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationServiceRunner.cs @@ -26,14 +26,10 @@ namespace Microsoft.CodeAnalysis.CSharp.Recommendations; internal partial class CSharpRecommendationService { - private sealed partial class CSharpRecommendationServiceRunner : AbstractRecommendationServiceRunner + private sealed partial class CSharpRecommendationServiceRunner( + CSharpSyntaxContext context, bool filterOutOfScopeLocals, CancellationToken cancellationToken) + : AbstractRecommendationServiceRunner(context, filterOutOfScopeLocals, cancellationToken) { - public CSharpRecommendationServiceRunner( - CSharpSyntaxContext context, bool filterOutOfScopeLocals, CancellationToken cancellationToken) - : base(context, filterOutOfScopeLocals, cancellationToken) - { - } - protected override int GetLambdaParameterCount(AnonymousFunctionExpressionSyntax lambdaSyntax) => lambdaSyntax switch { @@ -330,6 +326,9 @@ private ImmutableArray GetSymbolsForTypeOrNamespaceContext() private ImmutableArray GetSymbolsForExpressionOrStatementContext() { + var contextNode = _context.LeftToken.GetRequiredParent(); + var semanticModel = _context.SemanticModel; + // Check if we're in an interesting situation like this: // // i // <-- here @@ -346,22 +345,38 @@ private ImmutableArray GetSymbolsForExpressionOrStatementContext() var filterOutOfScopeLocals = _filterOutOfScopeLocals; if (filterOutOfScopeLocals) { - var contextNode = _context.LeftToken.GetRequiredParent(); filterOutOfScopeLocals = !contextNode.IsFoundUnder(d => d.Declaration.Type) && !contextNode.IsFoundUnder(d => d.Type); } - var symbols = !_context.IsNameOfContext && _context.LeftToken.GetRequiredParent().IsInStaticContext() - ? _context.SemanticModel.LookupStaticMembers(_context.LeftToken.SpanStart) - : _context.SemanticModel.LookupSymbols(_context.LeftToken.SpanStart); + ImmutableArray symbols; + if (_context.IsNameOfContext) + { + symbols = semanticModel.LookupSymbols(_context.LeftToken.SpanStart); + + // We may be inside of a nameof() on a method. In that case, we want to include the parameters in + // the nameof if LookupSymbols didn't already return them. + var enclosingMethodOrLambdaNode = contextNode.AncestorsAndSelf().FirstOrDefault(n => n is AnonymousFunctionExpressionSyntax or BaseMethodDeclarationSyntax); + var enclosingMethodOrLambda = enclosingMethodOrLambdaNode is null + ? null + : semanticModel.GetSymbolInfo(enclosingMethodOrLambdaNode).GetAnySymbol() ?? semanticModel.GetDeclaredSymbol(enclosingMethodOrLambdaNode); + if (enclosingMethodOrLambda is IMethodSymbol method) + symbols = [.. symbols.Concat(method.Parameters)]; + } + else + { + symbols = contextNode.IsInStaticContext() + ? semanticModel.LookupStaticMembers(_context.LeftToken.SpanStart) + : semanticModel.LookupSymbols(_context.LeftToken.SpanStart); + } // Filter out any extension methods that might be imported by a using static directive. // But include extension methods declared in the context's type or it's parents var contextOuterTypes = ComputeOuterTypes(_context, _cancellationToken); - var contextEnclosingNamedType = _context.SemanticModel.GetEnclosingNamedType(_context.Position, _cancellationToken); + var contextEnclosingNamedType = semanticModel.GetEnclosingNamedType(_context.Position, _cancellationToken); - return symbols.WhereAsArray( + return symbols.Distinct().WhereAsArray( static (symbol, args) => !IsUndesirable(args._context, args.contextEnclosingNamedType, args.contextOuterTypes, args.filterOutOfScopeLocals, symbol, args._cancellationToken), (_context, contextOuterTypes, contextEnclosingNamedType, filterOutOfScopeLocals, _cancellationToken));