提交 611ca93e 编写于 作者: M Manish Vasani

Merge remote-tracking branch 'upstream/master' into AnalyzerSpecificDiagnostics

......@@ -371,12 +371,7 @@ internal static bool IsStatementExpression(CSharpSyntaxNode syntax)
/// </summary>
public static bool IsLambdaBody(SyntaxNode node)
{
if (node == null)
{
return false;
}
var parent = node.Parent;
var parent = node?.Parent;
if (parent == null)
{
return false;
......
......@@ -23,19 +23,24 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Case SyntaxKind.WhereClause
Return DirectCast(newLambda, WhereClauseSyntax).Condition
' source sequence in From and Aggregate (other than the first in the query)
Case SyntaxKind.CollectionRangeVariable
Return DirectCast(newLambda, CollectionRangeVariableSyntax).Expression
' function call in Group By, Group Join, Aggregate: the argument
Case SyntaxKind.FunctionAggregation
Return DirectCast(newLambda, FunctionAggregationSyntax).Argument
' variable in Let, Select, Group By: the RHS
Case SyntaxKind.ExpressionRangeVariable
Return DirectCast(newLambda, ExpressionRangeVariableSyntax).Expression
Case SyntaxKind.TakeWhileClause, SyntaxKind.SkipWhileClause
Case SyntaxKind.TakeWhileClause,
SyntaxKind.SkipWhileClause
Return DirectCast(newLambda, PartitionWhileClauseSyntax).Condition
Case SyntaxKind.AscendingOrdering, SyntaxKind.DescendingOrdering
Case SyntaxKind.AscendingOrdering,
SyntaxKind.DescendingOrdering
Return DirectCast(newLambda, OrderingSyntax).Expression
Case SyntaxKind.JoinCondition
......
......@@ -5,6 +5,7 @@
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text;
......@@ -82,15 +83,15 @@ public override void Initialize(AnalysisContext context)
{
context.RegisterCompilationStartAction(compilationContext =>
{
AdditionalText publicApiAdditionalText = TryGetPublicApiSpec(compilationContext.Options.AdditionalFiles);
var publicApiSourceText = publicApiAdditionalText.GetText(compilationContext.CancellationToken);
AdditionalText publicApiAdditionalText = TryGetPublicApiSpec(compilationContext.Options.AdditionalFiles, compilationContext.CancellationToken);
if (publicApiAdditionalText == null)
{
return;
}
HashSet<string> declaredPublicSymbols = ReadPublicSymbols(publicApiSourceText);
SourceText publicApiSourceText = publicApiAdditionalText.GetText(compilationContext.CancellationToken);
HashSet<string> declaredPublicSymbols = ReadPublicSymbols(publicApiSourceText, compilationContext.CancellationToken);
HashSet<string> examinedPublicTypes = new HashSet<string>();
object lockObj = new object();
......@@ -203,12 +204,14 @@ internal static string GetPublicApiName(ISymbol symbol)
return null;
}
private static HashSet<string> ReadPublicSymbols(SourceText file)
private static HashSet<string> ReadPublicSymbols(SourceText file, CancellationToken cancellationToken)
{
HashSet<string> publicSymbols = new HashSet<string>();
foreach (var line in file.Lines)
{
cancellationToken.ThrowIfCancellationRequested();
var text = line.ToString();
if (!string.IsNullOrWhiteSpace(text))
......@@ -248,10 +251,12 @@ private static bool IsPublicOrPublicProtected(ISymbol symbol)
return false;
}
private static AdditionalText TryGetPublicApiSpec(ImmutableArray<AdditionalText> additionalTexts)
private static AdditionalText TryGetPublicApiSpec(ImmutableArray<AdditionalText> additionalTexts, CancellationToken cancellationToken)
{
foreach (var text in additionalTexts)
{
cancellationToken.ThrowIfCancellationRequested();
if (Path.GetFileName(text.Path).Equals(PublicApiFileName, StringComparison.OrdinalIgnoreCase))
{
return text;
......
......@@ -68,8 +68,13 @@ public void OnIdentifier(SyntaxNodeAnalysisContext context)
var info = context.SemanticModel.GetSymbolInfo(context.Node, context.CancellationToken);
var hasLocations = info.Symbol?.OriginalDefinition?.Locations.Length > 0;
if (!hasLocations)
{
return;
}
var inSource = info.Symbol?.OriginalDefinition?.Locations[0].IsInSource == true;
if (!hasLocations || !inSource || AccessibleFromOutside(info.Symbol.OriginalDefinition))
if (!inSource || AccessibleFromOutside(info.Symbol.OriginalDefinition))
{
return;
}
......
......@@ -6178,6 +6178,42 @@ static void Main(string[] args)
Diagnostic(RudeEditKind.RUDE_EDIT_LAMBDA_EXPRESSION, "b", "method"));
}
[Fact]
public void Lambdas_ActiveStatementRemoved4()
{
string src1 = @"
class C
{
static void Main(string[] args)
{
Func<int, Func<int, int>> f = a =>
{
<AS:1>z(2);</AS:1>
return b =>
{
<AS:0>return b;</AS:0>
};
};
}
}";
string src2 = @"
class C
{
static void Main(string[] args)
<AS:0,1>{</AS:0,1>
}
}
";
var edits = GetTopEdits(src1, src2);
var active = GetActiveStatements(src1, src2);
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "{", "lambda"),
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "{", "lambda"));
}
[Fact]
public void Queries_ActiveStatementRemoved_WhereClause()
{
......
......@@ -88,51 +88,48 @@ internal static Match<SyntaxNode> GetMethodMatch(string src1, string src2, Parse
return match;
}
internal static IEnumerable<Match<SyntaxNode>> GetMethodMatches(string src1, string src2, ParseOptions options = null, StateMachineKind stateMachine = StateMachineKind.None)
internal static IEnumerable<KeyValuePair<SyntaxNode, SyntaxNode>> GetMethodMatches(string src1, string src2, ParseOptions options = null, StateMachineKind stateMachine = StateMachineKind.None)
{
var methodMatch = GetMethodMatch(src1, src2, options, stateMachine);
var queue = new Queue<Match<SyntaxNode>>();
queue.Enqueue(methodMatch);
bool hasLambda;
Dictionary<SyntaxNode, AbstractEditAndContinueAnalyzer.LambdaInfo> lazyActiveOrMatchedLambdas = null;
var reverseMap = Analyzer.ComputeReverseMap(methodMatch, new AbstractEditAndContinueAnalyzer.ActiveNode[0], ref lazyActiveOrMatchedLambdas, new List<RudeEditDiagnostic>(), out hasLambda);
while (queue.Count > 0)
var result = new Dictionary<SyntaxNode, SyntaxNode>();
foreach (var pair in reverseMap)
{
var match = queue.Dequeue();
yield return match;
foreach (var m in match.Matches)
if (pair.Key == methodMatch.NewRoot)
{
if (m.Key == match.OldRoot)
{
Assert.Equal(match.NewRoot, m.Value);
continue;
}
foreach (var body in GetLambdaBodies(m.Key, m.Value))
{
var lambdaMatch = new StatementSyntaxComparer(body.Item1, body.Item2).ComputeMatch(m.Key, m.Value);
queue.Enqueue(lambdaMatch);
}
Assert.Same(pair.Value, methodMatch.OldRoot);
continue;
}
result.Add(pair.Value, pair.Key);
}
return result;
}
public static MatchingPairs ToMatchingPairs(Match<SyntaxNode> match)
{
return new MatchingPairs(ToMatchingPairs(match.Matches.Where(partners => partners.Key != match.OldRoot)));
return ToMatchingPairs(match.Matches.Where(partners => partners.Key != match.OldRoot));
}
public static MatchingPairs ToMatchingPairs(IEnumerable<Match<SyntaxNode>> match)
public static MatchingPairs ToMatchingPairs(IEnumerable<KeyValuePair<SyntaxNode, SyntaxNode>> matches)
{
return new MatchingPairs(ToMatchingPairs(match.SelectMany(m => m.Matches.Where(partners => partners.Key != m.OldRoot))));
return new MatchingPairs(matches
.OrderBy(partners => partners.Key.GetLocation().SourceSpan.Start)
.ThenByDescending(partners => partners.Key.Span.Length)
.Select(partners => new MatchingPair { Old = partners.Key.ToString().Replace("\r\n", " "), New = partners.Value.ToString().Replace("\r\n", " ") }));
}
private static IEnumerable<MatchingPair> ToMatchingPairs(IEnumerable<KeyValuePair<SyntaxNode, SyntaxNode>> matches)
private static IEnumerable<KeyValuePair<K, V>> ReverseMapping<K,V>(IEnumerable<KeyValuePair<V, K>> mapping)
{
return matches
.OrderBy(partners => partners.Key.GetLocation().SourceSpan.Start)
.ThenByDescending(partners => partners.Key.Span.Length)
.Select(partners => new MatchingPair { Old = partners.Key.ToString().Replace("\r\n", " "), New = partners.Value.ToString().Replace("\r\n", " ") });
foreach (var pair in mapping)
{
yield return KeyValuePair.Create(pair.Value, pair.Key);
}
}
internal static BlockSyntax MakeMethodBody(
......@@ -183,46 +180,6 @@ internal static SyntaxMapDescription GetSyntaxMap(string oldSource, string newSo
return new SyntaxMapDescription(oldSource, newSource);
}
private static ImmutableArray<ValueTuple<SyntaxNode, SyntaxNode>> GetLambdaBodies(SyntaxNode oldNode, SyntaxNode newNode)
{
switch (oldNode.Kind())
{
case SyntaxKind.ParenthesizedLambdaExpression:
case SyntaxKind.SimpleLambdaExpression:
case SyntaxKind.AnonymousMethodExpression:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((AnonymousFunctionExpressionSyntax)oldNode).Body, ((AnonymousFunctionExpressionSyntax)newNode).Body));
case SyntaxKind.FromClause:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((FromClauseSyntax)oldNode).Expression, ((FromClauseSyntax)newNode).Expression));
case SyntaxKind.LetClause:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((LetClauseSyntax)oldNode).Expression, ((LetClauseSyntax)newNode).Expression));
case SyntaxKind.WhereClause:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((WhereClauseSyntax)oldNode).Condition, ((WhereClauseSyntax)newNode).Condition));
case SyntaxKind.AscendingOrdering:
case SyntaxKind.DescendingOrdering:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((OrderingSyntax)oldNode).Expression, ((OrderingSyntax)newNode).Expression));
case SyntaxKind.SelectClause:
return ImmutableArray.Create(ValueTuple.Create<SyntaxNode, SyntaxNode>(((SelectClauseSyntax)oldNode).Expression, ((SelectClauseSyntax)newNode).Expression));
case SyntaxKind.JoinClause:
return ImmutableArray.Create(
ValueTuple.Create<SyntaxNode, SyntaxNode>(((JoinClauseSyntax)oldNode).LeftExpression, ((JoinClauseSyntax)newNode).LeftExpression),
ValueTuple.Create<SyntaxNode, SyntaxNode>(((JoinClauseSyntax)oldNode).RightExpression, ((JoinClauseSyntax)newNode).RightExpression));
case SyntaxKind.GroupClause:
return ImmutableArray.Create(
ValueTuple.Create<SyntaxNode, SyntaxNode>(((GroupClauseSyntax)oldNode).GroupExpression, ((GroupClauseSyntax)newNode).ByExpression),
ValueTuple.Create<SyntaxNode, SyntaxNode>(((GroupClauseSyntax)oldNode).GroupExpression, ((GroupClauseSyntax)newNode).ByExpression));
default:
return ImmutableArray<ValueTuple<SyntaxNode, SyntaxNode>>.Empty;
}
}
internal static void VerifyPreserveLocalVariables(EditScript<SyntaxNode> edits, bool preserveLocalVariables)
{
var decl1 = (MethodDeclarationSyntax)((ClassDeclarationSyntax)((CompilationUnitSyntax)edits.Match.OldRoot).Members[0]).Members[0];
......
......@@ -1816,6 +1816,34 @@ class C
new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.Main"), preserveLocalVariables: false) });
}
[Fact(Skip = "TODO: Enable lambda edits")]
public void MethodWithLambda_Update()
{
string src1 = @"
class C
{
static void F()
{
Func<int> a = () => 1;
}
}
";
string src2 = @"
class C
{
static void F()
{
Func<int> a = () => 1;
Console.WriteLine(1);
}
}";
var edits = GetTopEdits(src1, src2);
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
new[] { SemanticEdit(SemanticEditKind.Update, c => c.GetMember("C.F"), preserveLocalVariables: true) });
}
[Fact]
public void MethodUpdate_LocalVariableDeclaration()
{
......
......@@ -294,6 +294,15 @@ internal class EditorFeaturesResources {
}
}
/// <summary>
/// Looks up a localized string similar to Cannot apply operation while a rename session is active..
/// </summary>
internal static string CannotApplyOperationWhileRenameSessionIsActive {
get {
return ResourceManager.GetString("CannotApplyOperationWhileRenameSessionIsActive", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Cannot navigate to the symbol under the caret..
/// </summary>
......
......@@ -730,4 +730,7 @@ Do you want to proceed?</value>
<data name="FixAllOccurrences" xml:space="preserve">
<value>Fix all occurrences</value>
</data>
<data name="CannotApplyOperationWhileRenameSessionIsActive" xml:space="preserve">
<value>Cannot apply operation while a rename session is active.</value>
</data>
</root>
\ No newline at end of file
......@@ -11,6 +11,7 @@
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.Notification;
namespace Microsoft.CodeAnalysis.Editor.Implementation.CodeActions
{
......@@ -88,6 +89,14 @@ public SolutionPreviewResult GetPreviews(Workspace workspace, IEnumerable<CodeAc
public void Apply(Workspace workspace, Document fromDocument, IEnumerable<CodeActionOperation> operations, string title, CancellationToken cancellationToken)
{
if (_renameService.ActiveSession != null)
{
workspace.Services.GetService<INotificationService>()?.SendNotification(
EditorFeaturesResources.CannotApplyOperationWhileRenameSessionIsActive,
severity: NotificationSeverity.Error);
return;
}
#if DEBUG
var documentErrorLookup = new HashSet<DocumentId>();
foreach (var project in workspace.CurrentSolution.Projects)
......
......@@ -3,6 +3,7 @@
using System.Collections.Generic;
using System.Threading;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Notification;
namespace Microsoft.CodeAnalysis.Editor
{
......
......@@ -14,6 +14,7 @@
using Microsoft.VisualStudio.Language.Intellisense;
using Microsoft.VisualStudio.Text;
using Roslyn.Utilities;
using Microsoft.CodeAnalysis.Notification;
namespace Microsoft.CodeAnalysis.Editor.Implementation.Suggestions
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text.RegularExpressions;
using Microsoft.CodeAnalysis.Text;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.EditAndContinue.UnitTests
{
......@@ -45,23 +47,23 @@ internal static string ClearTags(string source)
}
private static readonly Regex s_tags = new Regex(
@"[<][/]?(AS|ER|N|TS)[:][.0-9]+[>]",
@"[<][/]?(AS|ER|N|TS)[:][.0-9,]+[>]",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline);
private static readonly Regex s_activeStatementPattern = new Regex(
@"[<]AS[:] (?<Id>[0-9]+) [>]
@"[<]AS[:] (?<Id>[0-9,]+) [>]
(?<ActiveStatement>.*)
[<][/]AS[:] (\k<Id>) [>]",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline);
public static readonly Regex ExceptionRegionPattern = new Regex(
@"[<]ER[:] (?<Id>[0-9]+[.][0-9]+) [>]
@"[<]ER[:] (?<Id>(?:[0-9]+[.][0-9]+[,]?)+) [>]
(?<ExceptionRegion>.*)
[<][/]ER[:] (\k<Id>) [>]",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline);
private static readonly Regex s_trackingStatementPattern = new Regex(
@"[<]TS[:] (?<Id>[0-9]+) [>]
@"[<]TS[:] (?<Id>[0-9,]+) [>]
(?<TrackingStatement>.*)
[<][/]TS[:] (\k<Id>) [>]",
RegexOptions.IgnorePatternWhitespace | RegexOptions.Singleline);
......@@ -84,6 +86,18 @@ internal static ActiveStatementSpan[] GetActiveStatements(string src)
return result.ToArray();
}
internal static IEnumerable<int> GetIds(Match match)
{
return match.Groups["Id"].Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Select(int.Parse);
}
internal static IEnumerable<ValueTuple<int, int>> GetDottedIds(Match match)
{
return from ids in match.Groups["Id"].Value.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)
let parts = ids.Split('.')
select ValueTuple.Create(int.Parse(parts[0]), int.Parse(parts[1]));
}
internal static TextSpan[] GetActiveSpans(string src)
{
var matches = s_activeStatementPattern.Matches(src);
......@@ -92,10 +106,11 @@ internal static TextSpan[] GetActiveSpans(string src)
for (int i = 0; i < matches.Count; i++)
{
var stmt = matches[i].Groups["ActiveStatement"];
var id = int.Parse(matches[i].Groups["Id"].Value);
EnsureSlot(result, id);
result[id] = new TextSpan(stmt.Index, stmt.Length);
foreach (int id in GetIds(matches[i]))
{
EnsureSlot(result, id);
result[id] = new TextSpan(stmt.Index, stmt.Length);
}
}
return result.ToArray();
......@@ -114,8 +129,10 @@ internal static TextSpan[] GetActiveSpans(string src)
for (int i = 0; i < matches.Count; i++)
{
var span = matches[i].Groups["TrackingStatement"];
var id = int.Parse(matches[i].Groups["Id"].Value);
result[id] = new TextSpan(span.Index, span.Length);
foreach (int id in GetIds(matches[i]))
{
result[id] = new TextSpan(span.Index, span.Length);
}
}
return result;
......@@ -128,18 +145,21 @@ internal static ImmutableArray<TextSpan>[] GetExceptionRegions(string src, int a
for (int i = 0; i < matches.Count; i++)
{
var stmt = matches[i].Groups["ExceptionRegion"];
var id = matches[i].Groups["Id"].Value.Split('.');
var asid = int.Parse(id[0]);
var erid = int.Parse(id[1]);
var exceptionRegion = matches[i].Groups["ExceptionRegion"];
if (result[asid] == null)
foreach (var id in GetDottedIds(matches[i]))
{
result[asid] = new List<TextSpan>();
}
var activeStatementId = id.Item1;
var exceptionRegionId = id.Item2;
if (result[activeStatementId] == null)
{
result[activeStatementId] = new List<TextSpan>();
}
EnsureSlot(result[asid], erid);
result[asid][erid] = new TextSpan(stmt.Index, stmt.Length);
EnsureSlot(result[activeStatementId], exceptionRegionId);
result[activeStatementId][exceptionRegionId] = new TextSpan(exceptionRegion.Index, exceptionRegion.Length);
}
}
return result.Select(r => r.AsImmutableOrEmpty()).ToArray();
......
......@@ -93,7 +93,7 @@ internal abstract class EditAndContinueTestHelpers
var diagnostics = new List<RudeEditDiagnostic>();
var actualNewActiveStatements = new LinePositionSpan[oldActiveStatements.Length];
var actualNewExceptionRegions = new ImmutableArray<LinePositionSpan>[oldActiveStatements.Length];
var updatedActiveMethodMatches = new List<ValueTuple<int, Match<SyntaxNode>>>();
var updatedActiveMethodMatches = new List<ValueTuple<int, IReadOnlyDictionary<SyntaxNode, SyntaxNode>>>();
var editMap = Analyzer.BuildEditMap(editScript);
DocumentId documentId = DocumentId.CreateNewId(ProjectId.CreateNewId("TestEnCProject"), "TestEnCDocument");
......@@ -239,7 +239,7 @@ internal abstract class EditAndContinueTestHelpers
var newModel = newCompilation.GetSemanticModel(newRoot.SyntaxTree);
var oldActiveStatements = activeStatements.OldSpans.AsImmutable();
var updatedActiveMethodMatches = new List<ValueTuple<int, Match<SyntaxNode>>>();
var updatedActiveMethodMatches = new List<ValueTuple<int, IReadOnlyDictionary<SyntaxNode, SyntaxNode>>>();
var triviaEdits = new List<KeyValuePair<SyntaxNode, SyntaxNode>>();
var actualLineEdits = new List<LineChange>();
var actualSemanticEdits = new List<SemanticEdit>();
......
......@@ -320,6 +320,7 @@
<Compile Include="Simplification\TypeInferenceSimplifierTests.vb" />
<Compile Include="Simplification\TypeNameSimplifierTest.vb" />
<Compile Include="Utilities\AssertEx.vb" />
<Compile Include="Utilities\TestNotificationService.vb" />
<Compile Include="Workspaces\SymbolDescriptionServiceTests.vb" />
<Compile Include="Workspaces\TryFindSourceDefinitionTests.vb" />
</ItemGroup>
......
' 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 Microsoft.CodeAnalysis.CodeActions
Imports Microsoft.CodeAnalysis.CodeRefactorings
Imports Microsoft.CodeAnalysis.CSharp.CodeRefactorings.IntroduceVariable
Imports Microsoft.CodeAnalysis.Editor.Host
Imports Microsoft.CodeAnalysis.Editor.UnitTests.RenameTracking
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Notification
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.Rename
Imports Microsoft.VisualStudio.Text
......@@ -984,5 +990,84 @@ End Class
VerifyTagsAreCorrect(workspace, "maw")
End Using
End Sub
<Fact>
<Trait(Traits.Feature, Traits.Features.Rename)>
<Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)>
<WorkItem(554, "https://github.com/dotnet/roslyn/issues/554")>
Public Sub CodeActionCannotCommitDuringInlineRename()
Using workspace = CreateWorkspaceWithWaiter(
<Workspace>
<Project Language="C#" CommonReferences="true" AssemblyName="CSProj">
<Document FilePath="C.cs">
class C
{
void M()
{
var z = {|introducelocal:5 + 5|};
var q = [|x$$|];
}
int [|x|];
}</Document>
</Project>
</Workspace>)
Dim session = StartSession(workspace)
' Type a bit in the file
Dim caretPosition = workspace.Documents.First(Function(d) d.CursorPosition.HasValue).CursorPosition.Value
Dim textBuffer = workspace.Documents.First().TextBuffer
textBuffer.Insert(caretPosition, "yz")
WaitForRename(workspace)
' Invoke a CodeAction
Dim introduceVariableRefactoringProvider = New IntroduceVariableCodeRefactoringProvider()
Dim actions = New List(Of CodeAction)
Dim context = New CodeRefactoringContext(
workspace.CurrentSolution.GetDocument(workspace.Documents.Single().Id),
workspace.Documents.Single().AnnotatedSpans()("introducelocal").Single(),
Sub(a) actions.Add(a),
CancellationToken.None)
workspace.Documents.Single().AnnotatedSpans.Clear()
introduceVariableRefactoringProvider.ComputeRefactoringsAsync(context).Wait()
Dim editHandler = workspace.ExportProvider.GetExportedValue(Of ICodeActionEditHandlerService)
Dim actualSeverity As NotificationSeverity = Nothing
Dim notificationService = DirectCast(workspace.Services.GetService(Of INotificationService)(), INotificationServiceCallback)
notificationService.NotificationCallback = Sub(message, title, severity) actualSeverity = severity
editHandler.Apply(
workspace,
workspace.CurrentSolution.GetDocument(workspace.Documents.Single().Id),
actions.First().GetOperationsAsync(CancellationToken.None).Result,
"unused",
CancellationToken.None)
' CodeAction should be rejected
Assert.Equal(NotificationSeverity.Error, actualSeverity)
Assert.Equal("
class C
{
void M()
{
var z = 5 + 5;
var q = xyz;
}
int xyz;
}",
textBuffer.CurrentSnapshot.GetText())
' Rename should still be active
VerifyTagsAreCorrect(workspace, "xyz")
textBuffer.Insert(caretPosition + 2, "q")
WaitForRename(workspace)
VerifyTagsAreCorrect(workspace, "xyzq")
End Using
End Sub
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 Microsoft.CodeAnalysis.Notification
Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Utilities
Friend Class TestNotificationService
Implements INotificationService
Public MessageText As String
Public MessageTitle As String
Public MessageSeverity As NotificationSeverity
Public ConfirmBoxText As String
Public ConfirmBoxTitle As String
Public ConfirmBoxSeverity As NotificationSeverity
Public DesiredConfirmBoxResult As Boolean
Public Sub SendNotification(message As String, Optional title As String = Nothing, Optional severity As NotificationSeverity = NotificationSeverity.Warning) Implements INotificationService.SendNotification
MessageText = message
MessageTitle = title
MessageSeverity = severity
End Sub
Public Function ConfirmMessageBox(message As String, Optional title As String = Nothing, Optional severity As NotificationSeverity = NotificationSeverity.Warning) As Boolean Implements INotificationService.ConfirmMessageBox
ConfirmBoxText = message
ConfirmBoxTitle = title
ConfirmBoxSeverity = severity
Return DesiredConfirmBoxResult
End Function
End Class
End Namespace
......@@ -4166,6 +4166,34 @@ End Class
Diagnostic(RudeEditKind.RUDE_EDIT_LAMBDA_EXPRESSION, "Function(b)", "method"))
End Sub
<Fact>
Public Sub Lambdas_ActiveStatementRemoved4()
Dim src1 = "
Class C
Shared Sub Main()
Dim f = Function(a)
<AS:1>z(2)</AS:1>
return Function (b)
<AS:0>Return b</AS:0>
End Function
End Function
End Sub
End Class"
Dim src2 = "
Class C
<AS:0,1>Shared Sub Main()</AS:0,1>
End Sub
End Class
"
Dim edits = GetTopEdits(src1, src2)
Dim active = GetActiveStatements(src1, src2)
edits.VerifyRudeDiagnostics(active,
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "Shared Sub Main()", "lambda"),
Diagnostic(RudeEditKind.ActiveStatementLambdaRemoved, "Shared Sub Main()", "lambda"))
End Sub
<Fact>
Public Sub Queries_ActiveStatementRemoved_WhereClause()
Dim src1 = "
......
......@@ -2001,6 +2001,30 @@ End Class
Diagnostic(RudeEditKind.AwaitStatementUpdate, "a += Await F(1)"))
End Sub
<Fact(Skip:="TODO: Enable lambda edits")>
Public Sub MethodWithLambda_Update()
Dim src1 = "
Class C
Shared Sub F()
Dim a = Function() 1
End Sub
End Class
"
Dim src2 = "
Class C
Shared Sub F()
Dim a = Function() 1
Console.WriteLine(1)
End Sub
End Class
"
Dim edits = GetTopEdits(src1, src2)
edits.VerifySemantics(
ActiveStatementsDescription.Empty,
{SemanticEdit(SemanticEditKind.Update, Function(c) c.GetMember("C.F"), preserveLocalVariables:=True)})
End Sub
<Fact>
Public Sub MethodUpdate_AddAttribute()
Dim src1 = "Class C : " & vbLf & "Sub F() : End Sub : End Class"
......
......@@ -839,6 +839,21 @@ private static bool IsGetterToExpressionBodyTransformation(EditKind editKind, Sy
return false;
}
internal override bool ContainsLambda(SyntaxNode declaration)
{
return declaration.DescendantNodes().Any(SyntaxUtilities.IsLambda);
}
internal override bool IsLambda(SyntaxNode node)
{
return SyntaxUtilities.IsLambda(node);
}
internal override bool TryGetLambdaBodies(SyntaxNode node, out SyntaxNode body1, out SyntaxNode body2)
{
return SyntaxUtilities.TryGetLambdaBodies(node, out body1, out body2);
}
#endregion
#region Diagnostic Info
......
......@@ -162,6 +162,11 @@ public static bool IsNotLambda(SyntaxNode node)
return !IsLambda(node.Kind());
}
public static bool IsLambda(SyntaxNode node)
{
return IsLambda(node.Kind());
}
public static bool IsLambda(SyntaxKind kind)
{
switch (kind)
......@@ -187,6 +192,62 @@ public static bool IsLambda(SyntaxKind kind)
return false;
}
public static bool TryGetLambdaBodies(SyntaxNode node, out SyntaxNode body1, out SyntaxNode body2)
{
body1 = null;
body2 = null;
switch (node.Kind())
{
case SyntaxKind.ParenthesizedLambdaExpression:
case SyntaxKind.SimpleLambdaExpression:
case SyntaxKind.AnonymousMethodExpression:
body1 = ((AnonymousFunctionExpressionSyntax)node).Body;
return true;
case SyntaxKind.FromClause:
var fromClause = (FromClauseSyntax)node;
if (!(fromClause.Parent is QueryBodySyntax))
{
return false;
}
body1 = fromClause.Expression;
return true;
case SyntaxKind.JoinClause:
var joinClause = (JoinClauseSyntax)node;
body1 = joinClause.LeftExpression;
body2 = joinClause.RightExpression;
return true;
case SyntaxKind.LetClause:
body1 = ((LetClauseSyntax)node).Expression;
return true;
case SyntaxKind.WhereClause:
body1 = ((WhereClauseSyntax)node).Condition;
return true;
case SyntaxKind.AscendingOrdering:
case SyntaxKind.DescendingOrdering:
body1 = ((OrderingSyntax)node).Expression;
return true;
case SyntaxKind.SelectClause:
body1 = ((SelectClauseSyntax)node).Expression;
return true;
case SyntaxKind.GroupClause:
var groupClause = (GroupClauseSyntax)node;
body1 = groupClause.GroupExpression;
body2 = groupClause.ByExpression;
return true;
}
return false;
}
public static bool Any(TypeParameterListSyntax listOpt)
{
return listOpt != null && listOpt.ChildNodesAndTokens().Count != 0;
......@@ -279,31 +340,10 @@ public static bool HasBackingField(PropertyDeclarationSyntax property)
public static bool IsAsyncMethodOrLambda(SyntaxNode declaration)
{
if (declaration.IsKind(SyntaxKind.ParenthesizedLambdaExpression))
{
var lambda = (ParenthesizedLambdaExpressionSyntax)declaration;
if (lambda.AsyncKeyword.IsKind(SyntaxKind.AsyncKeyword))
{
return true;
}
}
if (declaration.IsKind(SyntaxKind.SimpleLambdaExpression))
{
var lambda = (SimpleLambdaExpressionSyntax)declaration;
if (lambda.AsyncKeyword.IsKind(SyntaxKind.AsyncKeyword))
{
return true;
}
}
if (declaration.IsKind(SyntaxKind.AnonymousMethodExpression))
var anonymousFunction = declaration as AnonymousFunctionExpressionSyntax;
if (anonymousFunction != null)
{
var lambda = (AnonymousMethodExpressionSyntax)declaration;
if (lambda.AsyncKeyword.IsKind(SyntaxKind.AsyncKeyword))
{
return true;
}
return anonymousFunction.AsyncKeyword.IsKind(SyntaxKind.AsyncKeyword);
}
// expression bodied methods:
......
' Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis.Differencing
Imports Microsoft.CodeAnalysis.EditAndContinue
Imports Microsoft.CodeAnalysis.Host
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.Text
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
......@@ -16,8 +9,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
<Conditional("DEBUG")>
Public Shared Sub AssertIsBody(syntax As SyntaxNode, allowLambda As Boolean)
' lambda/query
Dim body As SyntaxNode = Nothing
If IsLambdaBodyStatement(syntax, body) AndAlso syntax Is body Then
If IsLambdaBody(syntax) Then
Debug.Assert(allowLambda)
Debug.Assert(TypeOf syntax Is ExpressionSyntax OrElse TypeOf syntax Is LambdaHeaderSyntax)
Return
......@@ -60,58 +52,85 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Debug.Assert(False)
End Sub
Public Shared Function IsLambdaBodyStatement(node As SyntaxNode, <Out> ByRef body As SyntaxNode) As Boolean
Dim parent = node.Parent
Public Shared Function IsLambdaBody(node As SyntaxNode) As Boolean
Dim body As SyntaxNode = Nothing
Return IsLambdaBodyStatementOrExpression(node, body) AndAlso node Is body
End Function
''' <summary>
''' Returns true if the specified <paramref name="node"/> is a statement whose parent is a <see cref="LambdaExpressionSyntax"/>,
''' or and expression of a query lambda. Returns the node that represents the body of the lambda in <paramref name="body"/>.
''' </summary>
Public Shared Function IsLambdaBodyStatementOrExpression(node As SyntaxNode, <Out> Optional ByRef body As SyntaxNode = Nothing) As Boolean
Dim parent = node?.Parent
If parent Is Nothing Then
body = Nothing
Return False
End If
body = node
Select Case parent.Kind
Dim body1 As SyntaxNode = Nothing
Dim body2 As SyntaxNode = Nothing
Dim result = TryGetLambdaBodies(parent, body1, body2)
body = If(node Is body2, body2, body1)
Return result
End Function
Public Shared Function TryGetLambdaBodies(node As SyntaxNode, <Out> ByRef body1 As SyntaxNode, <Out> ByRef body2 As SyntaxNode) As Boolean
body1 = Nothing
body2 = Nothing
Select Case node.Kind
Case SyntaxKind.MultiLineFunctionLambdaExpression,
SyntaxKind.SingleLineFunctionLambdaExpression,
SyntaxKind.MultiLineSubLambdaExpression,
SyntaxKind.SingleLineSubLambdaExpression
' The header of the lambda represents its body.
body = DirectCast(parent, LambdaExpressionSyntax).SubOrFunctionHeader
body1 = DirectCast(node, LambdaExpressionSyntax).SubOrFunctionHeader
Return True
Case SyntaxKind.WhereClause
Dim whereClause = DirectCast(parent, WhereClauseSyntax)
Return whereClause.Condition Is node
body1 = DirectCast(node, WhereClauseSyntax).Condition
Return True
' source sequence in From and Aggregate (other than the first in the query)
Case SyntaxKind.CollectionRangeVariable
Dim collectionRange = DirectCast(parent, CollectionRangeVariableSyntax)
Return collectionRange.Expression Is node AndAlso Not IsFirstInQuery(collectionRange)
Dim collectionRange = DirectCast(node, CollectionRangeVariableSyntax)
If IsFirstInQuery(collectionRange) Then
Return False
End If
body1 = collectionRange.Expression
Return True
' function call in Group By, Group Join, Aggregate: the argument
Case SyntaxKind.FunctionAggregation
Dim functionAggregation = DirectCast(parent, FunctionAggregationSyntax)
Return functionAggregation.Argument Is node
body1 = DirectCast(node, FunctionAggregationSyntax).Argument
Return True
' variable in Let, Select, Group By: the RHS
Case SyntaxKind.ExpressionRangeVariable
Dim expressionRange = DirectCast(parent, ExpressionRangeVariableSyntax)
Return expressionRange.Expression Is node
body1 = DirectCast(node, ExpressionRangeVariableSyntax).Expression
Return True
Case SyntaxKind.TakeWhileClause,
SyntaxKind.SkipWhileClause
Dim partitionWhileClause = DirectCast(parent, PartitionWhileClauseSyntax)
Return partitionWhileClause.Condition Is node
body1 = DirectCast(node, PartitionWhileClauseSyntax).Condition
Return True
Case SyntaxKind.AscendingOrdering,
SyntaxKind.DescendingOrdering
Dim ordering = DirectCast(parent, OrderingSyntax)
Return ordering.Expression Is node
body1 = DirectCast(node, OrderingSyntax).Expression
Return True
Case SyntaxKind.JoinCondition
Dim joinCondition = DirectCast(parent, JoinConditionSyntax)
Return joinCondition.Left Is node OrElse joinCondition.Right Is node
Dim joinCondition = DirectCast(node, JoinConditionSyntax)
body1 = joinCondition.Left
body2 = joinCondition.Right
Return True
End Select
Debug.Assert(Not SyntaxUtilities.IsLambda(parent.Kind))
Debug.Assert(Not IsLambda(node))
Return False
End Function
......@@ -192,6 +211,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Return Not IsLambda(node.Kind())
End Function
Public Shared Function IsLambda(node As SyntaxNode) As Boolean
Return IsLambda(node.Kind())
End Function
Public Shared Function IsLambda(kind As SyntaxKind) As Boolean
Select Case kind
Case SyntaxKind.MultiLineFunctionLambdaExpression,
......
......@@ -375,10 +375,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Debug.Assert(node IsNot Nothing)
Dim lambdaBody As SyntaxNode = Nothing
While node IsNot declarationBody AndAlso
Not StatementSyntaxComparer.HasLabel(node.Kind) AndAlso
Not SyntaxUtilities.IsLambdaBodyStatement(node, lambdaBody)
Not SyntaxUtilities.IsLambdaBodyStatementOrExpression(node)
node = node.Parent
If partnerOpt IsNot Nothing Then
......@@ -408,7 +407,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
While node IsNot root
Dim body As SyntaxNode = Nothing
If SyntaxUtilities.IsLambdaBodyStatement(node, body) Then
If SyntaxUtilities.IsLambdaBodyStatementOrExpression(node, body) Then
Return body
End If
......@@ -880,6 +879,17 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Return model.GetDeclaredSymbol(node, cancellationToken)
End Function
Friend Overrides Function ContainsLambda(declaration As SyntaxNode) As Boolean
Return declaration.DescendantNodes().Any(AddressOf SyntaxUtilities.IsLambda)
End Function
Friend Overrides Function IsLambda(node As SyntaxNode) As Boolean
Return SyntaxUtilities.IsLambda(node)
End Function
Friend Overrides Function TryGetLambdaBodies(node As SyntaxNode, ByRef body1 As SyntaxNode, ByRef body2 As SyntaxNode) As Boolean
Return SyntaxUtilities.TryGetLambdaBodies(node, body1, body2)
End Function
#End Region
#Region "Diagnostic Info"
......@@ -2720,8 +2730,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.EditAndContinue
Return node
End Select
Dim lambdaBody As SyntaxNode = Nothing
If SyntaxUtilities.IsLambdaBodyStatement(node, lambdaBody) Then
If SyntaxUtilities.IsLambdaBodyStatementOrExpression(node) Then
Return node
End If
......
......@@ -12,6 +12,7 @@ Imports Microsoft.CodeAnalysis.ChangeSignature
Imports Microsoft.CodeAnalysis.Shared.Extensions
Imports Roslyn.Test.Utilities
Imports Microsoft.VisualStudio.LanguageServices.Implementation.ChangeSignature
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities
Namespace Microsoft.VisualStudio.LanguageServices.UnitTests.ChangeSignature
Public Class ReorderParametersViewModelTests
......@@ -287,33 +288,5 @@ class MyClass
Return New ChangeSignatureViewModelTestState(viewModel, symbol.GetParameters())
End Using
End Function
Friend Class TestNotificationService
Implements INotificationService
Public MessageText As String
Public MessageTitle As String
Public MessageSeverity As NotificationSeverity
Public ConfirmBoxText As String
Public ConfirmBoxTitle As String
Public ConfirmBoxSeverity As NotificationSeverity
Public DesiredConfirmBoxResult As Boolean
Public Sub SendNotification(message As String, Optional title As String = Nothing, Optional severity As NotificationSeverity = NotificationSeverity.Warning) Implements INotificationService.SendNotification
MessageText = message
MessageTitle = title
MessageSeverity = severity
End Sub
Public Function ConfirmMessageBox(message As String, Optional title As String = Nothing, Optional severity As NotificationSeverity = NotificationSeverity.Warning) As Boolean Implements INotificationService.ConfirmMessageBox
ConfirmBoxText = message
ConfirmBoxTitle = title
ConfirmBoxSeverity = severity
Return DesiredConfirmBoxResult
End Function
End Class
End Class
End Namespace
......@@ -3,6 +3,7 @@
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.LanguageServices
Imports Microsoft.CodeAnalysis.Notification
......@@ -484,16 +485,5 @@ public class $$MyClass
fileExtension:=If(languageName = LanguageNames.CSharp, ".cs", ".vb"))
End Using
End Function
Friend Class TestNotificationService
Implements INotificationService
Public Sub SendNotification(message As String, Optional title As String = Nothing, Optional severity As NotificationSeverity = NotificationSeverity.Warning) Implements INotificationService.SendNotification
End Sub
Public Function ConfirmMessageBox(message As String, Optional title As String = Nothing, Optional severity As NotificationSeverity = NotificationSeverity.Warning) As Boolean Implements INotificationService.ConfirmMessageBox
Throw New NotImplementedException()
End Function
End Class
End Class
End Namespace
......@@ -4,6 +4,7 @@ Imports System.IO
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Extensions
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Utilities
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.GeneratedCodeRecognition
Imports Microsoft.CodeAnalysis.GenerateType
......@@ -882,17 +883,6 @@ namespace A
End Function
End Class
Friend Class TestNotificationService
Implements INotificationService
Public Sub SendNotification(message As String, Optional title As String = Nothing, Optional severity As NotificationSeverity = NotificationSeverity.Warning) Implements INotificationService.SendNotification
End Sub
Public Function ConfirmMessageBox(message As String, Optional title As String = Nothing, Optional severity As NotificationSeverity = NotificationSeverity.Warning) As Boolean Implements INotificationService.ConfirmMessageBox
Throw New NotImplementedException()
End Function
End Class
Friend Class TestProjectManagementService
Implements IProjectManagementService
......
......@@ -338,7 +338,7 @@ public TNode NewRoot
}
}
public IEnumerable<KeyValuePair<TNode, TNode>> Matches
public IReadOnlyDictionary<TNode, TNode> Matches
{
get
{
......@@ -346,6 +346,14 @@ public TNode NewRoot
}
}
public IReadOnlyDictionary<TNode, TNode> ReverseMatches
{
get
{
return new ReadOnlyDictionary<TNode, TNode>(_twoToOne);
}
}
public bool TryGetNewNode(TNode oldNode, out TNode newNode)
{
return _oneToTwo.TryGetValue(oldNode, out newNode);
......
......@@ -121,6 +121,7 @@ Microsoft.CodeAnalysis.Differencing.Match<TNode>.GetTreeEdits()
Microsoft.CodeAnalysis.Differencing.Match<TNode>.Matches.get
Microsoft.CodeAnalysis.Differencing.Match<TNode>.NewRoot.get
Microsoft.CodeAnalysis.Differencing.Match<TNode>.OldRoot.get
Microsoft.CodeAnalysis.Differencing.Match<TNode>.ReverseMatches.get
Microsoft.CodeAnalysis.Differencing.Match<TNode>.TryGetNewNode(TNode oldNode, out TNode newNode)
Microsoft.CodeAnalysis.Differencing.Match<TNode>.TryGetOldNode(TNode newNode, out TNode oldNode)
Microsoft.CodeAnalysis.Differencing.TreeComparer<TNode>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册