diff --git a/UpgradeLog.htm b/UpgradeLog.htm new file mode 100644 index 00000000..ba88ac31 Binary files /dev/null and b/UpgradeLog.htm differ diff --git a/Viasfora.sln b/Viasfora.sln index cf32fd0a..fa9f12e8 100644 --- a/Viasfora.sln +++ b/Viasfora.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2003 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29728.190 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".nuget", ".nuget", "{1038A55E-607B-4449-924E-1861530C2C56}" ProjectSection(SolutionItems) = preProject @@ -15,6 +15,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution .editorconfig = .editorconfig appveyor.yml = appveyor.yml src\AssemblyInfo.Common.cs = src\AssemblyInfo.Common.cs + src\AssemblyInfo.Common.vb = src\AssemblyInfo.Common.vb Global.props = Global.props README.md = README.md EndProjectSection @@ -37,6 +38,20 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Viasfora.Rainbow", "src\Via EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Viasfora.Settings", "src\Viasfora.Settings\Viasfora.Settings.csproj", "{DD9ED7C5-417C-43A2-BE13-14CD948566B4}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "LanguageServices", "LanguageServices", "{1C2B4B39-2230-4094-A69D-8120C4BAB37A}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Viasfora.LanguageService.Core", "src\Viasfora.LanguageServices\Viasfora.LanguageService.Core\Viasfora.LanguageService.Core.csproj", "{CE8301E8-E5BA-446B-9715-4B0F07E69190}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Viasfora.LanguageService.CSharp", "src\Viasfora.LanguageServices\Viasfora.LanguageService.CSharp\Viasfora.LanguageService.CSharp.csproj", "{003C3D3D-B966-4AC5-A622-D4931810587D}" +EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Viasfora.LanguageService.Basic", "src\Viasfora.LanguageServices\Viasfora.LanguageService.Basic\Viasfora.LanguageService.Basic.vbproj", "{AC41A83D-0565-47CD-AA55-4A09874D722F}" +EndProject +Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "Viasfora.LanguageService.Basic.Tests", "tests\Viasfora.LanguageService.Basic.Tests\Viasfora.LanguageService.Basic.Tests.vbproj", "{C7826A1B-55C0-40AA-B9F3-A58D7B155DA9}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Viasfora.LanguageService.CSharp.Tests", "tests\Viasfora.LanguageService.CSharp.Tests\Viasfora.LanguageService.CSharp.Tests.csproj", "{51698343-EE6B-44AC-B0A9-3F606AE7FE80}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Viasfora.LanguageService.Core.Tests", "tests\Viasfora.LanguageService.Core.Tests\Viasfora.LanguageService.Core.Tests.csproj", "{04588DE4-349F-466C-A57B-95D00D10269A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -71,6 +86,30 @@ Global {DD9ED7C5-417C-43A2-BE13-14CD948566B4}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD9ED7C5-417C-43A2-BE13-14CD948566B4}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD9ED7C5-417C-43A2-BE13-14CD948566B4}.Release|Any CPU.Build.0 = Release|Any CPU + {CE8301E8-E5BA-446B-9715-4B0F07E69190}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {CE8301E8-E5BA-446B-9715-4B0F07E69190}.Debug|Any CPU.Build.0 = Debug|Any CPU + {CE8301E8-E5BA-446B-9715-4B0F07E69190}.Release|Any CPU.ActiveCfg = Release|Any CPU + {CE8301E8-E5BA-446B-9715-4B0F07E69190}.Release|Any CPU.Build.0 = Release|Any CPU + {003C3D3D-B966-4AC5-A622-D4931810587D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {003C3D3D-B966-4AC5-A622-D4931810587D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {003C3D3D-B966-4AC5-A622-D4931810587D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {003C3D3D-B966-4AC5-A622-D4931810587D}.Release|Any CPU.Build.0 = Release|Any CPU + {AC41A83D-0565-47CD-AA55-4A09874D722F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AC41A83D-0565-47CD-AA55-4A09874D722F}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AC41A83D-0565-47CD-AA55-4A09874D722F}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AC41A83D-0565-47CD-AA55-4A09874D722F}.Release|Any CPU.Build.0 = Release|Any CPU + {C7826A1B-55C0-40AA-B9F3-A58D7B155DA9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7826A1B-55C0-40AA-B9F3-A58D7B155DA9}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7826A1B-55C0-40AA-B9F3-A58D7B155DA9}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7826A1B-55C0-40AA-B9F3-A58D7B155DA9}.Release|Any CPU.Build.0 = Release|Any CPU + {51698343-EE6B-44AC-B0A9-3F606AE7FE80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {51698343-EE6B-44AC-B0A9-3F606AE7FE80}.Debug|Any CPU.Build.0 = Debug|Any CPU + {51698343-EE6B-44AC-B0A9-3F606AE7FE80}.Release|Any CPU.ActiveCfg = Release|Any CPU + {51698343-EE6B-44AC-B0A9-3F606AE7FE80}.Release|Any CPU.Build.0 = Release|Any CPU + {04588DE4-349F-466C-A57B-95D00D10269A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {04588DE4-349F-466C-A57B-95D00D10269A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {04588DE4-349F-466C-A57B-95D00D10269A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {04588DE4-349F-466C-A57B-95D00D10269A}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -83,6 +122,13 @@ Global {3274163B-E647-40E2-8D9F-6DF7E7C50411} = {567EF174-A312-43D2-9955-2396CEA9E6DA} {944758A5-90A4-4DE7-8EF6-4FB2E7F4EFB3} = {567EF174-A312-43D2-9955-2396CEA9E6DA} {DD9ED7C5-417C-43A2-BE13-14CD948566B4} = {567EF174-A312-43D2-9955-2396CEA9E6DA} + {1C2B4B39-2230-4094-A69D-8120C4BAB37A} = {567EF174-A312-43D2-9955-2396CEA9E6DA} + {CE8301E8-E5BA-446B-9715-4B0F07E69190} = {1C2B4B39-2230-4094-A69D-8120C4BAB37A} + {003C3D3D-B966-4AC5-A622-D4931810587D} = {1C2B4B39-2230-4094-A69D-8120C4BAB37A} + {AC41A83D-0565-47CD-AA55-4A09874D722F} = {1C2B4B39-2230-4094-A69D-8120C4BAB37A} + {C7826A1B-55C0-40AA-B9F3-A58D7B155DA9} = {6271BFBC-846E-48A5-86BD-0729B65DA933} + {51698343-EE6B-44AC-B0A9-3F606AE7FE80} = {6271BFBC-846E-48A5-86BD-0729B65DA933} + {04588DE4-349F-466C-A57B-95D00D10269A} = {6271BFBC-846E-48A5-86BD-0729B65DA933} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A9497739-5303-4008-905D-EDFF9FC38583} diff --git a/src/AssemblyInfo.Common.vb b/src/AssemblyInfo.Common.vb new file mode 100644 index 00000000..fa60f3d2 --- /dev/null +++ b/src/AssemblyInfo.Common.vb @@ -0,0 +1,20 @@ +Imports System +Imports System.Reflection +Imports System.Runtime.InteropServices + + + + + + + + + + + + + +Friend Class VsfVersion + Public Const Version As String = "2.9.0.0" +End Class + diff --git a/src/Viasfora.Core/Classifications/CodeClassificationDefinitions.cs b/src/Viasfora.Core/Classifications/CodeClassificationDefinitions.cs index afa6e686..86bd4d39 100644 --- a/src/Viasfora.Core/Classifications/CodeClassificationDefinitions.cs +++ b/src/Viasfora.Core/Classifications/CodeClassificationDefinitions.cs @@ -31,6 +31,12 @@ public static class CodeClassificationDefinitions { internal static ClassificationTypeDefinition CurrentColumnClassificationType = null; [Export, Name(Constants.OBFUSCATED_TEXT)] - internal static ClassificationTypeDefinition ObfuscatedTextType = null; + internal static ClassificationTypeDefinition ObfuscatedTextType = null; + + [Export, Name(Constants.LANGUAGESERVICE_ARGUMENT_VALIDATION_NAME)] + internal static ClassificationTypeDefinition LANGUAGESERVICE_ARGUMENT_VALIDATION_NAME = null; + + [Export, Name(Constants.LANGAUGESERVICE_METHOD_OVERLOAD_NAME)] + internal static ClassificationTypeDefinition LANGAUGESERVICE_METHOD_OVERLOAD_NAME = null; } } diff --git a/src/Viasfora.Core/Constants.cs b/src/Viasfora.Core/Constants.cs index 21817873..f60e5969 100644 --- a/src/Viasfora.Core/Constants.cs +++ b/src/Viasfora.Core/Constants.cs @@ -10,7 +10,9 @@ public static class Constants { public const String FORMAT_SPECIFIER_NAME = "viasfora.string.format.specifier"; public const String LINE_HIGHLIGHT = "viasfora.line.current"; public const String COLUMN_HIGHLIGHT = "viasfora.column.current"; - public const String DEV_MARGIN = "viasfora.dev.margin"; + public const String DEV_MARGIN = "viasfora.dev.margin"; + public const String LANGUAGESERVICE_ARGUMENT_VALIDATION_NAME = "viasfora.statement.argument_validation"; + public const String LANGAUGESERVICE_METHOD_OVERLOAD_NAME = "viasfora.statement.method_overload"; public const String OBFUSCATED_TEXT = "viasfora.text.obfuscated"; diff --git a/src/Viasfora.Core/EditorFormats/ArgumentValidationFormat.cs b/src/Viasfora.Core/EditorFormats/ArgumentValidationFormat.cs new file mode 100644 index 00000000..13e3d94c --- /dev/null +++ b/src/Viasfora.Core/EditorFormats/ArgumentValidationFormat.cs @@ -0,0 +1,19 @@ +using System; +using System.ComponentModel.Composition; +using System.Windows.Media; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Utilities; + +namespace Winterdom.Viasfora.EditorFormats { + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = Constants.LANGUAGESERVICE_ARGUMENT_VALIDATION_NAME)] + [Name(Constants.LANGUAGESERVICE_ARGUMENT_VALIDATION_NAME)] + [UserVisible(true)] + [Order(After = Priority.High)] + public sealed class ArgumentValidationFormat : ClassificationFormatDefinition { + public ArgumentValidationFormat() { + this.DisplayName = "Viasfora Argument Validation"; + this.ForegroundOpacity = 0.5; + } + } +} diff --git a/src/Viasfora.Core/EditorFormats/MethodOverloadFormat.cs b/src/Viasfora.Core/EditorFormats/MethodOverloadFormat.cs new file mode 100644 index 00000000..892d12db --- /dev/null +++ b/src/Viasfora.Core/EditorFormats/MethodOverloadFormat.cs @@ -0,0 +1,17 @@ +using System.ComponentModel.Composition; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Utilities; + +namespace Winterdom.Viasfora.EditorFormats { + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = Constants.LANGAUGESERVICE_METHOD_OVERLOAD_NAME)] + [Name(Constants.LANGAUGESERVICE_METHOD_OVERLOAD_NAME)] + [UserVisible(true)] + [Order(After = Priority.High)] + public sealed class MethodOverloadFormat : ClassificationFormatDefinition { + public MethodOverloadFormat() { + this.DisplayName = "Viasfora Method Overload"; + this.ForegroundOpacity = 0.5; + } + } +} diff --git a/src/Viasfora.Core/IVsfSettings.cs b/src/Viasfora.Core/IVsfSettings.cs index 8e616f12..8efcdcd3 100644 --- a/src/Viasfora.Core/IVsfSettings.cs +++ b/src/Viasfora.Core/IVsfSettings.cs @@ -8,8 +8,11 @@ public interface IVsfSettings : IUpdatableSettings { bool VisibilityKeywordsEnabled { get; set; } bool QueryKeywordsEnabled { get; set; } bool FlowControlUseItalics { get; set; } - bool EscapeSequencesEnabled { get; set; } - + bool EscapeSequencesEnabled { get; set; } + + bool ArgumentValidationClassifierEnabled { get; set; } + bool MethodOverloadsClassifierEnabled { get; set; } + bool CurrentLineHighlightEnabled { get; set; } bool CurrentColumnHighlightEnabled { get; set; } ColumnStyle CurrentColumnHighlightStyle { get; set; } diff --git a/src/Viasfora.Core/Settings/VsfSettings.cs b/src/Viasfora.Core/Settings/VsfSettings.cs index 681a62b7..18205232 100644 --- a/src/Viasfora.Core/Settings/VsfSettings.cs +++ b/src/Viasfora.Core/Settings/VsfSettings.cs @@ -90,8 +90,17 @@ public String TextObfuscationRegexes { public bool TelemetryEnabled { get { return this.Store.GetBoolean(nameof(TelemetryEnabled), true); } set { this.Store.SetValue(nameof(TelemetryEnabled), value); } - } - + } + public bool ArgumentValidationClassifierEnabled { + get { return this.Store.GetBoolean(nameof(ArgumentValidationClassifierEnabled), true); } + set { this.Store.SetValue(nameof(ArgumentValidationClassifierEnabled), value); } + } + + public bool MethodOverloadsClassifierEnabled { + get { return this.Store.GetBoolean(nameof(MethodOverloadsClassifierEnabled), true); } + set { this.Store.SetValue(nameof(MethodOverloadsClassifierEnabled), value); } + } + [ImportingConstructor] public VsfSettings(ITypedSettingsStore store, IVsfTelemetry telemetry) : base(store) { @@ -103,7 +112,9 @@ public VsfSettings(ITypedSettingsStore store, IVsfTelemetry telemetry) telemetry.FeatureStatus("EscapeSequences", EscapeSequencesEnabled); telemetry.FeatureStatus("QueryKeywords", QueryKeywordsEnabled); telemetry.FeatureStatus("FlowControlKeywords", FlowControlKeywordsEnabled); - telemetry.FeatureStatus("VisibilityKeywords", VisibilityKeywordsEnabled); + telemetry.FeatureStatus("VisibilityKeywords", VisibilityKeywordsEnabled); + telemetry.FeatureStatus("ArgumentValidationClassifier", ArgumentValidationClassifierEnabled); + telemetry.FeatureStatus("MethodOverloadsClassifier", MethodOverloadsClassifierEnabled); } } } diff --git a/src/Viasfora.Core/Viasfora.Core.csproj b/src/Viasfora.Core/Viasfora.Core.csproj index 61f1bc70..dafef42e 100644 --- a/src/Viasfora.Core/Viasfora.Core.csproj +++ b/src/Viasfora.Core/Viasfora.Core.csproj @@ -177,11 +177,13 @@ ZoomTrackBar.cs + + diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/ArgumentValidationTagger/ArgumentValidationTaggerProvider.vb b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/ArgumentValidationTagger/ArgumentValidationTaggerProvider.vb new file mode 100644 index 00000000..7612ec30 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/ArgumentValidationTagger/ArgumentValidationTaggerProvider.vb @@ -0,0 +1,38 @@ +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.VisualStudio.Text.Classification +Imports Microsoft.VisualStudio.Text.Tagging +Imports Microsoft.VisualStudio.Utilities +Imports System.ComponentModel.Composition +Imports Winterdom.Viasfora.Contracts +Imports Winterdom.Viasfora.Languages +Imports Winterdom.Viasfora.LanguageService.Core.ArgumentValidationTagger + +Namespace ArgumentValidationTagger + + + + + Public Class ArgumentValidationTaggerProvider + Inherits BaseArgumentValidationTaggerProvider + + + Public Sub New( + telemetry As IVsfTelemetry, + classificationRegistry As IClassificationTypeRegistryService, + languageFactory As ILanguageFactory, + settings As IVsfSettings + ) + MyBase.New(telemetry, classificationRegistry, languageFactory, settings) + End Sub + + Public Overrides Function IsArgumentValidationSpan(node As SyntaxNode) As TextSpan + If Not SyntaxHelper.IsAnyIfArgumentThrowSyntaxStatement(node) Then + Return New TextSpan() + End If + + Return node.FullSpan + End Function + End Class + +End Namespace \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/ArgumentValidationTagger/SyntaxHelper.vb b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/ArgumentValidationTagger/SyntaxHelper.vb new file mode 100644 index 00000000..c9d841d5 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/ArgumentValidationTagger/SyntaxHelper.vb @@ -0,0 +1,166 @@ +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace ArgumentValidationTagger + + Public Class SyntaxHelper + + + Public Shared Function IsAnyIfArgumentThrowSyntaxStatement(node As SyntaxNode) As Boolean + If node Is Nothing Then + Return False + End If + + Dim methodBlockBaseSyntax = node.FirstAncestorOrSelf(Of MethodBlockBaseSyntax)() + If methodBlockBaseSyntax Is Nothing OrElse methodBlockBaseSyntax Is node Then + Return False + End If + + Dim accessorBlockSyntax = TryCast(methodBlockBaseSyntax, AccessorBlockSyntax) + If accessorBlockSyntax IsNot Nothing Then + ' Skip Properties + Return False + End If + + For Each statementSyntax In methodBlockBaseSyntax.Statements + If node.Span.IntersectsWith(statementSyntax.Span) Then + Return IsAnyIfArgumentThrowSyntaxStatement(statementSyntax) + End If + Next + + Return False + End Function + + Public Shared Function IsAnyIfArgumentThrowSyntaxStatement(statementSyntax As StatementSyntax) As Boolean + If statementSyntax Is Nothing Then + Return False + End If + + Dim methodBlockBaseSyntax = statementSyntax.FirstAncestorOrSelf(Of MethodBlockBaseSyntax)() + If methodBlockBaseSyntax Is Nothing Then + Return False + End If + + Dim methodBaseSyntax = methodBlockBaseSyntax.BlockStatement + If methodBaseSyntax Is Nothing Then + Return False + End If + + If methodBaseSyntax.ParameterList Is Nothing Then + Return False + End If + + Dim parameterNames = methodBaseSyntax.ParameterList.Parameters _ + .Select(Function(x) x.Identifier.Identifier.ValueText) + + If Not IsAnyIfArgumentThrowSyntaxStatementCore(statementSyntax, parameterNames) Then + Return False + End If + + If Not CheckPreviouslyStatements(statementSyntax, methodBlockBaseSyntax.Statements, parameterNames) Then + Return False + End If + + Return True + End Function + + Friend Shared Function IsAnyIfArgumentThrowSyntaxStatementCore(statementSyntax As StatementSyntax, parameterNames As IEnumerable(Of String)) As Boolean + Dim multiLineIfBlockSyntax = TryCast(statementSyntax, MultiLineIfBlockSyntax) + If multiLineIfBlockSyntax IsNot Nothing Then + Return IsIfArgumentThrowSyntaxStatementCore(multiLineIfBlockSyntax, parameterNames) + End If + + Dim singleLineIfStatementSyntax = TryCast(statementSyntax, SingleLineIfStatementSyntax) + If singleLineIfStatementSyntax IsNot Nothing Then + Return IsIfArgumentThrowSyntaxStatementCore(singleLineIfStatementSyntax, parameterNames) + End If + + Return False + End Function + + Private Shared Function IsIfArgumentThrowSyntaxStatementCore(multiLineIfBlockSyntax As MultiLineIfBlockSyntax, parameterNames As IEnumerable(Of String)) As Boolean + If multiLineIfBlockSyntax.Statements.Count <> 1 Then + Return False + End If + + Return IsIfArgumentThrowSyntaxStatementCore(multiLineIfBlockSyntax.IfStatement.Condition, multiLineIfBlockSyntax.Statements(0), parameterNames) + End Function + + Private Shared Function IsIfArgumentThrowSyntaxStatementCore(singleLineIfStatementSyntax As SingleLineIfStatementSyntax, parameterNames As IEnumerable(Of String)) As Boolean + If singleLineIfStatementSyntax.Statements.Count <> 1 Then + Return False + End If + + Return IsIfArgumentThrowSyntaxStatementCore(singleLineIfStatementSyntax.Condition, singleLineIfStatementSyntax.Statements(0), parameterNames) + End Function + + Private Shared Function IsIfArgumentThrowSyntaxStatementCore(condition As ExpressionSyntax, trueStatementSyntax As StatementSyntax, parameterNames As IEnumerable(Of String)) As Boolean + If condition Is Nothing Then + Return False + End If + + If trueStatementSyntax Is Nothing Then + Return False + End If + + Dim throwStatement = TryCast(trueStatementSyntax, ThrowStatementSyntax) + If throwStatement Is Nothing Then + Return False + End If + + ' the if expression must contain at least one parameter(identifername) + If Not ExpressionContainsParameter(condition, parameterNames) AndAlso + Not ExpressionContainsParameter(throwStatement.Expression, parameterNames) Then + + Return False + End If + + Return True + End Function + + Private Shared Function ExpressionContainsParameter(expressionSyntax As ExpressionSyntax, parameterNames As IEnumerable(Of String)) As Boolean + If expressionSyntax Is Nothing Then + Return False + End If + + Dim identifiers = expressionSyntax.DescendantNodesAndSelf() _ + .OfType(Of IdentifierNameSyntax) + + Return identifiers.Any( + Function(x) + Dim memberAccessExpressionSyntax = x.FirstAncestorOrSelf(Of MemberAccessExpressionSyntax)() + If memberAccessExpressionSyntax IsNot Nothing AndAlso memberAccessExpressionSyntax.SpanStart >= expressionSyntax.SpanStart Then + If memberAccessExpressionSyntax.Expression IsNot x Then + Return False + End If + End If + + Dim invocationExpression = x.FirstAncestorOrSelf(Of InvocationExpressionSyntax)() + If invocationExpression IsNot Nothing AndAlso invocationExpression.SpanStart >= expressionSyntax.SpanStart Then + Return False + End If + + Return parameterNames.Contains(x.Identifier.ValueText, StringComparer.OrdinalIgnoreCase) + End Function + ) + End Function + + Private Shared Function CheckPreviouslyStatements(statementSyntax As StatementSyntax, parentStatementList As SyntaxList(Of StatementSyntax), parameterNames As IEnumerable(Of String)) As Boolean + If parentStatementList.Count < 2 Then + Return True + End If + + Dim startIdx = parentStatementList.IndexOf(statementSyntax) + For i = startIdx - 1 To 0 Step -1 + If Not IsAnyIfArgumentThrowSyntaxStatementCore(parentStatementList(i), parameterNames) Then + Return False + End If + Next + + Return True + End Function + + End Class + +End Namespace + diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/MethodOverloadsTagger/MethodOverloadsTaggerProvider.vb b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/MethodOverloadsTagger/MethodOverloadsTaggerProvider.vb new file mode 100644 index 00000000..0137d9f2 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/MethodOverloadsTagger/MethodOverloadsTaggerProvider.vb @@ -0,0 +1,38 @@ +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.Text +Imports Microsoft.VisualStudio.Text.Classification +Imports Microsoft.VisualStudio.Text.Tagging +Imports Microsoft.VisualStudio.Utilities +Imports System.ComponentModel.Composition +Imports Winterdom.Viasfora.Contracts +Imports Winterdom.Viasfora.Languages +Imports Winterdom.Viasfora.LanguageService.Core.MethodOverloadsTagger + +Namespace MethodOverloadsTagger + + + + + Public Class MethodOverloadsTaggerProvider + Inherits BaseMethodOverloadsTaggerProvider + + + Public Sub New( + telemetry As IVsfTelemetry, + classificationRegistry As IClassificationTypeRegistryService, + languageFactory As ILanguageFactory, + settings As IVsfSettings + ) + MyBase.New(telemetry, classificationRegistry, languageFactory, settings) + End Sub + + Public Overrides Function IsMethodOverload(node As SyntaxNode) As TextSpan + If Not SyntaxHelper.IsMethodOverload(node) Then + Return New TextSpan() + End If + + Return node.FullSpan + End Function + End Class + +End Namespace \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/MethodOverloadsTagger/SyntaxHelper.vb b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/MethodOverloadsTagger/SyntaxHelper.vb new file mode 100644 index 00000000..b6c7fc7d --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/MethodOverloadsTagger/SyntaxHelper.vb @@ -0,0 +1,87 @@ +Imports Microsoft.CodeAnalysis +Imports Microsoft.CodeAnalysis.VisualBasic.Syntax + +Namespace MethodOverloadsTagger + + Public Class SyntaxHelper + + Public Shared Function IsMethodOverload(node As SyntaxNode) As Boolean + If node Is Nothing Then + Return False + End If + + Dim methodBlockSyntax = node.FirstAncestorOrSelf(Of MethodBlockSyntax)() + + Return IsMethodOverload(methodBlockSyntax) + End Function + + Public Shared Function IsMethodOverload(methodBlockSyntax As MethodBlockSyntax) As Boolean + If methodBlockSyntax Is Nothing Then + Return False + End If + + If methodBlockSyntax.SubOrFunctionStatement Is Nothing Then + Return False + End If + + If methodBlockSyntax.Statements.Count = 0 Then + Return False + End If + + Dim statementList = methodBlockSyntax.Statements + Dim parameterNames = methodBlockSyntax.SubOrFunctionStatement.ParameterList.Parameters _ + .Select(Function(x) x.Identifier.Identifier.ValueText) + + ' check argument validation + For i = 0 To statementList.Count - 2 + Dim statementSyntax = statementList(i) + If Not ArgumentValidationTagger.SyntaxHelper.IsAnyIfArgumentThrowSyntaxStatementCore(statementSyntax, parameterNames) Then + Return False + End If + Next + + ' check last statement + Dim lastStatement = statementList(statementList.Count - 1) + Dim expressionSyntax As ExpressionSyntax = Nothing + + Dim lastReturnStatement = TryCast(lastStatement, ReturnStatementSyntax) + If lastReturnStatement IsNot Nothing Then + expressionSyntax = lastReturnStatement.Expression + Else + Dim expressionStatementSyntax = TryCast(lastStatement, ExpressionStatementSyntax) + If expressionStatementSyntax IsNot Nothing Then + expressionSyntax = expressionStatementSyntax.Expression + End If + End If + + If expressionSyntax Is Nothing Then + Return False + Else + Return IsValidExpression(expressionSyntax) + End If + End Function + + Private Shared Function IsValidExpression(expressionSyntax As ExpressionSyntax) As Boolean + Dim awaitExpressionSyntax = TryCast(expressionSyntax, AwaitExpressionSyntax) + If awaitExpressionSyntax IsNot Nothing Then + expressionSyntax = awaitExpressionSyntax.Expression + End If + + Dim invocationExpressionSyntax = TryCast(expressionSyntax, InvocationExpressionSyntax) + If invocationExpressionSyntax Is Nothing Then + Return False + End If + + Dim memberAccessExpressionSyntax = TryCast(invocationExpressionSyntax.Expression, MemberAccessExpressionSyntax) + If memberAccessExpressionSyntax IsNot Nothing Then + If TypeOf memberAccessExpressionSyntax.Expression IsNot MeExpressionSyntax Then + Return False + End If + End If + + Return True + End Function + + End Class + +End Namespace diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Application.Designer.vb b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Application.Designer.vb new file mode 100644 index 00000000..db8b8b32 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Application.Designer.vb @@ -0,0 +1,13 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Application.myapp b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Application.myapp new file mode 100644 index 00000000..0167050e --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Application.myapp @@ -0,0 +1,10 @@ + + + false + false + 0 + true + 0 + 1 + true + diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/AssemblyInfo.vb b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/AssemblyInfo.vb new file mode 100644 index 00000000..2b017a35 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/AssemblyInfo.vb @@ -0,0 +1,4 @@ +Imports System.Reflection + + + \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Resources.Designer.vb b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Resources.Designer.vb new file mode 100644 index 00000000..f92bbf19 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Resources.Designer.vb @@ -0,0 +1,63 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + +Imports System + +Namespace My.Resources + + 'This class was auto-generated by the StronglyTypedResourceBuilder + 'class via a tool like ResGen or Visual Studio. + 'To add or remove a member, edit your .ResX file then rerun ResGen + 'with the /str option, or rebuild your VS project. + ''' + ''' A strongly-typed resource class, for looking up localized strings, etc. + ''' + _ + Friend Module Resources + + Private resourceMan As Global.System.Resources.ResourceManager + + Private resourceCulture As Global.System.Globalization.CultureInfo + + ''' + ''' Returns the cached ResourceManager instance used by this class. + ''' + _ + Friend ReadOnly Property ResourceManager() As Global.System.Resources.ResourceManager + Get + If Object.ReferenceEquals(resourceMan, Nothing) Then + Dim temp As Global.System.Resources.ResourceManager = New Global.System.Resources.ResourceManager("Winterdom.Viasfora.LanguageService.Basic.Resources", GetType(Resources).Assembly) + resourceMan = temp + End If + Return resourceMan + End Get + End Property + + ''' + ''' Overrides the current thread's CurrentUICulture property for all + ''' resource lookups using this strongly typed resource class. + ''' + _ + Friend Property Culture() As Global.System.Globalization.CultureInfo + Get + Return resourceCulture + End Get + Set + resourceCulture = value + End Set + End Property + End Module +End Namespace diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Resources.resx b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Resources.resx new file mode 100644 index 00000000..ffecec85 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Settings.Designer.vb b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Settings.Designer.vb new file mode 100644 index 00000000..e18e92db --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Settings.Designer.vb @@ -0,0 +1,73 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My + + _ + Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings) + +#Region "My.Settings Auto-Save Functionality" +#If _MyType = "WindowsForms" Then + Private Shared addedHandler As Boolean + + Private Shared addedHandlerLockObject As New Object + + _ + Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs) + If My.Application.SaveMySettingsOnExit Then + My.Settings.Save() + End If + End Sub +#End If +#End Region + + Public Shared ReadOnly Property [Default]() As MySettings + Get + +#If _MyType = "WindowsForms" Then + If Not addedHandler Then + SyncLock addedHandlerLockObject + If Not addedHandler Then + AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings + addedHandler = True + End If + End SyncLock + End If +#End If + Return defaultInstance + End Get + End Property + End Class +End Namespace + +Namespace My + + _ + Friend Module MySettingsProperty + + _ + Friend ReadOnly Property Settings() As Global.Winterdom.Viasfora.LanguageService.Basic.My.MySettings + Get + Return Global.Winterdom.Viasfora.LanguageService.Basic.My.MySettings.Default + End Get + End Property + End Module +End Namespace diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Settings.settings b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Settings.settings new file mode 100644 index 00000000..377f56d6 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/My Project/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/Viasfora.LanguageService.Basic.vbproj b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/Viasfora.LanguageService.Basic.vbproj new file mode 100644 index 00000000..eac796f3 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/Viasfora.LanguageService.Basic.vbproj @@ -0,0 +1,223 @@ + + + + + + + Debug + AnyCPU + {AC41A83D-0565-47CD-AA55-4A09874D722F} + Library + Winterdom.Viasfora.LanguageService.Basic + Viasfora.LanguageService.Basic + 512 + Windows + v4.7.2 + true + false + + + + + + + 11.0 + + + + 12.0 + + + 14.0 + + + 15.0 + + + 16.0 + + + true + full + true + true + ..\..\..\bin\Debug\ + Viasfora.LanguageService.Basic.xml + + + true + 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036 + + + + pdbonly + false + true + true + ..\..\..\bin\Release\ + Viasfora.LanguageService.Basic.xml + + + true + 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036 + + + + On + + + Binary + + + On + + + On + + + + ..\..\..\packages\Microsoft.CodeAnalysis.Common.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.dll + + + ..\..\..\packages\Microsoft.CodeAnalysis.VisualBasic.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.VisualBasic.dll + + + ..\..\..\packages\VSSDK.CoreUtility.10.0.4\lib\net40\Microsoft.VisualStudio.CoreUtility.dll + False + + + ..\..\..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.Data.dll + False + + + ..\..\..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.Logic.dll + False + + + ..\..\..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.UI.dll + False + + + ..\..\..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.UI.Wpf.dll + False + + + + + + ..\..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + + + ..\..\..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll + + + + + ..\..\..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll + + + + ..\..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + + ..\..\..\packages\System.Reflection.Metadata.1.6.0\lib\netstandard2.0\System.Reflection.Metadata.dll + + + ..\..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\..\packages\System.Text.Encoding.CodePages.4.5.1\lib\net461\System.Text.Encoding.CodePages.dll + + + ..\..\..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + + + + + + + + + + + + + + + + + + + + + Properties\AssemblyInfo.Common.vb + + + + + + + + + + True + Application.myapp + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + VbMyResourcesResXFileCodeGenerator + Resources.Designer.vb + My.Resources + Designer + + + + + MyApplicationCodeGenerator + Application.Designer.vb + + + SettingsSingleFileGenerator + My + Settings.Designer.vb + + + + + + {419c99ad-185a-4d31-a7ff-e1754c7b806d} + Viasfora.Core + + + {14B957DB-0ED2-4DEA-85C9-B670F6653C1B} + Viasfora.Languages + + + {ce8301e8-e5ba-446b-9715-4b0f07e69190} + Viasfora.LanguageService.Core + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/packages.config b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/packages.config new file mode 100644 index 00000000..0393bd15 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Basic/packages.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/ArgumentValidationTagger/ArgumentValidationTaggerProvider.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/ArgumentValidationTagger/ArgumentValidationTaggerProvider.cs new file mode 100644 index 00000000..487f4be7 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/ArgumentValidationTagger/ArgumentValidationTaggerProvider.cs @@ -0,0 +1,34 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; +using System.ComponentModel.Composition; +using Winterdom.Viasfora.Contracts; +using Winterdom.Viasfora.Languages; +using Winterdom.Viasfora.LanguageService.Core.ArgumentValidationTagger; + +namespace Winterdom.Viasfora.LanguageService.CSharp.ArgumentValidationTagger { + + [Export(typeof(IViewTaggerProvider))] + [ContentType(ContentTypes.CSharp)] + [TagType(typeof(ArgumentValidationTag))] + public sealed class ArgumentValidationTaggerProvider : BaseArgumentValidationTaggerProvider { + + [ImportingConstructor] + public ArgumentValidationTaggerProvider( + IVsfTelemetry telemetry, + IClassificationTypeRegistryService classificationRegistry, + ILanguageFactory languageFactory, + IVsfSettings settings + ) : base(telemetry, classificationRegistry, languageFactory, settings) { + } + + public override TextSpan IsArgumentValidationSpan(SyntaxNode node) { + if ( !SyntaxHelper.IsAnyIfArgumentThrowSyntaxStatement(node) ) + return new TextSpan(); + + return node.FullSpan; + } + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/ArgumentValidationTagger/SyntaxHelper.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/ArgumentValidationTagger/SyntaxHelper.cs new file mode 100644 index 00000000..5b56088b --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/ArgumentValidationTagger/SyntaxHelper.cs @@ -0,0 +1,158 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Winterdom.Viasfora.LanguageService.CSharp.ArgumentValidationTagger { + public static class SyntaxHelper { + + public static bool IsAnyIfArgumentThrowSyntaxStatement(SyntaxNode node) { + if ( node == null ) + return false; + + var baseMethodDeclarationSyntax = node.FirstAncestorOrSelf(); + if ( baseMethodDeclarationSyntax == null || baseMethodDeclarationSyntax == node) + return false; + + if ( baseMethodDeclarationSyntax.Body == null || baseMethodDeclarationSyntax.Body.Statements == null ) + return false; + + foreach (var statementSyntax in baseMethodDeclarationSyntax.Body.Statements) { + if (node.Span.IntersectsWith(statementSyntax.Span)) { + return IsAnyIfArgumentThrowSyntaxStatement(statementSyntax); + } + } + + return false; + } + + public static bool IsAnyIfArgumentThrowSyntaxStatement(StatementSyntax statementSyntax) { + if ( statementSyntax == null ) + return false; + + var baseMethodDeclarationSyntax = statementSyntax.FirstAncestorOrSelf(); + if ( baseMethodDeclarationSyntax == null ) + return false; + + bool isCtor = baseMethodDeclarationSyntax is ConstructorDeclarationSyntax; + + var parameterNames = baseMethodDeclarationSyntax.ParameterList.Parameters + .Select(x => x.Identifier.ValueText); + + if ( !IsAnyIfArgumentThrowSyntaxStatementCore(statementSyntax, isCtor, parameterNames) ) + return false; + + if ( !CheckPreviouslyStatements(statementSyntax, isCtor, parameterNames) ) + return false; + + return true; + } + + private static bool CheckPreviouslyStatements(StatementSyntax statementSyntax, bool isCtor, IEnumerable parameterNames) { + var blockSyntax = statementSyntax.Parent as BlockSyntax; + if ( blockSyntax == null || blockSyntax.Statements.Count < 2 ) + return true; + + var statementList = blockSyntax.Statements; + + var startIdx = statementList.IndexOf(statementSyntax); + for (var i=startIdx - 1; i>-1; --i ) { + if ( !IsAnyIfArgumentThrowSyntaxStatementCore(statementList[i], isCtor, parameterNames) ) + return false; + } + + return true; + } + + internal static bool IsAnyIfArgumentThrowSyntaxStatementCore(StatementSyntax statementSyntax, bool isCtor, IEnumerable parameterNames) { + var ifStatementSyntax = statementSyntax as IfStatementSyntax; + if ( ifStatementSyntax != null ) + return IsIfArgumentThrowSyntaxStatementCore(ifStatementSyntax, parameterNames); + + if ( isCtor ) { + if ( IsAssignmentCoalescThrowStatement(statementSyntax, parameterNames) ) + return true; + } + + return false; + } + + private static bool IsIfArgumentThrowSyntaxStatementCore(IfStatementSyntax ifStatementSyntax, IEnumerable parameterNames) { + // check if statement with only one statement in true part + StatementSyntax innerIfStatementSyntax; + if ( ifStatementSyntax.Statement is BlockSyntax blockSyntax ) { + if ( blockSyntax.Statements.Count != 1 ) + return false; + innerIfStatementSyntax = blockSyntax.Statements[0]; + } else + innerIfStatementSyntax = ifStatementSyntax.Statement; + + // the inner if statement must be a throw Statement + var throwStatementSyntax = innerIfStatementSyntax as ThrowStatementSyntax; + if ( throwStatementSyntax == null ) + return false; + + // the if expression must contain at least one parameter(identifername) + if ( !ExpressionContainsParameter(ifStatementSyntax.Condition, parameterNames) && !ExpressionContainsParameter(throwStatementSyntax.Expression, parameterNames) ) + return false; + + return true; + } + + private static bool ExpressionContainsParameter(ExpressionSyntax expressionSyntax, IEnumerable parameterNames) { + if ( expressionSyntax == null ) + return false; + + // checks if the givven expression contains any ParameterNames. + // Skips MemberAccessExpressions like `this.XXX` + + var identifiers = expressionSyntax.DescendantNodesAndSelf() + .OfType() + ; + + return identifiers.Any(x => { + var memberAccessExpressionSyntax = x.FirstAncestorOrSelf(); + if ( memberAccessExpressionSyntax != null && memberAccessExpressionSyntax.SpanStart >= expressionSyntax.SpanStart ) { + if (memberAccessExpressionSyntax.Expression != x) + return false; + } + + var invocationExpression = x.FirstAncestorOrSelf(); + if ( invocationExpression != null && invocationExpression.SpanStart >= expressionSyntax.SpanStart ) + return false; + + return parameterNames.Contains(x.Identifier.ValueText); + }); + } + + private static bool IsAssignmentCoalescThrowStatement(StatementSyntax statementSyntax, IEnumerable parameterNames) { + var expressionStatementSyntax = statementSyntax as ExpressionStatementSyntax; + if ( expressionStatementSyntax == null ) + return false; + + var assignmentExpressionSyntax = expressionStatementSyntax.Expression as AssignmentExpressionSyntax; + if ( assignmentExpressionSyntax == null ) + return false; + + var binaryExpressionSyntax = assignmentExpressionSyntax.Right as BinaryExpressionSyntax; + if ( binaryExpressionSyntax == null ) + return false; + + if ( binaryExpressionSyntax.Kind() != Microsoft.CodeAnalysis.CSharp.SyntaxKind.CoalesceExpression ) + return false; + + var leftIdentifierNameSyntax = binaryExpressionSyntax.Left as IdentifierNameSyntax; + if ( leftIdentifierNameSyntax == null ) + return false; + if ( !parameterNames.Contains(leftIdentifierNameSyntax.Identifier.ValueText) ) + return false; + + // skip check right throw ... + // `this.xyz = xyz ?? xyzDefault;` should be also a valid expression + + return true; + } + + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/MethodOverloadsTagger/MethodOverloadsTaggerProvider.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/MethodOverloadsTagger/MethodOverloadsTaggerProvider.cs new file mode 100644 index 00000000..8940310e --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/MethodOverloadsTagger/MethodOverloadsTaggerProvider.cs @@ -0,0 +1,33 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; +using System.ComponentModel.Composition; +using Winterdom.Viasfora.Contracts; +using Winterdom.Viasfora.Languages; +using Winterdom.Viasfora.LanguageService.Core.MethodOverloadsTagger; + +namespace Winterdom.Viasfora.LanguageService.CSharp.MethodOverloadsTagger { + [Export(typeof(IViewTaggerProvider))] + [ContentType(ContentTypes.CSharp)] + [TagType(typeof(MethodOverloadsTag))] + public sealed class MethodOverloadsTaggerProvider : BaseMethodOverloadsTaggerProvider { + + [ImportingConstructor] + public MethodOverloadsTaggerProvider( + IVsfTelemetry telemetry, + IClassificationTypeRegistryService classificationRegistry, + ILanguageFactory languageFactory, + IVsfSettings settings + ) : base(telemetry, classificationRegistry, languageFactory, settings) { + } + + public override TextSpan IsMethodOverload(SyntaxNode node) { + if ( !SyntaxHelper.IsMethodOverload(node) ) + return new TextSpan(); + + return node.FullSpan; + } + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/MethodOverloadsTagger/SyntaxHelper.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/MethodOverloadsTagger/SyntaxHelper.cs new file mode 100644 index 00000000..b38f3af2 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/MethodOverloadsTagger/SyntaxHelper.cs @@ -0,0 +1,69 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.CSharp.Syntax; +using System.Linq; + +namespace Winterdom.Viasfora.LanguageService.CSharp.MethodOverloadsTagger { + public static class SyntaxHelper { + + public static bool IsMethodOverload(SyntaxNode node) { + if ( node == null ) + return false; + + var methodDeclarationSyntax = node.FirstAncestorOrSelf(); + if ( methodDeclarationSyntax == null ) + return false; + + return IsMethodOverload(methodDeclarationSyntax); + } + + public static bool IsMethodOverload(MethodDeclarationSyntax methodDeclarationSyntax) { + if ( methodDeclarationSyntax.Body == null || methodDeclarationSyntax.Body.Statements.Count == 0 ) + return false; + + var statementList = methodDeclarationSyntax.Body.Statements; + var parameterNames = methodDeclarationSyntax.ParameterList.Parameters + .Select(x => x.Identifier.ValueText); + + // check argument validation + for (var i=0; i< statementList.Count -1; ++i ) { + var statementSyntax = statementList[i]; + if ( !ArgumentValidationTagger.SyntaxHelper.IsAnyIfArgumentThrowSyntaxStatementCore(statementSyntax, false, parameterNames)) + return false; + } + + // check last statement + var lastStatement = statementList[statementList.Count - 1]; + ExpressionSyntax expressionSyntax = null; + + if ( lastStatement is ReturnStatementSyntax returnStatementSyntax ) + expressionSyntax = returnStatementSyntax.Expression; + else if ( lastStatement is ExpressionStatementSyntax expressionStatementSyntax ) + expressionSyntax = expressionStatementSyntax.Expression; + + + if ( expressionSyntax == null ) + return false; + else + return IsValidExpression(expressionSyntax); + } + + private static bool IsValidExpression(ExpressionSyntax expressionSyntax) { + var awaitExpressionSyntax = expressionSyntax as AwaitExpressionSyntax; + if ( awaitExpressionSyntax != null ) + expressionSyntax = awaitExpressionSyntax.Expression; + + var invocationExpressionSyntax = expressionSyntax as InvocationExpressionSyntax; + if ( invocationExpressionSyntax == null ) + return false; + + var memberAccessExpression = invocationExpressionSyntax.Expression as MemberAccessExpressionSyntax; + if ( memberAccessExpression != null ) { + if ( !(memberAccessExpression.Expression is ThisExpressionSyntax) ) + return false; + } + + return true; + } + + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/Properties/AssemblyInfo.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..65440c73 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/Properties/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Reflection; +[assembly: AssemblyTitle("Viasfora.LanguageService.CSharp")] +[assembly: AssemblyDescription("")] \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/Viasfora.LanguageService.CSharp.csproj b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/Viasfora.LanguageService.CSharp.csproj new file mode 100644 index 00000000..b33b8cf3 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/Viasfora.LanguageService.CSharp.csproj @@ -0,0 +1,161 @@ + + + + + + + Debug + AnyCPU + {003C3D3D-B966-4AC5-A622-D4931810587D} + Library + Properties + Winterdom.Viasfora.LanguageService.CSharp + Viasfora.LanguageService.CSharp + v4.7.2 + 512 + true + false + + + + + + + 11.0 + + + + 12.0 + + + 14.0 + + + 15.0 + + + 16.0 + + + true + full + false + ..\..\..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + ..\..\..\bin\Release\ + TRACE + prompt + 4 + true + + + + ..\..\..\packages\Microsoft.CodeAnalysis.Common.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.dll + + + ..\..\..\packages\Microsoft.CodeAnalysis.CSharp.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.dll + + + ..\..\..\packages\VSSDK.CoreUtility.10.0.4\lib\net40\Microsoft.VisualStudio.CoreUtility.dll + False + + + ..\..\..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.Data.dll + False + + + ..\..\..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.Logic.dll + False + + + ..\..\..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.UI.dll + False + + + ..\..\..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.UI.Wpf.dll + False + + + + + + ..\..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + + + ..\..\..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll + + + + + ..\..\..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll + + + + ..\..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + + ..\..\..\packages\System.Reflection.Metadata.1.6.0\lib\netstandard2.0\System.Reflection.Metadata.dll + + + ..\..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\..\packages\System.Text.Encoding.CodePages.4.5.1\lib\net461\System.Text.Encoding.CodePages.dll + + + ..\..\..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + + + + + + + + + + + Properties\AssemblyInfo.Common.cs + + + + + + + + + + {419c99ad-185a-4d31-a7ff-e1754c7b806d} + Viasfora.Core + + + {14B957DB-0ED2-4DEA-85C9-B670F6653C1B} + Viasfora.Languages + + + {ce8301e8-e5ba-446b-9715-4b0f07e69190} + Viasfora.LanguageService.Core + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/packages.config b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/packages.config new file mode 100644 index 00000000..bb203a1b --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.CSharp/packages.config @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/ArgumentValidationTagger/ArgumentValidationTag.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/ArgumentValidationTagger/ArgumentValidationTag.cs new file mode 100644 index 00000000..638ee821 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/ArgumentValidationTagger/ArgumentValidationTag.cs @@ -0,0 +1,18 @@ +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Tagging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Winterdom.Viasfora.LanguageService.Core.ArgumentValidationTagger { + public class ArgumentValidationTag : IClassificationTag { + + public IClassificationType ClassificationType { get; private set; } + + public ArgumentValidationTag(IClassificationType classification) { + this.ClassificationType = classification ?? throw new ArgumentNullException(nameof(classification)); + } + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/ArgumentValidationTagger/ArgumentValidationTagger.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/ArgumentValidationTagger/ArgumentValidationTagger.cs new file mode 100644 index 00000000..910a375b --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/ArgumentValidationTagger/ArgumentValidationTagger.cs @@ -0,0 +1,35 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text; +using System; +using System.Collections.Generic; +using Winterdom.Viasfora.Contracts; +using Winterdom.Viasfora.Languages; +using Winterdom.Viasfora.LanguageService.Core.RoslynTaggerProvider; + +namespace Winterdom.Viasfora.LanguageService.Core.ArgumentValidationTagger { + public class ArgumentValidationTagger : BaseRoslynTagger { + private BaseArgumentValidationTaggerProvider provider; + private ArgumentValidationTag argumentValidationClassification; + + internal ArgumentValidationTagger( + IVsfTelemetry telemetry, + ITextBuffer buffer, + BaseArgumentValidationTaggerProvider provider + ) : base (telemetry, buffer, provider.LanguageFactory) { + this.provider = provider ?? throw new ArgumentNullException(nameof(provider)); + + this.argumentValidationClassification = provider.GetTag(Constants.LANGUAGESERVICE_ARGUMENT_VALIDATION_NAME); + } + + protected override IEnumerable<(TextSpan Span, ArgumentValidationTag Tag)> GetTags(SyntaxNode node, TextSpan span) { + var resultSpan = this.provider.IsArgumentValidationSpan(node); + if ( span.IsEmpty ) + yield break; + else + yield return (Span: resultSpan, Tag: this.argumentValidationClassification); + } + + protected override bool IsEnabled(ILanguage lang) => lang.Settings.ReduceOpacityForArgumentValidation && this.provider.Settings.ArgumentValidationClassifierEnabled; + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/ArgumentValidationTagger/BaseArgumentValidationTaggerProvider.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/ArgumentValidationTagger/BaseArgumentValidationTaggerProvider.cs new file mode 100644 index 00000000..95f10cdf --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/ArgumentValidationTagger/BaseArgumentValidationTaggerProvider.cs @@ -0,0 +1,46 @@ +using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using System; +using Winterdom.Viasfora.Languages; +using Microsoft.CodeAnalysis.Text; +using Winterdom.Viasfora.Contracts; + +namespace Winterdom.Viasfora.LanguageService.Core.ArgumentValidationTagger { + + + public abstract class BaseArgumentValidationTaggerProvider: IViewTaggerProvider + { + + public IVsfTelemetry Telemetry { get; } + public IClassificationTypeRegistryService ClassificationRegistry { get; } + public ILanguageFactory LanguageFactory { get; } + public IVsfSettings Settings { get; } + + + protected BaseArgumentValidationTaggerProvider( + IVsfTelemetry telemetry, + IClassificationTypeRegistryService classificationRegistry, + ILanguageFactory languageFactory, + IVsfSettings settings + ) { + this.Telemetry = telemetry ?? throw new ArgumentNullException(nameof(telemetry)); + this.ClassificationRegistry = classificationRegistry ?? throw new ArgumentNullException(nameof(classificationRegistry)); + this.LanguageFactory = languageFactory ?? throw new ArgumentNullException(nameof(languageFactory)); + this.Settings = settings ?? throw new ArgumentNullException(nameof(settings)); + } + + public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) where T : ITag { + return new ArgumentValidationTagger(this.Telemetry, buffer, this) as ITagger; + } + + public ArgumentValidationTag GetTag(string name) { + var type = ClassificationRegistry.GetClassificationType(name); + return new ArgumentValidationTag(type); + } + + public abstract TextSpan IsArgumentValidationSpan(SyntaxNode node); + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Extensions/SyntaxTreeExtensions.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Extensions/SyntaxTreeExtensions.cs new file mode 100644 index 00000000..9f3d4094 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Extensions/SyntaxTreeExtensions.cs @@ -0,0 +1,20 @@ +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Winterdom.Viasfora.LanguageService.Core { + static class SyntaxTreeExtensions { + + public static TextSpan ToTextSpan(this SnapshotSpan span) { + return new TextSpan(span.Start.Position, span.Length); + } + + public static SnapshotSpan ToSnapshotSpan(this TextSpan span, ITextSnapshot snapshot) { + return new SnapshotSpan(snapshot, span.Start, span.Length); + } + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/MethodOverloadsTagger/BaseMethodOverloadsTaggerProvider.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/MethodOverloadsTagger/BaseMethodOverloadsTaggerProvider.cs new file mode 100644 index 00000000..c9767c3e --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/MethodOverloadsTagger/BaseMethodOverloadsTaggerProvider.cs @@ -0,0 +1,44 @@ +using Microsoft.CodeAnalysis; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using System; +using Winterdom.Viasfora.Languages; +using Microsoft.CodeAnalysis.Text; +using Winterdom.Viasfora.Contracts; + +namespace Winterdom.Viasfora.LanguageService.Core.MethodOverloadsTagger { + public abstract class BaseMethodOverloadsTaggerProvider : IViewTaggerProvider { + + public IVsfTelemetry Telemetry { get; } + public IClassificationTypeRegistryService ClassificationRegistry { get; } + public ILanguageFactory LanguageFactory { get; } + public IVsfSettings Settings { get; } + + + protected BaseMethodOverloadsTaggerProvider( + IVsfTelemetry telemetry, + IClassificationTypeRegistryService classificationRegistry, + ILanguageFactory languageFactory, + IVsfSettings settings + ) { + this.Telemetry = telemetry ?? throw new ArgumentNullException(nameof(telemetry)); + this.ClassificationRegistry = classificationRegistry ?? throw new ArgumentNullException(nameof(classificationRegistry)); + this.LanguageFactory = languageFactory ?? throw new ArgumentNullException(nameof(languageFactory)); + this.Settings = settings ?? throw new ArgumentNullException(nameof(settings)); + } + + public ITagger CreateTagger(ITextView textView, ITextBuffer buffer) where T : ITag { + return new MethodOverloadsTagger(this.Telemetry, buffer, this) as ITagger; + } + + public MethodOverloadsTag GetTag(string name) { + var type = ClassificationRegistry.GetClassificationType(name); + return new MethodOverloadsTag(type); + } + + public abstract TextSpan IsMethodOverload(SyntaxNode node); + + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/MethodOverloadsTagger/MethodOverloadsTag.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/MethodOverloadsTagger/MethodOverloadsTag.cs new file mode 100644 index 00000000..73036005 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/MethodOverloadsTagger/MethodOverloadsTag.cs @@ -0,0 +1,14 @@ +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Tagging; +using System; + +namespace Winterdom.Viasfora.LanguageService.Core.MethodOverloadsTagger { + public class MethodOverloadsTag : IClassificationTag { + + public IClassificationType ClassificationType { get; private set; } + + public MethodOverloadsTag(IClassificationType classification) { + this.ClassificationType = classification ?? throw new ArgumentNullException(nameof(classification)); + } + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/MethodOverloadsTagger/MethodOverloadsTagger.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/MethodOverloadsTagger/MethodOverloadsTagger.cs new file mode 100644 index 00000000..7d9f6017 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/MethodOverloadsTagger/MethodOverloadsTagger.cs @@ -0,0 +1,35 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text; +using System; +using System.Collections.Generic; +using Winterdom.Viasfora.Contracts; +using Winterdom.Viasfora.Languages; +using Winterdom.Viasfora.LanguageService.Core.RoslynTaggerProvider; + +namespace Winterdom.Viasfora.LanguageService.Core.MethodOverloadsTagger { + public class MethodOverloadsTagger : BaseRoslynTagger { + private BaseMethodOverloadsTaggerProvider provider; + private MethodOverloadsTag methodOverloadsClassification; + + internal MethodOverloadsTagger( + IVsfTelemetry telemetry, + ITextBuffer buffer, + BaseMethodOverloadsTaggerProvider provider + ) : base(telemetry, buffer, provider.LanguageFactory) { + this.provider = provider ?? throw new ArgumentNullException(nameof(provider)); + + this.methodOverloadsClassification = provider.GetTag(Constants.LANGAUGESERVICE_METHOD_OVERLOAD_NAME); + } + + protected override IEnumerable<(TextSpan Span, MethodOverloadsTag Tag)> GetTags(SyntaxNode node, TextSpan span) { + var resultSpan = this.provider.IsMethodOverload(node); + if ( span.IsEmpty ) + yield break; + else + yield return (Span: resultSpan, Tag: this.methodOverloadsClassification); + } + + protected override bool IsEnabled(ILanguage lang) => lang.Settings.ReduceOpacityForMethodOverloads && this.provider.Settings.MethodOverloadsClassifierEnabled; + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Properties/AssemblyInfo.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..7e1b293b --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Properties/AssemblyInfo.cs @@ -0,0 +1,6 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +[assembly: AssemblyTitle("Viasfora.LanguageService.Core")] +[assembly: AssemblyDescription("")] +[assembly: InternalsVisibleTo("Viasfora.LanguageService.Core.Tests")] \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/RoslynException.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/RoslynException.cs new file mode 100644 index 00000000..4c03ab94 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/RoslynException.cs @@ -0,0 +1,49 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace Winterdom.Viasfora.LanguageService.Core { + public class RoslynException : Exception { + + public SyntaxNode SyntaxNode { get; private set; } + public TextSpan TextSpan { get; private set; } + + private RoslynException(string message) : base(message) { } + private RoslynException(string message, Exception innerException) : base(message, innerException) { } + + public static RoslynException Create( + string message, + SyntaxNode node, + TextSpan span, + Exception innerException = null + ) { + if ( String.IsNullOrEmpty(message) ) throw new ArgumentException($"{nameof(message)} must not be empty.", nameof(message)); + if ( node == null ) throw new ArgumentNullException(nameof(node)); + + string textSpanText = null; + if (!span.IsEmpty) { + textSpanText = $"Span: {span.ToString()}\nSpanValue: `{node.FindNode(span)?.GetText()}`"; + } + + message += $"\n\nSyntaxNode: `{node.ToFullString()}`({node.GetType().FullName}{textSpanText})"; + + var ex = new RoslynException(message, innerException); + ex.SyntaxNode = node; + ex.TextSpan = span; + return ex; + } + + public static RoslynException Create( + string message, + SyntaxNode node, + Exception innerException = null + ) { + return Create(message, node, new TextSpan(), innerException); + } + + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/RoslynTaggerProvider/BaseRoslynTagger.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/RoslynTaggerProvider/BaseRoslynTagger.cs new file mode 100644 index 00000000..1eb193b6 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/RoslynTaggerProvider/BaseRoslynTagger.cs @@ -0,0 +1,115 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; +using System; +using System.Collections.Generic; +using System.Linq; +using Winterdom.Viasfora.Contracts; +using Winterdom.Viasfora.Languages; +using Winterdom.Viasfora.LanguageService.Core.Utils; + +namespace Winterdom.Viasfora.LanguageService.Core.RoslynTaggerProvider { + public abstract class BaseRoslynTagger : ITagger + where TTag : ITag { + + protected IVsfTelemetry Telemetry { get; } + protected ITextBuffer Buffer { get; } + protected ILanguageFactory LangFactory { get; } + + private TextBufferCodeAnalysesCache cache; + + protected BaseRoslynTagger( + IVsfTelemetry telemetry, + ITextBuffer buffer, + ILanguageFactory langFactory + ) { + this.Telemetry = telemetry ?? throw new ArgumentNullException(nameof(telemetry)); + this.Buffer = buffer ?? throw new ArgumentNullException(nameof(buffer)); + this.LangFactory = langFactory ?? throw new ArgumentNullException(nameof(langFactory)); + } + + public event EventHandler TagsChanged; + + public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) { + try { + var tags = GetTagsInner(spans) + .ToArray() + ; + return tags; + } catch (Exception ex) { + Telemetry.WriteException($@"Error analysing source code. +Spans: {spans.ToString()} +Source: {Buffer.CurrentSnapshot.GetText()} +", ex); + return Enumerable.Empty>(); + } + } + private IEnumerable> GetTagsInner(NormalizedSnapshotSpanCollection spans) { + if ( spans.Count == 0 ) + yield break; + + ILanguage lang = GetLanguageByContentType(this.Buffer.ContentType); + if ( !lang.Settings.Enabled || !this.IsEnabled(lang) ) { + yield break; + } + + var snapshot = spans[0].Snapshot; + + if ( this.cache == null || this.cache.Snapshot != snapshot ) { + this.cache = TextBufferCodeAnalysesCache.ResolveAsync(this.Buffer, snapshot) + .ConfigureAwait(false).GetAwaiter().GetResult(); + } + + if (this.cache == null || this.cache.SyntaxRoot == null) { + yield break; + } + + var roslynTextSpan = spans[0].ToTextSpan(); + var rootNode = this.cache.SyntaxRoot.Value; + + if ( !rootNode.Span.Contains(roslynTextSpan) ) + yield break; + + var node = rootNode.FindNode(roslynTextSpan); + + // get roslyn tags and catch unhandled exceptions + var tagsIter = GetTags(node, roslynTextSpan).GetEnumerator(); + var catchIter = new TryCatchEnumerator<(TextSpan Span, TTag Tag)>(tagsIter, ex => { + if (ex is RoslynException) { + this.Telemetry.WriteException($"Error getting tags from {this.GetType().FullName}", ex); + } else { + this.Telemetry.WriteException($"Error getting tags from {this.GetType().FullName}", RoslynException.Create( + "Unknown error getting tags", + node, + roslynTextSpan, + ex + )); + } + return false; + }); + + while ( catchIter.MoveNext() ) { + var snapshotSpan = catchIter.Current.Span.ToSnapshotSpan(snapshot); + + yield return new TagSpan(snapshotSpan, catchIter.Current.Tag); + } + } + + + + private ILanguage GetLanguageByContentType(IContentType contentType) { + return this.LangFactory.TryCreateLanguage(contentType); + } + + void OnSettingsChanged(object sender, EventArgs e) { + if ( this.Buffer == null ) + return; + TagsChanged?.Invoke(this, new SnapshotSpanEventArgs(this.Buffer.CurrentSnapshot.GetSpan())); + } + + protected virtual bool IsEnabled(ILanguage lang) => true; + protected abstract IEnumerable<(TextSpan Span, TTag Tag)> GetTags(SyntaxNode node, TextSpan span); + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Utils/TextBufferCodeAnalysesCache.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Utils/TextBufferCodeAnalysesCache.cs new file mode 100644 index 00000000..147a3293 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Utils/TextBufferCodeAnalysesCache.cs @@ -0,0 +1,40 @@ +using Microsoft.CodeAnalysis; +using Microsoft.CodeAnalysis.Text; +using Microsoft.VisualStudio.Text; +using System; +using System.Threading.Tasks; + +namespace Winterdom.Viasfora.LanguageService.Core.Utils { + + internal class TextBufferCodeAnalysesCache { + public Workspace Workspace { get; private set; } + public Document Document { get; private set; } + public Lazy SemanticModel { get; } + public Lazy SyntaxRoot { get; } + public ITextSnapshot Snapshot { get; private set; } + + private TextBufferCodeAnalysesCache() { + this.SemanticModel = new Lazy(() => this.Document.GetSemanticModelAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + this.SyntaxRoot = new Lazy(() => Document.GetSyntaxRootAsync().ConfigureAwait(false).GetAwaiter().GetResult()); + } + + public static async Task ResolveAsync(ITextBuffer buffer, ITextSnapshot snapshot) { + var workspace = buffer.GetWorkspace(); + var document = snapshot.GetOpenDocumentInCurrentContextWithChanges(); + if ( document == null ) { + // Razor cshtml returns a null document for some reason. + return null; + } + + // the ConfigureAwait() calls are important, + // otherwise we'll deadlock VS + var semanticModel = await document.GetSemanticModelAsync().ConfigureAwait(false); + var syntaxRoot = await document.GetSyntaxRootAsync().ConfigureAwait(false); + return new TextBufferCodeAnalysesCache { + Workspace = workspace, + Document = document, + Snapshot = snapshot + }; + } + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Utils/TryCatchEnumerator.cs b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Utils/TryCatchEnumerator.cs new file mode 100644 index 00000000..899b3cb8 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Utils/TryCatchEnumerator.cs @@ -0,0 +1,51 @@ +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Winterdom.Viasfora.LanguageService.Core.Utils { + class TryCatchEnumerator : IEnumerator { + + public delegate bool OnExceptionDelegate(Exception ex); + + private IEnumerator _inner; + private OnExceptionDelegate _onException; + + public TryCatchEnumerator(IEnumerator inner, OnExceptionDelegate onException) { + this._inner = inner ?? throw new ArgumentNullException(nameof(inner)); + this._onException = onException ?? throw new ArgumentNullException(nameof(onException)); + } + + private T _current; + public T Current => this._current; + object IEnumerator.Current => this.Current; + + public void Dispose() { + this._inner.Dispose(); + } + + public bool MoveNext() { + var isFaulted = false; + for (; ; ) { + try { + var ok = this._inner.MoveNext(); + if ( ok ) + this._current = this._inner.Current; + else + this._current = default(T); + + isFaulted = false; + return ok; + } catch ( Exception ex ) { + if ( isFaulted || !this._onException(ex) ) { + this._current = default(T); + return false; + } + } + } + } + + public void Reset() { + this._inner.Reset(); + } + } +} diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Viasfora.LanguageService.Core.csproj b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Viasfora.LanguageService.Core.csproj new file mode 100644 index 00000000..d153bb12 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/Viasfora.LanguageService.Core.csproj @@ -0,0 +1,203 @@ + + + + + + + Debug + AnyCPU + {CE8301E8-E5BA-446B-9715-4B0F07E69190} + Library + Properties + Winterdom.Viasfora.LanguageService.Core + Viasfora.LanguageService.Core + v4.7.2 + 512 + true + + false + + + + + + 11.0 + + + + 12.0 + + + 14.0 + + + 15.0 + + + 16.0 + + + true + full + false + ..\..\..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + ..\..\..\bin\Release\ + TRACE + prompt + 4 + true + + + + ..\..\..\packages\Microsoft.CodeAnalysis.Common.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.dll + + + ..\..\..\packages\Microsoft.CodeAnalysis.CSharp.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.dll + + + ..\..\..\packages\Microsoft.CodeAnalysis.CSharp.Workspaces.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.Workspaces.dll + + + ..\..\..\packages\Microsoft.CodeAnalysis.EditorFeatures.Text.3.4.0\lib\net472\Microsoft.CodeAnalysis.EditorFeatures.Text.dll + + + ..\..\..\packages\Microsoft.CodeAnalysis.VisualBasic.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.VisualBasic.dll + + + ..\..\..\packages\Microsoft.CodeAnalysis.VisualBasic.Workspaces.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.VisualBasic.Workspaces.dll + + + ..\..\..\packages\Microsoft.CodeAnalysis.Workspaces.Common.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.Workspaces.dll + + + ..\..\..\packages\Microsoft.VisualStudio.CoreUtility.16.4.280\lib\net472\Microsoft.VisualStudio.CoreUtility.dll + + + ..\..\..\packages\Microsoft.VisualStudio.Text.Data.16.4.280\lib\net472\Microsoft.VisualStudio.Text.Data.dll + + + ..\..\..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.Logic.dll + False + + + ..\..\..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.UI.dll + False + + + ..\..\..\packages\VSSDK.Text.10.0.4\lib\net40\Microsoft.VisualStudio.Text.UI.Wpf.dll + False + + + ..\..\..\packages\Microsoft.VisualStudio.Threading.16.4.16\lib\net472\Microsoft.VisualStudio.Threading.dll + + + ..\..\..\packages\Microsoft.VisualStudio.Validation.15.3.15\lib\net45\Microsoft.VisualStudio.Validation.dll + + + ..\..\..\packages\Microsoft.Win32.Registry.4.5.0\lib\net461\Microsoft.Win32.Registry.dll + + + + ..\..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + + + ..\..\..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll + + + + ..\..\..\packages\System.Composition.AttributedModel.1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.AttributedModel.dll + + + ..\..\..\packages\System.Composition.Convention.1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.Convention.dll + + + ..\..\..\packages\System.Composition.Hosting.1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.Hosting.dll + + + ..\..\..\packages\System.Composition.Runtime.1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.Runtime.dll + + + ..\..\..\packages\System.Composition.TypedParts.1.0.31\lib\portable-net45+win8+wp8+wpa81\System.Composition.TypedParts.dll + + + ..\..\..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll + + + + ..\..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + + ..\..\..\packages\System.Reflection.Metadata.1.6.0\lib\netstandard2.0\System.Reflection.Metadata.dll + + + ..\..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\..\packages\System.Security.AccessControl.4.5.0\lib\net461\System.Security.AccessControl.dll + + + ..\..\..\packages\System.Security.Principal.Windows.4.5.0\lib\net461\System.Security.Principal.Windows.dll + + + ..\..\..\packages\System.Text.Encoding.CodePages.4.5.1\lib\net461\System.Text.Encoding.CodePages.dll + + + ..\..\..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + + + + + + + + + + + Properties\AssemblyInfo.Common.cs + + + + + + + + + + + + + + + + + + + + + {419c99ad-185a-4d31-a7ff-e1754c7b806d} + Viasfora.Core + + + {14B957DB-0ED2-4DEA-85C9-B670F6653C1B} + Viasfora.Languages + + + {dd9ed7c5-417c-43a2-be13-14cd948566b4} + Viasfora.Settings + + + + + + + + \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/app.config b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/app.config new file mode 100644 index 00000000..e1974a42 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/app.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/packages.config b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/packages.config new file mode 100644 index 00000000..19e0a203 --- /dev/null +++ b/src/Viasfora.LanguageServices/Viasfora.LanguageService.Core/packages.config @@ -0,0 +1,37 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/Viasfora.Languages/CSharp.cs b/src/Viasfora.Languages/CSharp.cs index ee0c5489..b8632186 100644 --- a/src/Viasfora.Languages/CSharp.cs +++ b/src/Viasfora.Languages/CSharp.cs @@ -44,7 +44,9 @@ public class CSharpSettings : LanguageSettings { }; protected override String[] VisibilityDefaults => new String[] { "public", "private", "protected", "internal" - }; + }; + protected override bool ReduceOpacityForArgumentValidationDefaults => true; + protected override bool ReduceOpacityForMethodOverloadsDefaults => true; public CSharpSettings(ITypedSettingsStore store) : base (Langs.CSharp, store) { diff --git a/src/Viasfora.Languages/DefaultLanguage.cs b/src/Viasfora.Languages/DefaultLanguage.cs index 90ffba40..fdb8137d 100644 --- a/src/Viasfora.Languages/DefaultLanguage.cs +++ b/src/Viasfora.Languages/DefaultLanguage.cs @@ -25,7 +25,8 @@ protected override IBraceScanner NewBraceScanner() public class DefaultSettings : LanguageSettings { protected override String[] ControlFlowDefaults => EMPTY; protected override String[] LinqDefaults => EMPTY; - protected override String[] VisibilityDefaults => EMPTY; + protected override String[] VisibilityDefaults => EMPTY; + protected override bool ReduceOpacityForArgumentValidationDefaults => false; public DefaultSettings(ITypedSettingsStore store) : base ("Text", store) { diff --git a/src/Viasfora.Languages/ILanguageSettings.cs b/src/Viasfora.Languages/ILanguageSettings.cs index cd42f50d..91ebde1a 100644 --- a/src/Viasfora.Languages/ILanguageSettings.cs +++ b/src/Viasfora.Languages/ILanguageSettings.cs @@ -7,6 +7,8 @@ public interface ILanguageSettings { String[] Linq { get; set; } String[] Visibility { get; set; } bool Enabled { get; set; } + bool ReduceOpacityForArgumentValidation { get; set; } + bool ReduceOpacityForMethodOverloads { get; set; } void Load(); void Save(); } diff --git a/src/Viasfora.Languages/LanguageSettings.cs b/src/Viasfora.Languages/LanguageSettings.cs index 76b1c2a5..8a87a4c9 100644 --- a/src/Viasfora.Languages/LanguageSettings.cs +++ b/src/Viasfora.Languages/LanguageSettings.cs @@ -7,7 +7,9 @@ public abstract class LanguageSettings : SettingsBase, ILanguageSettings, ICusto protected static readonly String[] EMPTY = { }; protected abstract String[] ControlFlowDefaults { get; } protected abstract String[] LinqDefaults { get; } - protected abstract String[] VisibilityDefaults { get; } + protected abstract String[] VisibilityDefaults { get; } + protected virtual bool ReduceOpacityForArgumentValidationDefaults => false; + protected virtual bool ReduceOpacityForMethodOverloadsDefaults => false; public String KeyName { get; private set; } public String[] ControlFlow { @@ -25,6 +27,15 @@ public String[] Visibility { public bool Enabled { get { return this.Store.GetBoolean(KeyName + "_Enabled", true); } set { this.Store.SetValue(KeyName + "_Enabled", value); } + } + public bool ReduceOpacityForArgumentValidation { + get { return this.Store.GetBoolean(KeyName + "_ReduceOpacityForArgumentValidation", ReduceOpacityForArgumentValidationDefaults); } + set { this.Store.SetValue(KeyName + "_ReduceOpacityForArgumentValidation", value); } + } + + public bool ReduceOpacityForMethodOverloads { + get { return this.Store.GetBoolean(KeyName + "_ReduceOpacityForMethodOverloads", ReduceOpacityForMethodOverloadsDefaults); } + set { this.Store.SetValue(KeyName + "_ReduceOpacityForMethodOverloads", value); } } public LanguageSettings(String key, ITypedSettingsStore store) @@ -37,7 +48,8 @@ public IDictionary Export() { { KeyName + "_ControlFlow", ControlFlow }, { KeyName + "_Linq", Linq }, { KeyName + "_Visibility", Visibility }, - { KeyName + "_Enabled", Enabled } + { KeyName + "_Enabled", Enabled }, + { KeyName + "_ReduceOpacityForArgumentValidation", ReduceOpacityForArgumentValidation } }; } } diff --git a/src/Viasfora.Languages/VB.cs b/src/Viasfora.Languages/VB.cs index 364020b3..e71ceddb 100644 --- a/src/Viasfora.Languages/VB.cs +++ b/src/Viasfora.Languages/VB.cs @@ -43,7 +43,9 @@ class VBSettings : LanguageSettings { }; protected override String[] VisibilityDefaults => new String[] { "friend", "public", "private", "protected" - }; + }; + protected override bool ReduceOpacityForArgumentValidationDefaults => true; + protected override bool ReduceOpacityForMethodOverloadsDefaults => true; public VBSettings(ITypedSettingsStore store) : base (Langs.VB, store) { diff --git a/src/Viasfora/Options/CSharpOptionsPage.cs b/src/Viasfora/Options/CSharpOptionsPage.cs index 04176397..7be71f46 100644 --- a/src/Viasfora/Options/CSharpOptionsPage.cs +++ b/src/Viasfora/Options/CSharpOptionsPage.cs @@ -17,7 +17,9 @@ public override void SaveSettingsToStorage() { this.language.Settings.ControlFlow = ControlFlowKeywords.ToArray(); this.language.Settings.Linq = LinqKeywords.ToArray(); this.language.Settings.Visibility = VisibilityKeywords.ToArray(); - this.language.Settings.Enabled = Enabled; + this.language.Settings.Enabled = Enabled; + this.language.Settings.ReduceOpacityForArgumentValidation = ReduceOpacityForArgumentValidation; + this.language.Settings.ReduceOpacityForMethodOverloads = ReduceOpacityForMethodOverloads; this.language.Settings.Save(); } public override void LoadSettingsFromStorage() { @@ -25,7 +27,9 @@ public override void LoadSettingsFromStorage() { ControlFlowKeywords = this.language.Settings.ControlFlow.ToList(); LinqKeywords = this.language.Settings.Linq.ToList(); VisibilityKeywords = this.language.Settings.Visibility.ToList(); - Enabled = this.language.Settings.Enabled; + Enabled = this.language.Settings.Enabled; + ReduceOpacityForArgumentValidation = this.language.Settings.ReduceOpacityForArgumentValidation; + ReduceOpacityForMethodOverloads = this.language.Settings.ReduceOpacityForMethodOverloads; } [LocDisplayName("Enabled")] @@ -51,6 +55,16 @@ public override void LoadSettingsFromStorage() { [Category("CSharp")] [Editor(Constants.STRING_COLLECTION_EDITOR, typeof(UITypeEditor))] [TypeConverter(typeof(Design.StringListConverter))] - public List LinqKeywords { get; set; } + public List LinqKeywords { get; set; } + + [Category("Reduce Opacity")] + [LocDisplayName("ArgumentValidation")] + [Description("Reduce opacity for argument validation")] + public bool ReduceOpacityForArgumentValidation { get; set; } + + [Category("Reduce Opacity")] + [LocDisplayName("MethodOverloads")] + [Description("Reduce opacity for method overloads")] + public bool ReduceOpacityForMethodOverloads { get; set; } } } diff --git a/src/Viasfora/Options/GeneralOptionsPage.cs b/src/Viasfora/Options/GeneralOptionsPage.cs index 8ea38fa2..85dfd631 100644 --- a/src/Viasfora/Options/GeneralOptionsPage.cs +++ b/src/Viasfora/Options/GeneralOptionsPage.cs @@ -28,7 +28,10 @@ public override void SaveSettingsToStorage() { settings.BoldAsItalicsEnabled = BoldAsItalicsEnabled; settings.ModelinesEnabled = ModelinesEnabled; settings.ModelinesNumLines = (int)ModelinesNumLines; - settings.TelemetryEnabled = TelemetryEnabled; + settings.TelemetryEnabled = TelemetryEnabled; + settings.ArgumentValidationClassifierEnabled = ArgumentValidationClassifierEnabled; + settings.MethodOverloadsClassifierEnabled = MethodOverloadsClassifierEnabled; + settings.Save(); this.colors.Save(); @@ -51,7 +54,9 @@ public override void LoadSettingsFromStorage() { BoldAsItalicsEnabled = settings.BoldAsItalicsEnabled; ModelinesEnabled = settings.ModelinesEnabled; ModelinesNumLines = (uint)settings.ModelinesNumLines; - TelemetryEnabled = settings.TelemetryEnabled; + TelemetryEnabled = settings.TelemetryEnabled; + ArgumentValidationClassifierEnabled = settings.ArgumentValidationClassifierEnabled; + MethodOverloadsClassifierEnabled = settings.MethodOverloadsClassifierEnabled; this.colors = new ClassificationList(new ColorStorage(this.Site)); this.colors.Load( @@ -106,7 +111,17 @@ public override void LoadSettingsFromStorage() { [LocDisplayName("Use italics on Flow Control Keywords")] [Description("Use italics on text highlighted by the Keyword Classifier")] [Category("Text Editor")] - public bool FlowControlUseItalics { get; set; } + public bool FlowControlUseItalics { get; set; } + + [LocDisplayName("Enable Argument Validation Classifier (Roslyn)")] + [Description("Enable reducing opacity of argument validation")] + [Category("Text Editor")] + public bool ArgumentValidationClassifierEnabled { get; set; } + + [LocDisplayName("Enable Method Overload Classifier (Roslyn)")] + [Description("Enable reducing opacity of method overloads")] + [Category("Text Editor")] + public bool MethodOverloadsClassifierEnabled { get; set; } [LocDisplayName("Enable 'Bold As Italics'")] diff --git a/src/Viasfora/Options/VBOptionsPage.cs b/src/Viasfora/Options/VBOptionsPage.cs index 4fe84488..43017916 100644 --- a/src/Viasfora/Options/VBOptionsPage.cs +++ b/src/Viasfora/Options/VBOptionsPage.cs @@ -17,7 +17,9 @@ public override void SaveSettingsToStorage() { this.language.Settings.ControlFlow = ControlFlowKeywords.ToArray(); this.language.Settings.Linq = LinqKeywords.ToArray(); this.language.Settings.Visibility = VisibilityKeywords.ToArray(); - this.language.Settings.Enabled = Enabled; + this.language.Settings.Enabled = Enabled; + this.language.Settings.ReduceOpacityForArgumentValidation = ReduceOpacityForArgumentValidation; + this.language.Settings.ReduceOpacityForMethodOverloads = ReduceOpacityForMethodOverloads; this.language.Settings.Save(); } public override void LoadSettingsFromStorage() { @@ -25,7 +27,9 @@ public override void LoadSettingsFromStorage() { ControlFlowKeywords = this.language.Settings.ControlFlow.ToList(); LinqKeywords = this.language.Settings.Linq.ToList(); VisibilityKeywords = this.language.Settings.Visibility.ToList(); - Enabled = this.language.Settings.Enabled; + Enabled = this.language.Settings.Enabled; + ReduceOpacityForArgumentValidation = this.language.Settings.ReduceOpacityForArgumentValidation; + ReduceOpacityForMethodOverloads = this.language.Settings.ReduceOpacityForMethodOverloads; } [LocDisplayName("Enabled")] @@ -51,6 +55,16 @@ public override void LoadSettingsFromStorage() { [Category("Basic")] [Editor(Constants.STRING_COLLECTION_EDITOR, typeof(UITypeEditor))] [TypeConverter(typeof(Design.StringListConverter))] - public List LinqKeywords { get; set; } + public List LinqKeywords { get; set; } + + [Category("Reduce Opacity")] + [LocDisplayName("ArgumentValidation")] + [Description("Reduce opacity for argument validation")] + public bool ReduceOpacityForArgumentValidation { get; set; } + + [Category("Reduce Opacity")] + [LocDisplayName("MethodOverloads")] + [Description("Reduce opacity for method overloads")] + public bool ReduceOpacityForMethodOverloads { get; set; } } } diff --git a/src/Viasfora/Viasfora.csproj b/src/Viasfora/Viasfora.csproj index 2db80235..d0b001e2 100644 --- a/src/Viasfora/Viasfora.csproj +++ b/src/Viasfora/Viasfora.csproj @@ -1,5 +1,5 @@  - + @@ -10,7 +10,7 @@ Properties Winterdom.Viasfora Viasfora - v4.5 + v4.7.2 False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/tests/Viasfora.LanguageService.Basic.Tests/My Project/Settings.Designer.vb b/tests/Viasfora.LanguageService.Basic.Tests/My Project/Settings.Designer.vb new file mode 100644 index 00000000..d787b2a3 --- /dev/null +++ b/tests/Viasfora.LanguageService.Basic.Tests/My Project/Settings.Designer.vb @@ -0,0 +1,73 @@ +'------------------------------------------------------------------------------ +' +' This code was generated by a tool. +' Runtime Version:4.0.30319.42000 +' +' Changes to this file may cause incorrect behavior and will be lost if +' the code is regenerated. +' +'------------------------------------------------------------------------------ + +Option Strict On +Option Explicit On + + +Namespace My + + _ + Partial Friend NotInheritable Class MySettings + Inherits Global.System.Configuration.ApplicationSettingsBase + + Private Shared defaultInstance As MySettings = CType(Global.System.Configuration.ApplicationSettingsBase.Synchronized(New MySettings()),MySettings) + +#Region "My.Settings Auto-Save Functionality" +#If _MyType = "WindowsForms" Then + Private Shared addedHandler As Boolean + + Private Shared addedHandlerLockObject As New Object + + _ + Private Shared Sub AutoSaveSettings(sender As Global.System.Object, e As Global.System.EventArgs) + If My.Application.SaveMySettingsOnExit Then + My.Settings.Save() + End If + End Sub +#End If +#End Region + + Public Shared ReadOnly Property [Default]() As MySettings + Get + +#If _MyType = "WindowsForms" Then + If Not addedHandler Then + SyncLock addedHandlerLockObject + If Not addedHandler Then + AddHandler My.Application.Shutdown, AddressOf AutoSaveSettings + addedHandler = True + End If + End SyncLock + End If +#End If + Return defaultInstance + End Get + End Property + End Class +End Namespace + +Namespace My + + _ + Friend Module MySettingsProperty + + _ + Friend ReadOnly Property Settings() As Global.Winterdom.Viasfora.LanguageService.Basic.Tests.My.MySettings + Get + Return Global.Winterdom.Viasfora.LanguageService.Basic.Tests.My.MySettings.Default + End Get + End Property + End Module +End Namespace diff --git a/tests/Viasfora.LanguageService.Basic.Tests/My Project/Settings.settings b/tests/Viasfora.LanguageService.Basic.Tests/My Project/Settings.settings new file mode 100644 index 00000000..377f56d6 --- /dev/null +++ b/tests/Viasfora.LanguageService.Basic.Tests/My Project/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/tests/Viasfora.LanguageService.Basic.Tests/Viasfora.LanguageService.Basic.Tests.vbproj b/tests/Viasfora.LanguageService.Basic.Tests/Viasfora.LanguageService.Basic.Tests.vbproj new file mode 100644 index 00000000..1618f475 --- /dev/null +++ b/tests/Viasfora.LanguageService.Basic.Tests/Viasfora.LanguageService.Basic.Tests.vbproj @@ -0,0 +1,186 @@ + + + + + + + Debug + AnyCPU + {C7826A1B-55C0-40AA-B9F3-A58D7B155DA9} + Library + Winterdom.Viasfora.LanguageService.Basic.Tests + Viasfora.LanguageService.Basic.Tests + 512 + Windows + v4.7.2 + true + + + + + + true + full + true + true + bin\Debug\ + Viasfora.LanguageService.Basic.Tests.xml + + + true + 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036 + + + pdbonly + false + true + true + bin\Release\ + Viasfora.LanguageService.Basic.Tests.xml + + + true + 41999,42016,42017,42018,42019,42020,42021,42022,42032,42036 + + + On + + + Binary + + + On + + + On + + + + ..\..\packages\Microsoft.CodeAnalysis.Common.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.dll + + + ..\..\packages\Microsoft.CodeAnalysis.VisualBasic.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.VisualBasic.dll + + + + ..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + + + ..\..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll + + + + ..\..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll + + + + ..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + + ..\..\packages\System.Reflection.Metadata.1.6.0\lib\netstandard2.0\System.Reflection.Metadata.dll + + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\packages\System.Text.Encoding.CodePages.4.5.1\lib\net461\System.Text.Encoding.CodePages.dll + + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + + + + + + + ..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll + + + ..\..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll + + + ..\..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll + + + ..\..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll + + + + + + + + + + + + + + + + + + + True + Application.myapp + + + True + True + Resources.resx + + + True + Settings.settings + True + + + + + VbMyResourcesResXFileCodeGenerator + Resources.Designer.vb + My.Resources + Designer + + + + + + MyApplicationCodeGenerator + Application.Designer.vb + + + SettingsSingleFileGenerator + My + Settings.Designer.vb + + + + + + {ac41a83d-0565-47cd-aa55-4a09874d722f} + Viasfora.LanguageService.Basic + + + {ce8301e8-e5ba-446b-9715-4b0f07e69190} + Viasfora.LanguageService.Core + + + {04588DE4-349F-466C-A57B-95D00D10269A} + Viasfora.LanguageService.Core.Tests + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/tests/Viasfora.LanguageService.Basic.Tests/app.config b/tests/Viasfora.LanguageService.Basic.Tests/app.config new file mode 100644 index 00000000..95cf1647 --- /dev/null +++ b/tests/Viasfora.LanguageService.Basic.Tests/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Viasfora.LanguageService.Basic.Tests/packages.config b/tests/Viasfora.LanguageService.Basic.Tests/packages.config new file mode 100644 index 00000000..75a57f44 --- /dev/null +++ b/tests/Viasfora.LanguageService.Basic.Tests/packages.config @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Viasfora.LanguageService.CSharp.Tests/ArgumentValidationTagger/SyntaxHelper.CSharp.Tests.cs b/tests/Viasfora.LanguageService.CSharp.Tests/ArgumentValidationTagger/SyntaxHelper.CSharp.Tests.cs new file mode 100644 index 00000000..1d8e58b3 --- /dev/null +++ b/tests/Viasfora.LanguageService.CSharp.Tests/ArgumentValidationTagger/SyntaxHelper.CSharp.Tests.cs @@ -0,0 +1,317 @@ +using Winterdom.Viasfora.LanguageService.Core.Tests.ArgumentValidationTagger; +using Xunit; + +namespace Winterdom.Viasfora.LanguageService.CSharp.Tests.ArgumentValidationTagger { + public class SyntaxHelperCSharpTests : SyntaxHelperTests { + + + [Fact] + public void test_simple_if_throw_statement() { + var test = @" +class MyTest { + + void myfn(string arg1) { + if (arg1 == null) throw new ArgumentNullException(nameof(of arg1)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 5, expectation: true); + } + + + [Fact] + public void test_ctor_simple_if_throw_statement() { + var test = @" +class MyTest { + + MyTest(string arg1) { + if (arg1 == null) throw new ArgumentNullException(nameof(of arg1)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 5, expectation: true); + } + + [Fact] + public void test_ctor_simple_coalesce_throw_statement() { + var test = @" +class MyTest { + + private string myarg1; + MyTest(string arg1) { + this.myarg1 = arg1 ?? throw new ArgumentNullException(nameof(myarg1)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 6, column: 5, expectation: true); + } + + + [Fact] + public void test_multiple_if_throw_statement() { + var test = @" +class MyTest { + + void myfn(string arg1, string arg2) { + if (arg1 == null) throw new ArgumentNullException(nameof(of arg1)); + if (arg2 == null) throw new ArgumentNullException(nameof(of arg2)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 6, column: 5, expectation: true); + } + + [Fact] + public void test_ctor_multiple_if_throw_statement() { + var test = @" +class MyTest { + + MyTest(string arg1, string arg2) { + if (arg1 == null) throw new ArgumentNullException(nameof(of arg1)); + if (arg2 == null) throw new ArgumentNullException(nameof(of arg2)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 6, column: 5, expectation: true); + } + + [Fact] + public void test_ctor_multiple_coalesce_throw_statement() { + var test = @" +class MyTest { + + private string myarg1; + private string myarg2; + MyTest(string arg1, string arg2) { + this.myarg1 = arg1 ?? throw new ArgumentNullException(nameof(myarg1)); + this.myarg2 = arg2 ?? throw new ArgumentNullException(nameof(myarg2)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 8, column: 5, expectation: true); + } + + + + [Fact] + public void test_skip_if_previous_is_not_argument_throw_syntax() { + var test = @" +class MyTest { + + void myfn(string arg1, string arg2) { + if (arg1 == null) throw new ArgumentNullException(nameof(of arg1)); + Foo(); + if (arg2 == null) throw new ArgumentNullException(nameof(of arg2)); + } + + void Foo() { } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 7, column: 5, expectation: false); + } + + [Fact] + public void test_argument_out_of_range_exception() { + var test = @" +class MyTest { + + void myfn(int i) { + if (i < 0) throw new ArgumentOutOfRange(nameof(of arg1)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 5, expectation: true); + } + + [Fact] + public void test_argument_exception() { + var test = @" +class MyTest { + + void myfn(int i) { + if (i < 0) throw new ArgumentException(""lorem ipsum"", nameof(of arg1)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 5, expectation: true); + } + + [Fact] + public void test_skip_expression_function_calls() { + var test = @" +class MyTest { + + void myfn(int i) { + if (IsSomething(i)) throw new ArgumentException(""lorem ipsum""); + } + + bool IsSomething(int i) { + return false; + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 5, expectation: false); + } + + [Fact] + public void test_skip_member_access_with_same_name_like_paramter() { + var test = @" +class MyTest { + + void myfn(int i) { + if (this.i == 0) throw new InvalidOperationException(); + } + + int i; +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 5, expectation: false); + } + + [Fact] + public void test_skip_multiple_if_statements() { + var test = @" +class MyTest { + + void myfn(string arg1) { + if (arg1 == null) { + var x = 5; + throw new ArgumentNullException(nameof(of arg1)); + } + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 5, expectation: false); + } + + + [Fact] + public void test_throw_function_syntax() { + var test = @" +class MyTest { + + void myfn(string arg1) { + if (arg1 == null) throw ThrowHelper.ArgumentNull(nameof(arg1)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 5, expectation: true); + } + + [Fact] + public void test_bool_expression() { + var test = @" +class MyTest { + + void myfn(bool arg1) { + if (arg1) throw new ArgumentException(nameof(arg1)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 5, expectation: true); + } + + + [Fact] + public void test_multiline_if_statement() { + var test = @" +class MyTest { + + void myfn(bool arg1) { + if (arg1) { + throw new ArgumentException(nameof(arg1)); + } + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 5, expectation: true); // Position if + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 6, column: 10, expectation: true); // Position throw + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 7, column: 6, expectation: true); // } + } + + [Fact] + public void test_skip_multiline_if_statement_inside_another_block() { + var test = @" +class MyTest { + + void myfn(bool arg1) { + if (something == something2) { + if (arg1) { + throw new ArgumentException(nameof(arg1)); + } + } + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 7, column: 12, expectation: false); + } + + [Fact] + public void test_skip_method_itself() { + var test = @" +class MyTest { + + void myfn(bool arg1) { + if (arg1) throw new ArgumentException(nameof(arg1)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 4, column: 5, expectation: false); + } + + [Fact] + public void test_member_access_parameter() { + var test = @" +class MyTest { + + void myfn(string arg1) { + if (arg1.Length == 0) throw new ArgumentException(nameof(arg1)); + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 5, expectation: true); + } + + [Fact] + public void test_skip_property() { + var test = @" +class MyTest { + + public bool MyProp1 => False; + public bool MyProp2 { get; set; } + public bool MyProp3 { + get { + return false; + } + set { + if (value) throw new ArgumentException(); + } + } +} +"; + + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 4, column: 5, expectation: false); + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 27, expectation: false); + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 5, column: 32, expectation: false); + VerifyCSharpIsIfArgumentThrowSyntaxStatement(test, line: 11, column: 10, expectation: false); + } + } +} diff --git a/tests/Viasfora.LanguageService.CSharp.Tests/MethodOverloadsTagger/SyntaxHelper.CSharp.Tests.cs b/tests/Viasfora.LanguageService.CSharp.Tests/MethodOverloadsTagger/SyntaxHelper.CSharp.Tests.cs new file mode 100644 index 00000000..df246ebb --- /dev/null +++ b/tests/Viasfora.LanguageService.CSharp.Tests/MethodOverloadsTagger/SyntaxHelper.CSharp.Tests.cs @@ -0,0 +1,296 @@ +using Winterdom.Viasfora.LanguageService.Core.Tests.MethodOverloadsTagger; +using Xunit; + +namespace Winterdom.Viasfora.LanguageService.CSharp.Tests.MethodOverloadsTagger { + public class SyntaxHelperCSharpTests : SyntaxHelperTests { + + [Fact] + public void test_parameter_default() { + var test = @" +class MyTest { + + void Foo() { + Foo(true); + } + + void Foo(bool v) { + } +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 5, expectation: true); + } + + [Fact] + public void test_with_this() { + var test = @" +class MyTest { + + void Foo() { + this.Foo(true); + } + + void Foo(bool v) { + } +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 5, expectation: true); + } + + + [Fact] + public void test_with_return() { + var test = @" +class MyTest { + + bool Foo() { + return this.Foo(true); + } + + bool Foo(bool v) { + } +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 5, expectation: true); + } + + + [Fact] + public void test_calling_same_name_with_core_prefix() { + var test = @" +class MyTest { + + void Foo() { + FooCore(true); + } + + void FooCore(bool v) { + } +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 5, expectation: true); + } + + + [Fact] + public void test_with_null_checking() { + var test = @" +class MyTest { + + void Foo(string arg1, string arg2) { + if (arg1 == null) throw new ArgumentNullException(nameof(arg1)); + if (arg2 == null) throw new ArgumentNullException(nameof(arg2)); + + FooCore(true); + } + + void FooCore(bool v) { + } +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 5, expectation: true); + } + + [Fact] + public void test_generic() { + var test = @" +class MyTest { + + string Foo() { + return Foo(); + } + + T Foo() { + return FooCore(true); + } + T FooCore(bool v) { + } +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 5, expectation: true); + VerifyCSharpIsMethodOverload(test, line: 9, column: 5, expectation: true); + } + + + [Fact] + public void test_async() { + var test = @" +class MyTest { + + async Task Foo() { + await Foo(true); + } + + async Task Foo(bool v) { + } +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 5, expectation: true); + } + + + [Fact] + public void test_async_return() { + var test = @" +class MyTest { + + async Task Foo() { + return await Foo(true); + } + + async Task Foo(bool v) { + } +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 5, expectation: true); + } + + + + + + + [Fact] + public void test_skip_empty() { + var test = @" +class MyTest { + + void Foo() { + } + +} +"; + + VerifyCSharpIsMethodOverload(test, line: 4, column: 10, expectation: false); + } + + [Fact] + public void test_skip_abstract() { + var test = @" +abstract class MyTest { + + protected abstract void Foo(); + +} +"; + + VerifyCSharpIsMethodOverload(test, line: 4, column: 10, expectation: false); + } + + [Fact] + public void test_skip_interface() { + var test = @" +interface MyTest { + + void Foo(); + +} +"; + + VerifyCSharpIsMethodOverload(test, line: 4, column: 10, expectation: false); + } + + + [Fact] + public void test_skip_multi_statements() { + var test = @" +class MyTest { + + void Foo() { + Bar(); + Baz(); + } + + void Bar() {} + void Baz() {} + +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 8, expectation: false); + } + + [Fact] + public void test_skip_calling_another_class() { + var test = @" +class MyTest { + + void Foo() { + Console.WriteLine(); + } + +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 8, expectation: false); + } + + + [Fact] + public void test_skip_expressions() { + var test = @" +class MyTest { + + void Foo() { + bar = 42; + } + + int bar; + +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 8, expectation: false); + } + + [Fact] + public void test_skip_throw_statement() { + var test = @" +class MyTest { + + void Foo() { + throw new NotImplentedException(); + } + +} +"; + + VerifyCSharpIsMethodOverload(test, line: 5, column: 8, expectation: false); + } + + [Fact] + public void test_skip_property() { + var test = @" +class MyTest { + + public bool MyProp1 => GetProp(); + public bool MyProp2 { get; set; } + public bool MyProp3 { + get { + return GetProp(); + } + set { + SetProp(value) + } + } + + bool GetProp() => true; + void SetProp(bool v) { } +} +"; + + VerifyCSharpIsMethodOverload(test, line: 4, column: 5, expectation: false); + VerifyCSharpIsMethodOverload(test, line: 5, column: 27, expectation: false); + VerifyCSharpIsMethodOverload(test, line: 5, column: 32, expectation: false); + VerifyCSharpIsMethodOverload(test, line: 11, column: 10, expectation: false); + } + + } +} diff --git a/tests/Viasfora.LanguageService.CSharp.Tests/Properties/AssemblyInfo.cs b/tests/Viasfora.LanguageService.CSharp.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..fe163e9c --- /dev/null +++ b/tests/Viasfora.LanguageService.CSharp.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Viasfora.LanguageService.CSharp.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Viasfora.LanguageService.CSharp.Tests")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("51698343-ee6b-44ac-b0a9-3f606ae7fe80")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tests/Viasfora.LanguageService.CSharp.Tests/Viasfora.LanguageService.CSharp.Tests.csproj b/tests/Viasfora.LanguageService.CSharp.Tests/Viasfora.LanguageService.CSharp.Tests.csproj new file mode 100644 index 00000000..07917e75 --- /dev/null +++ b/tests/Viasfora.LanguageService.CSharp.Tests/Viasfora.LanguageService.CSharp.Tests.csproj @@ -0,0 +1,98 @@ + + + + + + Debug + AnyCPU + {51698343-EE6B-44AC-B0A9-3F606AE7FE80} + Library + Properties + Winterdom.Viasfora.LanguageService.CSharp.Tests + Viasfora.LanguageService.CSharp.Tests + v4.7.2 + 512 + true + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + + + ..\..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + + + + + + + + + ..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll + + + ..\..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll + + + ..\..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll + + + ..\..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll + + + + + + + + + + {ce8301e8-e5ba-446b-9715-4b0f07e69190} + Viasfora.LanguageService.Core + + + {003c3d3d-b966-4ac5-a622-d4931810587d} + Viasfora.LanguageService.CSharp + + + {04588de4-349f-466c-a57b-95d00d10269a} + Viasfora.LanguageService.Core.Tests + + + {dd25feeb-2cfd-4dcb-82a2-764dc563cf98} + Viasfora.Tests + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + \ No newline at end of file diff --git a/tests/Viasfora.LanguageService.CSharp.Tests/app.config b/tests/Viasfora.LanguageService.CSharp.Tests/app.config new file mode 100644 index 00000000..95cf1647 --- /dev/null +++ b/tests/Viasfora.LanguageService.CSharp.Tests/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Viasfora.LanguageService.CSharp.Tests/packages.config b/tests/Viasfora.LanguageService.CSharp.Tests/packages.config new file mode 100644 index 00000000..49eca54c --- /dev/null +++ b/tests/Viasfora.LanguageService.CSharp.Tests/packages.config @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Viasfora.LanguageService.Core.Tests/ArgumentValidationTagger/SyntaxHelper.Tests.cs b/tests/Viasfora.LanguageService.Core.Tests/ArgumentValidationTagger/SyntaxHelper.Tests.cs new file mode 100644 index 00000000..d18587c3 --- /dev/null +++ b/tests/Viasfora.LanguageService.Core.Tests/ArgumentValidationTagger/SyntaxHelper.Tests.cs @@ -0,0 +1,44 @@ +using Microsoft.CodeAnalysis.Text; +using Xunit; + +namespace Winterdom.Viasfora.LanguageService.Core.Tests.ArgumentValidationTagger { + public class SyntaxHelperTests { + + protected void VerifyCSharpIsIfArgumentThrowSyntaxStatement(string input, int line, int column, bool expectation) { + var tree = Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(input); + var source = tree.GetText(); + var root = tree.GetRoot(); + var inputSpan = TextSpan.FromBounds(source.Lines[line - 1].Start + column, source.Lines[line - 1].Start + column); + + var node = root.FindNode(inputSpan); + bool result = CSharp.ArgumentValidationTagger.SyntaxHelper.IsAnyIfArgumentThrowSyntaxStatement(node); + + if ( result != expectation ) { + if ( expectation ) { + Assert.True(result, "Expected IsIfArgumentThrowSyntaxStatement=True"); + } else { + Assert.False(result, "Expected IsIfArgumentThrowSyntaxStatement=False"); + } + } + + } + + protected void VerifyBasicIsIfArgumentThrowSyntaxStatement(string input, int line, int column, bool expectation) { + var tree = Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.ParseText(input); + var source = tree.GetText(); + var root = tree.GetRoot(); + var inputSpan = TextSpan.FromBounds(source.Lines[line - 1].Start + column, source.Lines[line - 1].Start + column); + + var node = root.FindNode(inputSpan); + bool result = Basic.ArgumentValidationTagger.SyntaxHelper.IsAnyIfArgumentThrowSyntaxStatement(node); + + if ( result != expectation ) { + if ( expectation ) { + Assert.True(result, "Expected IsIfArgumentThrowSyntaxStatement=True"); + } else { + Assert.False(result, "Expected IsIfArgumentThrowSyntaxStatement=False"); + } + } + } + } +} \ No newline at end of file diff --git a/tests/Viasfora.LanguageService.Core.Tests/MethodOverloadsTagger/SyntaxHelper.Tests.cs b/tests/Viasfora.LanguageService.Core.Tests/MethodOverloadsTagger/SyntaxHelper.Tests.cs new file mode 100644 index 00000000..c1f8b182 --- /dev/null +++ b/tests/Viasfora.LanguageService.Core.Tests/MethodOverloadsTagger/SyntaxHelper.Tests.cs @@ -0,0 +1,45 @@ +using Microsoft.CodeAnalysis.Text; +using Xunit; + +namespace Winterdom.Viasfora.LanguageService.Core.Tests.MethodOverloadsTagger { + public class SyntaxHelperTests { + + protected void VerifyCSharpIsMethodOverload(string input, int line, int column, bool expectation) { + var tree = Microsoft.CodeAnalysis.CSharp.CSharpSyntaxTree.ParseText(input); + var source = tree.GetText(); + var root = tree.GetRoot(); + var inputSpan = TextSpan.FromBounds(source.Lines[line - 1].Start + column, source.Lines[line - 1].Start + column); + + var node = root.FindNode(inputSpan); + bool result = CSharp.MethodOverloadsTagger.SyntaxHelper.IsMethodOverload(node); + + if ( result != expectation ) { + if ( expectation ) { + Assert.True(result, "Expected IsMethodOverload=True"); + } else { + Assert.False(result, "Expected IsMethodOverload=False"); + } + } + + } + + protected void VerifyBasicIsMethodOverload(string input, int line, int column, bool expectation) { + var tree = Microsoft.CodeAnalysis.VisualBasic.VisualBasicSyntaxTree.ParseText(input); + var source = tree.GetText(); + var root = tree.GetRoot(); + var inputSpan = TextSpan.FromBounds(source.Lines[line - 1].Start + column, source.Lines[line - 1].Start + column); + + var node = root.FindNode(inputSpan); + bool result = Basic.MethodOverloadsTagger.SyntaxHelper.IsMethodOverload(node); + + if ( result != expectation ) { + if ( expectation ) { + Assert.True(result, "Expected IsMethodOverload=True"); + } else { + Assert.False(result, "Expected IsMethodOverload=False"); + } + } + } + + } +} diff --git a/tests/Viasfora.LanguageService.Core.Tests/Properties/AssemblyInfo.cs b/tests/Viasfora.LanguageService.Core.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..82811c3f --- /dev/null +++ b/tests/Viasfora.LanguageService.Core.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Viasfora.LanguageService.Core.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Viasfora.LanguageService.Core.Tests")] +[assembly: AssemblyCopyright("Copyright © 2020")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("04588de4-349f-466c-a57b-95d00d10269a")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tests/Viasfora.LanguageService.Core.Tests/Viasfora.LanguageService.Core.Tests.csproj b/tests/Viasfora.LanguageService.Core.Tests/Viasfora.LanguageService.Core.Tests.csproj new file mode 100644 index 00000000..5d5b2ac5 --- /dev/null +++ b/tests/Viasfora.LanguageService.Core.Tests/Viasfora.LanguageService.Core.Tests.csproj @@ -0,0 +1,149 @@ + + + + + + + Debug + AnyCPU + {04588DE4-349F-466C-A57B-95D00D10269A} + Library + Properties + Winterdom.Viasfora.LanguageService.Core.Tests + Viasfora.LanguageService.Core.Tests + v4.7.2 + 512 + true + + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + true + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + true + + + + ..\..\packages\Microsoft.CodeAnalysis.Common.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.dll + + + ..\..\packages\Microsoft.CodeAnalysis.CSharp.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.dll + + + ..\..\packages\Microsoft.CodeAnalysis.CSharp.Workspaces.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.CSharp.Workspaces.dll + + + ..\..\packages\Microsoft.CodeAnalysis.VisualBasic.3.4.0\lib\netstandard2.0\Microsoft.CodeAnalysis.VisualBasic.dll + + + + + + ..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + + + ..\..\packages\System.Collections.Immutable.1.5.0\lib\netstandard2.0\System.Collections.Immutable.dll + + + + + ..\..\packages\System.Memory.4.5.3\lib\netstandard2.0\System.Memory.dll + + + + ..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + + ..\..\packages\System.Reflection.Metadata.1.6.0\lib\netstandard2.0\System.Reflection.Metadata.dll + + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.2\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\packages\System.Text.Encoding.CodePages.4.5.1\lib\net461\System.Text.Encoding.CodePages.dll + + + ..\..\packages\System.Threading.Tasks.Extensions.4.5.3\lib\netstandard2.0\System.Threading.Tasks.Extensions.dll + + + ..\..\packages\System.ValueTuple.4.5.0\lib\net461\System.ValueTuple.dll + + + + + + + + + + ..\..\packages\xunit.abstractions.2.0.0\lib\net35\xunit.abstractions.dll + + + ..\..\packages\xunit.assert.2.1.0\lib\dotnet\xunit.assert.dll + + + ..\..\packages\xunit.extensibility.core.2.1.0\lib\dotnet\xunit.core.dll + + + ..\..\packages\xunit.extensibility.execution.2.1.0\lib\net45\xunit.execution.desktop.dll + + + + + + + + + + {ac41a83d-0565-47cd-aa55-4a09874d722f} + Viasfora.LanguageService.Basic + + + {ce8301e8-e5ba-446b-9715-4b0f07e69190} + Viasfora.LanguageService.Core + + + {003c3d3d-b966-4ac5-a622-d4931810587d} + Viasfora.LanguageService.CSharp + + + {14B957DB-0ED2-4DEA-85C9-B670F6653C1B} + Viasfora.Languages + + + {dd25feeb-2cfd-4dcb-82a2-764dc563cf98} + Viasfora.Tests + + + + + + + + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + \ No newline at end of file diff --git a/tests/Viasfora.LanguageService.Core.Tests/app.config b/tests/Viasfora.LanguageService.Core.Tests/app.config new file mode 100644 index 00000000..95cf1647 --- /dev/null +++ b/tests/Viasfora.LanguageService.Core.Tests/app.config @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Viasfora.LanguageService.Core.Tests/packages.config b/tests/Viasfora.LanguageService.Core.Tests/packages.config new file mode 100644 index 00000000..7c3151f8 --- /dev/null +++ b/tests/Viasfora.LanguageService.Core.Tests/packages.config @@ -0,0 +1,24 @@ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Viasfora.Tests/Viasfora.Tests.csproj b/tests/Viasfora.Tests/Viasfora.Tests.csproj index 53785a6d..42f92347 100644 --- a/tests/Viasfora.Tests/Viasfora.Tests.csproj +++ b/tests/Viasfora.Tests/Viasfora.Tests.csproj @@ -211,6 +211,18 @@ {419c99ad-185a-4d31-a7ff-e1754c7b806d} Viasfora.Core + + {ac41a83d-0565-47cd-aa55-4a09874d722f} + Viasfora.LanguageService.Basic + + + {ce8301e8-e5ba-446b-9715-4b0f07e69190} + Viasfora.LanguageService.Core + + + {003c3d3d-b966-4ac5-a622-d4931810587d} + Viasfora.LanguageService.CSharp + {14b957db-0ed2-4dea-85c9-b670f6653c1b} Viasfora.Languages