From 4b733b8b12b1154c176fad480204fc8f7f9680df Mon Sep 17 00:00:00 2001 From: Jonathon Marolf Date: Sun, 26 Apr 2015 21:51:55 -0700 Subject: [PATCH] Report semantic errors in lambdas The compiler does not generate semantic errors inside lambdas in the presence of syntactic errors causing several features to not work in lambdas with syntax errors. The bug for this ([1867](https://github.com/dotnet/roslyn/issues/1867)) was moved to milestone 1.1 so we are going to use an analyzer in the interim for 1.0. 1. We now check for IncompleteMemberSyntax nodes and LambdaExpressionSyntax nodes which contain syntax diagnostics on any of their descendant nodes. 2. We report both unbound identifier names and constructors that the compiler reports as binding, but which fail overload resolution (actually don't exist). Performance considerations should be mitigated by only doing these checks only lambdas with syntax errors. Other notes: - Renamed analyzer to UnboundIdentifier instead of AddImport since it is being used in more places than just the AddImport fixer - Updated the DiagnosticDescriptor for this analyzer to take localizeable strings. Fixes #1744 Fixes #1241 Fixes #1239 --- .../Diagnostics/AddUsing/AddUsingTests.cs | 31 +++++++- .../GenerateConstructorTests.cs | 19 +++++ .../Diagnostics/AddImport/AddImportTests.vb | 20 ++++- .../FullyQualify/FullyQualifyTests.vb | 4 +- .../GenerateConstructorTests.vb | 17 ++++ .../GenerateType/GenerateTypeTests.vb | 4 +- .../Diagnostics/Spellcheck/SpellcheckTests.vb | 4 +- src/Features/CSharp/CSharpFeatures.csproj | 2 +- .../CSharpFeaturesResources.Designer.cs | 18 +++++ .../CSharp/CSharpFeaturesResources.resx | 8 +- .../CSharpAddImportDiagnosticAnalyzer.cs | 37 --------- ...arpUnboundIdentifiersDiagnosticAnalyzer.cs | 76 ++++++++++++++++++ ...boundIdentifiersDiagnosticAnalyzerBase.cs} | 27 +++++-- src/Features/Core/Features.csproj | 2 +- src/Features/VisualBasic/BasicFeatures.vbproj | 2 +- .../VisualBasicFullyQualifyCodeFixProvider.vb | 2 +- .../GenerateTypeCodeFixProvider.vb | 2 +- .../VisualBasicAddImportDiagnosticAnalyzer.vb | 32 -------- ...sicUnboundIdentifiersDiagnosticAnalyzer.vb | 79 +++++++++++++++++++ .../VBFeaturesResources.Designer.vb | 18 +++++ .../VisualBasic/VBFeaturesResources.resx | 14 +++- 21 files changed, 321 insertions(+), 97 deletions(-) delete mode 100644 src/Features/CSharp/Diagnostics/Analyzers/CSharpAddImportDiagnosticAnalyzer.cs create mode 100644 src/Features/CSharp/Diagnostics/Analyzers/CSharpUnboundIdentifiersDiagnosticAnalyzer.cs rename src/Features/Core/Diagnostics/Analyzers/{AddImportDiagnosticAnalyzerBase.cs => UnboundIdentifiersDiagnosticAnalyzerBase.cs} (60%) delete mode 100644 src/Features/VisualBasic/Diagnostics/Analyzers/VisualBasicAddImportDiagnosticAnalyzer.vb create mode 100644 src/Features/VisualBasic/Diagnostics/Analyzers/VisualBasicUnboundIdentifiersDiagnosticAnalyzer.vb diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs index eb823f14bf6..9468ba32e21 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/AddUsing/AddUsingTests.cs @@ -5,7 +5,7 @@ using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp.CodeFixes.AddImport; -using Microsoft.CodeAnalysis.CSharp.Diagnostics.AddImport; +using Microsoft.CodeAnalysis.CSharp.Diagnostics; using Microsoft.CodeAnalysis.CSharp.Test.Utilities; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces; @@ -1975,7 +1975,7 @@ public partial class AddUsingTestsWithAddImportDiagnosticProvider : AbstractCSha internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) { return Tuple.Create( - new CSharpAddImportDiagnosticAnalyzer(), + new CSharpUnboundIdentifiersDiagnosticAnalyzer(), new CSharpAddImportCodeFixProvider()); } @@ -2060,6 +2060,33 @@ public void TestOutsideOfMethodWithMalformedGenericParameters() @"using System ; class Program { Func < [|FlowControl|] x } ", @"using System ; using System . Reflection . Emit ; class Program { Func < FlowControl x } "); } + + [WorkItem(1744, @"https://github.com/dotnet/roslyn/issues/1744")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddUsing)] + public void TestIncompleteCatchBlockInLambda() + { + Test( + @"class A { System . Action a = ( ) => { try { } catch ( [|Exception|] ", + @"using System ; class A { System . Action a = ( ) => { try { } catch ( Exception "); + } + + [WorkItem(1239, @"https://github.com/dotnet/roslyn/issues/1239")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddUsing)] + public void TestIncompleteLambda1() + { + Test( + @"using System . Linq ; class C { C ( ) { """" . Select ( ( ) => { new [|Byte|] ", + @"using System ; using System . Linq ; class C { C ( ) { """" . Select ( ( ) => { new Byte "); + } + + [WorkItem(1239, @"https://github.com/dotnet/roslyn/issues/1239")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsAddUsing)] + public void TestIncompleteLambda2() + { + Test( + @"using System . Linq ; class C { C ( ) { """" . Select ( ( ) => { new [|Byte|] ( ) } ", + @"using System ; using System . Linq ; class C { C ( ) { """" . Select ( ( ) => { new Byte ( ) } "); + } } } } diff --git a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateConstructor/GenerateConstructorTests.cs b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateConstructor/GenerateConstructorTests.cs index fd3e8a689c5..c8d212cc2c1 100644 --- a/src/EditorFeatures/CSharpTest/Diagnostics/GenerateConstructor/GenerateConstructorTests.cs +++ b/src/EditorFeatures/CSharpTest/Diagnostics/GenerateConstructor/GenerateConstructorTests.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using Microsoft.CodeAnalysis.CodeFixes; using Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateConstructor; +using Microsoft.CodeAnalysis.CSharp.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Options; using Microsoft.CodeAnalysis.Simplification; @@ -617,5 +618,23 @@ public void TestGenerateInInaccessibleType() @"class Foo { class Bar { } } class A { static void Main(string[] args) { var s = new [|Foo.Bar(5)|]; } }", @"class Foo { class Bar { private int v; public Bar(int v) { this.v = v; } } } class A { static void Main(string[] args) { var s = new Foo.Bar(5); } }"); } + + public partial class GenerateConstructorTestsWithFindMissingIdentifiersAnalyzer : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest + { + internal override Tuple CreateDiagnosticProviderAndFixer(Workspace workspace) + { + return new Tuple( + new CSharpUnboundIdentifiersDiagnosticAnalyzer(), new GenerateConstructorCodeFixProvider()); + } + + [WorkItem(1241, @"https://github.com/dotnet/roslyn/issues/1241")] + [Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateMethod)] + public void TestGenerateConstructorInIncompleteLambda() + { + Test( + @"using System . Threading . Tasks ; class C { C ( ) { Task . Run ( ( ) => { new [|C|] ( 0 ) } ) ; } } ", + @"using System . Threading . Tasks ; class C { private int v ; public C ( int v ) { this . v = v ; } C ( ) { Task . Run ( ( ) => { new C ( 0 ) } ) ; } } "); + } + } } } diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests.vb index 691f719e27e..b499772e54f 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/AddImport/AddImportTests.vb @@ -5,7 +5,7 @@ Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.AddImport -Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics.AddImport +Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.CodeActions.AddImport Public Class AddImportTests @@ -1065,7 +1065,7 @@ Nothing, 1, True, True, Nothing, False, Nothing) Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - New VisualBasicAddImportDiagnosticAnalyzer(), + New VisualBasicUnboundIdentifiersDiagnosticAnalyzer(), New VisualBasicAddImportCodeFixProvider()) End Function @@ -1119,6 +1119,22 @@ Class MultiDictionary(Of K, V) End Sub End Class") End Sub + + + + Public Sub TestImportIncompleteSub() + Test( + NewLines("Class A \n Dim a As Action = Sub() \n Try \n Catch ex As [|TestException|] \n End Sub \n End Class \n Namespace T \n Class TestException \n Inherits Exception \n End Class \n End Namespace"), + NewLines("Imports T \n Class A \n Dim a As Action = Sub() \n Try \n Catch ex As TestException \n End Sub \n End Class \n Namespace T \n Class TestException \n Inherits Exception \n End Class \n End Namespace")) + End Sub + + + + Public Sub TestImportIncompleteSub2() + Test( + NewLines("Imports System.Linq \n Namespace X \n Class Test \n End Class \n End Namespace \n Class C \n Sub New() \n Dim s As Action = Sub() \n Dim a = New [|Test|]()"), + NewLines("Imports System.Linq \n Imports X \n Namespace X \n Class Test \n End Class \n End Namespace \n Class C \n Sub New() \n Dim s As Action = Sub() \n Dim a = New Test()")) + End Sub End Class End Class End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/FullyQualify/FullyQualifyTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/FullyQualify/FullyQualifyTests.vb index c9588623f9a..c0f37150db4 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/FullyQualify/FullyQualifyTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/FullyQualify/FullyQualifyTests.vb @@ -4,7 +4,7 @@ Option Strict Off Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.FullyQualify -Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics.AddImport +Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.FullyQualify Public Class FullyQualifyTests @@ -378,7 +378,7 @@ compareTokens:=False) Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - New VisualBasicAddImportDiagnosticAnalyzer(), + New VisualBasicUnboundIdentifiersDiagnosticAnalyzer(), New VisualBasicFullyQualifyCodeFixProvider()) End Function diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateConstructor/GenerateConstructorTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateConstructor/GenerateConstructorTests.vb index 0ac034038ed..8bc48a3c6f1 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateConstructor/GenerateConstructorTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateConstructor/GenerateConstructorTests.vb @@ -4,6 +4,7 @@ Option Strict Off Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateConstructor +Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.GenerateConstructor Public Class GenerateConstructorTests @@ -476,5 +477,21 @@ NewLines("Class Foo \n Private Class Bar \n End Class \n End Class \n Class A \n NewLines("Class Foo \n Private Class Bar \n Private v As Integer \n Public Sub New(v As Integer) \n Me.v = v \n End Sub \n End Class \n End Class \n Class A \n Sub Main() \n Dim s = New Foo.Bar(5) \n End Sub \n End Class")) End Sub + Public Class GenerateConstructorTestsWithFindMissingIdentifiersAnalyzer + Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest + + Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) + Return New Tuple(Of DiagnosticAnalyzer, CodeFixProvider)(New VisualBasicUnboundIdentifiersDiagnosticAnalyzer(), New GenerateConstructorCodeFixProvider()) + End Function + + + + Public Sub TestGenerateConstructorInIncompleteLambda() + Test( +NewLines("Imports System.Linq \n Class C \n Sub New() \n Dim s As Action = Sub() \n Dim a = New [|C|](0)"), +NewLines("Imports System.Linq \n Class C \n Private v As Integer \n Sub New() \n Dim s As Action = Sub() \n Dim a = New C(0)Public Sub New(v As Integer) \n Me.v = v \n End Sub \n End Class")) + End Sub + End Class + End Class End Namespace diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests.vb index 352719ca3cb..d74722e37da 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/GenerateType/GenerateTypeTests.vb @@ -7,7 +7,7 @@ Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions Imports Microsoft.CodeAnalysis.Options Imports Microsoft.CodeAnalysis.Simplification Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateType -Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics.AddImport +Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.GenerateType Public Class GenerateTypeTests @@ -752,7 +752,7 @@ index:=0) Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - New VisualBasicAddImportDiagnosticAnalyzer(), + New VisualBasicUnboundIdentifiersDiagnosticAnalyzer(), New GenerateTypeCodeFixProvider()) End Function diff --git a/src/EditorFeatures/VisualBasicTest/Diagnostics/Spellcheck/SpellcheckTests.vb b/src/EditorFeatures/VisualBasicTest/Diagnostics/Spellcheck/SpellcheckTests.vb index 214d56d71e3..66f6bdb0474 100644 --- a/src/EditorFeatures/VisualBasicTest/Diagnostics/Spellcheck/SpellcheckTests.vb +++ b/src/EditorFeatures/VisualBasicTest/Diagnostics/Spellcheck/SpellcheckTests.vb @@ -4,7 +4,7 @@ Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.Diagnostics Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions Imports Microsoft.CodeAnalysis.VisualBasic.CodeFixes.Spellcheck -Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics.AddImport +Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Spellcheck Public Class SpellcheckTests @@ -467,7 +467,7 @@ index:=0) Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As Tuple(Of DiagnosticAnalyzer, CodeFixProvider) Return Tuple.Create(Of DiagnosticAnalyzer, CodeFixProvider)( - New VisualBasicAddImportDiagnosticAnalyzer(), + New VisualBasicUnboundIdentifiersDiagnosticAnalyzer(), New SpellcheckCodeFixProvider()) End Function diff --git a/src/Features/CSharp/CSharpFeatures.csproj b/src/Features/CSharp/CSharpFeatures.csproj index 18971d54e28..8c90978ce95 100644 --- a/src/Features/CSharp/CSharpFeatures.csproj +++ b/src/Features/CSharp/CSharpFeatures.csproj @@ -285,7 +285,7 @@ True CSharpFeaturesResources.resx - + diff --git a/src/Features/CSharp/CSharpFeaturesResources.Designer.cs b/src/Features/CSharp/CSharpFeaturesResources.Designer.cs index 1beb9917be7..d8d2f2f06d7 100644 --- a/src/Features/CSharp/CSharpFeaturesResources.Designer.cs +++ b/src/Features/CSharp/CSharpFeaturesResources.Designer.cs @@ -258,6 +258,24 @@ internal class CSharpFeaturesResources { } } + /// + /// Looks up a localized string similar to '{0}' does not contain a constructor that takes that many arguments.. + /// + internal static string ERR_BadCtorArgCount { + get { + return ResourceManager.GetString("ERR_BadCtorArgCount", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to The name '{0}' does not exist in the current context.. + /// + internal static string ERR_NameNotInContext { + get { + return ResourceManager.GetString("ERR_NameNotInContext", resourceCulture); + } + } + /// /// Looks up a localized string similar to event field. /// diff --git a/src/Features/CSharp/CSharpFeaturesResources.resx b/src/Features/CSharp/CSharpFeaturesResources.resx index d5aa0b42354..684732d5d1e 100644 --- a/src/Features/CSharp/CSharpFeaturesResources.resx +++ b/src/Features/CSharp/CSharpFeaturesResources.resx @@ -383,7 +383,7 @@ struct - {Locked} + {Locked} event field @@ -412,4 +412,10 @@ attribute target + + '{0}' does not contain a constructor that takes that many arguments. + + + The name '{0}' does not exist in the current context. + \ No newline at end of file diff --git a/src/Features/CSharp/Diagnostics/Analyzers/CSharpAddImportDiagnosticAnalyzer.cs b/src/Features/CSharp/Diagnostics/Analyzers/CSharpAddImportDiagnosticAnalyzer.cs deleted file mode 100644 index 0bbc3f35be9..00000000000 --- a/src/Features/CSharp/Diagnostics/Analyzers/CSharpAddImportDiagnosticAnalyzer.cs +++ /dev/null @@ -1,37 +0,0 @@ -// 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 Microsoft.CodeAnalysis.CSharp.Syntax; -using Microsoft.CodeAnalysis.Diagnostics; -using Microsoft.CodeAnalysis.Diagnostics.AddImport; -using Roslyn.Utilities; - -namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.AddImport -{ - [DiagnosticAnalyzer(LanguageNames.CSharp)] - internal sealed class CSharpAddImportDiagnosticAnalyzer : AddImportDiagnosticAnalyzerBase - { - private const string NameNotInContext = "CS0103"; - private const string MessageFormat = "The name '{0}' does not exist in the current context"; - - private static readonly ImmutableArray s_kindsOfInterest = ImmutableArray.Create(SyntaxKind.IncompleteMember); - - protected override ImmutableArray SyntaxKindsOfInterest - { - get - { - return s_kindsOfInterest; - } - } - - protected override DiagnosticDescriptor DiagnosticDescriptor - { - get - { - return GetDiagnosticDescriptor(NameNotInContext, MessageFormat); - } - } - } -} diff --git a/src/Features/CSharp/Diagnostics/Analyzers/CSharpUnboundIdentifiersDiagnosticAnalyzer.cs b/src/Features/CSharp/Diagnostics/Analyzers/CSharpUnboundIdentifiersDiagnosticAnalyzer.cs new file mode 100644 index 00000000000..005cf61be22 --- /dev/null +++ b/src/Features/CSharp/Diagnostics/Analyzers/CSharpUnboundIdentifiersDiagnosticAnalyzer.cs @@ -0,0 +1,76 @@ +// 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 Microsoft.CodeAnalysis.CSharp.Syntax; +using Microsoft.CodeAnalysis.Diagnostics; +using Microsoft.CodeAnalysis.Diagnostics.AddImport; +using Roslyn.Utilities; + +namespace Microsoft.CodeAnalysis.CSharp.Diagnostics +{ + [DiagnosticAnalyzer(LanguageNames.CSharp)] + internal sealed class CSharpUnboundIdentifiersDiagnosticAnalyzer : UnboundIdentifiersDiagnosticAnalyzerBase + { + private const string NameNotInContext = "CS0103"; + private readonly LocalizableString NameNotInContextMessageFormat = new LocalizableResourceString(nameof(CSharpFeaturesResources.ERR_NameNotInContext), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources)); + + private const string ConstructorOverloadResolutionFailure = "CS1729"; + private readonly LocalizableString ConstructorOverloadResolutionFailureMessageFormat = new LocalizableResourceString(nameof(CSharpFeaturesResources.ERR_BadCtorArgCount), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources)); + + private static readonly ImmutableArray s_kindsOfInterest = ImmutableArray.Create(SyntaxKind.IncompleteMember, SyntaxKind.ParenthesizedLambdaExpression, SyntaxKind.SimpleLambdaExpression); + + protected override ImmutableArray SyntaxKindsOfInterest + { + get + { + return s_kindsOfInterest; + } + } + + protected override DiagnosticDescriptor DiagnosticDescriptor => GetDiagnosticDescriptor(NameNotInContext, NameNotInContextMessageFormat); + + protected override DiagnosticDescriptor DiagnosticDescriptor2 => GetDiagnosticDescriptor(ConstructorOverloadResolutionFailure, ConstructorOverloadResolutionFailureMessageFormat); + + protected override bool ConstructorDoesNotExist(SyntaxNode node, SymbolInfo info, SemanticModel model) + { + var argList = (node.Parent as ObjectCreationExpressionSyntax)?.ArgumentList.Arguments; + if (!argList.HasValue) + { + return false; + } + + var args = argList.Value; + + var constructors = (info.Symbol?.OriginalDefinition as INamedTypeSymbol)?.Constructors; + if (!constructors.HasValue) + { + return false; + } + + var count = constructors.Value + .WhereAsArray(constructor => constructor.Parameters.Length == args.Count) + .WhereAsArray(constructor => + { + for (int i = 0; i < constructor.Parameters.Length; i++) + { + var typeInfo = model.GetTypeInfo(args[i].Expression); + if (!constructor.Parameters[i].Type.Equals(typeInfo.ConvertedType)) + { + return false; + } + } + + return true; + }).Length; + + if (count == 0) + { + return true; + } + + return false; + } + } +} diff --git a/src/Features/Core/Diagnostics/Analyzers/AddImportDiagnosticAnalyzerBase.cs b/src/Features/Core/Diagnostics/Analyzers/UnboundIdentifiersDiagnosticAnalyzerBase.cs similarity index 60% rename from src/Features/Core/Diagnostics/Analyzers/AddImportDiagnosticAnalyzerBase.cs rename to src/Features/Core/Diagnostics/Analyzers/UnboundIdentifiersDiagnosticAnalyzerBase.cs index c489697be8d..287844226f3 100644 --- a/src/Features/Core/Diagnostics/Analyzers/AddImportDiagnosticAnalyzerBase.cs +++ b/src/Features/Core/Diagnostics/Analyzers/UnboundIdentifiersDiagnosticAnalyzerBase.cs @@ -7,20 +7,23 @@ namespace Microsoft.CodeAnalysis.Diagnostics.AddImport { - internal abstract class AddImportDiagnosticAnalyzerBase : DiagnosticAnalyzer, IBuiltInAnalyzer + internal abstract class UnboundIdentifiersDiagnosticAnalyzerBase : DiagnosticAnalyzer, IBuiltInAnalyzer where TLanguageKindEnum : struct where TSimpleNameSyntax : SyntaxNode where TQualifiedNameSyntax : SyntaxNode where TIncompleteMemberSyntax : SyntaxNode + where TLambdaExpressionSyntax : SyntaxNode { protected abstract DiagnosticDescriptor DiagnosticDescriptor { get; } + protected abstract DiagnosticDescriptor DiagnosticDescriptor2 { get; } protected abstract ImmutableArray SyntaxKindsOfInterest { get; } + protected abstract bool ConstructorDoesNotExist(SyntaxNode node, SymbolInfo info, SemanticModel semanticModel); public override ImmutableArray SupportedDiagnostics { get { - return ImmutableArray.Create(DiagnosticDescriptor); + return ImmutableArray.Create(DiagnosticDescriptor, DiagnosticDescriptor2); } } @@ -29,7 +32,7 @@ public override void Initialize(AnalysisContext context) context.RegisterSyntaxNodeAction(AnalyzeNode, this.SyntaxKindsOfInterest.ToArray()); } - protected DiagnosticDescriptor GetDiagnosticDescriptor(string id, string messageFormat) + protected DiagnosticDescriptor GetDiagnosticDescriptor(string id, LocalizableString messageFormat) { // it is not configurable diagnostic, title doesn't matter return new DiagnosticDescriptor( @@ -42,19 +45,27 @@ protected DiagnosticDescriptor GetDiagnosticDescriptor(string id, string message private void AnalyzeNode(SyntaxNodeAnalysisContext context) { - var member = (TIncompleteMemberSyntax)context.Node; + if ((context.Node is TLambdaExpressionSyntax && context.Node.ContainsDiagnostics) || context.Node is TIncompleteMemberSyntax) + { + ReportUnboundIdentifierNames(context, context.Node); + } + } + private void ReportUnboundIdentifierNames(SyntaxNodeAnalysisContext context, SyntaxNode member) + { Func isQualifiedOrSimpleName = (SyntaxNode n) => n is TQualifiedNameSyntax || n is TSimpleNameSyntax; var typeNames = member.DescendantNodes().Where(n => isQualifiedOrSimpleName(n) && !n.Span.IsEmpty); foreach (var typeName in typeNames) { var info = context.SemanticModel.GetSymbolInfo(typeName); - if (info.Symbol != null || info.CandidateSymbols.Length > 0) + if (info.Symbol == null && info.CandidateSymbols.Length == 0) { - continue; + context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptor, typeName.GetLocation(), typeName.ToString())); + } + else if (ConstructorDoesNotExist(typeName, info, context.SemanticModel)) + { + context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptor2, typeName.GetLocation(), typeName.ToString())); } - - context.ReportDiagnostic(Diagnostic.Create(DiagnosticDescriptor, typeName.GetLocation(), typeName.ToString())); } } } diff --git a/src/Features/Core/Features.csproj b/src/Features/Core/Features.csproj index 6df9b694aee..3da8c810a73 100644 --- a/src/Features/Core/Features.csproj +++ b/src/Features/Core/Features.csproj @@ -253,7 +253,7 @@ - + diff --git a/src/Features/VisualBasic/BasicFeatures.vbproj b/src/Features/VisualBasic/BasicFeatures.vbproj index 0a429f273a5..18ef320cf7b 100644 --- a/src/Features/VisualBasic/BasicFeatures.vbproj +++ b/src/Features/VisualBasic/BasicFeatures.vbproj @@ -335,7 +335,7 @@ - + diff --git a/src/Features/VisualBasic/CodeFixes/FullyQualify/VisualBasicFullyQualifyCodeFixProvider.vb b/src/Features/VisualBasic/CodeFixes/FullyQualify/VisualBasicFullyQualifyCodeFixProvider.vb index c58888a99ed..f31feff3d63 100644 --- a/src/Features/VisualBasic/CodeFixes/FullyQualify/VisualBasicFullyQualifyCodeFixProvider.vb +++ b/src/Features/VisualBasic/CodeFixes/FullyQualify/VisualBasicFullyQualifyCodeFixProvider.vb @@ -8,7 +8,7 @@ Imports Microsoft.CodeAnalysis.CaseCorrection Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.CodeFixes.FullyQualify Imports Microsoft.CodeAnalysis.Formatting -Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics.AddImport +Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.FullyQualify diff --git a/src/Features/VisualBasic/CodeFixes/GenerateType/GenerateTypeCodeFixProvider.vb b/src/Features/VisualBasic/CodeFixes/GenerateType/GenerateTypeCodeFixProvider.vb index 3c877466eb6..ae2b1a90a89 100644 --- a/src/Features/VisualBasic/CodeFixes/GenerateType/GenerateTypeCodeFixProvider.vb +++ b/src/Features/VisualBasic/CodeFixes/GenerateType/GenerateTypeCodeFixProvider.vb @@ -7,7 +7,7 @@ Imports Microsoft.CodeAnalysis.CodeActions Imports Microsoft.CodeAnalysis.CodeFixes Imports Microsoft.CodeAnalysis.CodeFixes.GenerateMember Imports Microsoft.CodeAnalysis.GenerateType -Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics.AddImport +Imports Microsoft.CodeAnalysis.VisualBasic.Diagnostics Imports Microsoft.CodeAnalysis.VisualBasic.Syntax Namespace Microsoft.CodeAnalysis.VisualBasic.CodeFixes.GenerateType diff --git a/src/Features/VisualBasic/Diagnostics/Analyzers/VisualBasicAddImportDiagnosticAnalyzer.vb b/src/Features/VisualBasic/Diagnostics/Analyzers/VisualBasicAddImportDiagnosticAnalyzer.vb deleted file mode 100644 index 0672d59f098..00000000000 --- a/src/Features/VisualBasic/Diagnostics/Analyzers/VisualBasicAddImportDiagnosticAnalyzer.vb +++ /dev/null @@ -1,32 +0,0 @@ -' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. - -Imports System.Collections.Immutable -Imports System.Threading -Imports Microsoft.CodeAnalysis.Diagnostics -Imports Microsoft.CodeAnalysis.Diagnostics.AddImport -Imports Microsoft.CodeAnalysis.Text -Imports Microsoft.CodeAnalysis.VisualBasic.Syntax - -Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics.AddImport - - Friend NotInheritable Class VisualBasicAddImportDiagnosticAnalyzer - Inherits AddImportDiagnosticAnalyzerBase(Of SyntaxKind, SimpleNameSyntax, QualifiedNameSyntax, IncompleteMemberSyntax) - - Private Const s_undefinedType1 As String = "BC30002" - Private Const s_messageFormat As String = "Type '{0}' is not defined." - - Private Shared ReadOnly s_kindsOfInterest As ImmutableArray(Of SyntaxKind) = ImmutableArray.Create(SyntaxKind.IncompleteMember) - - Protected Overrides ReadOnly Property SyntaxKindsOfInterest As ImmutableArray(Of SyntaxKind) - Get - Return s_kindsOfInterest - End Get - End Property - - Protected Overrides ReadOnly Property DiagnosticDescriptor As DiagnosticDescriptor - Get - Return GetDiagnosticDescriptor(s_undefinedType1, s_messageFormat) - End Get - End Property - End Class -End Namespace diff --git a/src/Features/VisualBasic/Diagnostics/Analyzers/VisualBasicUnboundIdentifiersDiagnosticAnalyzer.vb b/src/Features/VisualBasic/Diagnostics/Analyzers/VisualBasicUnboundIdentifiersDiagnosticAnalyzer.vb new file mode 100644 index 00000000000..aa9b41e708f --- /dev/null +++ b/src/Features/VisualBasic/Diagnostics/Analyzers/VisualBasicUnboundIdentifiersDiagnosticAnalyzer.vb @@ -0,0 +1,79 @@ +' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +Imports System.Collections.Immutable +Imports System.Threading +Imports Microsoft.CodeAnalysis.Diagnostics +Imports Microsoft.CodeAnalysis.Diagnostics.AddImport +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace Microsoft.CodeAnalysis.VisualBasic.Diagnostics + + Friend NotInheritable Class VisualBasicUnboundIdentifiersDiagnosticAnalyzer + Inherits UnboundIdentifiersDiagnosticAnalyzerBase(Of SyntaxKind, SimpleNameSyntax, QualifiedNameSyntax, IncompleteMemberSyntax, LambdaExpressionSyntax) + + Private Const s_undefinedType1 As String = "BC30002" + Private ReadOnly s_messageFormat As LocalizableString = New LocalizableResourceString(NameOf(VBFeaturesResources.ERR_UndefinedType1), VBFeaturesResources.ResourceManager, GetType(VBFeaturesResources.VBFeaturesResources)) + Private Const s_undefinedType2 As String = "BC30057" + Private ReadOnly s_messageFormat2 As LocalizableString = New LocalizableResourceString(NameOf(VBFeaturesResources.ERR_TooManyArgs1), VBFeaturesResources.ResourceManager, GetType(VBFeaturesResources.VBFeaturesResources)) + + + Private Shared ReadOnly s_kindsOfInterest As ImmutableArray(Of SyntaxKind) = ImmutableArray.Create( + SyntaxKind.IncompleteMember, + SyntaxKind.MultiLineFunctionLambdaExpression, + SyntaxKind.MultiLineSubLambdaExpression, + SyntaxKind.SingleLineFunctionLambdaExpression, + SyntaxKind.SingleLineSubLambdaExpression) + + Protected Overrides ReadOnly Property SyntaxKindsOfInterest As ImmutableArray(Of SyntaxKind) + Get + Return s_kindsOfInterest + End Get + End Property + + Protected Overrides ReadOnly Property DiagnosticDescriptor As DiagnosticDescriptor + Get + Return GetDiagnosticDescriptor(s_undefinedType1, s_messageFormat) + End Get + End Property + + Protected Overrides ReadOnly Property DiagnosticDescriptor2 As DiagnosticDescriptor + Get + Return GetDiagnosticDescriptor(s_undefinedType2, s_messageFormat2) + End Get + End Property + + Protected Overrides Function ConstructorDoesNotExist(node As SyntaxNode, info As SymbolInfo, semanticModel As SemanticModel) As Boolean + Dim arguments = (TryCast(node.Parent, ObjectCreationExpressionSyntax)?.ArgumentList.Arguments) + If Not arguments.HasValue Then + Return False + End If + + Dim args = arguments.Value + + Dim constructors = TryCast(info.Symbol?.OriginalDefinition, INamedTypeSymbol)?.Constructors + If constructors Is Nothing Then + Return False + End If + + Dim count = constructors.Value _ + .WhereAsArray(Function(constructor) constructor.Parameters.Length = args.Count) _ + .WhereAsArray(Function(constructor) + For index = 0 To constructor.Parameters.Length + Dim typeInfo = semanticModel.GetTypeInfo(args(index).GetExpression) + If Not constructor.Parameters(index).Type.Equals(typeInfo.ConvertedType) Then + Return False + End If + Next + Return True + End Function) _ + .Length + + If count = 0 Then + Return True + End If + + Return False + End Function + End Class +End Namespace diff --git a/src/Features/VisualBasic/VBFeaturesResources.Designer.vb b/src/Features/VisualBasic/VBFeaturesResources.Designer.vb index 6f1f9a15868..0b99a84f535 100644 --- a/src/Features/VisualBasic/VBFeaturesResources.Designer.vb +++ b/src/Features/VisualBasic/VBFeaturesResources.Designer.vb @@ -836,6 +836,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.VBFeaturesResources End Get End Property + ''' + ''' Looks up a localized string similar to Too many arguments to '{0}'.. + ''' + Friend ReadOnly Property ERR_TooManyArgs1() As String + Get + Return ResourceManager.GetString("ERR_TooManyArgs1", resourceCulture) + End Get + End Property + + ''' + ''' Looks up a localized string similar to Type '{0}' is not defined.. + ''' + Friend ReadOnly Property ERR_UndefinedType1() As String + Get + Return ResourceManager.GetString("ERR_UndefinedType1", resourceCulture) + End Get + End Property + ''' ''' Looks up a localized string similar to Simulates the occurrence of an error.. ''' diff --git a/src/Features/VisualBasic/VBFeaturesResources.resx b/src/Features/VisualBasic/VBFeaturesResources.resx index 35af84c4ab9..039f236898a 100644 --- a/src/Features/VisualBasic/VBFeaturesResources.resx +++ b/src/Features/VisualBasic/VBFeaturesResources.resx @@ -1140,19 +1140,19 @@ Sub(<parameterList>) <statement> option - {Locked} + {Locked} import - {Locked} + {Locked} structure - {Locked} + {Locked} module - {Locked} + {Locked} WithEvents field @@ -1172,4 +1172,10 @@ Sub(<parameterList>) <statement> attributes + + Too many arguments to '{0}'. + + + Type '{0}' is not defined. + \ No newline at end of file -- GitLab