提交 5a63208b 编写于 作者: C Cyrus Najmabadi

Reverting regex feature work. That will go on in the regexFeatures branch

上级 8aef4601
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Editor.Implementation.BraceMatching;
namespace Microsoft.CodeAnalysis.Editor.CSharp.BraceMatching
{
[ExportBraceMatcher(LanguageNames.CSharp)]
internal class CSharpRegexBraceMatcher : IBraceMatcher
{
public Task<BraceMatchingResult?> FindBracesAsync(Document document, int position, CancellationToken cancellationToken)
=> CommonRegexBraceMatcher.FindBracesAsync(document, position, cancellationToken);
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CSharp.ValidateRegexString;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.Diagnostics;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.RegularExpressions;
using Roslyn.Test.Utilities;
using Xunit;
namespace Microsoft.CodeAnalysis.Editor.CSharp.UnitTests.ValidateRegexString
{
public class ValidateRegexStringTests : AbstractCSharpDiagnosticProviderBasedUserDiagnosticTest
{
internal override (DiagnosticAnalyzer, CodeFixProvider) CreateDiagnosticProviderAndFixer(Workspace workspace)
=> (new CSharpValidateRegexStringDiagnosticAnalyzer(), null);
private IDictionary<OptionKey, object> OptionOn()
{
var optionsSet = new Dictionary<OptionKey, object>();
optionsSet.Add(new OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.CSharp), true);
optionsSet.Add(new OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.VisualBasic), true);
return optionsSet;
}
private IDictionary<OptionKey, object> OptionOff()
{
var optionsSet = new Dictionary<OptionKey, object>();
optionsSet.Add(new OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.CSharp), false);
optionsSet.Add(new OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.VisualBasic), false);
return optionsSet;
}
[Fact, Trait(Traits.Feature, Traits.Features.ValidateRegexString)]
public async Task TestWarning1()
{
await TestDiagnosticInfoAsync(@"
using System.Text.RegularExpressions;
class Program
{
void Main()
{
var r = new Regex(@""[|)|]"");
}
}",
options: OptionOn(),
diagnosticId: IDEDiagnosticIds.RegexPatternDiagnosticId,
diagnosticSeverity: DiagnosticSeverity.Warning,
diagnosticMessage: string.Format(FeaturesResources.Regex_issue_0, WorkspacesResources.Too_many_close_parens));
}
[Fact, Trait(Traits.Feature, Traits.Features.ValidateRegexString)]
public async Task TestWarning2()
{
await TestDiagnosticInfoAsync(@"
using System.Text.RegularExpressions;
class Program
{
void Main()
{
var r = new Regex(""[|\u0029|]"");
}
}",
options: OptionOn(),
diagnosticId: IDEDiagnosticIds.RegexPatternDiagnosticId,
diagnosticSeverity: DiagnosticSeverity.Warning,
diagnosticMessage: string.Format(FeaturesResources.Regex_issue_0, WorkspacesResources.Too_many_close_parens));
}
[Fact, Trait(Traits.Feature, Traits.Features.ValidateRegexString)]
public async Task TestWarningMissing1()
{
await TestDiagnosticMissingAsync(@"
using System.Text.RegularExpressions;
class Program
{
void Main()
{
var r = new Regex(@""[|\u0029|]"");
}
}");
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.RegularExpressions;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.VirtualChars;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.Editor.Implementation.BraceMatching
{
internal static class CommonRegexBraceMatcher
{
internal static async Task<BraceMatchingResult?> FindBracesAsync(
Document document, int position, CancellationToken cancellationToken)
{
var option = document.Project.Solution.Workspace.Options.GetOption(
RegularExpressionsOptions.HighlightRelatedRegexComponentsUnderCursor, document.Project.Language);
if (!option)
{
return default;
}
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
if (RegexPatternDetector.IsDefinitelyNotPattern(token, syntaxFacts))
{
return null;
}
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var detector = RegexPatternDetector.TryGetOrCreate(semanticModel, syntaxFacts, document.GetLanguageService<ISemanticFactsService>());
var tree = detector?.TryParseRegexPattern(token, document.GetLanguageService<IVirtualCharService>(), cancellationToken);
if (tree == null)
{
return null;
}
return GetMatchingBraces(tree, position);
}
private static BraceMatchingResult? GetMatchingBraces(RegexTree tree, int position)
{
var virtualChar = tree.Text.FirstOrNullable(vc => vc.Span.Contains(position));
if (virtualChar == null)
{
return null;
}
var ch = virtualChar.Value;
if (ch != '(' && ch != ')')
{
return null;
}
return FindBraceHighlights(tree, ch);
}
private static BraceMatchingResult? FindBraceHighlights(RegexTree tree, VirtualChar ch)
{
var node = FindGroupingNode(tree.Root, ch);
if (node == null)
{
return null;
}
if (node.OpenParenToken.IsMissing || node.CloseParenToken.IsMissing)
{
return null;
}
return new BraceMatchingResult(
node.OpenParenToken.VirtualChars[0].Span,
node.CloseParenToken.VirtualChars[0].Span);
}
private static RegexGroupingNode FindGroupingNode(RegexNode node, VirtualChar ch)
{
if (node is RegexGroupingNode grouping &&
(grouping.OpenParenToken.VirtualChars.Contains(ch) || grouping.CloseParenToken.VirtualChars.Contains(ch)))
{
return grouping;
}
foreach (var child in node)
{
if (child.IsNode)
{
var result = FindGroupingNode(child.Node, ch);
if (result != null)
{
return result;
}
}
}
return null;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
using Microsoft.CodeAnalysis.Classification;
namespace Microsoft.CodeAnalysis.Editor.UnitTests.Classification
{
public static partial class FormattedClassifications
{
public static class Regex
{
[DebuggerStepThrough]
public static FormattedClassification Anchor(string value) => New(value, ClassificationTypeNames.RegexAnchor);
[DebuggerStepThrough]
public static FormattedClassification Grouping(string value) => New(value, ClassificationTypeNames.RegexGrouping);
[DebuggerStepThrough]
public static FormattedClassification Escape(string value) => New(value, ClassificationTypeNames.RegexEscape);
[DebuggerStepThrough]
public static FormattedClassification Alternation(string value) => New(value, ClassificationTypeNames.RegexAlternation);
[DebuggerStepThrough]
public static FormattedClassification CharacterClass(string value) => New(value, ClassificationTypeNames.RegexCharacterClass);
[DebuggerStepThrough]
public static FormattedClassification Text(string value) => New(value, ClassificationTypeNames.RegexText);
[DebuggerStepThrough]
public static FormattedClassification Quantifier(string value) => New(value, ClassificationTypeNames.RegexQuantifier);
[DebuggerStepThrough]
public static FormattedClassification Comment(string value) => New(value, ClassificationTypeNames.RegexComment);
}
}
}
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Threading
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis.Editor.Implementation.BraceMatching
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.BraceMatching
<ExportBraceMatcher(LanguageNames.VisualBasic)>
Friend Class VisualBasicRegexBraceMatcher
Implements IBraceMatcher
Public Function FindBracesAsync(document As Document, position As Integer, Optional cancellationToken As CancellationToken = Nothing) As Task(Of BraceMatchingResult?) Implements IBraceMatcher.FindBracesAsync
Return CommonRegexBraceMatcher.FindBracesAsync(document, position, cancellationToken)
End Function
End Class
End Namespace
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Generic
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis.CodeFixes
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.RegularExpressions
Imports Microsoft.CodeAnalysis.VisualBasic.ValidateRegexString
Imports Roslyn.Test.Utilities
Imports Xunit
Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.ValidateRegexString
Public Class ValidateRegexStringTests
Inherits AbstractVisualBasicDiagnosticProviderBasedUserDiagnosticTest
Friend Overrides Function CreateDiagnosticProviderAndFixer(workspace As Workspace) As (DiagnosticAnalyzer, CodeFixProvider)
Return (New VisualBasicValidateRegexStringDiagnosticAnalyzer(), Nothing)
End Function
Private Function OptionOn() As IDictionary(Of OptionKey, Object)
Dim values = New Dictionary(Of OptionKey, Object)
values.Add(New OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.CSharp), True)
values.Add(New OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.VisualBasic), True)
Return values
End Function
Private Function OptionOff() As IDictionary(Of OptionKey, Object)
Dim values = New Dictionary(Of OptionKey, Object)
values.Add(New OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.CSharp), False)
values.Add(New OptionKey(RegularExpressionsOptions.ReportInvalidRegexPatterns, LanguageNames.VisualBasic), False)
Return values
End Function
<Fact, Trait(Traits.Feature, Traits.Features.ValidateRegexString)>
Public Async Function TestWarning1() As Task
Await TestDiagnosticInfoAsync("
imports System.Text.RegularExpressions
class Program
sub Main()
var r = new Regex(""[|)|]"")
end sub
end class",
options:=OptionOn(),
diagnosticId:=IDEDiagnosticIds.RegexPatternDiagnosticId,
diagnosticSeverity:=DiagnosticSeverity.Warning,
diagnosticMessage:=String.Format(FeaturesResources.Regex_issue_0, WorkspacesResources.Too_many_close_parens))
End Function
<Fact, Trait(Traits.Feature, Traits.Features.ValidateRegexString)>
Public Async Function TestWarning2() As Task
Await TestDiagnosticInfoAsync("
imports System.Text.RegularExpressions
class Program
sub Main()
var r = new Regex(""""""[|)|]"")
end sub
end class",
options:=OptionOn(),
diagnosticId:=IDEDiagnosticIds.RegexPatternDiagnosticId,
diagnosticSeverity:=DiagnosticSeverity.Warning,
diagnosticMessage:=String.Format(FeaturesResources.Regex_issue_0, WorkspacesResources.Too_many_close_parens))
End Function
End Class
End Namespace
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using Microsoft.CodeAnalysis.CSharp.VirtualChars;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.ValidateRegexString;
namespace Microsoft.CodeAnalysis.CSharp.ValidateRegexString
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class CSharpValidateRegexStringDiagnosticAnalyzer : AbstractValidateRegexStringDiagnosticAnalyzer
{
public CSharpValidateRegexStringDiagnosticAnalyzer()
: base((int)SyntaxKind.StringLiteralToken,
CSharpSyntaxFactsService.Instance,
CSharpSemanticFactsService.Instance,
CSharpVirtualCharService.Instance)
{
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.RegularExpressions;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.VirtualChars;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.DocumentHighlighting
{
internal abstract partial class AbstractDocumentHighlightsService : IDocumentHighlightsService
{
private async Task<ImmutableArray<DocumentHighlights>> TryGetRegexPatternHighlightsAsync(
Document document, int position, CancellationToken cancellationToken)
{
var option = document.Project.Solution.Workspace.Options.GetOption(RegularExpressionsOptions.HighlightRelatedRegexComponentsUnderCursor, document.Project.Language);
if (!option)
{
return default;
}
var root = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var token = root.FindToken(position);
var syntaxFacts = document.GetLanguageService<ISyntaxFactsService>();
if (RegexPatternDetector.IsDefinitelyNotPattern(token, syntaxFacts))
{
return default;
}
var semanticModel = await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var detector = RegexPatternDetector.TryGetOrCreate(semanticModel, syntaxFacts, document.GetLanguageService<ISemanticFactsService>());
var tree = detector?.TryParseRegexPattern(token, document.GetLanguageService<IVirtualCharService>(), cancellationToken);
if (tree == null)
{
return default;
}
return GetHighlights(document, tree, position);
}
private ImmutableArray<DocumentHighlights> GetHighlights(
Document document, RegexTree tree, int position)
{
var referencesOnTheRight = GetReferences(document, tree, position, caretOnLeft: true);
if (!referencesOnTheRight.IsEmpty)
{
return referencesOnTheRight;
}
// Nothing was on the right of the caret. Return anything we were able to find on
// the left of the caret
var referencesOnTheLeft = GetReferences(document, tree, position - 1, caretOnLeft: false);
return referencesOnTheLeft;
}
private ImmutableArray<DocumentHighlights> GetReferences(
Document document, RegexTree tree, int position, bool caretOnLeft)
{
var virtualChar = tree.Text.FirstOrNullable(vc => vc.Span.Contains(position));
if (virtualChar == null)
{
return ImmutableArray<DocumentHighlights>.Empty;
}
var ch = virtualChar.Value;
return FindReferenceHighlights(document, tree, ch);
}
private ImmutableArray<DocumentHighlights> FindReferenceHighlights(
Document document, RegexTree tree, VirtualChar ch)
{
var node = FindReferenceNode(tree.Root, ch);
if (node == null)
{
return ImmutableArray<DocumentHighlights>.Empty;
}
var captureToken = GetCaptureToken(node);
if (captureToken.Kind == RegexKind.NumberToken)
{
var val = (int)captureToken.Value;
if (tree.CaptureNumbersToSpan.TryGetValue(val, out var captureSpan))
{
return CreateHighlights(document, node, captureSpan);
}
}
else
{
var val = (string)captureToken.Value;
if (tree.CaptureNamesToSpan.TryGetValue(val, out var captureSpan))
{
return CreateHighlights(document, node, captureSpan);
}
}
return ImmutableArray<DocumentHighlights>.Empty;
}
private ImmutableArray<DocumentHighlights> CreateHighlights(
Document document, RegexEscapeNode node, TextSpan captureSpan)
{
return ImmutableArray.Create(new DocumentHighlights(document,
ImmutableArray.Create(
new HighlightSpan(RegexHelpers.GetSpan(node), HighlightSpanKind.None),
new HighlightSpan(captureSpan, HighlightSpanKind.None))));
}
private RegexToken GetCaptureToken(RegexEscapeNode node)
{
switch (node)
{
case RegexBackreferenceEscapeNode backReference:
return backReference.NumberToken;
case RegexCaptureEscapeNode captureEscape:
return captureEscape.CaptureToken;
case RegexKCaptureEscapeNode kCaptureEscape:
return kCaptureEscape.CaptureToken;
}
throw new InvalidOperationException();
}
private RegexEscapeNode FindReferenceNode(RegexNode node, VirtualChar virtualChar)
{
if (node.Kind == RegexKind.BackreferenceEscape ||
node.Kind == RegexKind.CaptureEscape ||
node.Kind == RegexKind.KCaptureEscape)
{
if (RegexHelpers.Contains(node, virtualChar))
{
return (RegexEscapeNode)node;
}
}
foreach (var child in node)
{
if (child.IsNode)
{
var result = FindReferenceNode(child.Node, virtualChar);
if (result != null)
{
return result;
}
}
}
return null;
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.RegularExpressions;
using Microsoft.CodeAnalysis.VirtualChars;
namespace Microsoft.CodeAnalysis.ValidateRegexString
{
internal abstract class AbstractValidateRegexStringDiagnosticAnalyzer : AbstractCodeStyleDiagnosticAnalyzer
{
private readonly int _stringLiteralKind;
private readonly ISyntaxFactsService _syntaxFacts;
private readonly ISemanticFactsService _semanticFacts;
private readonly IVirtualCharService _virtualCharService;
protected AbstractValidateRegexStringDiagnosticAnalyzer(
int stringLiteralKind,
ISyntaxFactsService syntaxFacts,
ISemanticFactsService semanticFacts,
IVirtualCharService virtualCharService)
: base(IDEDiagnosticIds.RegexPatternDiagnosticId,
new LocalizableResourceString(nameof(FeaturesResources.Regex_issue_0), FeaturesResources.ResourceManager, typeof(FeaturesResources)))
{
_stringLiteralKind = stringLiteralKind;
_syntaxFacts = syntaxFacts;
_semanticFacts = semanticFacts;
_virtualCharService = virtualCharService;
}
public override DiagnosticAnalyzerCategory GetAnalyzerCategory()
=> DiagnosticAnalyzerCategory.SemanticSpanAnalysis;
public override bool OpenFileOnly(Workspace workspace)
=> false;
protected override void InitializeWorker(AnalysisContext context)
=> context.RegisterSemanticModelAction(AnalyzeSemanticModel);
private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
{
var semanticModel = context.SemanticModel;
var syntaxTree = semanticModel.SyntaxTree;
var cancellationToken = context.CancellationToken;
var options = context.Options;
var optionSet = options.GetDocumentOptionSetAsync(syntaxTree, cancellationToken).GetAwaiter().GetResult();
if (optionSet == null)
{
return;
}
var option = optionSet.GetOption(RegularExpressionsOptions.ReportInvalidRegexPatterns, syntaxTree.Options.Language);
if (!option)
{
return;
}
var detector = RegexPatternDetector.TryGetOrCreate(semanticModel, _syntaxFacts, _semanticFacts);
if (detector == null)
{
return;
}
var root = syntaxTree.GetRoot(cancellationToken);
Analyze(context, detector, _virtualCharService, root, cancellationToken);
}
private void Analyze(
SemanticModelAnalysisContext context, RegexPatternDetector detector,
IVirtualCharService virtualCharService, SyntaxNode node, CancellationToken cancellationToken)
{
cancellationToken.ThrowIfCancellationRequested();
foreach (var child in node.ChildNodesAndTokens())
{
if (child.IsNode)
{
Analyze(context, detector, virtualCharService, child.AsNode(), cancellationToken);
}
else
{
var token = child.AsToken();
if (token.RawKind == _stringLiteralKind)
{
var tree = detector.TryParseRegexPattern(token, virtualCharService, cancellationToken);
if (tree != null)
{
foreach (var diag in tree.Diagnostics)
{
context.ReportDiagnostic(Diagnostic.Create(
this.GetDescriptorWithSeverity(DiagnosticSeverity.Warning),
Location.Create(context.SemanticModel.SyntaxTree, diag.Span),
diag.Message));
}
}
}
}
}
}
}
}
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports Microsoft.CodeAnalysis.Diagnostics
Imports Microsoft.CodeAnalysis.ValidateRegexString
Imports Microsoft.CodeAnalysis.VisualBasic.VirtualChars
Namespace Microsoft.CodeAnalysis.VisualBasic.ValidateRegexString
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend Class VisualBasicValidateRegexStringDiagnosticAnalyzer
Inherits AbstractValidateRegexStringDiagnosticAnalyzer
Public Sub New()
MyBase.New(SyntaxKind.StringLiteralToken,
VisualBasicSyntaxFactsService.Instance,
VisualBasicSemanticFactsService.Instance,
VisualBasicVirtualCharService.Instance)
End Sub
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册