diff --git a/build/scripts/test-determinism.ps1 b/build/scripts/test-determinism.ps1 index 8f5fdf273855f4f798273a9a229a3dd5f27883bc..bb3c9c881472acccf802f34e8ccc73d681076fa8 100644 --- a/build/scripts/test-determinism.ps1 +++ b/build/scripts/test-determinism.ps1 @@ -27,6 +27,11 @@ function Run-Build() $debugDir = join-path $rootDir "Binaries\Debug" $objDir = join-path $rootDir "Binaries\Obj" + # Temporarily forcing MSBuild 14.0 here to work around a bug in 15.0 + # MSBuild bug: https://github.com/Microsoft/msbuild/issues/1183 + # Roslyn tracking bug: https://github.com/dotnet/roslyn/issues/14451 + $msbuild = "c:\Program Files (x86)\MSBuild\14.0\bin\MSBuild.exe" + # Create directories that may or may not exist to make the script execution below # clean in either case. mkdir $debugDir -errorAction SilentlyContinue | out-null @@ -38,10 +43,10 @@ function Run-Build() write-host "Cleaning the Binaries" rm -re -fo $debugDir rm -re -fo $objDir - & msbuild /nologo /v:m /nodeReuse:false /t:clean $sln + & $msbuild /nologo /v:m /nodeReuse:false /t:clean $sln write-host "Building the Solution" - & msbuild /nologo /v:m /nodeReuse:false /m /p:DebugDeterminism=true /p:BootstrapBuildPath=$script:buildDir '/p:Features="debug-determinism;pdb-path-determinism"' /p:UseRoslynAnalyzers=false $pathMapBuildOption $sln + & $msbuild /nologo /v:m /nodeReuse:false /m /p:DebugDeterminism=true /p:BootstrapBuildPath=$script:buildDir '/p:Features="debug-determinism;pdb-path-determinism"' /p:UseRoslynAnalyzers=false $pathMapBuildOption $sln popd } diff --git a/netci.groovy b/netci.groovy index 4c8738eb4487155d91e8e07099d959efa57abe1a..c684651cc1c6212a1ae29e548d05986e6ad9992f 100644 --- a/netci.groovy +++ b/netci.groovy @@ -152,7 +152,7 @@ set TMP=%TEMP% } } - def triggerPhraseOnly = true + def triggerPhraseOnly = false def triggerPhraseExtra = "determinism" Utilities.setMachineAffinity(myJob, 'Windows_NT', 'latest-or-auto-dev15') addRoslynJob(myJob, jobName, branchName, isPr, triggerPhraseExtra, triggerPhraseOnly) diff --git a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs index e6b7a29d9fc346faee5dac9c9046f9e4a934bf26..cc565b910e336dadb330decb8cb34b1fbb117986 100644 --- a/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs +++ b/src/Compilers/CSharp/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.cs @@ -1655,5 +1655,233 @@ public partial class C33 { } Diagnostic("UniqueTextFileDiagnostic").WithArguments("Source3_File5.designer.cs").WithLocation(1, 1), Diagnostic("NumberOfUniqueTextFileDescriptor").WithArguments("3").WithLocation(1, 1)); } + + [Fact, WorkItem(8753, "https://github.com/dotnet/roslyn/issues/8753")] + public void TestParametersAnalyzer_InConstructor() + { + string source = @" +public class C +{ + public C(int a, int b) + { + } +} +"; + var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerForParameters() }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("Parameter_ID", "a").WithLocation(4, 18), + Diagnostic("Parameter_ID", "b").WithLocation(4, 25)); + } + + [Fact, WorkItem(8753, "https://github.com/dotnet/roslyn/issues/8753")] + public void TestParametersAnalyzer_InRegularMethod() + { + string source = @" +public class C +{ + void M1(string a, string b) + { + } +} +"; + var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerForParameters() }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("Parameter_ID", "a").WithLocation(4, 20), + Diagnostic("Parameter_ID", "b").WithLocation(4, 30)); + } + + [Fact, WorkItem(8753, "https://github.com/dotnet/roslyn/issues/8753")] + public void TestParametersAnalyzer_InIndexers() + { + string source = @" +public class C +{ + public int this[int index] + { + get { return 0; } + set { } + } +} +"; + var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerForParameters() }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("Parameter_ID", "index").WithLocation(4, 25)); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/14061"), WorkItem(8753, "https://github.com/dotnet/roslyn/issues/8753")] + public void TestParametersAnalyzer_Lambdas() + { + string source = @" +public class C +{ + void M2() + { + System.Func x = (int a, int b) => b; + } +} +"; + var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerForParameters() }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("Local_ID", "x").WithLocation(6, 36), + Diagnostic("Parameter_ID", "a").WithLocation(6, 45), + Diagnostic("Parameter_ID", "b").WithLocation(6, 52)); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/14061"), WorkItem(8753, "https://github.com/dotnet/roslyn/issues/8753")] + public void TestParametersAnalyzer_InAnonymousMethods() + { + string source = @" +public class C +{ + void M3() + { + M4(delegate (int x, int y) { }); + } + + void M4(System.Action a) { } +} +"; + var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerForParameters() }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("Parameter_ID", "a").WithLocation(9, 37), + Diagnostic("Parameter_ID", "x").WithLocation(6, 26), + Diagnostic("Parameter_ID", "y").WithLocation(6, 33)); + } + + [Fact, WorkItem(8753, "https://github.com/dotnet/roslyn/issues/8753")] + public void TestParametersAnalyzer_InDelegateTypes() + { + string source = @" +public class C +{ + delegate void D(int x, string y); +} +"; + var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerForParameters() }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("Parameter_ID", "x").WithLocation(4, 25), + Diagnostic("Parameter_ID", "y").WithLocation(4, 35)); + } + + [Fact, WorkItem(8753, "https://github.com/dotnet/roslyn/issues/8753")] + public void TestParametersAnalyzer_InOperators() + { + string source = @" +public class C +{ + public static implicit operator int (C c) { return 0; } +} +"; + var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerForParameters() }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("Parameter_ID", "c").WithLocation(4, 44)); + } + + [Fact, WorkItem(8753, "https://github.com/dotnet/roslyn/issues/8753")] + public void TestParametersAnalyzer_InExplicitInterfaceImplementations() + { + string source = @" +interface I +{ + void M(int a, int b); +} + +public class C : I +{ + void I.M(int c, int d) { } +} +"; + var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerForParameters() }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("Parameter_ID", "c").WithLocation(9, 18), + Diagnostic("Parameter_ID", "d").WithLocation(9, 25), + Diagnostic("Parameter_ID", "a").WithLocation(4, 16), + Diagnostic("Parameter_ID", "b").WithLocation(4, 23)); + } + + [Fact, WorkItem(8753, "https://github.com/dotnet/roslyn/issues/8753")] + public void TestParametersAnalyzer_InExtensionMethods() + { + string source = @" +public static class C +{ + static void M(this int x, int y) { } +} +"; + var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerForParameters() }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("Parameter_ID", "x").WithLocation(4, 28), + Diagnostic("Parameter_ID", "y").WithLocation(4, 35)); + } + + [Fact(Skip = "https://github.com/dotnet/roslyn/issues/14061"), WorkItem(8753, "https://github.com/dotnet/roslyn/issues/8753")] + public void TestParametersAnalyzer_InLocalFunctions() + { + string source = @" +public class C +{ + void M1() + { + M2(1, 2); + + void M2(int a, int b) + { + } + } +} +"; + var tree = CSharpSyntaxTree.ParseText(source, path: "Source.cs"); + var compilation = CreateCompilationWithMscorlib45(new[] { tree }); + compilation.VerifyDiagnostics(); + + var analyzers = new DiagnosticAnalyzer[] { new AnalyzerForParameters() }; + compilation.VerifyAnalyzerDiagnostics(analyzers, null, null, true, + Diagnostic("Parameter_ID", "a").WithLocation(4, 18), // ctor + Diagnostic("Parameter_ID", "b").WithLocation(4, 25), + Diagnostic("Local_ID", "c").WithLocation(6, 13), + Diagnostic("Local_ID", "d").WithLocation(6, 20), + Diagnostic("Parameter_ID", "a").WithLocation(10, 20), // M1 + Diagnostic("Parameter_ID", "b").WithLocation(10, 30), + Diagnostic("Local_ID", "c").WithLocation(12, 11), + Diagnostic("Local_ID", "x").WithLocation(18, 36), // M2 + Diagnostic("Parameter_ID", "a").WithLocation(26, 37), // M4 + Diagnostic("Parameter_ID", "index").WithLocation(28, 25)); // indexer + } } } diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs index 4ad3a5865e5dc4e69f94ac398dc9c59dba4be303..2a28032a49233ac8c66ac7401f60d6086f04a851 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticAnalysisContext.cs @@ -701,6 +701,8 @@ public struct SymbolAnalysisContext /// public CancellationToken CancellationToken { get { return _cancellationToken; } } + internal Func IsSupportedDiagnostic => _isSupportedDiagnostic; + public SymbolAnalysisContext(ISymbol symbol, Compilation compilation, AnalyzerOptions options, Action reportDiagnostic, Func isSupportedDiagnostic, CancellationToken cancellationToken) { _symbol = symbol; diff --git a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs index 672225a0a2e58ecdb8f6e0492b7f8a424067b695..7544d7a7609cfb2318bed67861692d2e2263052a 100644 --- a/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs +++ b/src/Compilers/Core/Portable/DiagnosticAnalyzer/DiagnosticStartAnalysisScope.cs @@ -608,6 +608,53 @@ public void RegisterSymbolAction(DiagnosticAnalyzer analyzer, Action + { + ImmutableArray parameters; + + switch (context.Symbol.Kind) + { + case SymbolKind.Method: + parameters = ((IMethodSymbol)context.Symbol).Parameters; + break; + case SymbolKind.Property: + parameters = ((IPropertySymbol)context.Symbol).Parameters; + break; + case SymbolKind.NamedType: + var namedType = (INamedTypeSymbol)context.Symbol; + var delegateInvokeMethod = namedType.DelegateInvokeMethod; + parameters = delegateInvokeMethod?.Parameters ?? ImmutableArray.Create(); + break; + default: + throw new ArgumentException(nameof(context)); + } + + foreach (var parameter in parameters) + { + if (!parameter.IsImplicitlyDeclared) + { + action(new SymbolAnalysisContext( + parameter, + context.Compilation, + context.Options, + context.ReportDiagnostic, + context.IsSupportedDiagnostic, + context.CancellationToken)); + } + } + }, + ImmutableArray.Create(SymbolKind.Method, SymbolKind.Property, SymbolKind.NamedType)); + } } public void RegisterCodeBlockStartAction(DiagnosticAnalyzer analyzer, Action> action) where TLanguageKindEnum : struct diff --git a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb index c78f03f4e538c277de8250d7fb4932402b717d75..b0f9c53f29c941c472ab7c84a7f219b05878b452 100644 --- a/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb +++ b/src/Compilers/VisualBasic/Test/Semantic/Diagnostics/DiagnosticAnalyzerTests.vb @@ -981,6 +981,162 @@ End Class Diagnostic(OwningSymbolTestAnalyzer.ExpressionDescriptor.Id, "12").WithLocation(12, 36)) End Sub + + Public Sub TestParametersAnalyzer_InRegularMethods() + Dim source = + + + + + + Dim comp = CreateCompilationWithMscorlibAndVBRuntime(source) + comp.VerifyDiagnostics() + comp.VerifyAnalyzerDiagnostics({New AnalyzerForParameters}, Nothing, Nothing, False, + Diagnostic("Parameter_ID", "a").WithLocation(2, 18), + Diagnostic("Parameter_ID", "b").WithLocation(2, 32)) + End Sub + + + Public Sub TestParametersAnalyzer_InConstructors() + Dim source = + + + + + + Dim comp = CreateCompilationWithMscorlibAndVBRuntime(source) + comp.VerifyDiagnostics() + comp.VerifyAnalyzerDiagnostics({New AnalyzerForParameters}, Nothing, Nothing, False, + Diagnostic("Parameter_ID", "a").WithLocation(2, 20), + Diagnostic("Parameter_ID", "b").WithLocation(2, 34)) + End Sub + + + Public Sub TestParametersAnalyzer_InIndexers() + Dim source = + + + + + + Dim comp = CreateCompilationWithMscorlibAndVBRuntime(source) + comp.VerifyDiagnostics() + comp.VerifyAnalyzerDiagnostics({New AnalyzerForParameters}, Nothing, Nothing, False, + Diagnostic("Parameter_ID", "a").WithLocation(2, 34), + Diagnostic("Parameter_ID", "b").WithLocation(2, 48), + Diagnostic("Parameter_ID", "Value").WithLocation(6, 19)) + End Sub + + + Public Sub TestParametersAnalyzer_InDelegateTypes() + Dim source = + + + + + + Dim comp = CreateCompilationWithMscorlibAndVBRuntime(source) + comp.VerifyDiagnostics() + comp.VerifyAnalyzerDiagnostics({New AnalyzerForParameters}, Nothing, Nothing, False, + Diagnostic("Parameter_ID", "a").WithLocation(2, 34), + Diagnostic("Parameter_ID", "b").WithLocation(2, 48)) + End Sub + + + Public Sub TestParametersAnalyzer_InOperators() + Dim source = + + + + + + Dim comp = CreateCompilationWithMscorlibAndVBRuntime(source) + comp.VerifyDiagnostics() + comp.VerifyAnalyzerDiagnostics({New AnalyzerForParameters}, Nothing, Nothing, False, + Diagnostic("Parameter_ID", "h1").WithLocation(2, 36), + Diagnostic("Parameter_ID", "h2").WithLocation(2, 51)) + End Sub + + + Public Sub TestParametersAnalyzer_InInterfaceImplementations() + Dim source = + + + + + + Dim comp = CreateCompilationWithMscorlibAndVBRuntime(source) + comp.VerifyDiagnostics() + comp.VerifyAnalyzerDiagnostics({New AnalyzerForParameters}, Nothing, Nothing, False, + Diagnostic("Parameter_ID", "a").WithLocation(2, 11), + Diagnostic("Parameter_ID", "b").WithLocation(2, 25), + Diagnostic("Parameter_ID", "a").WithLocation(7, 18), + Diagnostic("Parameter_ID", "b").WithLocation(7, 32)) + End Sub + + + Public Sub TestParametersAnalyzer_InParameterizedProperties() + Dim source = + + + + + + Dim comp = CreateCompilationWithMscorlibAndVBRuntime(source) + comp.VerifyDiagnostics() + comp.VerifyAnalyzerDiagnostics({New AnalyzerForParameters}, Nothing, Nothing, False, + Diagnostic("Parameter_ID", "a").WithLocation(2, 35), + Diagnostic("Parameter_ID", "b").WithLocation(2, 49)) + End Sub + Private Shared Sub VerifyGeneratedCodeAnalyzerDiagnostics(compilation As Compilation, isGeneratedFileName As Func(Of String, Boolean), generatedCodeAnalysisFlagsOpt As GeneratedCodeAnalysisFlags?) Dim expected = GetExpectedGeneratedCodeAnalyzerDiagnostics(compilation, isGeneratedFileName, generatedCodeAnalysisFlagsOpt) VerifyGeneratedCodeAnalyzerDiagnostics(compilation, expected, generatedCodeAnalysisFlagsOpt) diff --git a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs index 455375ce68a0c47830e0c3594a582057f98d8fdc..29746ab539f3c3d2afa0ff8a07f53f9e9a5f4fe8 100644 --- a/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs +++ b/src/EditorFeatures/Core/Implementation/IntelliSense/Completion/Controller.Session_FilterModel.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System; using System.Collections.Generic; using System.Collections.Immutable; using System.Diagnostics; @@ -131,13 +132,27 @@ internal partial class Session var filterResults = new List(); - var filterText = model.GetCurrentTextInSnapshot(model.OriginalList.Span, textSnapshot, textSpanToText); + var filterText = model.GetCurrentTextInSnapshot( + model.OriginalList.Span, textSnapshot, textSpanToText); - // If the user was typing a number, then immediately dismiss completion. + // Check if the user is typing a number. If so, only proceed if it's a number + // directly after a . That's because it is actually reasonable for completion + // to be brought up after a and for the user to want to filter completion + // items based on a number that exists in the name of the item. However, when + // we are not after a dot (i.e. we're being brought up after is typed) + // then we don't want to filter things. Consider the user writing: + // + // dim i = + // + // We'll bring up the completion list here (as VB has completion on ). + // If the user then types '3', we don't want to match against Int32. var filterTextStartsWithANumber = filterText.Length > 0 && char.IsNumber(filterText[0]); if (filterTextStartsWithANumber) { - return null; + if (!IsAfterDot(model, textSnapshot, textSpanToText)) + { + return null; + } } foreach (var currentItem in model.TotalItems) @@ -196,6 +211,17 @@ internal partial class Session helper, recentItems, filterText, filterResults); } + private Boolean IsAfterDot(Model model, ITextSnapshot textSnapshot, Dictionary textSpanToText) + { + var span = model.OriginalList.Span; + + // Move the span back one character if possible. + span = TextSpan.FromBounds(Math.Max(0, span.Start - 1), span.End); + + var text = model.GetCurrentTextInSnapshot(span, textSnapshot, textSpanToText); + return text.Length > 0 && text[0] == '.'; + } + private Model HandleNormalFiltering( Model model, Document document, @@ -424,13 +450,6 @@ public FilterResult(PresentationItem presentationItem, string filterText, bool m } } - if (filterText.Length > 0 && IsAllDigits(filterText)) - { - // The user is just typing a number. We never want this to match against - // anything we would put in a completion list. - return false; - } - return helper.MatchesFilterText(item, filterText, CultureInfo.CurrentCulture); } diff --git a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb index 3029443707a626e05e110984e073f860f919385c..f69fec4acfa5a6a3e4b9ab2e3dd1130f3634cd42 100644 --- a/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb +++ b/src/EditorFeatures/Test2/IntelliSense/CSharpCompletionCommandHandlerTests.vb @@ -2244,5 +2244,26 @@ class Program Await state.AssertSelectedCompletionItem("DateTime") End Using End Function + + + + Public Async Function TypingNumberShouldNotDismiss1() As Task + Using state = TestState.CreateCSharpTestState( + ) + + state.SendTypeChars(".") + Await state.AssertCompletionSession() + state.SendTypeChars("1") + Await state.AssertSelectedCompletionItem("Moo1") + End Using + End Function End Class End Namespace \ No newline at end of file diff --git a/src/Features/Core/Portable/Completion/CompletionItemFilter.cs b/src/Features/Core/Portable/Completion/CompletionItemFilter.cs index f65e2fd4a317ecbb3b9513610ddfe0183d8fac95..a419fb3d4e1d7761a3ff147abb12ba6682015a5d 100644 --- a/src/Features/Core/Portable/Completion/CompletionItemFilter.cs +++ b/src/Features/Core/Portable/Completion/CompletionItemFilter.cs @@ -47,6 +47,8 @@ public bool Matches(CompletionItem item) public static readonly CompletionItemFilter MethodFilter = new CompletionItemFilter(FeaturesResources.Methods, CompletionTags.Method, 'm'); public static readonly CompletionItemFilter ExtensionMethodFilter = new CompletionItemFilter(FeaturesResources.Extension_methods, CompletionTags.ExtensionMethod, 'x'); public static readonly CompletionItemFilter LocalAndParameterFilter = new CompletionItemFilter(FeaturesResources.Locals_and_parameters, ImmutableArray.Create(CompletionTags.Local, CompletionTags.Parameter), 'l'); + public static readonly CompletionItemFilter KeywordFilter = new CompletionItemFilter(FeaturesResources.Keywords, ImmutableArray.Create(CompletionTags.Keyword), 'k'); + public static readonly CompletionItemFilter SnippetFilter = new CompletionItemFilter(FeaturesResources.Snippets, ImmutableArray.Create(CompletionTags.Snippet), 't'); public static readonly ImmutableArray NamespaceFilters = ImmutableArray.Create(NamespaceFilter); public static readonly ImmutableArray ClassFilters = ImmutableArray.Create(ClassFilter); @@ -62,6 +64,8 @@ public bool Matches(CompletionItem item) public static readonly ImmutableArray MethodFilters = ImmutableArray.Create(MethodFilter); public static readonly ImmutableArray ExtensionMethodFilters = ImmutableArray.Create(ExtensionMethodFilter); public static readonly ImmutableArray LocalAndParameterFilters = ImmutableArray.Create(LocalAndParameterFilter); + public static readonly ImmutableArray KeywordFilters = ImmutableArray.Create(KeywordFilter); + public static readonly ImmutableArray SnippetFilters = ImmutableArray.Create(SnippetFilter); public static ImmutableArray AllFilters { get; } = ImmutableArray.Create( @@ -78,6 +82,8 @@ public bool Matches(CompletionItem item) StructureFilter, EnumFilter, DelegateFilter, - NamespaceFilter); + NamespaceFilter, + KeywordFilter, + SnippetFilter); } -} +} \ No newline at end of file diff --git a/src/Features/Core/Portable/FeaturesResources.Designer.cs b/src/Features/Core/Portable/FeaturesResources.Designer.cs index 9a08d9b4aa53e713d104ef20b277c1964ea7b61f..97fa3d0ed1252f17086be0b2b654ff767dec384b 100644 --- a/src/Features/Core/Portable/FeaturesResources.Designer.cs +++ b/src/Features/Core/Portable/FeaturesResources.Designer.cs @@ -1062,6 +1062,15 @@ internal class FeaturesResources { } } + /// + /// Looks up a localized string similar to Fully qualify '{0}'. + /// + internal static string Fully_qualify_0 { + get { + return ResourceManager.GetString("Fully_qualify_0", resourceCulture); + } + } + /// /// Looks up a localized string similar to Fully qualify name. /// @@ -1602,6 +1611,15 @@ internal class FeaturesResources { } } + /// + /// Looks up a localized string similar to Keywords. + /// + internal static string Keywords { + get { + return ResourceManager.GetString("Keywords", resourceCulture); + } + } + /// /// Looks up a localized string similar to label. /// @@ -2166,6 +2184,15 @@ internal class FeaturesResources { } } + /// + /// Looks up a localized string similar to Remove reference to '{0}'.. + /// + internal static string Remove_reference_to_0 { + get { + return ResourceManager.GetString("Remove_reference_to_0", resourceCulture); + } + } + /// /// Looks up a localized string similar to Remove Suppression. /// @@ -2376,6 +2403,15 @@ internal class FeaturesResources { } } + /// + /// Looks up a localized string similar to Snippets. + /// + internal static string Snippets { + get { + return ResourceManager.GetString("Snippets", resourceCulture); + } + } + /// /// Looks up a localized string similar to Solution. /// @@ -2403,6 +2439,15 @@ internal class FeaturesResources { } } + /// + /// Looks up a localized string similar to Spell check '{0}'. + /// + internal static string Spell_check_0 { + get { + return ResourceManager.GetString("Spell_check_0", resourceCulture); + } + } + /// /// Looks up a localized string similar to Structures. /// @@ -2911,6 +2956,114 @@ internal class FeaturesResources { } } + /// + /// Looks up a localized string similar to Use block body for accessors. + /// + internal static string Use_block_body_for_accessors { + get { + return ResourceManager.GetString("Use_block_body_for_accessors", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use block body for constructors. + /// + internal static string Use_block_body_for_constructors { + get { + return ResourceManager.GetString("Use_block_body_for_constructors", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use block body for indexers. + /// + internal static string Use_block_body_for_indexers { + get { + return ResourceManager.GetString("Use_block_body_for_indexers", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use block body for methods. + /// + internal static string Use_block_body_for_methods { + get { + return ResourceManager.GetString("Use_block_body_for_methods", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use block body for operators. + /// + internal static string Use_block_body_for_operators { + get { + return ResourceManager.GetString("Use_block_body_for_operators", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use block body for properties. + /// + internal static string Use_block_body_for_properties { + get { + return ResourceManager.GetString("Use_block_body_for_properties", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use expression body for accessors. + /// + internal static string Use_expression_body_for_accessors { + get { + return ResourceManager.GetString("Use_expression_body_for_accessors", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use expression body for constructors. + /// + internal static string Use_expression_body_for_constructors { + get { + return ResourceManager.GetString("Use_expression_body_for_constructors", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use expression body for indexers. + /// + internal static string Use_expression_body_for_indexers { + get { + return ResourceManager.GetString("Use_expression_body_for_indexers", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use expression body for methods. + /// + internal static string Use_expression_body_for_methods { + get { + return ResourceManager.GetString("Use_expression_body_for_methods", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use expression body for operators. + /// + internal static string Use_expression_body_for_operators { + get { + return ResourceManager.GetString("Use_expression_body_for_operators", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Use expression body for properties. + /// + internal static string Use_expression_body_for_properties { + get { + return ResourceManager.GetString("Use_expression_body_for_properties", resourceCulture); + } + } + /// /// Looks up a localized string similar to Use framework type. /// diff --git a/src/Features/Core/Portable/FeaturesResources.resx b/src/Features/Core/Portable/FeaturesResources.resx index 97e974c2a0ac86760c057ce5d0b9ead65d34351f..e1d7828af609c7296e68eff41e5be25d505b82f7 100644 --- a/src/Features/Core/Portable/FeaturesResources.resx +++ b/src/Features/Core/Portable/FeaturesResources.resx @@ -1088,4 +1088,55 @@ This version used in: {2} Fully qualify name + + Use expression body for methods + + + Use block body for methods + + + Use block body for accessors + + + Use block body for constructors + + + Use block body for indexers + + + Use block body for operators + + + Use block body for properties + + + Use expression body for accessors + + + Use expression body for constructors + + + Use expression body for indexers + + + Use expression body for operators + + + Use expression body for properties + + + Spell check '{0}' + + + Fully qualify '{0}' + + + Remove reference to '{0}'. + + + Keywords + + + Snippets + \ No newline at end of file diff --git a/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs b/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs index 477981c07330427bd85717b7fecb160f7658d214..744523bf3f41245244ceeedafef5135c031fbc10 100644 --- a/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs +++ b/src/Test/Utilities/Desktop/CommonDiagnosticAnalyzers.cs @@ -1067,5 +1067,29 @@ private void OnCompilationStart(CompilationStartAnalysisContext context) }); } } + + [DiagnosticAnalyzer(LanguageNames.CSharp, LanguageNames.VisualBasic)] + public class AnalyzerForParameters : DiagnosticAnalyzer + { + public static readonly DiagnosticDescriptor ParameterDescriptor = new DiagnosticDescriptor( + "Parameter_ID", + "Parameter_Title", + "Parameter_Message", + "Parameter_Category", + defaultSeverity: DiagnosticSeverity.Warning, + isEnabledByDefault: true); + + public override ImmutableArray SupportedDiagnostics => ImmutableArray.Create(ParameterDescriptor); + + public override void Initialize(AnalysisContext context) + { + context.RegisterSymbolAction(SymbolAction, SymbolKind.Parameter); + } + + private void SymbolAction(SymbolAnalysisContext context) + { + context.ReportDiagnostic(Diagnostic.Create(ParameterDescriptor, context.Symbol.Locations[0])); + } + } } } \ No newline at end of file diff --git a/src/Test/Utilities/Shared/Diagnostics/TrackingDiagnosticAnalyzer.cs b/src/Test/Utilities/Shared/Diagnostics/TrackingDiagnosticAnalyzer.cs index 8dcd556da1aa0997f917ce84a80beb3c9a58aaea..30b9fdd1ebe0a3a553acea5ca15f1a5d6f284748 100644 --- a/src/Test/Utilities/Shared/Diagnostics/TrackingDiagnosticAnalyzer.cs +++ b/src/Test/Utilities/Shared/Diagnostics/TrackingDiagnosticAnalyzer.cs @@ -75,7 +75,11 @@ public void VerifyAllAnalyzerMembersWereCalled() public void VerifyAnalyzeSymbolCalledForAllSymbolKinds() { - var expectedSymbolKinds = new[] { SymbolKind.Event, SymbolKind.Field, SymbolKind.Method, SymbolKind.NamedType, SymbolKind.Namespace, SymbolKind.Property }; + var expectedSymbolKinds = new[] + { + SymbolKind.Event, SymbolKind.Field, SymbolKind.Method, SymbolKind.NamedType, SymbolKind.Namespace, SymbolKind.Parameter, SymbolKind.Property + }; + var actualSymbolKinds = _callLog.Where(a => FilterByAbstractName(a, "Symbol")).Where(e => e.SymbolKind.HasValue).Select(e => e.SymbolKind.Value).Distinct(); AssertSequenceEqual(expectedSymbolKinds, actualSymbolKinds); } diff --git a/src/VisualStudio/Core/Next/Diagnostics/OutOfProcDiagnosticAnalyzerExecutor.cs b/src/VisualStudio/Core/Next/Diagnostics/OutOfProcDiagnosticAnalyzerExecutor.cs index df1d361c72063db4288860b7f573261385160743..ce0b464db9e220c0138833452d89406a05be7ca4 100644 --- a/src/VisualStudio/Core/Next/Diagnostics/OutOfProcDiagnosticAnalyzerExecutor.cs +++ b/src/VisualStudio/Core/Next/Diagnostics/OutOfProcDiagnosticAnalyzerExecutor.cs @@ -80,7 +80,7 @@ internal class OutOfProcDiagnosticAnalyzerExecutor : IRemoteHostDiagnosticAnalyz } var optionAsset = GetOptionsAsset(solution, project.Language, cancellationToken); - var hostChecksums = GetHostAnalyzerReferences(snapshotService, _analyzerService.GetHostAnalyzerReferences(), cancellationToken); + var hostChecksums = GetHostAnalyzerReferences(snapshotService, project.Language, _analyzerService.GetHostAnalyzerReferences(), cancellationToken); var argument = new DiagnosticArguments( analyzerDriver.AnalysisOptions.ReportSuppressedDiagnostics, @@ -160,12 +160,22 @@ private void ReportAnalyzerExceptions(Project project, ImmutableDictionary GetHostAnalyzerReferences(ISolutionSynchronizationService snapshotService, IEnumerable references, CancellationToken cancellationToken) + private ImmutableArray GetHostAnalyzerReferences( + ISolutionSynchronizationService snapshotService, string language, IEnumerable references, CancellationToken cancellationToken) { // TODO: cache this to somewhere var builder = ImmutableArray.CreateBuilder(); foreach (var reference in references) { + var analyzers = reference.GetAnalyzers(language); + if (analyzers.Length == 0) + { + // skip reference that doesn't contain any analyzers for the given language + // we do this so that we don't load analyzer dlls that MEF exported from vsix + // not related to this solution + continue; + } + var asset = snapshotService.GetGlobalAsset(reference, cancellationToken); builder.Add(asset.Checksum.ToArray()); } diff --git a/src/Workspaces/Core/Portable/Execution/CustomAsset.cs b/src/Workspaces/Core/Portable/Execution/CustomAsset.cs index 84b49a17160d7997757536932707bb845c2cb761..5b123828024330562d46a1f608b293907bc3c32c 100644 --- a/src/Workspaces/Core/Portable/Execution/CustomAsset.cs +++ b/src/Workspaces/Core/Portable/Execution/CustomAsset.cs @@ -3,6 +3,8 @@ using System; using System.Threading; using System.Threading.Tasks; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Serialization; using Roslyn.Utilities; namespace Microsoft.CodeAnalysis.Execution @@ -48,4 +50,47 @@ private static Checksum CreateChecksumFromStreamWriter(string kind, Action + /// workspace analyzer specific asset. + /// + /// we need this to prevent dlls from other languages such as typescript, f#, xaml and etc + /// from loading at OOP start up. + /// + /// unlike project analyzer, analyzer that got installed from vsix doesn't do shadow copying + /// so we don't need to load assembly to find out actual filepath. + /// + /// this also will be temporary solution for RC since we will move to MVID for checksum soon + /// + internal sealed class WorkspaceAnalyzerReferenceAsset : CustomAsset + { + private readonly AnalyzerReference _reference; + private readonly Serializer _serializer; + + public WorkspaceAnalyzerReferenceAsset(AnalyzerReference reference, Serializer serializer) : + base(CreateChecksum(reference), WellKnownSynchronizationKinds.AnalyzerReference) + { + _reference = reference; + _serializer = serializer; + } + + public override Task WriteObjectToAsync(ObjectWriter writer, CancellationToken cancellationToken) + { + _serializer.SerializeAnalyzerReference(_reference, writer, cancellationToken); + + return SpecializedTasks.EmptyTask; + } + + private static Checksum CreateChecksum(AnalyzerReference reference) + { + using (var stream = SerializableBytes.CreateWritableStream()) + using (var objectWriter = new ObjectWriter(stream)) + { + objectWriter.WriteString(WellKnownSynchronizationKinds.AnalyzerReference); + objectWriter.WriteString(reference.FullPath); + + return Checksum.Create(stream); + } + } + } } diff --git a/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs b/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs index 9533fb39c5e966172d136bd81378cbe70d86dcbe..7097107e74139816497c1e4f6c8d846667a8334e 100644 --- a/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs +++ b/src/Workspaces/Core/Portable/Execution/CustomAssetBuilder.cs @@ -23,16 +23,14 @@ public CustomAsset Build(OptionSet options, string language, CancellationToken c { cancellationToken.ThrowIfCancellationRequested(); - return new SimpleCustomAsset(WellKnownSynchronizationKinds.OptionSet, - (writer, cancellationTokenOnStreamWriting) => + return new SimpleCustomAsset(WellKnownSynchronizationKinds.OptionSet, + (writer, cancellationTokenOnStreamWriting) => _serializer.SerializeOptionSet(options, language, writer, cancellationTokenOnStreamWriting)); } public CustomAsset Build(AnalyzerReference reference, CancellationToken cancellationToken) { - return new SimpleCustomAsset(WellKnownSynchronizationKinds.AnalyzerReference, - (writer, cancellationTokenOnStreamWriting) => - _serializer.SerializeAnalyzerReference(reference, writer, cancellationTokenOnStreamWriting)); + return new WorkspaceAnalyzerReferenceAsset(reference, _serializer); } } }