diff --git a/src/Compilers/CSharp/Portable/CSharpExtensions.cs b/src/Compilers/CSharp/Portable/CSharpExtensions.cs index d2e4bf6f714b8ccb77f61768aa9a2c5baa6324c2..a9332cd949347638c21a2d8ed3d59c6a18965b4c 100644 --- a/src/Compilers/CSharp/Portable/CSharpExtensions.cs +++ b/src/Compilers/CSharp/Portable/CSharpExtensions.cs @@ -610,7 +610,6 @@ public static TypeInfo GetTypeInfo(this SemanticModel semanticModel, Constructor } } - public static TypeInfo GetTypeInfo(this SemanticModel semanticModel, SelectOrGroupClauseSyntax node, CancellationToken cancellationToken = default(CancellationToken)) { var csmodel = semanticModel as CSharpSemanticModel; @@ -1339,4 +1338,4 @@ public static IRangeVariableSymbol GetDeclaredSymbol(this SemanticModel semantic } #endregion } -} +} \ No newline at end of file diff --git a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs index 0d7876c9b422271c7b8ed7b2fb7cb3fdd6f6b8ab..76ffe81a6c7a48cc9a3f45fbc4dbbd05896f2a31 100644 --- a/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs +++ b/src/Compilers/CSharp/Portable/Parser/LanguageParser.cs @@ -7010,6 +7010,56 @@ private bool IsPossibleLocalDeclarationStatement(bool allowAnyExpression) return typedIdentifier.Value; } + // It's common to have code like the following: + // + // Task. + // await Task.Delay() + // + // In this case we don't want to parse this as as a local declaration like: + // + // Task.await Task + // + // This does not represent user intent, and it causes all sorts of problems to higher + // layers. This is because both the parse tree is strage, and the symbol tables have + // entries that throw things off (like a bogus 'Task' local). + // + // Note that we explicitly do this check when we see that the code spreads over multiple + // lines. We don't want this if the user has actually written "X.Y z" + if (tk == SyntaxKind.IdentifierToken) + { + var token1 = PeekToken(1); + if (token1.Kind == SyntaxKind.DotToken && + token1.TrailingTrivia.Any((int)SyntaxKind.EndOfLineTrivia)) + { + if (PeekToken(2).Kind == SyntaxKind.IdentifierToken && + PeekToken(3).Kind == SyntaxKind.IdentifierToken) + { + // We have something like: + // + // X. + // Y z + // + // This is only a local declaration if we have: + // + // X.Y z; + // X.Y z = ... + // X.Y z, ... + // X.Y z( ... (local function) + // X.Y z(); } } +"; + ParseAndValidate(text, + // (7,9): error CS4003: 'await' cannot be used as an identifier within an async method or lambda expression + // await Task(); + Diagnostic(ErrorCode.ERR_BadAwaitAsIdentifier, "await").WithLocation(7, 9)); + + UsingTree(text); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.LocalFunctionStatement); + { + N(SyntaxKind.QualifiedName); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Task"); + } + N(SyntaxKind.DotToken); + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "await"); + } + } + N(SyntaxKind.IdentifierToken, "Task"); + N(SyntaxKind.TypeParameterList); + { + N(SyntaxKind.LessThanToken); + N(SyntaxKind.TypeParameter); + { + N(SyntaxKind.IdentifierToken, "T"); + } + N(SyntaxKind.GreaterThanToken); + } + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + + [Fact, WorkItem(15885, "https://github.com/dotnet/roslyn/pull/15885")] + public void InProgressLocalDeclaration8() + { + const string text = @" +class C +{ + async void M() + { + Task. + await Task[1]; + } +} +"; + ParseAndValidate(text, + // (6,14): error CS1001: Identifier expected + // Task. + Diagnostic(ErrorCode.ERR_IdentifierExpected, "").WithLocation(6, 14), + // (6,14): error CS1002: ; expected + // Task. + Diagnostic(ErrorCode.ERR_SemicolonExpected, "").WithLocation(6, 14)); + + UsingTree(text); + N(SyntaxKind.CompilationUnit); + { + N(SyntaxKind.ClassDeclaration); + { + N(SyntaxKind.ClassKeyword); + N(SyntaxKind.IdentifierToken, "C"); + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.MethodDeclaration); + { + N(SyntaxKind.AsyncKeyword); + N(SyntaxKind.PredefinedType); + { + N(SyntaxKind.VoidKeyword); + } + N(SyntaxKind.IdentifierToken, "M"); + N(SyntaxKind.ParameterList); + { + N(SyntaxKind.OpenParenToken); + N(SyntaxKind.CloseParenToken); + } + N(SyntaxKind.Block); + { + N(SyntaxKind.OpenBraceToken); + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.SimpleMemberAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Task"); + } + N(SyntaxKind.DotToken); + M(SyntaxKind.IdentifierName); + { + M(SyntaxKind.IdentifierToken); + } + } + M(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.ExpressionStatement); + { + N(SyntaxKind.AwaitExpression); + { + N(SyntaxKind.AwaitKeyword); + N(SyntaxKind.ElementAccessExpression); + { + N(SyntaxKind.IdentifierName); + { + N(SyntaxKind.IdentifierToken, "Task"); + } + N(SyntaxKind.BracketedArgumentList); + { + N(SyntaxKind.OpenBracketToken); + N(SyntaxKind.Argument); + { + N(SyntaxKind.NumericLiteralExpression); + { + N(SyntaxKind.NumericLiteralToken, "1"); + } + } + N(SyntaxKind.CloseBracketToken); + } + } + } + N(SyntaxKind.SemicolonToken); + } + N(SyntaxKind.CloseBraceToken); + } + } + N(SyntaxKind.CloseBraceToken); + } + N(SyntaxKind.EndOfFileToken); + } + EOF(); + } + } +} \ No newline at end of file diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index 92249062d03b87a673e2c289176749ea22ab2ef7..ba2e71e549f0461a32ca8322eb8c26bb1d8b41d8 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -2730,6 +2730,7 @@ class C End Function End Class + Public Async Function Filters_EmptyList1() As Task Using state = TestState.CreateCSharpTestState( Public Async Function Filters_EmptyList2() As Task Using state = TestState.CreateCSharpTestState( Public Async Function Filters_EmptyList3() As Task Using state = TestState.CreateCSharpTestState( Public Async Function Filters_EmptyList4() As Task Using state = TestState.CreateCSharpTestState( + + Public Async Function CompletionAfterDotBeforeAwaitTask() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendInvokeCompletionList() + Await state.AssertCompletionSession() + End Using + End Function End Class End Namespace diff --git a/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs b/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs index d2c9411c3df58cffe9e7fa7b6779c9f0745850cb..cc432be9021edec0840a1cdfcf83bb1905a1d579 100644 --- a/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs +++ b/src/Workspaces/CSharp/Portable/Recommendations/CSharpRecommendationService.cs @@ -305,9 +305,16 @@ private static ImmutableArray GetSymbolsForNamespaceDeclarationNameCont if (name.IsFoundUnder(d => d.Declaration.Type) || name.IsFoundUnder(d => d.Declaration.Type)) { - var speculativeBinding = context.SemanticModel.GetSpeculativeSymbolInfo(name.SpanStart, name, SpeculativeBindingOption.BindAsExpression); - var container = context.SemanticModel.GetSpeculativeTypeInfo(name.SpanStart, name, SpeculativeBindingOption.BindAsExpression).Type; - return GetSymbolsOffOfBoundExpression(context, name, name, speculativeBinding, container, cancellationToken); + var speculativeBinding = context.SemanticModel.GetSpeculativeSymbolInfo( + name.SpanStart, name, SpeculativeBindingOption.BindAsExpression); + + var container = context.SemanticModel.GetSpeculativeTypeInfo( + name.SpanStart, name, SpeculativeBindingOption.BindAsExpression).Type; + + var speculativeResult = GetSymbolsOffOfBoundExpression( + context, name, name, speculativeBinding, container, cancellationToken); + + return speculativeResult; } // We're in a name-only context, since if we were an expression we'd be a @@ -436,7 +443,7 @@ private static ImmutableArray GetSymbolsForNamespaceDeclarationNameCont var useBaseReferenceAccessibility = false; var excludeInstance = false; var excludeStatic = false; - var symbol = leftHandBinding.GetBestOrAllSymbols().FirstOrDefault(); + var symbol = leftHandBinding.GetAnySymbol(); if (symbol != null) { @@ -540,4 +547,4 @@ private static ImmutableArray GetSymbolsForNamespaceDeclarationNameCont : symbols; } } -} +} \ No newline at end of file