未验证 提交 170d0bb7 编写于 作者: D dotnet-automerge-bot 提交者: GitHub

Merge pull request #31307 from dotnet/merges/master-to-master-vs-deps

Merge master to master-vs-deps
......@@ -36,7 +36,30 @@ try {
# Verify the state of our generated syntax files
Write-Host "Checking generated compiler files"
Exec-Block { & (Join-Path $PSScriptRoot "generate-compiler-code.ps1") -test }
Write-Host ""
# Verfiy the state of creating run settings for optprof
Write-Host "Checking run generation for optprof"
# set environment variables
if (-not (Test-Path env:SYSTEM_TEAMPROJECT)) { $env:SYSTEM_TEAMPROJECT = "DevDiv" }
if (-not (Test-Path env:BUILD_REPOSITORY_NAME)) { $env:BUILD_REPOSITORY_NAME = "dotnet/roslyn" }
if (-not (Test-Path env:BUILD_SOURCEBRANCHNAME)) { $env:BUILD_SOURCEBRANCHNAME = "test" }
if (-not (Test-Path env:BUILD_BUILDID)) { $env:BUILD_BUILDID = "42.42.42.42" }
if (-not (Test-Path env:BUILD_SOURCESDIRECTORY)) { $env:BUILD_SOURCESDIRECTORY = $RepoRoot }
if (-not (Test-Path env:BUILD_STAGINGDIRECTORY)) { $env:BUILD_STAGINGDIRECTORY = $configDir }
# create a fake BootstrapperInfo.json file
$bootstrapperInfoFolder = Join-Path $configDir "MicroBuild\Output"
Create-Directory $bootstrapperInfoFolder
$bootstrapperInfoPath = Join-Path $bootstrapperInfoFolder "BootstrapperInfo.json"
$bootstrapperInfoContent = "[{""VSBuildVersion"": ""42.42.42424.42""}]"
$bootstrapperInfoContent >> $bootstrapperInfoPath
# generate run settings
Exec-Block { & (Join-Path $PSScriptRoot "createrunsettings.ps1") }
exit 0
}
catch [exception] {
......
......@@ -8,7 +8,6 @@
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace;
using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.SyncNamespace;
using Microsoft.CodeAnalysis.Editor.UnitTests.CodeActions;
using Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces;
......@@ -26,7 +25,7 @@ public abstract class CSharpSyncNamespaceTestsBase : AbstractCodeActionTest
protected override string GetLanguage() => LanguageNames.CSharp;
protected override CodeRefactoringProvider CreateCodeRefactoringProvider(Workspace workspace, TestParameters parameters)
=> new SyncNamespaceCodeRefactoringProvider();
=> new CSharpSyncNamespaceCodeRefactoringProvider();
protected override TestWorkspace CreateWorkspaceFromFile(string initialMarkup, TestParameters parameters)
{
......@@ -102,8 +101,8 @@ protected async Task TestMoveFileToMatchNamespace(string initialMarkup, List<str
var (actions, _) = await GetCodeActionsAsync(workspace, testOptions);
if (actions.Length > 0)
{
var renameFileAction = actions.Any(action => action is CSharpSyncNamespaceService.MoveFileCodeAction);
Assert.False(renameFileAction, "Rename File to match type code action was not expected, but shows up.");
var renameFileAction = actions.Any(action => !(action is CodeAction.SolutionChangeAction));
Assert.False(renameFileAction, "Move File to match namespace code action was not expected, but shows up.");
}
}
}
......@@ -116,7 +115,7 @@ protected async Task TestMoveFileToMatchNamespace(string initialMarkup, List<str
var results = new List<Tuple<Solution, Solution>>();
var (actions, _) = await GetCodeActionsAsync(workspace, parameters);
var moveFileActions = actions.Where(a => a is CSharpSyncNamespaceService.MoveFileCodeAction);
var moveFileActions = actions.Where(a => !(a is CodeAction.SolutionChangeAction));
foreach (var action in moveFileActions)
{
......@@ -207,7 +206,7 @@ protected async Task TestMoveFileToMatchNamespace(string initialMarkup, List<str
var (actions, _) = await GetCodeActionsAsync(workspace, testOptions);
if (actions.Length > 0)
{
var hasChangeNamespaceAction = actions.Any(action => action is CSharpSyncNamespaceService.ChangeNamespaceCodeAction);
var hasChangeNamespaceAction = actions.Any(action => action is CodeAction.SolutionChangeAction);
Assert.False(hasChangeNamespaceAction, "Change namespace to match folder action was not expected, but shows up.");
}
}
......@@ -216,7 +215,7 @@ protected async Task TestMoveFileToMatchNamespace(string initialMarkup, List<str
async Task<Tuple<Solution, Solution>> TestOperationAsync(TestParameters parameters, TestWorkspace workspace)
{
var (actions, _) = await GetCodeActionsAsync(workspace, parameters);
var changeNamespaceAction = actions.Single(a => a is CSharpSyncNamespaceService.ChangeNamespaceCodeAction);
var changeNamespaceAction = actions.Single(a => a is CodeAction.SolutionChangeAction);
var operations = await changeNamespaceAction.GetOperationsAsync(CancellationToken.None);
return ApplyOperationsAndGetSolution(workspace, operations);
......
......@@ -6276,5 +6276,22 @@ class X
}",
Documentation("Summary for event Goo"));
}
[WorkItem(30642, "https://github.com/dotnet/roslyn/issues/30642")]
[Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)]
public async Task BuiltInOperatorWithUserDefinedEquivalent()
{
await TestAsync(
@"
class X
{
void N(string a, string b)
{
var v = a $$== b;
}
}",
MainDescription("bool string.operator ==(string a, string b)"),
SymbolGlyph(Glyph.Operator));
}
}
}
......@@ -56,7 +56,7 @@ private IInlineRenameInfo GetRenameInfo(Document document, int position, Cancell
Document document, SyntaxToken triggerToken, CancellationToken cancellationToken)
{
var syntaxFactsService = document.GetLanguageService<ISyntaxFactsService>();
if (syntaxFactsService.IsKeyword(triggerToken))
if (syntaxFactsService.IsReservedOrContextualKeyword(triggerToken))
{
return new FailureInlineRenameInfo(EditorFeaturesResources.You_must_rename_an_identifier);
}
......
......@@ -186,6 +186,114 @@ Class A
End Class
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WorkItem(30642, "https://github.com/dotnet/roslyn/issues/30642")>
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestCSharpFindReferencesOnBuiltInOperatorWithUserDefinedEquivalent() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class A
{
void Goo(string a, string b, int x, int y)
{
var m = a $$[|==|] b;
var n = a [|==|] b;
var o = x == y;
}
}
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WorkItem(30642, "https://github.com/dotnet/roslyn/issues/30642")>
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestVisualBasicFindReferencesOnBuiltInOperatorWithUserDefinedEquivalent() As Task
Dim input =
<Workspace>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
class A
sub Goo(a as string, b as string, x as integer, y as integer)
dim m = a $$[|=|] b
dim n = a [|=|] b
dim o = x = y
end sub
end class
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WorkItem(30642, "https://github.com/dotnet/roslyn/issues/30642")>
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestCrossLanguageFindReferencesOnBuiltInOperatorWithUserDefinedEquivalent_FromCSharp() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class A
{
void Goo(string a, string b, int x, int y)
{
var m = a $$[|==|] b;
var n = a [|==|] b;
var o = x == y;
}
}
</Document>
</Project>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
class A
sub Goo(a as string, b as string, x as integer, y as integer)
dim m = a [|=|] b
dim n = a [|=|] b
dim o = x = y
end sub
end class
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
<WorkItem(30642, "https://github.com/dotnet/roslyn/issues/30642")>
<WpfFact, Trait(Traits.Feature, Traits.Features.FindReferences)>
Public Async Function TestCrossLanguageFindReferencesOnBuiltInOperatorWithUserDefinedEquivalent_FromVisualBasic() As Task
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class A
{
void Goo(string a, string b, int x, int y)
{
var m = a [|==|] b;
var n = a [|==|] b;
var o = x == y;
}
}
</Document>
</Project>
<Project Language="Visual Basic" CommonReferences="true">
<Document>
class A
sub Goo(a as string, b as string, x as integer, y as integer)
dim m = a $$[|=|] b
dim n = a [|=|] b
dim o = x = y
end sub
end class
</Document>
</Project>
</Workspace>
Await TestAPIAndFeature(input)
End Function
......
......@@ -2370,6 +2370,20 @@ End Class",
MainDescription("Function C2.ViewData() As C1"))
End Function
<WorkItem(30642, "https://github.com/dotnet/roslyn/issues/30642")>
<Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)>
Public Async Function BuiltInOperatorWithUserDefinedEquivalent() As Task
Await TestAsync(
"
class X
sub N(a as string, b as string)
dim v = a $$= b
end sub
end class",
MainDescription("Operator String.=(a As String, b As String) As Boolean"),
SymbolGlyph(Glyph.Operator))
End Function
<WorkItem(29703, "https://github.com/dotnet/roslyn/issues/29703")>
<Fact, Trait(Traits.Feature, Traits.Features.QuickInfo)>
Public Async Function TestGetAccessorDocumentation() As Task
......
......@@ -4,24 +4,20 @@
using System.Composition;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace;
using Microsoft.CodeAnalysis.CSharp.CodeGeneration;
using Microsoft.CodeAnalysis.ChangeNamespace;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.Host.Mef;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.SyncNamespace
namespace Microsoft.CodeAnalysis.CSharp.ChangeNamespace
{
[ExportLanguageService(typeof(ISyncNamespaceService), LanguageNames.CSharp), Shared]
internal sealed class CSharpSyncNamespaceService :
AbstractSyncNamespaceService<NamespaceDeclarationSyntax, CompilationUnitSyntax, MemberDeclarationSyntax>
[ExportLanguageService(typeof(IChangeNamespaceService), LanguageNames.CSharp), Shared]
internal sealed class CSharpChangeNamespaceService :
AbstractChangeNamespaceService<NamespaceDeclarationSyntax, CompilationUnitSyntax, MemberDeclarationSyntax>
{
protected override SyntaxList<MemberDeclarationSyntax> GetMemberDeclarationsInContainer(SyntaxNode compilationUnitOrNamespaceDecl)
{
......@@ -138,72 +134,6 @@ protected override SyntaxList<MemberDeclarationSyntax> GetMemberDeclarationsInCo
return false;
}
protected override string EscapeIdentifier(string identifier)
=> identifier.EscapeIdentifier();
protected override async Task<SyntaxNode> ShouldPositionTriggerRefactoringAsync(Document document, int position, CancellationToken cancellationToken)
{
var compilationUnit = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false) as CompilationUnitSyntax;
// Here's conditions that trigger the refactoring (all have to be true in each scenario):
//
// - There's only one namespace declaration in the document and all types are declared in it:
// 1. No nested namespace declaration (even it's empty).
// 2. The cursor is on the name of the namespace declaration.
// 3. The name of the namespace is valid (i.e. no errors).
// 4. No partial type declared in the namespace. Otherwise its multiple declaration will
// end up in different namespace.
//
// - There's no namespace declaration and all types in the document are declared in global namespace:
// 1. The cursor is on the name of first declared type.
// 2. No partial type declared in the document. Otherwise its multiple declaration will
// end up in different namespace.
var triggeringNode = GetTriggeringNode(compilationUnit, position);
if (triggeringNode != null)
{
var containsPartial = await ContainsPartialTypeWithMultipleDeclarationsAsync(document, triggeringNode, cancellationToken)
.ConfigureAwait(false);
if (!containsPartial)
{
return triggeringNode;
}
}
return default;
SyntaxNode GetTriggeringNode(CompilationUnitSyntax compUnit, int pos)
{
var namespaceDecls = compilationUnit.DescendantNodes(n => n is CompilationUnitSyntax || n is NamespaceDeclarationSyntax)
.OfType<NamespaceDeclarationSyntax>().ToImmutableArray();
if (namespaceDecls.Length == 1 && compilationUnit.Members.Count == 1)
{
var namespaceDeclaration = namespaceDecls[0];
Debug.Assert(namespaceDeclaration == compilationUnit.Members[0]);
if (namespaceDeclaration.Name.Span.IntersectsWith(position)
&& namespaceDeclaration.Name.GetDiagnostics().All(diag => diag.DefaultSeverity != DiagnosticSeverity.Error))
{
return namespaceDeclaration;
}
}
else if (namespaceDecls.Length == 0)
{
var firstMemberDeclarationName = compilationUnit.Members.FirstOrDefault().GetNameToken();
if (firstMemberDeclarationName != default
&& firstMemberDeclarationName.Span.IntersectsWith(position))
{
return compilationUnit;
}
}
return null;
}
}
/// <summary>
/// Try to change the namespace declaration based on the refacoring rules:
/// - if neither declared and target namespace are "" (i.e. global 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 System.Collections.Immutable;
using System.Composition;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.SyncNamespace
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.SyncNamespace), Shared]
internal sealed class CSharpSyncNamespaceCodeRefactoringProvider
: AbstractSyncNamespaceCodeRefactoringProvider<NamespaceDeclarationSyntax, CompilationUnitSyntax, MemberDeclarationSyntax>
{
protected override async Task<SyntaxNode> TryGetApplicableInvocationNode(Document document, int position, CancellationToken cancellationToken)
{
var compilationUnit = await document.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false) as CompilationUnitSyntax;
// Here's conditions that trigger the refactoring (all have to be true in each scenario):
//
// - There's only one namespace declaration in the document and all types are declared in it:
// 1. No nested namespace declaration (even it's empty).
// 2. The cursor is on the name of the namespace declaration.
// 3. The name of the namespace is valid (i.e. no errors).
// 4. No partial type declared in the namespace. Otherwise its multiple declaration will
// end up in different namespace.
//
// - There's no namespace declaration and all types in the document are declared in global namespace:
// 1. The cursor is on the name of first declared type.
// 2. No partial type declared in the document. Otherwise its multiple declaration will
// end up in different namespace.
var triggeringNode = GetTriggeringNode(compilationUnit, position);
if (triggeringNode != null)
{
var containsPartial = await ContainsPartialTypeWithMultipleDeclarationsAsync(document, triggeringNode, cancellationToken)
.ConfigureAwait(false);
if (!containsPartial)
{
return triggeringNode;
}
}
return default;
}
private static SyntaxNode GetTriggeringNode(CompilationUnitSyntax compilationUnit, int position)
{
var namespaceDecls = compilationUnit.DescendantNodes(n => n is CompilationUnitSyntax || n is NamespaceDeclarationSyntax)
.OfType<NamespaceDeclarationSyntax>().ToImmutableArray();
if (namespaceDecls.Length == 1 && compilationUnit.Members.Count == 1)
{
var namespaceDeclaration = namespaceDecls[0];
Debug.Assert(namespaceDeclaration == compilationUnit.Members[0]);
if (namespaceDeclaration.Name.Span.IntersectsWith(position)
&& namespaceDeclaration.Name.GetDiagnostics().All(diag => diag.DefaultSeverity != DiagnosticSeverity.Error))
{
return namespaceDeclaration;
}
}
else if (namespaceDecls.Length == 0)
{
var firstMemberDeclarationName = compilationUnit.Members.FirstOrDefault().GetNameToken();
if (firstMemberDeclarationName != default
&& firstMemberDeclarationName.Span.IntersectsWith(position))
{
return compilationUnit;
}
}
return null;
}
protected override SyntaxList<MemberDeclarationSyntax> GetMemberDeclarationsInContainer(SyntaxNode compilationUnitOrNamespaceDecl)
{
if (compilationUnitOrNamespaceDecl is NamespaceDeclarationSyntax namespaceDecl)
{
return namespaceDecl.Members;
}
else if (compilationUnitOrNamespaceDecl is CompilationUnitSyntax compilationUnit)
{
return compilationUnit.Members;
}
throw ExceptionUtilities.Unreachable;
}
protected override string EscapeIdentifier(string identifier)
=> identifier.EscapeIdentifier();
}
}
......@@ -14,12 +14,13 @@
namespace Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace
{
internal abstract partial class AbstractSyncNamespaceService<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax>
internal abstract partial class AbstractSyncNamespaceCodeRefactoringProvider<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax>
: CodeRefactoringProvider
where TNamespaceDeclarationSyntax : SyntaxNode
where TCompilationUnitSyntax : SyntaxNode
where TMemberDeclarationSyntax : SyntaxNode
{
internal sealed class MoveFileCodeAction : CodeAction
private class MoveFileCodeAction : CodeAction
{
private readonly State _state;
private readonly ImmutableArray<string> _newfolders;
......
......@@ -16,7 +16,8 @@
namespace Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace
{
internal abstract partial class AbstractSyncNamespaceService<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax>
internal abstract partial class AbstractSyncNamespaceCodeRefactoringProvider<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax>
: CodeRefactoringProvider
where TNamespaceDeclarationSyntax : SyntaxNode
where TCompilationUnitSyntax : SyntaxNode
where TMemberDeclarationSyntax : SyntaxNode
......@@ -153,7 +154,7 @@ private static string GetDefaultNamespace(ImmutableArray<Document> documents, IS
private static async Task<(bool shouldTrigger, string declaredNamespace)> TryGetNamespaceDeclarationAsync(
TextSpan textSpan,
ImmutableArray<Document> documents,
AbstractSyncNamespaceService<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax> service,
AbstractSyncNamespaceCodeRefactoringProvider<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax> provider,
CancellationToken cancellationToken)
{
// If the cursor location doesn't meet the requirement to trigger the refactoring in any of the documents
......@@ -167,7 +168,7 @@ private static string GetDefaultNamespace(ImmutableArray<Document> documents, IS
{
foreach (var document in documents)
{
var compilationUnitOrNamespaceDeclOpt = await service.ShouldPositionTriggerRefactoringAsync(document, textSpan.Start, cancellationToken)
var compilationUnitOrNamespaceDeclOpt = await provider.TryGetApplicableInvocationNode(document, textSpan.Start, cancellationToken)
.ConfigureAwait(false);
if (compilationUnitOrNamespaceDeclOpt is TNamespaceDeclarationSyntax namespaceDeclaration)
......@@ -208,7 +209,7 @@ private static string GetDefaultNamespace(ImmutableArray<Document> documents, IS
}
public static async Task<State> CreateAsync(
AbstractSyncNamespaceService<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax> service,
AbstractSyncNamespaceCodeRefactoringProvider<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax> provider,
Document document,
TextSpan textSpan,
CancellationToken cancellationToken)
......@@ -237,7 +238,7 @@ private static string GetDefaultNamespace(ImmutableArray<Document> documents, IS
}
var (shouldTrigger, declaredNamespace) =
await TryGetNamespaceDeclarationAsync(textSpan, documents, service, cancellationToken).ConfigureAwait(false);
await TryGetNamespaceDeclarationAsync(textSpan, documents, provider, cancellationToken).ConfigureAwait(false);
if (!shouldTrigger)
{
......@@ -246,7 +247,7 @@ private static string GetDefaultNamespace(ImmutableArray<Document> documents, IS
// Namespace can't be changed if we can't construct a valid qualified identifier from folder names.
// In this case, we might still be able to provide refactoring to move file to new location.
var namespaceFromFolders = TryBuildNamespaceFromFolders(service, document.Folders, syntaxFacts);
var namespaceFromFolders = TryBuildNamespaceFromFolders(provider, document.Folders, syntaxFacts);
var targetNamespace = namespaceFromFolders == null
? null
: ConcatNamespace(defaultNamespace, namespaceFromFolders);
......@@ -277,7 +278,7 @@ private static string GetDefaultNamespace(ImmutableArray<Document> documents, IS
/// Create a qualified identifier as the suffix of namespace based on a list of folder names.
/// </summary>
private static string TryBuildNamespaceFromFolders(
AbstractSyncNamespaceService<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax> service,
AbstractSyncNamespaceCodeRefactoringProvider<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax> service,
IEnumerable<string> folders,
ISyntaxFactsService syntaxFacts)
{
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.ChangeNamespace;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using static Microsoft.CodeAnalysis.CodeActions.CodeAction;
namespace Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace
{
internal abstract partial class AbstractSyncNamespaceService<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax> :
ISyncNamespaceService
internal abstract partial class AbstractSyncNamespaceCodeRefactoringProvider<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax>
: CodeRefactoringProvider
where TNamespaceDeclarationSyntax : SyntaxNode
where TCompilationUnitSyntax : SyntaxNode
where TMemberDeclarationSyntax : SyntaxNode
{
public async Task<ImmutableArray<CodeAction>> GetRefactoringsAsync(
Document document, TextSpan textSpan, CancellationToken cancellationToken)
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var document = context.Document;
var textSpan = context.Span;
var cancellationToken = context.CancellationToken;
var state = await State.CreateAsync(this, document, textSpan, cancellationToken).ConfigureAwait(false);
if (state == null)
{
return default;
return;
}
return CreateCodeActions(this, state);
}
public abstract bool TryGetReplacementReferenceSyntax(
SyntaxNode reference, ImmutableArray<string> newNamespaceParts, ISyntaxFactsService syntaxFacts, out SyntaxNode old, out SyntaxNode @new);
protected abstract string EscapeIdentifier(string identifier);
protected abstract TCompilationUnitSyntax ChangeNamespaceDeclaration(
TCompilationUnitSyntax root, ImmutableArray<string> declaredNamespaceParts, ImmutableArray<string> targetNamespaceParts);
// No move file action if rootnamespace isn't a prefix of current declared namespace
if (state.RelativeDeclaredNamespace != null)
{
context.RegisterRefactorings(MoveFileCodeAction.Create(state));
}
protected abstract SyntaxList<TMemberDeclarationSyntax> GetMemberDeclarationsInContainer(SyntaxNode compilationUnitOrNamespaceDecl);
// No change namespace action if we can't construct a valid namespace from rootnamespace and folder names.
if (state.TargetNamespace != null)
{
var service = document.GetLanguageService<IChangeNamespaceService>();
var solutionChangeAction = new SolutionChangeAction(ChangeNamespaceActionTitle(state), token => service.ChangeNamespaceAsync(state.Solution, state.DocumentIds, state.DeclaredNamespace, state.TargetNamespace, token));
context.RegisterRefactoring(solutionChangeAction);
}
}
/// <summary>
/// Determine if this refactoring should be triggered based on current cursor position and if there's any partial
......@@ -49,15 +50,15 @@ internal abstract partial class AbstractSyncNamespaceService<TNamespaceDeclarati
/// (2) in the name of first declaration in global namespace if there's no namespace declaration in this document.
/// </summary>
/// <returns>
/// If the refactoring should be triggered, then returns the only namespace declaration node in the document (or type
/// If the refactoring should be triggered, then returns the only namespace declaration node in the document (of type
/// <typeparamref name="TNamespaceDeclarationSyntax"/>) or the compilation unit node (of type <typeparamref name="TCompilationUnitSyntax"/>)
/// if no namespace declaration in the document. Otherwise, return null.
/// </returns>
protected abstract Task<SyntaxNode> ShouldPositionTriggerRefactoringAsync(Document document, int position, CancellationToken cancellationToken);
protected abstract Task<SyntaxNode> TryGetApplicableInvocationNode(Document document, int position, CancellationToken cancellationToken);
protected abstract string EscapeIdentifier(string identifier);
protected static SyntaxAnnotation WarningAnnotation { get; }
= CodeActions.WarningAnnotation.Create(
FeaturesResources.Warning_colon_changing_namespace_may_produce_invalid_code_and_change_code_meaning);
protected abstract SyntaxList<TMemberDeclarationSyntax> GetMemberDeclarationsInContainer(SyntaxNode compilationUnitOrNamespaceDecl);
protected async Task<bool> ContainsPartialTypeWithMultipleDeclarationsAsync(
Document document, SyntaxNode compilationUnitOrNamespaceDecl, CancellationToken cancellationToken)
......@@ -81,6 +82,11 @@ internal abstract partial class AbstractSyncNamespaceService<TNamespaceDeclarati
return false;
}
private static string ChangeNamespaceActionTitle(State state)
=> state.TargetNamespace.Length == 0
? FeaturesResources.Change_to_global_namespace
: string.Format(FeaturesResources.Change_namespace_to_0, state.TargetNamespace);
/// <summary>
/// Try get the relative namespace for <paramref name="namespace"/> based on <paramref name="relativeTo"/>,
/// if <paramref name="relativeTo"/> is the containing namespace of <paramref name="namespace"/>.
......@@ -115,25 +121,5 @@ private static string GetRelativeNamespace(string relativeTo, string @namespace,
? @namespace.Substring(relativeTo.Length + 1)
: null;
}
private static ImmutableArray<CodeAction> CreateCodeActions(
AbstractSyncNamespaceService<TNamespaceDeclarationSyntax, TCompilationUnitSyntax, TMemberDeclarationSyntax> service, State state)
{
var builder = ArrayBuilder<CodeAction>.GetInstance();
// No move file action if rootnamespace isn't a prefix of current declared namespace
if (state.RelativeDeclaredNamespace != null)
{
builder.AddRange(MoveFileCodeAction.Create(state));
}
// No change namespace action if we can't construct a valid namespace from rootnamespace and folder names.
if (state.TargetNamespace != null)
{
builder.Add(new ChangeNamespaceCodeAction(service, state));
}
return builder.ToImmutableAndFree();
}
}
}
// 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.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.ChangeNamespace
{
internal interface IChangeNamespaceService : ILanguageService
{
Task<Solution> ChangeNamespaceAsync(Solution solution, ImmutableArray<DocumentId> documentIds, string declaredNamespace, string targetNamespace, CancellationToken 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.Immutable;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.Host;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Text;
namespace Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace
{
internal interface ISyncNamespaceService : ILanguageService
{
Task<ImmutableArray<CodeAction>> GetRefactoringsAsync(Document document, TextSpan textSpan, CancellationToken cancellationToken);
/// <summary>
/// Try to get a new node to replace given node, which is a reference to a top-level type declared inside the
/// namespce to be changed. If this reference is the right side of a qualified name, the new node returned would
/// be the entire qualified name. Depends on whether <paramref name="newNamespaceParts"/> is provided, the name
/// in the new node might be qualified with this new namespace instead.
/// </summary>
/// <param name="reference">A reference to a type declared inside the namespce to be changed, which is calculated
/// based on results from `SymbolFinder.FindReferencesAsync`.</param>
/// <param name="newNamespaceParts">If specified, the namespace of original reference will be replaced with given
/// namespace in the replacement node.</param>
/// <param name="old">The node to be replaced. This might be an ancestor of original </param>
/// <param name="new">The replacement node.</param>
bool TryGetReplacementReferenceSyntax(
SyntaxNode reference,
ImmutableArray<string> newNamespaceParts,
ISyntaxFactsService syntaxFacts,
out SyntaxNode old,
out SyntaxNode @new);
}
}
// 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.Composition;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.SyncNamespace), Shared]
internal sealed class SyncNamespaceCodeRefactoringProvider : CodeRefactoringProvider
{
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var document = context.Document;
var textSpan = context.Span;
var cancellationToken = context.CancellationToken;
var service = document.GetLanguageService<ISyncNamespaceService>();
var actions = await service.GetRefactoringsAsync(document, textSpan, cancellationToken).ConfigureAwait(false);
context.RegisterRefactorings(actions);
}
}
}
......@@ -93,11 +93,14 @@ public static Glyph GetGlyph(this ISymbol symbol)
{
var methodSymbol = (IMethodSymbol)symbol;
if (methodSymbol.MethodKind == MethodKind.UserDefinedOperator || methodSymbol.MethodKind == MethodKind.Conversion)
if (methodSymbol.MethodKind == MethodKind.UserDefinedOperator ||
methodSymbol.MethodKind == MethodKind.Conversion ||
methodSymbol.MethodKind == MethodKind.BuiltinOperator)
{
return Glyph.Operator;
}
else if (methodSymbol.IsExtensionMethod || methodSymbol.MethodKind == MethodKind.ReducedExtension)
else if (methodSymbol.IsExtensionMethod ||
methodSymbol.MethodKind == MethodKind.ReducedExtension)
{
publicIcon = Glyph.ExtensionMethodPublic;
}
......
......@@ -2,16 +2,15 @@
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Threading
Imports Microsoft.CodeAnalysis.CodeRefactorings.SyncNamespace
Imports Microsoft.CodeAnalysis.ChangeNamespace
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.LanguageServices
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.SyncNamespace
<ExportLanguageService(GetType(ISyncNamespaceService), LanguageNames.VisualBasic), [Shared]>
Friend Class VisualBasicSyncNamespaceService
Inherits AbstractSyncNamespaceService(Of NamespaceStatementSyntax, CompilationUnitSyntax, StatementSyntax)
Namespace Microsoft.CodeAnalysis.VisualBasic.ChangeNamespace
<ExportLanguageService(GetType(IChangeNamespaceService), LanguageNames.VisualBasic), [Shared]>
Friend Class VisualBasicChangeNamespaceService
Inherits AbstractChangeNamespaceService(Of NamespaceStatementSyntax, CompilationUnitSyntax, StatementSyntax)
Public Overrides Function TryGetReplacementReferenceSyntax(reference As SyntaxNode, newNamespaceParts As ImmutableArray(Of String), syntaxFacts As ISyntaxFactsService, ByRef old As SyntaxNode, ByRef [new] As SyntaxNode) As Boolean
Dim nameRef = TryCast(reference, SimpleNameSyntax)
......@@ -37,10 +36,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.SyncNamespace
End If
End Function
Protected Overrides Function EscapeIdentifier(identifier As String) As String
Return identifier.EscapeIdentifier()
End Function
' This is only reachable when called from a VB refacoring provider, which is not implemented yet.
Protected Overrides Function ChangeNamespaceDeclaration(root As CompilationUnitSyntax, declaredNamespaceParts As ImmutableArray(Of String), targetNamespaceParts As ImmutableArray(Of String)) As CompilationUnitSyntax
Throw ExceptionUtilities.Unreachable
......@@ -51,11 +46,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CodeRefactorings.SyncNamespace
Throw ExceptionUtilities.Unreachable
End Function
' This is only reachable when called from a VB refacoring provider, which is not implemented yet.
Protected Overrides Function ShouldPositionTriggerRefactoringAsync(document As Document, position As Integer, cancellationToken As CancellationToken) As Task(Of SyntaxNode)
Throw ExceptionUtilities.Unreachable
End Function
Private Function CreateNameSyntax(namespaceParts As ImmutableArray(Of String), index As Integer) As NameSyntax
Dim part = namespaceParts(index).EscapeIdentifier()
Dim namePiece = SyntaxFactory.IdentifierName(part)
......
......@@ -8,6 +8,7 @@
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Formatting;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.Simplification;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.Collections;
using Microsoft.VisualStudio.LanguageServices.Implementation.CodeModel.InternalElements;
......@@ -541,7 +542,7 @@ internal EnvDTE.CodeElement CodeElementFromPosition(int position, EnvDTE.vsCMEle
// If both tokens are touching, we prefer identifiers and keywords to
// separators. Note that the language doesn't allow both tokens to be a
// keyword or identifier.
if (SyntaxFactsService.IsKeyword(rightToken) ||
if (SyntaxFactsService.IsReservedOrContextualKeyword(rightToken) ||
SyntaxFactsService.IsIdentifier(rightToken))
{
token = rightToken;
......
......@@ -81,31 +81,14 @@ public bool IsOperator(SyntaxToken token)
(SyntaxFacts.IsAssignmentExpressionOperatorToken(kind) && token.Parent is AssignmentExpressionSyntax);
}
public bool IsKeyword(SyntaxToken token)
{
var kind = (SyntaxKind)token.RawKind;
return
SyntaxFacts.IsKeywordKind(kind); // both contextual and reserved keywords
}
public bool IsReservedKeyword(string text)
{
return SyntaxFacts.GetKeywordKind(text) != SyntaxKind.None; // reserved keywords only
}
public bool IsReservedKeyword(SyntaxToken token)
=> SyntaxFacts.IsReservedKeyword(token.Kind());
public bool IsContextualKeyword(SyntaxToken token)
{
var kind = (SyntaxKind)token.RawKind;
return
SyntaxFacts.IsContextualKeyword(kind);
}
=> SyntaxFacts.IsContextualKeyword(token.Kind());
public bool IsPreprocessorKeyword(SyntaxToken token)
{
var kind = (SyntaxKind)token.RawKind;
return
SyntaxFacts.IsPreprocessorKeyword(kind);
}
=> SyntaxFacts.IsPreprocessorKeyword(token.Kind());
public bool IsHashToken(SyntaxToken token)
{
......
......@@ -31,29 +31,48 @@ internal interface ISyntaxFactsService : ILanguageService
bool IsPredefinedType(SyntaxToken token, PredefinedType type);
bool IsPredefinedOperator(SyntaxToken token);
bool IsPredefinedOperator(SyntaxToken token, PredefinedOperator op);
/// <summary>
/// Determine if <paramref name="token"/> is a keyword. i.e. both reserved and contextual.
/// For example:
/// IsKeyword("class") == true
/// IsKeyword("async") == true
/// Returns 'true' if this a 'reserved' keyword for the language. A 'reserved' keyword is a
/// identifier that is always treated as being a special keyword, regardless of where it is
/// found in the token stream. Examples of this are tokens like <see langword="class"/> and
/// <see langword="Class"/> in C# and VB respectively.
///
/// Importantly, this does *not* include contextual keywords. If contextual keywords are
/// important for your scenario, use <see cref="IsContextualKeyword"/> or <see
/// cref="ISyntaxFactsServiceExtensions.IsReservedOrContextualKeyword"/>. Also, consider using
/// <see cref="ISyntaxFactsServiceExtensions.IsWord"/> if all you need is the ability to know
/// if this is effectively any identifier in the language, regardless of whether the language
/// is treating it as a keyword or not.
/// </summary>
bool IsKeyword(SyntaxToken token);
bool IsReservedKeyword(SyntaxToken token);
/// <summary>
/// Determine if <paramref name="text"/> is a reserved keyword. i.e. not contextual.
/// For example:
/// IsReservedKeyword("class") == true
/// IsReservedKeyword("async") == false
/// Returns <see langword="true"/> if this a 'contextual' keyword for the language. A
/// 'contextual' keyword is a identifier that is only treated as being a special keyword in
/// certain *syntactic* contexts. Examples of this is 'yield' in C#. This is only a
/// keyword if used as 'yield return' or 'yield break'. Importantly, identifiers like <see
/// langword="var"/>, <see langword="dynamic"/> and <see langword="nameof"/> are *not*
/// 'contextual' keywords. This is because they are not treated as keywords depending on
/// the syntactic context around them. Instead, the language always treats them identifiers
/// that have special *semantic* meaning if they end up not binding to an existing symbol.
///
/// Importantly, if <paramref name="token"/> is not in the syntactic construct where the
/// language thinks an identifier should be contextually treated as a keyword, then this
/// will return <see langword="false"/>.
///
/// Or, in other words, the parser must be able to identify these cases in order to be a
/// contextual keyword. If identification happens afterwards, it's not contextual.
/// </summary>
bool IsReservedKeyword(string text);
bool IsContextualKeyword(SyntaxToken token);
/// <summary>
/// Determine if <paramref name="token"/> is a contextual keyword. i.e. not reserved.
/// For example:
/// IsContextualKeyword("class") == false
/// IsContextualKeyword("async") == true
/// The set of identifiers that have special meaning directly after the `#` token in a
/// preprocessor directive. For example `if` or `pragma`.
/// </summary>
bool IsContextualKeyword(SyntaxToken token);
bool IsPreprocessorKeyword(SyntaxToken token);
bool IsHashToken(SyntaxToken token);
bool IsLiteral(SyntaxToken token);
bool IsStringLiteralOrInterpolatedStringLiteral(SyntaxToken token);
......
......@@ -32,11 +32,13 @@ public static bool IsLegalIdentifier(this ISyntaxFactsService syntaxFacts, strin
return true;
}
public static bool IsReservedOrContextualKeyword(this ISyntaxFactsService syntaxFacts, SyntaxToken token)
=> syntaxFacts.IsReservedKeyword(token) || syntaxFacts.IsContextualKeyword(token);
public static bool IsWord(this ISyntaxFactsService syntaxFacts, SyntaxToken token)
{
return syntaxFacts.IsIdentifier(token)
|| syntaxFacts.IsKeyword(token)
|| syntaxFacts.IsContextualKeyword(token)
|| syntaxFacts.IsReservedOrContextualKeyword(token)
|| syntaxFacts.IsPreprocessorKeyword(token);
}
......
......@@ -327,7 +327,8 @@ private static string TrimNameToAfterLastDot(string name)
if (location.IsInSource)
{
var token = location.FindToken(cancellationToken);
if (!syntaxFacts.IsKeyword(token) && token.ValueText == referencedSymbol.Name)
if (!syntaxFacts.IsReservedOrContextualKeyword(token) &&
token.ValueText == referencedSymbol.Name)
{
results.Add(new RenameLocation(location, solution.GetDocument(location.SourceTree).Id));
}
......
......@@ -170,6 +170,25 @@ private static ISymbol MapSymbol(ISymbol symbol, ITypeSymbol type)
}
}
// see if we can map the built-in language operator to a real method on the containing
// type of the symbol. built-in operators can happen when querying the semantic model
// for operators. However, we would prefer to just use the real operator on the type
// if it has one.
if (symbol is IMethodSymbol methodSymbol &&
methodSymbol.MethodKind == MethodKind.BuiltinOperator &&
methodSymbol.ContainingType is ITypeSymbol containingType)
{
var comparer = SymbolEquivalenceComparer.Instance.ParameterEquivalenceComparer;
// Note: this will find the real method vs the built-in. That's because the
// built-in is synthesized operator that isn't actually in the list of members of
// its 'ContainingType'.
var mapped = containingType.GetMembers(methodSymbol.Name)
.OfType<IMethodSymbol>()
.FirstOrDefault(s => s.Parameters.SequenceEqual(methodSymbol.Parameters, comparer));
symbol = mapped ?? symbol;
}
return symbol;
}
......
......@@ -272,6 +272,13 @@ private bool AreCompatibleMethodKinds(MethodKind kind1, MethodKind kind2)
return true;
}
// User-defined and Built-in operators are comparable
if ((kind1 == MethodKind.BuiltinOperator && kind2 == MethodKind.UserDefinedOperator) ||
(kind1 == MethodKind.UserDefinedOperator && kind2 == MethodKind.BuiltinOperator))
{
return true;
}
return false;
}
......
......@@ -56,7 +56,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.CaseCorrection
If _syntaxFactsService.IsIdentifier(newToken) Then
Return VisitIdentifier(token, newToken)
ElseIf _syntaxFactsService.IsKeyword(newToken) OrElse _syntaxFactsService.IsContextualKeyword(newToken) Then
ElseIf _syntaxFactsService.IsReservedOrContextualKeyword(newToken) Then
Return VisitKeyword(newToken)
ElseIf token.IsNumericLiteral() Then
Return VisitNumericLiteral(newToken)
......
......@@ -101,12 +101,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return token.IsContextualKeyword()
End Function
Public Function IsKeyword(token As SyntaxToken) As Boolean Implements ISyntaxFactsService.IsKeyword
Return token.IsKeyword()
End Function
Public Function IsReservedKeyword(text As String) As Boolean Implements ISyntaxFactsService.IsReservedKeyword
Return GetKeywordKind(text) <> SyntaxKind.None
Public Function IsReservedKeyword(token As SyntaxToken) As Boolean Implements ISyntaxFactsService.IsReservedKeyword
Return token.IsReservedKeyword()
End Function
Public Function IsPreprocessorKeyword(token As SyntaxToken) As Boolean Implements ISyntaxFactsService.IsPreprocessorKeyword
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册