未验证 提交 b5edce47 编写于 作者: M Manish Vasani 提交者: GitHub

Merge pull request #45765 from mavasani/UnncessaryLocalSuppressMessageAttributeSuppressions

Add support to detect unnecessary inline SuppressMessageAttribute suppressions
......@@ -13,12 +13,13 @@
namespace Microsoft.CodeAnalysis.CSharp.RemoveUnnecessarySuppressions
{
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer
: AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer
internal sealed class CSharpRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
: AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
{
protected override string CompilerErrorCodePrefix => "CS";
protected override int CompilerErrorCodeDigitCount => 4;
protected override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance;
protected override ISemanticFacts SemanticFacts => CSharpSemanticFacts.Instance;
protected override (Assembly assembly, string typeName) GetCompilerDiagnosticAnalyzerInfo()
=> (typeof(SyntaxKind).Assembly, CompilerDiagnosticAnalyzerNames.CSharpCompilerAnalyzerTypeName);
}
......
......@@ -15,17 +15,18 @@
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.RemoveUnnecessarySuppressions
{
[ExportCodeFixProvider(LanguageNames.CSharp, LanguageNames.VisualBasic, Name = PredefinedCodeFixProviderNames.RemoveUnnecessaryPragmaSuppressions), Shared]
internal sealed class RemoveUnnecessaryPragmaSuppressionsCodeFixProvider : SyntaxEditorBasedCodeFixProvider
internal sealed class RemoveUnnecessaryInlineSuppressionsCodeFixProvider : SyntaxEditorBasedCodeFixProvider
{
[ImportingConstructor]
[SuppressMessage("RoslynDiagnosticsReliability", "RS0033:Importing constructor should be [Obsolete]", Justification = "Used in test code: https://github.com/dotnet/roslyn/issues/42814")]
public RemoveUnnecessaryPragmaSuppressionsCodeFixProvider()
public RemoveUnnecessaryInlineSuppressionsCodeFixProvider()
{
}
......@@ -38,10 +39,12 @@ internal override CodeFixCategory CodeFixCategory
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetRequiredSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
var syntaxFacts = context.Document.GetRequiredLanguageService<ISyntaxFactsService>();
foreach (var diagnostic in context.Diagnostics)
{
// Defensive check that we are operating on the diagnostic on a pragma.
if (root.FindTrivia(diagnostic.Location.SourceSpan.Start).HasStructure)
if (root.FindNode(diagnostic.Location.SourceSpan) is { } node && syntaxFacts.IsAttribute(node) ||
root.FindTrivia(diagnostic.Location.SourceSpan.Start).HasStructure)
{
context.RegisterCodeFix(
new MyCodeAction(c => FixAsync(context.Document, diagnostic, c)),
......@@ -58,22 +61,36 @@ protected override Task FixAllAsync(Document document, ImmutableArray<Diagnostic
// Our code fix ensures that we remove both the disable and restore directives with a single code fix application.
// So, we need to ensure that we do not attempt to remove the same node multiple times when performing a FixAll in document operation.
using var _ = PooledHashSet<SyntaxNode>.GetInstance(out var processedNodes);
var syntaxFacts = document.GetRequiredLanguageService<ISyntaxFactsService>();
foreach (var diagnostic in diagnostics)
{
RemoveNode(diagnostic.Location, editor, processedNodes);
RemoveNode(diagnostic.Location, editor, processedNodes, syntaxFacts);
foreach (var location in diagnostic.AdditionalLocations)
{
RemoveNode(location, editor, processedNodes);
RemoveNode(location, editor, processedNodes, syntaxFacts);
}
}
return Task.CompletedTask;
static void RemoveNode(Location location, SyntaxEditor editor, HashSet<SyntaxNode> processedNodes)
static void RemoveNode(
Location location,
SyntaxEditor editor,
HashSet<SyntaxNode> processedNodes,
ISyntaxFacts syntaxFacts)
{
var node = editor.OriginalRoot.FindTrivia(location.SourceSpan.Start).GetStructure()!;
SyntaxNode node;
if (editor.OriginalRoot.FindNode(location.SourceSpan) is { } attribute &&
syntaxFacts.IsAttribute(attribute))
{
node = attribute;
}
else
{
node = editor.OriginalRoot.FindTrivia(location.SourceSpan.Start).GetStructure()!;
}
if (processedNodes.Add(node))
{
editor.RemoveNode(node);
......@@ -84,7 +101,7 @@ static void RemoveNode(Location location, SyntaxEditor editor, HashSet<SyntaxNod
private class MyCodeAction : CustomCodeActions.DocumentChangeAction
{
public MyCodeAction(Func<CancellationToken, Task<Document>> createChangedDocument)
: base(AnalyzersResources.Remove_unnecessary_suppression, createChangedDocument, nameof(RemoveUnnecessaryPragmaSuppressionsCodeFixProvider))
: base(AnalyzersResources.Remove_unnecessary_suppression, createChangedDocument, nameof(RemoveUnnecessaryInlineSuppressionsCodeFixProvider))
{
}
}
......
......@@ -11,8 +11,8 @@ Imports Microsoft.CodeAnalysis.VisualBasic.LanguageServices
Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessarySuppressions
<DiagnosticAnalyzer(LanguageNames.VisualBasic)>
Friend NotInheritable Class VisualBasicRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer
Inherits AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer
Friend NotInheritable Class VisualBasicRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
Inherits AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
Protected Overrides ReadOnly Property CompilerErrorCodePrefix As String = "BC"
......@@ -20,6 +20,8 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.RemoveUnnecessarySuppressions
Protected Overrides ReadOnly Property SyntaxFacts As ISyntaxFacts = VisualBasicSyntaxFacts.Instance
Protected Overrides ReadOnly Property SemanticFacts As ISemanticFacts = VisualBasicSemanticFacts.Instance
Protected Overrides Function GetCompilerDiagnosticAnalyzerInfo() As (assembly As Assembly, typeName As String)
Return (GetType(SyntaxKind).Assembly, CompilerDiagnosticAnalyzerNames.VisualBasicCompilerAnalyzerTypeName)
End Function
......
......@@ -18,7 +18,7 @@ namespace Microsoft.CodeAnalysis.Editor.UnitTests.Diagnostics
public abstract class AbstractUnncessarySuppressionDiagnosticTest : AbstractUserDiagnosticTest
{
internal abstract CodeFixProvider CodeFixProvider { get; }
internal abstract AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer SuppressionAnalyzer { get; }
internal abstract AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer SuppressionAnalyzer { get; }
internal abstract ImmutableArray<DiagnosticAnalyzer> OtherAnalyzers { get; }
private void AddAnalyzersToWorkspace(TestWorkspace workspace)
......
......@@ -642,16 +642,19 @@ internal async Task TestDiagnosticSuppressor(bool includeAnalyzer, bool includeS
}
[Theory, CombinatorialData]
internal async Task TestPragmaSuppressionsAnalyzer(BackgroundAnalysisScope analysisScope)
internal async Task TestRemoveUnnecessaryInlineSuppressionsAnalyzer(BackgroundAnalysisScope analysisScope, bool testPragma)
{
var analyzers = ImmutableArray.Create<DiagnosticAnalyzer>(
new CSharpCompilerDiagnosticAnalyzer(),
new NamedTypeAnalyzer(),
new CSharpRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer());
new CSharpRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer());
var analyzerReference = new AnalyzerImageReference(analyzers);
var code = $@"
string code;
if (testPragma)
{
code = $@"
#pragma warning disable {NamedTypeAnalyzer.DiagnosticId} // Unnecessary
#pragma warning disable CS0168 // Variable is declared but never used - Unnecessary
......@@ -665,6 +668,23 @@ void M()
}}
}}
";
}
else
{
code = $@"
[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category1"", ""{NamedTypeAnalyzer.DiagnosticId}"")] // Necessary
class A
{{
[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category2"", ""{NamedTypeAnalyzer.DiagnosticId}"")] // Unnecessary
[System.Diagnostics.CodeAnalysis.SuppressMessage(""Category3"", ""CS0168"")] // Unnecessary
void M()
{{
#pragma warning disable CS0168 // Variable is declared but never used - Necessary
int x;
}}
}}
";
}
using var workspace = TestWorkspace.CreateCSharp(code, exportProvider: EditorServicesUtil.ExportProvider);
var options = workspace.Options.WithChangedOption(SolutionCrawlerOptions.BackgroundAnalysisScopeOption, LanguageNames.CSharp, analysisScope);
......@@ -710,10 +730,20 @@ void M()
Assert.Equal(2, diagnostics.Count);
var root = await document.GetSyntaxRootAsync();
var pragma1 = root.FindTrivia(diagnostics[0].GetTextSpan().Start).ToString();
Assert.Equal($"#pragma warning disable {NamedTypeAnalyzer.DiagnosticId} // Unnecessary", pragma1);
var pragma2 = root.FindTrivia(diagnostics[1].GetTextSpan().Start).ToString();
Assert.Equal($"#pragma warning disable CS0168 // Variable is declared but never used - Unnecessary", pragma2);
if (testPragma)
{
var pragma1 = root.FindTrivia(diagnostics[0].GetTextSpan().Start).ToString();
Assert.Equal($"#pragma warning disable {NamedTypeAnalyzer.DiagnosticId} // Unnecessary", pragma1);
var pragma2 = root.FindTrivia(diagnostics[1].GetTextSpan().Start).ToString();
Assert.Equal($"#pragma warning disable CS0168 // Variable is declared but never used - Unnecessary", pragma2);
}
else
{
var attribute1 = root.FindNode(diagnostics[0].GetTextSpan()).ToString();
Assert.Equal($@"System.Diagnostics.CodeAnalysis.SuppressMessage(""Category2"", ""{NamedTypeAnalyzer.DiagnosticId}"")", attribute1);
var attribute2 = root.FindNode(diagnostics[1].GetTextSpan()).ToString();
Assert.Equal($@"System.Diagnostics.CodeAnalysis.SuppressMessage(""Category3"", ""CS0168"")", attribute2);
}
}
private static Document GetDocumentFromIncompleteProject(AdhocWorkspace workspace)
......
......@@ -17,7 +17,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Remove
<Trait(Traits.Feature, Traits.Features.CodeActionsRemoveUnnecessarySuppressions)>
<WorkItem(44177, "https://github.com/dotnet/roslyn/issues/44177")>
Public NotInheritable Class RemoveUnnecessaryPragmaSuppressionsTests
Public NotInheritable Class RemoveUnnecessaryInlineSuppressionsTests
Inherits AbstractUnncessarySuppressionDiagnosticTest
Protected Overrides Function GetScriptOptions() As ParseOptions
......@@ -37,13 +37,13 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UnitTests.Diagnostics.Remove
Friend Overrides ReadOnly Property CodeFixProvider As CodeFixProvider
Get
Return New RemoveUnnecessaryPragmaSuppressionsCodeFixProvider()
Return New RemoveUnnecessaryInlineSuppressionsCodeFixProvider()
End Get
End Property
Friend Overrides ReadOnly Property SuppressionAnalyzer As AbstractRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer
Friend Overrides ReadOnly Property SuppressionAnalyzer As AbstractRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer
Get
Return New VisualBasicRemoveUnnecessaryPragmaSuppressionsDiagnosticAnalyzer()
Return New VisualBasicRemoveUnnecessaryInlineSuppressionsDiagnosticAnalyzer()
End Get
End Property
......@@ -123,6 +123,68 @@ Class C
Dim x As Integer
x = 1
End Sub
End Class")
End Function
<Fact>
Public Async Function TestRemoveUnnecessaryAttributeSuppression_Method() As Task
Await TestInRegularAndScript1Async($"
Imports System
Class C
[|<System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""UnknownId"")>|]
Sub Method()
Dim x As Integer
x = 1
End Sub
End Class", $"
Imports System
Class C
Sub Method()
Dim x As Integer
x = 1
End Sub
End Class")
End Function
<Fact>
Public Async Function TestRemoveUnnecessaryAttributeSuppression_Field() As Task
Await TestInRegularAndScript1Async($"
Imports System
Class C
[|<System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""UnknownId"")>|]
Dim f As Integer
End Class", $"
Imports System
Class C
Dim f As Integer
End Class")
End Function
<Fact>
Public Async Function TestRemoveUnnecessaryAttributeSuppression_Property() As Task
Await TestInRegularAndScript1Async($"
Imports System
Class C
[|<System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""UnknownId"")>|]
Public ReadOnly Property P As Integer
End Class", $"
Imports System
Class C
Public ReadOnly Property P As Integer
End Class")
End Function
<Fact>
Public Async Function TestRemoveUnnecessaryAttributeSuppression_Event() As Task
Await TestInRegularAndScript1Async($"
Imports System
Class C
[|<System.Diagnostics.CodeAnalysis.SuppressMessage(""Category"", ""UnknownId"")>|]
Public Event SampleEvent As EventHandler
End Class", $"
Imports System
Class C
Public Event SampleEvent As EventHandler
End Class")
End Function
End Class
......
......@@ -210,10 +210,10 @@ private async Task<ImmutableArray<Diagnostic>> GetSemanticDiagnosticsAsync(Seman
return await _compilationWithAnalyzers.GetAnalyzerSemanticDiagnosticsAsync(model, adjustedSpan, ImmutableArray.Create(analyzer), cancellationToken).ConfigureAwait(false);
}
// We specially handle IPragmaSuppressionsAnalyzer by passing in the 'CompilationWithAnalyzers'
// context to compute unnecessary pragma suppression diagnostics.
// We specially handle IInlineSourceSuppressionsAnalyzer by passing in the 'CompilationWithAnalyzers'
// context to compute unnecessary inline source suppression diagnostics.
// This is required because this analyzer relies on reported compiler + analyzer diagnostics
// for unnecessary pragma analysis.
// for unnecessary inline source suppression analysis.
if (analyzer is IPragmaSuppressionsAnalyzer suppressionsAnalyzer &&
!AnalysisScope.Span.HasValue)
{
......
......@@ -36,6 +36,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Extensions\MemberDeclarationSyntaxExtensions_GetAttributes.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\OperatorPrecedence.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ParenthesizedExpressionSyntaxExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SemanticModelExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SimpleNameSyntaxExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\StatementSyntaxExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SymbolDisplayPartExtensions.cs" />
......@@ -87,6 +88,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Services\Precedence\CSharpPatternPrecedenceService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\Precedence\CSharpExpressionPrecedenceService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SelectedMembers\CSharpSelectedMembers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SemanticFacts\CSharpSemanticFacts.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\CSharpSyntaxKinds.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\CSharpDocumentationCommentService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\CSharpSyntaxFacts.cs" />
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.LanguageServices;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
internal sealed class CSharpSemanticFacts : ISemanticFacts
{
internal static readonly CSharpSemanticFacts Instance = new CSharpSemanticFacts();
private CSharpSemanticFacts()
{
}
public bool SupportsImplicitInterfaceImplementation => true;
public bool ExposesAnonymousFunctionParameterNames => false;
public bool IsWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsWrittenTo();
public bool IsOnlyWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsOnlyWrittenTo();
public bool IsInOutContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsInOutContext();
public bool IsInRefContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsInRefContext();
public bool IsInInContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsInInContext();
public bool CanReplaceWithRValue(SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken)
=> (expression as ExpressionSyntax).CanReplaceWithRValue(semanticModel, cancellationToken);
public string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken)
=> semanticModel.GenerateNameForExpression((ExpressionSyntax)expression, capitalize, cancellationToken);
public ISymbol GetDeclaredSymbol(SemanticModel semanticModel, SyntaxToken token, CancellationToken cancellationToken)
{
var location = token.GetLocation();
foreach (var ancestor in token.GetAncestors<SyntaxNode>())
{
var symbol = semanticModel.GetDeclaredSymbol(ancestor, cancellationToken);
if (symbol != null)
{
if (symbol.Locations.Contains(location))
{
return symbol;
}
// We found some symbol, but it defined something else. We're not going to have a higher node defining _another_ symbol with this token, so we can stop now.
return null;
}
// If we hit an executable statement syntax and didn't find anything yet, we can just stop now -- anything higher would be a member declaration which won't be defined by something inside a statement.
if (CSharpSyntaxFacts.Instance.IsExecutableStatement(ancestor))
{
return null;
}
}
return null;
}
public bool LastEnumValueHasInitializer(INamedTypeSymbol namedTypeSymbol)
{
var enumDecl = namedTypeSymbol.DeclaringSyntaxReferences.Select(r => r.GetSyntax()).OfType<EnumDeclarationSyntax>().FirstOrDefault();
if (enumDecl != null)
{
var lastMember = enumDecl.Members.LastOrDefault();
if (lastMember != null)
{
return lastMember.EqualsValue != null;
}
}
return false;
}
public bool SupportsParameterizedProperties => false;
public bool TryGetSpeculativeSemanticModel(SemanticModel oldSemanticModel, SyntaxNode oldNode, SyntaxNode newNode, out SemanticModel speculativeModel)
{
Debug.Assert(oldNode.Kind() == newNode.Kind());
var model = oldSemanticModel;
if (!(oldNode is BaseMethodDeclarationSyntax oldMethod) || !(newNode is BaseMethodDeclarationSyntax newMethod) || oldMethod.Body == null)
{
speculativeModel = null;
return false;
}
var success = model.TryGetSpeculativeSemanticModelForMethodBody(oldMethod.Body.OpenBraceToken.Span.End, newMethod, out var csharpModel);
speculativeModel = csharpModel;
return success;
}
public ImmutableHashSet<string> GetAliasNameSet(SemanticModel model, CancellationToken cancellationToken)
{
var original = model.GetOriginalSemanticModel();
if (!original.SyntaxTree.HasCompilationUnitRoot)
{
return ImmutableHashSet.Create<string>();
}
var root = original.SyntaxTree.GetCompilationUnitRoot(cancellationToken);
var builder = ImmutableHashSet.CreateBuilder<string>(StringComparer.Ordinal);
AppendAliasNames(root.Usings, builder);
AppendAliasNames(root.Members.OfType<NamespaceDeclarationSyntax>(), builder, cancellationToken);
return builder.ToImmutable();
}
private static void AppendAliasNames(SyntaxList<UsingDirectiveSyntax> usings, ImmutableHashSet<string>.Builder builder)
{
foreach (var @using in usings)
{
if (@using.Alias == null || @using.Alias.Name == null)
{
continue;
}
@using.Alias.Name.Identifier.ValueText.AppendToAliasNameSet(builder);
}
}
private void AppendAliasNames(IEnumerable<NamespaceDeclarationSyntax> namespaces, ImmutableHashSet<string>.Builder builder, CancellationToken cancellationToken)
{
foreach (var @namespace in namespaces)
{
cancellationToken.ThrowIfCancellationRequested();
AppendAliasNames(@namespace.Usings, builder);
AppendAliasNames(@namespace.Members.OfType<NamespaceDeclarationSyntax>(), builder, cancellationToken);
}
}
public ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode forEachStatement)
{
if (forEachStatement is CommonForEachStatementSyntax csforEachStatement)
{
var info = semanticModel.GetForEachStatementInfo(csforEachStatement);
return new ForEachSymbols(
info.GetEnumeratorMethod,
info.MoveNextMethod,
info.CurrentProperty,
info.DisposeMethod,
info.ElementType);
}
else
{
return default;
}
}
public IMethodSymbol GetGetAwaiterMethod(SemanticModel semanticModel, SyntaxNode node)
{
if (node is AwaitExpressionSyntax awaitExpression)
{
var info = semanticModel.GetAwaitExpressionInfo(awaitExpression);
return info.GetAwaiterMethod;
}
return null;
}
public ImmutableArray<IMethodSymbol> GetDeconstructionAssignmentMethods(SemanticModel semanticModel, SyntaxNode node)
{
if (node is AssignmentExpressionSyntax assignment && assignment.IsDeconstruction())
{
var builder = ArrayBuilder<IMethodSymbol>.GetInstance();
FlattenDeconstructionMethods(semanticModel.GetDeconstructionInfo(assignment), builder);
return builder.ToImmutableAndFree();
}
return ImmutableArray<IMethodSymbol>.Empty;
}
public ImmutableArray<IMethodSymbol> GetDeconstructionForEachMethods(SemanticModel semanticModel, SyntaxNode node)
{
if (node is ForEachVariableStatementSyntax @foreach)
{
var builder = ArrayBuilder<IMethodSymbol>.GetInstance();
FlattenDeconstructionMethods(semanticModel.GetDeconstructionInfo(@foreach), builder);
return builder.ToImmutableAndFree();
}
return ImmutableArray<IMethodSymbol>.Empty;
}
private static void FlattenDeconstructionMethods(DeconstructionInfo deconstruction, ArrayBuilder<IMethodSymbol> builder)
{
var method = deconstruction.Method;
if (method != null)
{
builder.Add(method);
}
foreach (var nested in deconstruction.Nested)
{
FlattenDeconstructionMethods(nested, builder);
}
}
public bool IsPartial(ITypeSymbol typeSymbol, CancellationToken cancellationToken)
{
var syntaxRefs = typeSymbol.DeclaringSyntaxReferences;
return syntaxRefs.Any(n => ((BaseTypeDeclarationSyntax)n.GetSyntax(cancellationToken)).Modifiers.Any(SyntaxKind.PartialKeyword));
}
public IEnumerable<ISymbol> GetDeclaredSymbols(
SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken)
{
switch (memberDeclaration)
{
case FieldDeclarationSyntax field:
return field.Declaration.Variables.Select(
v => semanticModel.GetDeclaredSymbol(v, cancellationToken));
case EventFieldDeclarationSyntax eventField:
return eventField.Declaration.Variables.Select(
v => semanticModel.GetDeclaredSymbol(v, cancellationToken));
default:
return SpecializedCollections.SingletonEnumerable(
semanticModel.GetDeclaredSymbol(memberDeclaration, cancellationToken));
}
}
public IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken)
=> ((ArgumentSyntax)argumentNode).DetermineParameter(semanticModel, allowParams: false, cancellationToken);
public ImmutableArray<ISymbol> GetBestOrAllSymbols(SemanticModel semanticModel, SyntaxNode node, SyntaxToken token, CancellationToken cancellationToken)
{
if (node == null)
return ImmutableArray<ISymbol>.Empty;
return node switch
{
AssignmentExpressionSyntax _ when token.Kind() == SyntaxKind.EqualsToken => GetDeconstructionAssignmentMethods(semanticModel, node).As<ISymbol>(),
ForEachVariableStatementSyntax _ when token.Kind() == SyntaxKind.InKeyword => GetDeconstructionForEachMethods(semanticModel, node).As<ISymbol>(),
_ => GetSymbolInfo(semanticModel, node, token, cancellationToken).GetBestOrAllSymbols(),
};
}
private static SymbolInfo GetSymbolInfo(SemanticModel semanticModel, SyntaxNode node, SyntaxToken token, CancellationToken cancellationToken)
{
switch (node)
{
case OrderByClauseSyntax orderByClauseSyntax:
if (token.Kind() == SyntaxKind.CommaToken)
{
// Returning SymbolInfo for a comma token is the last resort
// in an order by clause if no other tokens to bind to a are present.
// See also the proposal at https://github.com/dotnet/roslyn/issues/23394
var separators = orderByClauseSyntax.Orderings.GetSeparators().ToImmutableList();
var index = separators.IndexOf(token);
if (index >= 0 && (index + 1) < orderByClauseSyntax.Orderings.Count)
{
var ordering = orderByClauseSyntax.Orderings[index + 1];
if (ordering.AscendingOrDescendingKeyword.Kind() == SyntaxKind.None)
{
return semanticModel.GetSymbolInfo(ordering, cancellationToken);
}
}
}
else if (orderByClauseSyntax.Orderings[0].AscendingOrDescendingKeyword.Kind() == SyntaxKind.None)
{
// The first ordering is displayed on the "orderby" keyword itself if there isn't a
// ascending/descending keyword.
return semanticModel.GetSymbolInfo(orderByClauseSyntax.Orderings[0], cancellationToken);
}
return default;
case QueryClauseSyntax queryClauseSyntax:
var queryInfo = semanticModel.GetQueryClauseInfo(queryClauseSyntax, cancellationToken);
var hasCastInfo = queryInfo.CastInfo.Symbol != null;
var hasOperationInfo = queryInfo.OperationInfo.Symbol != null;
if (hasCastInfo && hasOperationInfo)
{
// In some cases a single clause binds to more than one method. In those cases
// the tokens in the clause determine which of the two SymbolInfos are returned.
// See also the proposal at https://github.com/dotnet/roslyn/issues/23394
return token.IsKind(SyntaxKind.InKeyword) ? queryInfo.CastInfo : queryInfo.OperationInfo;
}
if (hasCastInfo)
{
return queryInfo.CastInfo;
}
return queryInfo.OperationInfo;
}
//Only in the orderby clause a comma can bind to a symbol.
if (token.IsKind(SyntaxKind.CommaToken))
{
return default;
}
return semanticModel.GetSymbolInfo(node, cancellationToken);
}
public bool IsInsideNameOfExpression(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsInsideNameOfExpression(semanticModel, cancellationToken);
}
}
......@@ -894,10 +894,17 @@ private static void AppendTypeParameterList(StringBuilder builder, TypeParameter
}
}
public List<SyntaxNode> GetTopLevelAndMethodLevelMembers(SyntaxNode root)
{
var list = new List<SyntaxNode>();
AppendMembers(root, list, topLevel: true, methodLevel: true);
return list;
}
public List<SyntaxNode> GetMethodLevelMembers(SyntaxNode root)
{
var list = new List<SyntaxNode>();
AppendMethodLevelMembers(root, list);
AppendMembers(root, list, topLevel: false, methodLevel: true);
return list;
}
......@@ -916,17 +923,24 @@ public SyntaxList<SyntaxNode> GetMembersOfNamespaceDeclaration(SyntaxNode namesp
public SyntaxList<SyntaxNode> GetMembersOfCompilationUnit(SyntaxNode compilationUnit)
=> ((CompilationUnitSyntax)compilationUnit).Members;
private void AppendMethodLevelMembers(SyntaxNode node, List<SyntaxNode> list)
private void AppendMembers(SyntaxNode node, List<SyntaxNode> list, bool topLevel, bool methodLevel)
{
Debug.Assert(topLevel || methodLevel);
foreach (var member in node.GetMembers())
{
if (IsTopLevelNodeWithMembers(member))
{
AppendMethodLevelMembers(member, list);
if (topLevel)
{
list.Add(member);
}
AppendMembers(member, list, topLevel, methodLevel);
continue;
}
if (IsMethodLevelMember(member))
if (methodLevel && IsMethodLevelMember(member))
{
list.Add(member);
}
......
......@@ -345,10 +345,12 @@
<Compile Include="$(MSBuildThisFileDirectory)Options\PerLanguageOption2.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Options\RoamingProfileStorageLocation.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SelectedMembers\AbstractSelectedMembers.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SemanticFacts\ForEachSymbols.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\AbstractDocumentationCommentService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\AbstractSyntaxFacts.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\ExternalSourceInfo.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\IDocumentationCommentService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SemanticFacts\ISemanticFacts.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\ISyntaxFacts.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\ISyntaxFactsExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\ISyntaxKinds.cs" />
......
......@@ -13,12 +13,13 @@
namespace Microsoft.CodeAnalysis.Diagnostics
{
/// <summary>
/// Special IDE analyzer to flag unnecessary pragma suppressions.
/// Special IDE analyzer to flag unnecessary inline source suppressions,
/// i.e. pragma and local SuppressMessageAttribute suppressions.
/// </summary>
internal interface IPragmaSuppressionsAnalyzer
{
/// <summary>
/// Analyzes the tree, with an optional span scope, and report unnecessary pragma suppressions.
/// Analyzes the tree, with an optional span scope, and report unnecessary inline suppressions.
/// </summary>
Task AnalyzeAsync(
SemanticModel semanticModel,
......
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
namespace Microsoft.CodeAnalysis.LanguageServices
{
internal interface ISemanticFacts
{
/// <summary>
/// True if this language supports implementing an interface by signature only. If false,
/// implementations must specific explicitly which symbol they're implementing.
/// </summary>
bool SupportsImplicitInterfaceImplementation { get; }
bool SupportsParameterizedProperties { get; }
/// <summary>
/// True if anonymous functions in this language have signatures that include named
/// parameters that can be referenced later on when the function is invoked. Or, if the
/// anonymous function is simply a signature that will be assigned to a delegate, and the
/// delegate's parameter names are used when invoking.
///
/// For example, in VB one can do this:
///
/// dim v = Sub(x as Integer) Blah()
/// v(x:=4)
///
/// However, in C# that would need to be:
///
/// Action&lt;int&gt; v = (int x) => Blah();
/// v(obj:=4)
///
/// Note that in VB one can access 'x' outside of the declaration of the anonymous type.
/// While in C# 'x' can only be accessed within the anonymous type.
/// </summary>
bool ExposesAnonymousFunctionParameterNames { get; }
/// <summary>
/// True if a write is performed to the given expression. Note: reads may also be performed
/// to the expression as well. For example, "++a". In this expression 'a' is both read from
/// and written to.
/// </summary>
bool IsWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
/// <summary>
/// True if a write is performed to the given expression. Note: unlike IsWrittenTo, this
/// will not return true if reads are performed on the expression as well. For example,
/// "++a" will return 'false'. However, 'a' in "out a" or "a = 1" will return true.
/// </summary>
bool IsOnlyWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
bool IsInOutContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
bool IsInRefContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
bool IsInInContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
bool CanReplaceWithRValue(SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken);
string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken);
ISymbol GetDeclaredSymbol(SemanticModel semanticModel, SyntaxToken token, CancellationToken cancellationToken);
bool LastEnumValueHasInitializer(INamedTypeSymbol namedTypeSymbol);
/// <summary>
/// return speculative semantic model for supported node. otherwise, it will return null
/// </summary>
bool TryGetSpeculativeSemanticModel(SemanticModel oldSemanticModel, SyntaxNode oldNode, SyntaxNode newNode, out SemanticModel speculativeModel);
/// <summary>
/// get all alias names defined in the semantic model
/// </summary>
ImmutableHashSet<string> GetAliasNameSet(SemanticModel model, CancellationToken cancellationToken);
ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode forEachStatement);
IMethodSymbol GetGetAwaiterMethod(SemanticModel semanticModel, SyntaxNode node);
ImmutableArray<IMethodSymbol> GetDeconstructionAssignmentMethods(SemanticModel semanticModel, SyntaxNode node);
ImmutableArray<IMethodSymbol> GetDeconstructionForEachMethods(SemanticModel semanticModel, SyntaxNode node);
bool IsPartial(ITypeSymbol typeSymbol, CancellationToken cancellationToken);
IEnumerable<ISymbol> GetDeclaredSymbols(SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken);
IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken);
#nullable enable
ImmutableArray<ISymbol> GetBestOrAllSymbols(SemanticModel semanticModel, SyntaxNode? node, SyntaxToken token, CancellationToken cancellationToken);
#nullable disable
bool IsInsideNameOfExpression(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
}
}
......@@ -369,6 +369,7 @@ internal partial interface ISyntaxFacts
bool IsClassDeclaration(SyntaxNode node);
bool IsNamespaceDeclaration(SyntaxNode node);
List<SyntaxNode> GetTopLevelAndMethodLevelMembers(SyntaxNode root);
List<SyntaxNode> GetMethodLevelMembers(SyntaxNode root);
SyntaxList<SyntaxNode> GetMembersOfTypeDeclaration(SyntaxNode typeDeclaration);
SyntaxList<SyntaxNode> GetMembersOfNamespaceDeclaration(SyntaxNode namespaceDeclaration);
......
......@@ -345,5 +345,196 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Return False
End Function
''' <summary>
''' Decompose a name or member access expression into its component parts.
''' </summary>
''' <param name="expression">The name or member access expression.</param>
''' <param name="qualifier">The qualifier (or left-hand-side) of the name expression. This may be null if there is no qualifier.</param>
''' <param name="name">The name of the expression.</param>
''' <param name="arity">The number of generic type parameters.</param>
<Extension()>
Public Sub DecomposeName(expression As ExpressionSyntax, ByRef qualifier As ExpressionSyntax, ByRef name As String, ByRef arity As Integer)
Select Case expression.Kind
Case SyntaxKind.SimpleMemberAccessExpression
Dim memberAccess = DirectCast(expression, MemberAccessExpressionSyntax)
qualifier = memberAccess.Expression
name = memberAccess.Name.Identifier.ValueText
arity = memberAccess.Name.Arity
Case SyntaxKind.QualifiedName
Dim qualifiedName = DirectCast(expression, QualifiedNameSyntax)
qualifier = qualifiedName.Left
name = qualifiedName.Right.Identifier.ValueText
arity = qualifiedName.Arity
Case SyntaxKind.GenericName
Dim genericName = DirectCast(expression, GenericNameSyntax)
qualifier = Nothing
name = genericName.Identifier.ValueText
arity = genericName.Arity
Case SyntaxKind.IdentifierName
Dim identifierName = DirectCast(expression, IdentifierNameSyntax)
qualifier = Nothing
name = identifierName.Identifier.ValueText
arity = 0
Case Else
qualifier = Nothing
name = Nothing
arity = 0
End Select
End Sub
Private Function CanReplace(symbol As ISymbol) As Boolean
Select Case symbol.Kind
Case SymbolKind.Field,
SymbolKind.Local,
SymbolKind.Method,
SymbolKind.Parameter,
SymbolKind.Property,
SymbolKind.RangeVariable
Return True
End Select
Return False
End Function
<Extension>
Public Function CanReplaceWithRValue(expression As ExpressionSyntax, semanticModel As SemanticModel, cancellationToken As CancellationToken) As Boolean
Return expression IsNot Nothing AndAlso
Not expression.IsWrittenTo(semanticModel, cancellationToken) AndAlso
expression.CanReplaceWithLValue(semanticModel, cancellationToken)
End Function
<Extension>
Public Function CanReplaceWithLValue(expression As ExpressionSyntax, semanticModel As SemanticModel, cancellationToken As CancellationToken) As Boolean
#If False Then
' Things that are definitely illegal to replace
If ContainsImplicitMemberAccess(expression) Then
Return False
End If
#End If
If expression.IsKind(SyntaxKind.MyBaseExpression) OrElse
expression.IsKind(SyntaxKind.MyClassExpression) Then
Return False
End If
If Not (TypeOf expression Is ObjectCreationExpressionSyntax) AndAlso
Not (TypeOf expression Is AnonymousObjectCreationExpressionSyntax) Then
Dim symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken)
If Not symbolInfo.GetBestOrAllSymbols().All(AddressOf CanReplace) Then
' If the expression is actually a reference to a type, then it can't be replaced
' with an arbitrary expression.
Return False
End If
End If
' Technically, you could introduce an LValue for "Goo" in "Goo()" even if "Goo" binds
' to a method. (i.e. by assigning to a Func<...> type). However, this is so contrived
' and none of the features that use this extension consider this replaceable.
If TypeOf expression.Parent Is InvocationExpressionSyntax Then
' If something is being invoked, then it's either something like Goo(), Goo.Bar(), or
' SomeExpr() (i.e. Blah[1]()). In the first and second case, we only allow
' replacement if Goo and Goo.Bar didn't bind to a method. If we can't bind it, we'll
' assume it's a method and we don't allow it to be replaced either. However, if it's
' an arbitrary expression, we do allow replacement.
If expression.IsKind(SyntaxKind.IdentifierName) OrElse expression.IsKind(SyntaxKind.SimpleMemberAccessExpression) Then
Dim symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken)
If Not symbolInfo.GetBestOrAllSymbols().Any() Then
Return False
End If
' don't allow it to be replaced if it is bound to an indexed property
Return Not symbolInfo.GetBestOrAllSymbols().OfType(Of IMethodSymbol)().Any() AndAlso
Not symbolInfo.GetBestOrAllSymbols().OfType(Of IPropertySymbol)().Any()
Else
Return True
End If
End If
' expression in next statement's control variables should match one in the head
Dim nextStatement = expression.FirstAncestorOrSelf(Of NextStatementSyntax)()
If nextStatement IsNot Nothing Then
Return False
End If
' Direct parent kind checks.
If expression.IsParentKind(SyntaxKind.EqualsValue) OrElse
expression.IsParentKind(SyntaxKind.ParenthesizedExpression) OrElse
expression.IsParentKind(SyntaxKind.SelectStatement) OrElse
expression.IsParentKind(SyntaxKind.SyncLockStatement) OrElse
expression.IsParentKind(SyntaxKind.CollectionInitializer) OrElse
expression.IsParentKind(SyntaxKind.InferredFieldInitializer) OrElse
expression.IsParentKind(SyntaxKind.BinaryConditionalExpression) OrElse
expression.IsParentKind(SyntaxKind.TernaryConditionalExpression) OrElse
expression.IsParentKind(SyntaxKind.ReturnStatement) OrElse
expression.IsParentKind(SyntaxKind.YieldStatement) OrElse
expression.IsParentKind(SyntaxKind.XmlEmbeddedExpression) OrElse
expression.IsParentKind(SyntaxKind.ThrowStatement) OrElse
expression.IsParentKind(SyntaxKind.IfStatement) OrElse
expression.IsParentKind(SyntaxKind.WhileStatement) OrElse
expression.IsParentKind(SyntaxKind.ElseIfStatement) OrElse
expression.IsParentKind(SyntaxKind.ForEachStatement) OrElse
expression.IsParentKind(SyntaxKind.ForStatement) OrElse
expression.IsParentKind(SyntaxKind.ConditionalAccessExpression) OrElse
expression.IsParentKind(SyntaxKind.TypeOfIsExpression) OrElse
expression.IsParentKind(SyntaxKind.TypeOfIsNotExpression) Then
Return True
End If
' Parent type checks
If TypeOf expression.Parent Is BinaryExpressionSyntax OrElse
TypeOf expression.Parent Is AssignmentStatementSyntax OrElse
TypeOf expression.Parent Is WhileOrUntilClauseSyntax OrElse
TypeOf expression.Parent Is SingleLineLambdaExpressionSyntax OrElse
TypeOf expression.Parent Is AwaitExpressionSyntax Then
Return True
End If
' Specific child checks.
If expression.CheckParent(Of NamedFieldInitializerSyntax)(Function(n) n.Expression Is expression) OrElse
expression.CheckParent(Of MemberAccessExpressionSyntax)(Function(m) m.Expression Is expression) OrElse
expression.CheckParent(Of TryCastExpressionSyntax)(Function(t) t.Expression Is expression) OrElse
expression.CheckParent(Of CatchFilterClauseSyntax)(Function(c) c.Filter Is expression) OrElse
expression.CheckParent(Of SimpleArgumentSyntax)(Function(n) n.Expression Is expression) OrElse
expression.CheckParent(Of DirectCastExpressionSyntax)(Function(d) d.Expression Is expression) OrElse
expression.CheckParent(Of FunctionAggregationSyntax)(Function(f) f.Argument Is expression) OrElse
expression.CheckParent(Of RangeArgumentSyntax)(Function(r) r.UpperBound Is expression) Then
Return True
End If
' Misc checks
If TypeOf expression.Parent Is ExpressionRangeVariableSyntax AndAlso
TypeOf expression.Parent.Parent Is QueryClauseSyntax Then
Dim rangeVariable = DirectCast(expression.Parent, ExpressionRangeVariableSyntax)
Dim selectClause = TryCast(rangeVariable.Parent, SelectClauseSyntax)
' Can't replace the expression in a select unless its the last select clause *or*
' it's a select of the form "select a = <expr>"
If selectClause IsNot Nothing Then
If rangeVariable.NameEquals IsNot Nothing Then
Return True
End If
Dim queryExpression = TryCast(selectClause.Parent, QueryExpressionSyntax)
If queryExpression IsNot Nothing Then
Return queryExpression.Clauses.Last() Is selectClause
End If
Dim aggregateClause = TryCast(selectClause.Parent, AggregateClauseSyntax)
If aggregateClause IsNot Nothing Then
Return aggregateClause.AdditionalQueryOperators().Last() Is selectClause
End If
Return False
End If
' Any other query type is ok. Note(cyrusn): This may be too broad.
Return True
End If
Return False
End Function
End Module
End Namespace
' Licensed to the .NET Foundation under one or more agreements.
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.LanguageServices
Imports Microsoft.CodeAnalysis.VisualBasic.LanguageServices
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic
Friend NotInheritable Class VisualBasicSemanticFacts
Implements ISemanticFacts
Public Shared ReadOnly Instance As New VisualBasicSemanticFacts()
Private Sub New()
End Sub
Public ReadOnly Property SupportsImplicitInterfaceImplementation As Boolean Implements ISemanticFacts.SupportsImplicitInterfaceImplementation
Get
Return False
End Get
End Property
Public ReadOnly Property ExposesAnonymousFunctionParameterNames As Boolean Implements ISemanticFacts.ExposesAnonymousFunctionParameterNames
Get
Return True
End Get
End Property
Public Function IsOnlyWrittenTo(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.IsOnlyWrittenTo
Return TryCast(node, ExpressionSyntax).IsOnlyWrittenTo()
End Function
Public Function IsWrittenTo(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.IsWrittenTo
Return TryCast(node, ExpressionSyntax).IsWrittenTo(semanticModel, cancellationToken)
End Function
Public Function IsInOutContext(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.IsInOutContext
Return TryCast(node, ExpressionSyntax).IsInOutContext()
End Function
Public Function IsInRefContext(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.IsInRefContext
Return TryCast(node, ExpressionSyntax).IsInRefContext(semanticModel, cancellationToken)
End Function
Public Function IsInInContext(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.IsInInContext
Return TryCast(node, ExpressionSyntax).IsInInContext()
End Function
Public Function CanReplaceWithRValue(semanticModel As SemanticModel, expression As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.CanReplaceWithRValue
Return TryCast(expression, ExpressionSyntax).CanReplaceWithRValue(semanticModel, cancellationToken)
End Function
Public Function GenerateNameForExpression(semanticModel As SemanticModel,
expression As SyntaxNode,
capitalize As Boolean,
cancellationToken As CancellationToken) As String Implements ISemanticFacts.GenerateNameForExpression
Return semanticModel.GenerateNameForExpression(
DirectCast(expression, ExpressionSyntax), capitalize, cancellationToken)
End Function
Public Function GetDeclaredSymbol(semanticModel As SemanticModel, token As SyntaxToken, cancellationToken As CancellationToken) As ISymbol Implements ISemanticFacts.GetDeclaredSymbol
Dim location = token.GetLocation()
For Each ancestor In token.GetAncestors(Of SyntaxNode)()
If Not TypeOf ancestor Is AggregationRangeVariableSyntax AndAlso
Not TypeOf ancestor Is CollectionRangeVariableSyntax AndAlso
Not TypeOf ancestor Is ExpressionRangeVariableSyntax AndAlso
Not TypeOf ancestor Is InferredFieldInitializerSyntax Then
Dim symbol = semanticModel.GetDeclaredSymbol(ancestor)
If symbol IsNot Nothing Then
If symbol.Locations.Contains(location) Then
Return symbol
End If
' We found some symbol, but it defined something else. We're not going to have a higher node defining _another_ symbol with this token, so we can stop now.
Return Nothing
End If
' If we hit an executable statement syntax and didn't find anything yet, we can just stop now -- anything higher would be a member declaration which won't be defined by something inside a statement.
If VisualBasicSyntaxFacts.Instance.IsExecutableStatement(ancestor) Then
Return Nothing
End If
End If
Next
Return Nothing
End Function
Public Function LastEnumValueHasInitializer(namedTypeSymbol As INamedTypeSymbol) As Boolean Implements ISemanticFacts.LastEnumValueHasInitializer
Dim enumStatement = namedTypeSymbol.DeclaringSyntaxReferences.Select(Function(r) r.GetSyntax()).OfType(Of EnumStatementSyntax).FirstOrDefault()
If enumStatement IsNot Nothing Then
Dim enumBlock = DirectCast(enumStatement.Parent, EnumBlockSyntax)
Dim lastMember = TryCast(enumBlock.Members.LastOrDefault(), EnumMemberDeclarationSyntax)
If lastMember IsNot Nothing Then
Return lastMember.Initializer IsNot Nothing
End If
End If
Return False
End Function
Public ReadOnly Property SupportsParameterizedProperties As Boolean Implements ISemanticFacts.SupportsParameterizedProperties
Get
Return True
End Get
End Property
Public Function TryGetSpeculativeSemanticModel(oldSemanticModel As SemanticModel, oldNode As SyntaxNode, newNode As SyntaxNode, <Out> ByRef speculativeModel As SemanticModel) As Boolean Implements ISemanticFacts.TryGetSpeculativeSemanticModel
Debug.Assert(oldNode.Kind = newNode.Kind)
Dim model = oldSemanticModel
' currently we only support method. field support will be added later.
Dim oldMethod = TryCast(oldNode, MethodBlockBaseSyntax)
Dim newMethod = TryCast(newNode, MethodBlockBaseSyntax)
If oldMethod Is Nothing OrElse newMethod Is Nothing Then
speculativeModel = Nothing
Return False
End If
' No method body?
If oldMethod.Statements.IsEmpty AndAlso oldMethod.EndBlockStatement.IsMissing Then
speculativeModel = Nothing
Return False
End If
Dim position As Integer
If model.IsSpeculativeSemanticModel Then
' Chaining speculative semantic model is not supported, use the original model.
position = model.OriginalPositionForSpeculation
model = model.ParentModel
Contract.ThrowIfNull(model)
Contract.ThrowIfTrue(model.IsSpeculativeSemanticModel)
Else
position = oldMethod.BlockStatement.FullSpan.End
End If
Dim vbSpeculativeModel As SemanticModel = Nothing
Dim success = model.TryGetSpeculativeSemanticModelForMethodBody(position, newMethod, vbSpeculativeModel)
speculativeModel = vbSpeculativeModel
Return success
End Function
Public Function GetAliasNameSet(model As SemanticModel, cancellationToken As CancellationToken) As ImmutableHashSet(Of String) Implements ISemanticFacts.GetAliasNameSet
Dim original = DirectCast(model.GetOriginalSemanticModel(), SemanticModel)
If Not original.SyntaxTree.HasCompilationUnitRoot Then
Return ImmutableHashSet.Create(Of String)()
End If
Dim root = original.SyntaxTree.GetCompilationUnitRoot()
Dim builder = ImmutableHashSet.CreateBuilder(Of String)(StringComparer.OrdinalIgnoreCase)
For Each globalImport In original.Compilation.AliasImports
globalImport.Name.AppendToAliasNameSet(builder)
Next
For Each importsClause In root.GetAliasImportsClauses()
importsClause.Alias.Identifier.ValueText.AppendToAliasNameSet(builder)
Next
Return builder.ToImmutable()
End Function
Public Function GetForEachSymbols(model As SemanticModel, forEachStatement As SyntaxNode) As ForEachSymbols Implements ISemanticFacts.GetForEachSymbols
Dim vbForEachStatement = TryCast(forEachStatement, ForEachStatementSyntax)
If vbForEachStatement IsNot Nothing Then
Dim info = model.GetForEachStatementInfo(vbForEachStatement)
Return New ForEachSymbols(
info.GetEnumeratorMethod,
info.MoveNextMethod,
info.CurrentProperty,
info.DisposeMethod,
info.ElementType)
End If
Dim vbForBlock = TryCast(forEachStatement, ForEachBlockSyntax)
If vbForBlock IsNot Nothing Then
Dim info = model.GetForEachStatementInfo(vbForBlock)
Return New ForEachSymbols(
info.GetEnumeratorMethod,
info.MoveNextMethod,
info.CurrentProperty,
info.DisposeMethod,
info.ElementType)
End If
Return Nothing
End Function
Public Function GetGetAwaiterMethod(model As SemanticModel, node As SyntaxNode) As IMethodSymbol Implements ISemanticFacts.GetGetAwaiterMethod
If node.IsKind(SyntaxKind.AwaitExpression) Then
Dim awaitExpression = DirectCast(node, AwaitExpressionSyntax)
Dim info = model.GetAwaitExpressionInfo(awaitExpression)
Return info.GetAwaiterMethod
End If
Return Nothing
End Function
Public Function GetDeconstructionAssignmentMethods(model As SemanticModel, deconstruction As SyntaxNode) As ImmutableArray(Of IMethodSymbol) Implements ISemanticFacts.GetDeconstructionAssignmentMethods
Return ImmutableArray(Of IMethodSymbol).Empty
End Function
Public Function GetDeconstructionForEachMethods(model As SemanticModel, deconstruction As SyntaxNode) As ImmutableArray(Of IMethodSymbol) Implements ISemanticFacts.GetDeconstructionForEachMethods
Return ImmutableArray(Of IMethodSymbol).Empty
End Function
Public Function IsPartial(typeSymbol As ITypeSymbol, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.IsPartial
Dim syntaxRefs = typeSymbol.DeclaringSyntaxReferences
Return syntaxRefs.Any(
Function(n As SyntaxReference)
Return DirectCast(n.GetSyntax(cancellationToken), TypeStatementSyntax).Modifiers.Any(SyntaxKind.PartialKeyword)
End Function)
End Function
Public Function GetDeclaredSymbols(semanticModel As SemanticModel, memberDeclaration As SyntaxNode, cancellationToken As CancellationToken) As IEnumerable(Of ISymbol) Implements ISemanticFacts.GetDeclaredSymbols
If TypeOf memberDeclaration Is FieldDeclarationSyntax Then
Return DirectCast(memberDeclaration, FieldDeclarationSyntax).Declarators.
SelectMany(Function(d) d.Names.AsEnumerable()).
Select(Function(n) semanticModel.GetDeclaredSymbol(n, cancellationToken))
End If
Return SpecializedCollections.SingletonEnumerable(semanticModel.GetDeclaredSymbol(memberDeclaration, cancellationToken))
End Function
Public Function FindParameterForArgument(semanticModel As SemanticModel, argumentNode As SyntaxNode, cancellationToken As CancellationToken) As IParameterSymbol Implements ISemanticFacts.FindParameterForArgument
Return DirectCast(argumentNode, ArgumentSyntax).DetermineParameter(semanticModel, allowParamArray:=False, cancellationToken)
End Function
Public Function GetBestOrAllSymbols(semanticModel As SemanticModel, node As SyntaxNode, token As SyntaxToken, cancellationToken As CancellationToken) As ImmutableArray(Of ISymbol) Implements ISemanticFacts.GetBestOrAllSymbols
Return If(node Is Nothing,
ImmutableArray(Of ISymbol).Empty,
semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols())
End Function
Public Function IsInsideNameOfExpression(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFacts.IsInsideNameOfExpression
Return node.FirstAncestorOrSelf(Of NameOfExpressionSyntax) IsNot Nothing
End Function
End Class
End Namespace
......@@ -893,9 +893,15 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices
Return TextSpan.FromBounds(list.First.SpanStart, list.Last.Span.End)
End Function
Public Function GetTopLevelAndMethodLevelMembers(root As SyntaxNode) As List(Of SyntaxNode) Implements ISyntaxFacts.GetTopLevelAndMethodLevelMembers
Dim list = New List(Of SyntaxNode)()
AppendMembers(root, list, topLevel:=True, methodLevel:=True)
Return list
End Function
Public Function GetMethodLevelMembers(root As SyntaxNode) As List(Of SyntaxNode) Implements ISyntaxFacts.GetMethodLevelMembers
Dim list = New List(Of SyntaxNode)()
AppendMethodLevelMembers(root, list)
AppendMembers(root, list, topLevel:=False, methodLevel:=True)
Return list
End Function
......@@ -1056,14 +1062,20 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.LanguageServices
End If
End Sub
Private Sub AppendMethodLevelMembers(node As SyntaxNode, list As List(Of SyntaxNode))
Private Sub AppendMembers(node As SyntaxNode, list As List(Of SyntaxNode), topLevel As Boolean, methodLevel As Boolean)
Debug.Assert(topLevel OrElse methodLevel)
For Each member In node.GetMembers()
If IsTopLevelNodeWithMembers(member) Then
AppendMethodLevelMembers(member, list)
If topLevel Then
list.Add(member)
End If
AppendMembers(member, list, topLevel, methodLevel)
Continue For
End If
If IsMethodLevelMember(member) Then
If methodLevel AndAlso IsMethodLevelMember(member) Then
list.Add(member)
End If
Next
......
......@@ -18,6 +18,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Extensions\MemberAccessExpressionSyntaxExtensions.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\OperatorPrecedence.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ParenthesizedExpressionSyntaxExtensions.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SemanticModelExtensions.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SingleLineRewriter.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\StatementSyntaxExtensions.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SyntaxNodeExtensions.vb" />
......@@ -28,6 +29,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Helpers\RemoveUnnecessaryImports\VisualBasicUnnecessaryImportsProvider.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Services\Precedence\VisualBasicPrecedenceService.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SelectedMembers\VisualBasicSelectedMembers.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SemanticFacts\VisualBasicSemanticFacts.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\VisualBasicDocumentationCommentService.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Services\SyntaxFacts\VisualBasicSyntaxFacts.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Simplification\VisualBasicInferredMemberNameSimplifier.vb" />
......
......@@ -28,7 +28,6 @@
<Compile Include="$(MSBuildThisFileDirectory)Extensions\NamespaceDeclarationSyntaxExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\NameSyntaxExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\QueryExpressionSyntaxExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SemanticModelExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SeparatedSyntaxListExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\StringExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SyntaxNodeExtensions.cs" />
......
......@@ -2,29 +2,25 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.LanguageServices;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.LanguageServices;
using Microsoft.CodeAnalysis.PooledObjects;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.CSharp
{
internal class CSharpSemanticFactsService : AbstractSemanticFactsService, ISemanticFactsService
internal sealed class CSharpSemanticFactsService : AbstractSemanticFactsService, ISemanticFactsService
{
internal static readonly CSharpSemanticFactsService Instance = new CSharpSemanticFactsService();
protected override ISyntaxFacts SyntaxFacts => CSharpSyntaxFacts.Instance;
protected override ISemanticFacts SemanticFacts => CSharpSemanticFacts.Instance;
private CSharpSemanticFactsService()
{
......@@ -57,10 +53,6 @@ bool ShouldDescendInto(SyntaxNode node)
}
}
public bool SupportsImplicitInterfaceImplementation => true;
public bool ExposesAnonymousFunctionParameterNames => false;
public bool IsExpressionContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken)
{
return semanticModel.SyntaxTree.IsExpressionContext(
......@@ -107,277 +99,5 @@ public bool IsLabelContext(SemanticModel semanticModel, int position, Cancellati
public bool IsAttributeNameContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken)
=> semanticModel.SyntaxTree.IsAttributeNameContext(position, cancellationToken);
public bool IsWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsWrittenTo();
public bool IsOnlyWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsOnlyWrittenTo();
public bool IsInOutContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsInOutContext();
public bool IsInRefContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsInRefContext();
public bool IsInInContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsInInContext();
public bool CanReplaceWithRValue(SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken)
=> (expression as ExpressionSyntax).CanReplaceWithRValue(semanticModel, cancellationToken);
public string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken)
=> semanticModel.GenerateNameForExpression((ExpressionSyntax)expression, capitalize, cancellationToken);
public ISymbol GetDeclaredSymbol(SemanticModel semanticModel, SyntaxToken token, CancellationToken cancellationToken)
{
var location = token.GetLocation();
foreach (var ancestor in token.GetAncestors<SyntaxNode>())
{
var symbol = semanticModel.GetDeclaredSymbol(ancestor, cancellationToken);
if (symbol != null)
{
if (symbol.Locations.Contains(location))
{
return symbol;
}
// We found some symbol, but it defined something else. We're not going to have a higher node defining _another_ symbol with this token, so we can stop now.
return null;
}
// If we hit an executable statement syntax and didn't find anything yet, we can just stop now -- anything higher would be a member declaration which won't be defined by something inside a statement.
if (SyntaxFacts.IsExecutableStatement(ancestor))
{
return null;
}
}
return null;
}
public bool LastEnumValueHasInitializer(INamedTypeSymbol namedTypeSymbol)
{
var enumDecl = namedTypeSymbol.DeclaringSyntaxReferences.Select(r => r.GetSyntax()).OfType<EnumDeclarationSyntax>().FirstOrDefault();
if (enumDecl != null)
{
var lastMember = enumDecl.Members.LastOrDefault();
if (lastMember != null)
{
return lastMember.EqualsValue != null;
}
}
return false;
}
public bool SupportsParameterizedProperties => false;
public ImmutableHashSet<string> GetAliasNameSet(SemanticModel model, CancellationToken cancellationToken)
{
var original = model.GetOriginalSemanticModel();
if (!original.SyntaxTree.HasCompilationUnitRoot)
{
return ImmutableHashSet.Create<string>();
}
var root = original.SyntaxTree.GetCompilationUnitRoot(cancellationToken);
var builder = ImmutableHashSet.CreateBuilder<string>(StringComparer.Ordinal);
AppendAliasNames(root.Usings, builder);
AppendAliasNames(root.Members.OfType<NamespaceDeclarationSyntax>(), builder, cancellationToken);
return builder.ToImmutable();
}
private static void AppendAliasNames(SyntaxList<UsingDirectiveSyntax> usings, ImmutableHashSet<string>.Builder builder)
{
foreach (var @using in usings)
{
if (@using.Alias == null || @using.Alias.Name == null)
{
continue;
}
@using.Alias.Name.Identifier.ValueText.AppendToAliasNameSet(builder);
}
}
private void AppendAliasNames(IEnumerable<NamespaceDeclarationSyntax> namespaces, ImmutableHashSet<string>.Builder builder, CancellationToken cancellationToken)
{
foreach (var @namespace in namespaces)
{
cancellationToken.ThrowIfCancellationRequested();
AppendAliasNames(@namespace.Usings, builder);
AppendAliasNames(@namespace.Members.OfType<NamespaceDeclarationSyntax>(), builder, cancellationToken);
}
}
public ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode forEachStatement)
{
if (forEachStatement is CommonForEachStatementSyntax csforEachStatement)
{
var info = semanticModel.GetForEachStatementInfo(csforEachStatement);
return new ForEachSymbols(
info.GetEnumeratorMethod,
info.MoveNextMethod,
info.CurrentProperty,
info.DisposeMethod,
info.ElementType);
}
else
{
return default;
}
}
public IMethodSymbol GetGetAwaiterMethod(SemanticModel semanticModel, SyntaxNode node)
{
if (node is AwaitExpressionSyntax awaitExpression)
{
var info = semanticModel.GetAwaitExpressionInfo(awaitExpression);
return info.GetAwaiterMethod;
}
return null;
}
public ImmutableArray<IMethodSymbol> GetDeconstructionAssignmentMethods(SemanticModel semanticModel, SyntaxNode node)
{
if (node is AssignmentExpressionSyntax assignment && assignment.IsDeconstruction())
{
var builder = ArrayBuilder<IMethodSymbol>.GetInstance();
FlattenDeconstructionMethods(semanticModel.GetDeconstructionInfo(assignment), builder);
return builder.ToImmutableAndFree();
}
return ImmutableArray<IMethodSymbol>.Empty;
}
public ImmutableArray<IMethodSymbol> GetDeconstructionForEachMethods(SemanticModel semanticModel, SyntaxNode node)
{
if (node is ForEachVariableStatementSyntax @foreach)
{
var builder = ArrayBuilder<IMethodSymbol>.GetInstance();
FlattenDeconstructionMethods(semanticModel.GetDeconstructionInfo(@foreach), builder);
return builder.ToImmutableAndFree();
}
return ImmutableArray<IMethodSymbol>.Empty;
}
private static void FlattenDeconstructionMethods(DeconstructionInfo deconstruction, ArrayBuilder<IMethodSymbol> builder)
{
var method = deconstruction.Method;
if (method != null)
{
builder.Add(method);
}
foreach (var nested in deconstruction.Nested)
{
FlattenDeconstructionMethods(nested, builder);
}
}
public bool IsPartial(ITypeSymbol typeSymbol, CancellationToken cancellationToken)
{
var syntaxRefs = typeSymbol.DeclaringSyntaxReferences;
return syntaxRefs.Any(n => ((BaseTypeDeclarationSyntax)n.GetSyntax(cancellationToken)).Modifiers.Any(SyntaxKind.PartialKeyword));
}
public IEnumerable<ISymbol> GetDeclaredSymbols(
SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken)
{
if (memberDeclaration is FieldDeclarationSyntax field)
{
return field.Declaration.Variables.Select(
v => semanticModel.GetDeclaredSymbol(v, cancellationToken));
}
return SpecializedCollections.SingletonEnumerable(
semanticModel.GetDeclaredSymbol(memberDeclaration, cancellationToken));
}
public IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken)
=> ((ArgumentSyntax)argumentNode).DetermineParameter(semanticModel, allowParams: false, cancellationToken);
public ImmutableArray<ISymbol> GetBestOrAllSymbols(SemanticModel semanticModel, SyntaxNode node, SyntaxToken token, CancellationToken cancellationToken)
{
if (node == null)
return ImmutableArray<ISymbol>.Empty;
return node switch
{
AssignmentExpressionSyntax _ when token.Kind() == SyntaxKind.EqualsToken => GetDeconstructionAssignmentMethods(semanticModel, node).As<ISymbol>(),
ForEachVariableStatementSyntax _ when token.Kind() == SyntaxKind.InKeyword => GetDeconstructionForEachMethods(semanticModel, node).As<ISymbol>(),
_ => GetSymbolInfo(semanticModel, node, token, cancellationToken).GetBestOrAllSymbols(),
};
}
private static SymbolInfo GetSymbolInfo(SemanticModel semanticModel, SyntaxNode node, SyntaxToken token, CancellationToken cancellationToken)
{
switch (node)
{
case OrderByClauseSyntax orderByClauseSyntax:
if (token.Kind() == SyntaxKind.CommaToken)
{
// Returning SymbolInfo for a comma token is the last resort
// in an order by clause if no other tokens to bind to a are present.
// See also the proposal at https://github.com/dotnet/roslyn/issues/23394
var separators = orderByClauseSyntax.Orderings.GetSeparators().ToImmutableList();
var index = separators.IndexOf(token);
if (index >= 0 && (index + 1) < orderByClauseSyntax.Orderings.Count)
{
var ordering = orderByClauseSyntax.Orderings[index + 1];
if (ordering.AscendingOrDescendingKeyword.Kind() == SyntaxKind.None)
{
return semanticModel.GetSymbolInfo(ordering, cancellationToken);
}
}
}
else if (orderByClauseSyntax.Orderings[0].AscendingOrDescendingKeyword.Kind() == SyntaxKind.None)
{
// The first ordering is displayed on the "orderby" keyword itself if there isn't a
// ascending/descending keyword.
return semanticModel.GetSymbolInfo(orderByClauseSyntax.Orderings[0], cancellationToken);
}
return default;
case QueryClauseSyntax queryClauseSyntax:
var queryInfo = semanticModel.GetQueryClauseInfo(queryClauseSyntax, cancellationToken);
var hasCastInfo = queryInfo.CastInfo.Symbol != null;
var hasOperationInfo = queryInfo.OperationInfo.Symbol != null;
if (hasCastInfo && hasOperationInfo)
{
// In some cases a single clause binds to more than one method. In those cases
// the tokens in the clause determine which of the two SymbolInfos are returned.
// See also the proposal at https://github.com/dotnet/roslyn/issues/23394
return token.IsKind(SyntaxKind.InKeyword) ? queryInfo.CastInfo : queryInfo.OperationInfo;
}
if (hasCastInfo)
{
return queryInfo.CastInfo;
}
return queryInfo.OperationInfo;
}
//Only in the orderby clause a comma can bind to a symbol.
if (token.IsKind(SyntaxKind.CommaToken))
{
return default;
}
return semanticModel.GetSymbolInfo(node, cancellationToken);
}
public bool IsInsideNameOfExpression(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> (node as ExpressionSyntax).IsInsideNameOfExpression(semanticModel, cancellationToken);
}
}
......@@ -4,6 +4,7 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.Shared.Extensions;
......@@ -11,9 +12,11 @@
namespace Microsoft.CodeAnalysis.LanguageServices
{
internal abstract class AbstractSemanticFactsService
internal abstract class AbstractSemanticFactsService : ISemanticFacts
{
protected abstract ISyntaxFacts SyntaxFacts { get; }
protected abstract ISemanticFacts SemanticFacts { get; }
protected abstract SyntaxToken ToIdentifierToken(string identifier);
public SyntaxToken GenerateUniqueName(
......@@ -80,5 +83,74 @@ public SyntaxToken GenerateUniqueName(string baseName, IEnumerable<string> usedN
NameGenerator.EnsureUniqueness(
baseName, usedNames, this.SyntaxFacts.IsCaseSensitive));
}
#region ISemanticFacts implementation
public bool SupportsImplicitInterfaceImplementation => SemanticFacts.SupportsImplicitInterfaceImplementation;
public bool SupportsParameterizedProperties => SemanticFacts.SupportsParameterizedProperties;
public bool ExposesAnonymousFunctionParameterNames => SemanticFacts.ExposesAnonymousFunctionParameterNames;
public bool IsWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsWrittenTo(semanticModel, node, cancellationToken);
public bool IsOnlyWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsOnlyWrittenTo(semanticModel, node, cancellationToken);
public bool IsInOutContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsInOutContext(semanticModel, node, cancellationToken);
public bool IsInRefContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsInRefContext(semanticModel, node, cancellationToken);
public bool IsInInContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsInInContext(semanticModel, node, cancellationToken);
public bool CanReplaceWithRValue(SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken)
=> SemanticFacts.CanReplaceWithRValue(semanticModel, expression, cancellationToken);
public string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken)
=> SemanticFacts.GenerateNameForExpression(semanticModel, expression, capitalize, cancellationToken);
public ISymbol GetDeclaredSymbol(SemanticModel semanticModel, SyntaxToken token, CancellationToken cancellationToken)
=> SemanticFacts.GetDeclaredSymbol(semanticModel, token, cancellationToken);
public bool LastEnumValueHasInitializer(INamedTypeSymbol namedTypeSymbol)
=> SemanticFacts.LastEnumValueHasInitializer(namedTypeSymbol);
public bool TryGetSpeculativeSemanticModel(SemanticModel oldSemanticModel, SyntaxNode oldNode, SyntaxNode newNode, out SemanticModel speculativeModel)
=> SemanticFacts.TryGetSpeculativeSemanticModel(oldSemanticModel, oldNode, newNode, out speculativeModel);
public ImmutableHashSet<string> GetAliasNameSet(SemanticModel model, CancellationToken cancellationToken)
=> SemanticFacts.GetAliasNameSet(model, cancellationToken);
public ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode forEachStatement)
=> SemanticFacts.GetForEachSymbols(semanticModel, forEachStatement);
public IMethodSymbol GetGetAwaiterMethod(SemanticModel semanticModel, SyntaxNode node)
=> SemanticFacts.GetGetAwaiterMethod(semanticModel, node);
public ImmutableArray<IMethodSymbol> GetDeconstructionAssignmentMethods(SemanticModel semanticModel, SyntaxNode node)
=> SemanticFacts.GetDeconstructionAssignmentMethods(semanticModel, node);
public ImmutableArray<IMethodSymbol> GetDeconstructionForEachMethods(SemanticModel semanticModel, SyntaxNode node)
=> SemanticFacts.GetDeconstructionForEachMethods(semanticModel, node);
public bool IsPartial(ITypeSymbol typeSymbol, CancellationToken cancellationToken)
=> SemanticFacts.IsPartial(typeSymbol, cancellationToken);
public IEnumerable<ISymbol> GetDeclaredSymbols(SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken)
=> SemanticFacts.GetDeclaredSymbols(semanticModel, memberDeclaration, cancellationToken);
public IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken)
=> SemanticFacts.FindParameterForArgument(semanticModel, argumentNode, cancellationToken);
public ImmutableArray<ISymbol> GetBestOrAllSymbols(SemanticModel semanticModel, SyntaxNode node, SyntaxToken token, CancellationToken cancellationToken)
=> SemanticFacts.GetBestOrAllSymbols(semanticModel, node, token, cancellationToken);
public bool IsInsideNameOfExpression(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken)
=> SemanticFacts.IsInsideNameOfExpression(semanticModel, node, cancellationToken);
#endregion
}
}
......@@ -4,43 +4,13 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
using Microsoft.CodeAnalysis.Host;
namespace Microsoft.CodeAnalysis.LanguageServices
{
internal interface ISemanticFactsService : ILanguageService
internal interface ISemanticFactsService : ISemanticFacts, ILanguageService
{
/// <summary>
/// True if this language supports implementing an interface by signature only. If false,
/// implementations must specific explicitly which symbol they're implementing.
/// </summary>
bool SupportsImplicitInterfaceImplementation { get; }
bool SupportsParameterizedProperties { get; }
/// <summary>
/// True if anonymous functions in this language have signatures that include named
/// parameters that can be referenced later on when the function is invoked. Or, if the
/// anonymous function is simply a signature that will be assigned to a delegate, and the
/// delegate's parameter names are used when invoking.
///
/// For example, in VB one can do this:
///
/// dim v = Sub(x as Integer) Blah()
/// v(x:=4)
///
/// However, in C# that would need to be:
///
/// Action&lt;int&gt; v = (int x) => Blah();
/// v(obj:=4)
///
/// Note that in VB one can access 'x' outside of the declaration of the anonymous type.
/// While in C# 'x' can only be accessed within the anonymous type.
/// </summary>
bool ExposesAnonymousFunctionParameterNames { get; }
bool IsExpressionContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
bool IsStatementContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
bool IsTypeContext(SemanticModel semanticModel, int position, CancellationToken cancellationToken);
......@@ -54,54 +24,6 @@ internal interface ISemanticFactsService : ILanguageService
bool IsInExpressionTree(SemanticModel semanticModel, SyntaxNode node, INamedTypeSymbol expressionTypeOpt, CancellationToken cancellationToken);
/// <summary>
/// True if a write is performed to the given expression. Note: reads may also be performed
/// to the expression as well. For example, "++a". In this expression 'a' is both read from
/// and written to.
/// </summary>
bool IsWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
/// <summary>
/// True if a write is performed to the given expression. Note: unlike IsWrittenTo, this
/// will not return true if reads are performed on the expression as well. For example,
/// "++a" will return 'false'. However, 'a' in "out a" or "a = 1" will return true.
/// </summary>
bool IsOnlyWrittenTo(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
bool IsInOutContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
bool IsInRefContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
bool IsInInContext(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
bool CanReplaceWithRValue(SemanticModel semanticModel, SyntaxNode expression, CancellationToken cancellationToken);
string GenerateNameForExpression(SemanticModel semanticModel, SyntaxNode expression, bool capitalize, CancellationToken cancellationToken);
ISymbol GetDeclaredSymbol(SemanticModel semanticModel, SyntaxToken token, CancellationToken cancellationToken);
bool LastEnumValueHasInitializer(INamedTypeSymbol namedTypeSymbol);
/// <summary>
/// get all alias names defined in the semantic model
/// </summary>
ImmutableHashSet<string> GetAliasNameSet(SemanticModel model, CancellationToken cancellationToken);
ForEachSymbols GetForEachSymbols(SemanticModel semanticModel, SyntaxNode forEachStatement);
IMethodSymbol GetGetAwaiterMethod(SemanticModel semanticModel, SyntaxNode node);
ImmutableArray<IMethodSymbol> GetDeconstructionAssignmentMethods(SemanticModel semanticModel, SyntaxNode node);
ImmutableArray<IMethodSymbol> GetDeconstructionForEachMethods(SemanticModel semanticModel, SyntaxNode node);
bool IsPartial(ITypeSymbol typeSymbol, CancellationToken cancellationToken);
IEnumerable<ISymbol> GetDeclaredSymbols(SemanticModel semanticModel, SyntaxNode memberDeclaration, CancellationToken cancellationToken);
IParameterSymbol FindParameterForArgument(SemanticModel semanticModel, SyntaxNode argumentNode, CancellationToken cancellationToken);
#nullable enable
ImmutableArray<ISymbol> GetBestOrAllSymbols(SemanticModel semanticModel, SyntaxNode? node, SyntaxToken token, CancellationToken cancellationToken);
#nullable disable
SyntaxToken GenerateUniqueName(
SemanticModel semanticModel, SyntaxNode location,
SyntaxNode containerOpt, string baseName, CancellationToken cancellationToken);
......@@ -118,7 +40,5 @@ internal interface ISemanticFactsService : ILanguageService
SyntaxNode containerOpt, string baseName, CancellationToken cancellationToken);
SyntaxToken GenerateUniqueName(string baseName, IEnumerable<string> usedNames);
bool IsInsideNameOfExpression(SemanticModel semanticModel, SyntaxNode node, CancellationToken cancellationToken);
}
}
......@@ -48,7 +48,6 @@
<Compile Include="$(MSBuildThisFileDirectory)Log\LogAggregator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Log\AbstractLogAggregator.cs" />
<Compile Include="$(MSBuildThisFileDirectory)LanguageServices\SemanticsFactsService\AbstractSemanticFactsService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)LanguageServices\SemanticsFactsService\ForEachSymbols.cs" />
<Compile Include="$(MSBuildThisFileDirectory)LanguageServices\SemanticsFactsService\ISemanticFactsService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)LanguageServices\SymbolDeclarationService\ISymbolDeclarationService.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Rename\Annotations\RenameActionAnnotation.cs" />
......
......@@ -39,43 +39,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
result)
End Function
''' <summary>
''' Decompose a name or member access expression into its component parts.
''' </summary>
''' <param name="expression">The name or member access expression.</param>
''' <param name="qualifier">The qualifier (or left-hand-side) of the name expression. This may be null if there is no qualifier.</param>
''' <param name="name">The name of the expression.</param>
''' <param name="arity">The number of generic type parameters.</param>
<Extension()>
Public Sub DecomposeName(expression As ExpressionSyntax, ByRef qualifier As ExpressionSyntax, ByRef name As String, ByRef arity As Integer)
Select Case expression.Kind
Case SyntaxKind.SimpleMemberAccessExpression
Dim memberAccess = DirectCast(expression, MemberAccessExpressionSyntax)
qualifier = memberAccess.Expression
name = memberAccess.Name.Identifier.ValueText
arity = memberAccess.Name.Arity
Case SyntaxKind.QualifiedName
Dim qualifiedName = DirectCast(expression, QualifiedNameSyntax)
qualifier = qualifiedName.Left
name = qualifiedName.Right.Identifier.ValueText
arity = qualifiedName.Arity
Case SyntaxKind.GenericName
Dim genericName = DirectCast(expression, GenericNameSyntax)
qualifier = Nothing
name = genericName.Identifier.ValueText
arity = genericName.Arity
Case SyntaxKind.IdentifierName
Dim identifierName = DirectCast(expression, IdentifierNameSyntax)
qualifier = Nothing
name = identifierName.Identifier.ValueText
arity = 0
Case Else
qualifier = Nothing
name = Nothing
arity = 0
End Select
End Sub
<Extension()>
Public Function TryGetNameParts(expression As ExpressionSyntax, ByRef parts As IList(Of String)) As Boolean
Dim partsList = New List(Of String)
......@@ -244,160 +207,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.Extensions
Return simpleArgument IsNot Nothing AndAlso simpleArgument.NameColonEquals.Name Is expression
End Function
Private Function CanReplace(symbol As ISymbol) As Boolean
Select Case symbol.Kind
Case SymbolKind.Field,
SymbolKind.Local,
SymbolKind.Method,
SymbolKind.Parameter,
SymbolKind.Property,
SymbolKind.RangeVariable
Return True
End Select
Return False
End Function
<Extension>
Public Function CanReplaceWithRValue(expression As ExpressionSyntax, semanticModel As SemanticModel, cancellationToken As CancellationToken) As Boolean
Return expression IsNot Nothing AndAlso
Not expression.IsWrittenTo(semanticModel, cancellationToken) AndAlso
expression.CanReplaceWithLValue(semanticModel, cancellationToken)
End Function
<Extension>
Public Function CanReplaceWithLValue(expression As ExpressionSyntax, semanticModel As SemanticModel, cancellationToken As CancellationToken) As Boolean
#If False Then
' Things that are definitely illegal to replace
If ContainsImplicitMemberAccess(expression) Then
Return False
End If
#End If
If expression.IsKind(SyntaxKind.MyBaseExpression) OrElse
expression.IsKind(SyntaxKind.MyClassExpression) Then
Return False
End If
If Not (TypeOf expression Is ObjectCreationExpressionSyntax) AndAlso
Not (TypeOf expression Is AnonymousObjectCreationExpressionSyntax) Then
Dim symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken)
If Not symbolInfo.GetBestOrAllSymbols().All(AddressOf CanReplace) Then
' If the expression is actually a reference to a type, then it can't be replaced
' with an arbitrary expression.
Return False
End If
End If
' Technically, you could introduce an LValue for "Goo" in "Goo()" even if "Goo" binds
' to a method. (i.e. by assigning to a Func<...> type). However, this is so contrived
' and none of the features that use this extension consider this replaceable.
If TypeOf expression.Parent Is InvocationExpressionSyntax Then
' If something is being invoked, then it's either something like Goo(), Goo.Bar(), or
' SomeExpr() (i.e. Blah[1]()). In the first and second case, we only allow
' replacement if Goo and Goo.Bar didn't bind to a method. If we can't bind it, we'll
' assume it's a method and we don't allow it to be replaced either. However, if it's
' an arbitrary expression, we do allow replacement.
If expression.IsKind(SyntaxKind.IdentifierName) OrElse expression.IsKind(SyntaxKind.SimpleMemberAccessExpression) Then
Dim symbolInfo = semanticModel.GetSymbolInfo(expression, cancellationToken)
If Not symbolInfo.GetBestOrAllSymbols().Any() Then
Return False
End If
' don't allow it to be replaced if it is bound to an indexed property
Return Not symbolInfo.GetBestOrAllSymbols().OfType(Of IMethodSymbol)().Any() AndAlso
Not symbolInfo.GetBestOrAllSymbols().OfType(Of IPropertySymbol)().Any()
Else
Return True
End If
End If
' expression in next statement's control variables should match one in the head
Dim nextStatement = expression.FirstAncestorOrSelf(Of NextStatementSyntax)()
If nextStatement IsNot Nothing Then
Return False
End If
' Direct parent kind checks.
If expression.IsParentKind(SyntaxKind.EqualsValue) OrElse
expression.IsParentKind(SyntaxKind.ParenthesizedExpression) OrElse
expression.IsParentKind(SyntaxKind.SelectStatement) OrElse
expression.IsParentKind(SyntaxKind.SyncLockStatement) OrElse
expression.IsParentKind(SyntaxKind.CollectionInitializer) OrElse
expression.IsParentKind(SyntaxKind.InferredFieldInitializer) OrElse
expression.IsParentKind(SyntaxKind.BinaryConditionalExpression) OrElse
expression.IsParentKind(SyntaxKind.TernaryConditionalExpression) OrElse
expression.IsParentKind(SyntaxKind.ReturnStatement) OrElse
expression.IsParentKind(SyntaxKind.YieldStatement) OrElse
expression.IsParentKind(SyntaxKind.XmlEmbeddedExpression) OrElse
expression.IsParentKind(SyntaxKind.ThrowStatement) OrElse
expression.IsParentKind(SyntaxKind.IfStatement) OrElse
expression.IsParentKind(SyntaxKind.WhileStatement) OrElse
expression.IsParentKind(SyntaxKind.ElseIfStatement) OrElse
expression.IsParentKind(SyntaxKind.ForEachStatement) OrElse
expression.IsParentKind(SyntaxKind.ForStatement) OrElse
expression.IsParentKind(SyntaxKind.ConditionalAccessExpression) OrElse
expression.IsParentKind(SyntaxKind.TypeOfIsExpression) OrElse
expression.IsParentKind(SyntaxKind.TypeOfIsNotExpression) Then
Return True
End If
' Parent type checks
If TypeOf expression.Parent Is BinaryExpressionSyntax OrElse
TypeOf expression.Parent Is AssignmentStatementSyntax OrElse
TypeOf expression.Parent Is WhileOrUntilClauseSyntax OrElse
TypeOf expression.Parent Is SingleLineLambdaExpressionSyntax OrElse
TypeOf expression.Parent Is AwaitExpressionSyntax Then
Return True
End If
' Specific child checks.
If expression.CheckParent(Of NamedFieldInitializerSyntax)(Function(n) n.Expression Is expression) OrElse
expression.CheckParent(Of MemberAccessExpressionSyntax)(Function(m) m.Expression Is expression) OrElse
expression.CheckParent(Of TryCastExpressionSyntax)(Function(t) t.Expression Is expression) OrElse
expression.CheckParent(Of CatchFilterClauseSyntax)(Function(c) c.Filter Is expression) OrElse
expression.CheckParent(Of SimpleArgumentSyntax)(Function(n) n.Expression Is expression) OrElse
expression.CheckParent(Of DirectCastExpressionSyntax)(Function(d) d.Expression Is expression) OrElse
expression.CheckParent(Of FunctionAggregationSyntax)(Function(f) f.Argument Is expression) OrElse
expression.CheckParent(Of RangeArgumentSyntax)(Function(r) r.UpperBound Is expression) Then
Return True
End If
' Misc checks
If TypeOf expression.Parent Is ExpressionRangeVariableSyntax AndAlso
TypeOf expression.Parent.Parent Is QueryClauseSyntax Then
Dim rangeVariable = DirectCast(expression.Parent, ExpressionRangeVariableSyntax)
Dim selectClause = TryCast(rangeVariable.Parent, SelectClauseSyntax)
' Can't replace the expression in a select unless its the last select clause *or*
' it's a select of the form "select a = <expr>"
If selectClause IsNot Nothing Then
If rangeVariable.NameEquals IsNot Nothing Then
Return True
End If
Dim queryExpression = TryCast(selectClause.Parent, QueryExpressionSyntax)
If queryExpression IsNot Nothing Then
Return queryExpression.Clauses.Last() Is selectClause
End If
Dim aggregateClause = TryCast(selectClause.Parent, AggregateClauseSyntax)
If aggregateClause IsNot Nothing Then
Return aggregateClause.AdditionalQueryOperators().Last() Is selectClause
End If
Return False
End If
' Any other query type is ok. Note(cyrusn): This may be too broad.
Return True
End If
Return False
End Function
<Extension>
Public Function ContainsImplicitMemberAccess(expression As ExpressionSyntax) As Boolean
Return ContainsImplicitMemberAccessWorker(expression)
......
......@@ -2,17 +2,13 @@
' The .NET Foundation licenses this file to you under the MIT license.
' See the LICENSE file in the project root for more information.
Imports System.Collections.Immutable
Imports System.Composition
Imports System.Runtime.InteropServices
Imports System.Threading
Imports Microsoft.CodeAnalysis
Imports Microsoft.CodeAnalysis.Host
Imports Microsoft.CodeAnalysis.Host.Mef
Imports Microsoft.CodeAnalysis.LanguageServices
Imports Microsoft.CodeAnalysis.VisualBasic.Extensions.ContextQuery
Imports Microsoft.CodeAnalysis.VisualBasic.LanguageServices
Imports Microsoft.CodeAnalysis.VisualBasic.Syntax
Namespace Microsoft.CodeAnalysis.VisualBasic
<ExportLanguageServiceFactory(GetType(ISemanticFactsService), LanguageNames.VisualBasic), [Shared]>
......@@ -29,29 +25,18 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
End Function
End Class
Friend Class VisualBasicSemanticFactsService
Friend NotInheritable Class VisualBasicSemanticFactsService
Inherits AbstractSemanticFactsService
Implements ISemanticFactsService
Public Shared ReadOnly Instance As New VisualBasicSemanticFactsService()
Protected Overrides ReadOnly Property SyntaxFacts As ISyntaxFacts = VisualBasicSyntaxFacts.Instance
Protected Overrides ReadOnly Property SemanticFacts As ISemanticFacts = VisualBasicSemanticFacts.Instance
Private Sub New()
End Sub
Public ReadOnly Property SupportsImplicitInterfaceImplementation As Boolean Implements ISemanticFactsService.SupportsImplicitInterfaceImplementation
Get
Return False
End Get
End Property
Public ReadOnly Property ExposesAnonymousFunctionParameterNames As Boolean Implements ISemanticFactsService.ExposesAnonymousFunctionParameterNames
Get
Return True
End Get
End Property
Protected Overrides Function ToIdentifierToken(identifier As String) As SyntaxToken
Return identifier.ToIdentifierToken
End Function
......@@ -114,186 +99,10 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return tree.IsAttributeNameContext(position, token, cancellationToken)
End Function
Public Function IsOnlyWrittenTo(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsOnlyWrittenTo
Return TryCast(node, ExpressionSyntax).IsOnlyWrittenTo()
End Function
Public Function IsWrittenTo(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsWrittenTo
Return TryCast(node, ExpressionSyntax).IsWrittenTo(semanticModel, cancellationToken)
End Function
Public Function IsInOutContext(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsInOutContext
Return TryCast(node, ExpressionSyntax).IsInOutContext()
End Function
Public Function IsInRefContext(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsInRefContext
Return TryCast(node, ExpressionSyntax).IsInRefContext(semanticModel, cancellationToken)
End Function
Public Function IsInInContext(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsInInContext
Return TryCast(node, ExpressionSyntax).IsInInContext()
End Function
Public Function CanReplaceWithRValue(semanticModel As SemanticModel, expression As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.CanReplaceWithRValue
Return TryCast(expression, ExpressionSyntax).CanReplaceWithRValue(semanticModel, cancellationToken)
End Function
Public Function GenerateNameForExpression(semanticModel As SemanticModel,
expression As SyntaxNode,
capitalize As Boolean,
cancellationToken As CancellationToken) As String Implements ISemanticFactsService.GenerateNameForExpression
Return semanticModel.GenerateNameForExpression(
DirectCast(expression, ExpressionSyntax), capitalize, cancellationToken)
End Function
Public Function GetDeclaredSymbol(semanticModel As SemanticModel, token As SyntaxToken, cancellationToken As CancellationToken) As ISymbol Implements ISemanticFactsService.GetDeclaredSymbol
Dim location = token.GetLocation()
For Each ancestor In token.GetAncestors(Of SyntaxNode)()
If Not TypeOf ancestor Is AggregationRangeVariableSyntax AndAlso
Not TypeOf ancestor Is CollectionRangeVariableSyntax AndAlso
Not TypeOf ancestor Is ExpressionRangeVariableSyntax AndAlso
Not TypeOf ancestor Is InferredFieldInitializerSyntax Then
Dim symbol = semanticModel.GetDeclaredSymbol(ancestor)
If symbol IsNot Nothing Then
If symbol.Locations.Contains(location) Then
Return symbol
End If
' We found some symbol, but it defined something else. We're not going to have a higher node defining _another_ symbol with this token, so we can stop now.
Return Nothing
End If
' If we hit an executable statement syntax and didn't find anything yet, we can just stop now -- anything higher would be a member declaration which won't be defined by something inside a statement.
If SyntaxFacts.IsExecutableStatement(ancestor) Then
Return Nothing
End If
End If
Next
Return Nothing
End Function
Public Function LastEnumValueHasInitializer(namedTypeSymbol As INamedTypeSymbol) As Boolean Implements ISemanticFactsService.LastEnumValueHasInitializer
Dim enumStatement = namedTypeSymbol.DeclaringSyntaxReferences.Select(Function(r) r.GetSyntax()).OfType(Of EnumStatementSyntax).FirstOrDefault()
If enumStatement IsNot Nothing Then
Dim enumBlock = DirectCast(enumStatement.Parent, EnumBlockSyntax)
Dim lastMember = TryCast(enumBlock.Members.LastOrDefault(), EnumMemberDeclarationSyntax)
If lastMember IsNot Nothing Then
Return lastMember.Initializer IsNot Nothing
End If
End If
Return False
End Function
Public ReadOnly Property SupportsParameterizedProperties As Boolean Implements ISemanticFactsService.SupportsParameterizedProperties
Get
Return True
End Get
End Property
Public Function GetAliasNameSet(model As SemanticModel, cancellationToken As CancellationToken) As ImmutableHashSet(Of String) Implements ISemanticFactsService.GetAliasNameSet
Dim original = DirectCast(model.GetOriginalSemanticModel(), SemanticModel)
If Not original.SyntaxTree.HasCompilationUnitRoot Then
Return ImmutableHashSet.Create(Of String)()
End If
Dim root = original.SyntaxTree.GetCompilationUnitRoot()
Dim builder = ImmutableHashSet.CreateBuilder(Of String)(StringComparer.OrdinalIgnoreCase)
For Each globalImport In original.Compilation.AliasImports
globalImport.Name.AppendToAliasNameSet(builder)
Next
For Each importsClause In root.GetAliasImportsClauses()
importsClause.Alias.Identifier.ValueText.AppendToAliasNameSet(builder)
Next
Return builder.ToImmutable()
End Function
Public Function GetForEachSymbols(model As SemanticModel, forEachStatement As SyntaxNode) As ForEachSymbols Implements ISemanticFactsService.GetForEachSymbols
Dim vbForEachStatement = TryCast(forEachStatement, ForEachStatementSyntax)
If vbForEachStatement IsNot Nothing Then
Dim info = model.GetForEachStatementInfo(vbForEachStatement)
Return New ForEachSymbols(
info.GetEnumeratorMethod,
info.MoveNextMethod,
info.CurrentProperty,
info.DisposeMethod,
info.ElementType)
End If
Dim vbForBlock = TryCast(forEachStatement, ForEachBlockSyntax)
If vbForBlock IsNot Nothing Then
Dim info = model.GetForEachStatementInfo(vbForBlock)
Return New ForEachSymbols(
info.GetEnumeratorMethod,
info.MoveNextMethod,
info.CurrentProperty,
info.DisposeMethod,
info.ElementType)
End If
Return Nothing
End Function
Public Function GetGetAwaiterMethod(model As SemanticModel, node As SyntaxNode) As IMethodSymbol Implements ISemanticFactsService.GetGetAwaiterMethod
If node.IsKind(SyntaxKind.AwaitExpression) Then
Dim awaitExpression = DirectCast(node, AwaitExpressionSyntax)
Dim info = model.GetAwaitExpressionInfo(awaitExpression)
Return info.GetAwaiterMethod
End If
Return Nothing
End Function
Public Function GetDeconstructionAssignmentMethods(model As SemanticModel, deconstruction As SyntaxNode) As ImmutableArray(Of IMethodSymbol) Implements ISemanticFactsService.GetDeconstructionAssignmentMethods
Return ImmutableArray(Of IMethodSymbol).Empty
End Function
Public Function GetDeconstructionForEachMethods(model As SemanticModel, deconstruction As SyntaxNode) As ImmutableArray(Of IMethodSymbol) Implements ISemanticFactsService.GetDeconstructionForEachMethods
Return ImmutableArray(Of IMethodSymbol).Empty
End Function
Public Function IsNamespaceDeclarationNameContext(semanticModel As SemanticModel, position As Integer, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsNamespaceDeclarationNameContext
Return semanticModel.SyntaxTree.IsNamespaceDeclarationNameContext(position, cancellationToken)
End Function
Public Function IsPartial(typeSymbol As ITypeSymbol, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsPartial
Dim syntaxRefs = typeSymbol.DeclaringSyntaxReferences
Return syntaxRefs.Any(
Function(n As SyntaxReference)
Return DirectCast(n.GetSyntax(cancellationToken), TypeStatementSyntax).Modifiers.Any(SyntaxKind.PartialKeyword)
End Function)
End Function
Public Function GetDeclaredSymbols(semanticModel As SemanticModel, memberDeclaration As SyntaxNode, cancellationToken As CancellationToken) As IEnumerable(Of ISymbol) Implements ISemanticFactsService.GetDeclaredSymbols
If TypeOf memberDeclaration Is FieldDeclarationSyntax Then
Return DirectCast(memberDeclaration, FieldDeclarationSyntax).Declarators.
SelectMany(Function(d) d.Names.AsEnumerable()).
Select(Function(n) semanticModel.GetDeclaredSymbol(n, cancellationToken))
End If
Return SpecializedCollections.SingletonEnumerable(semanticModel.GetDeclaredSymbol(memberDeclaration, cancellationToken))
End Function
Public Function FindParameterForArgument(semanticModel As SemanticModel, argumentNode As SyntaxNode, cancellationToken As CancellationToken) As IParameterSymbol Implements ISemanticFactsService.FindParameterForArgument
Return DirectCast(argumentNode, ArgumentSyntax).DetermineParameter(semanticModel, allowParamArray:=False, cancellationToken)
End Function
Public Function GetBestOrAllSymbols(semanticModel As SemanticModel, node As SyntaxNode, token As SyntaxToken, cancellationToken As CancellationToken) As ImmutableArray(Of ISymbol) Implements ISemanticFactsService.GetBestOrAllSymbols
Return If(node Is Nothing,
ImmutableArray(Of ISymbol).Empty,
semanticModel.GetSymbolInfo(node, cancellationToken).GetBestOrAllSymbols())
End Function
Private Function ISemanticFactsService_GenerateUniqueName(
semanticModel As SemanticModel, location As SyntaxNode, containerOpt As SyntaxNode, baseName As String, cancellationToken As CancellationToken) As SyntaxToken Implements ISemanticFactsService.GenerateUniqueName
Return MyBase.GenerateUniqueName(semanticModel, location, containerOpt, baseName, cancellationToken)
......@@ -309,10 +118,6 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return MyBase.GenerateUniqueLocalName(semanticModel, location, containerOpt, baseName, cancellationToken)
End Function
Public Function IsInsideNameOfExpression(semanticModel As SemanticModel, node As SyntaxNode, cancellationToken As CancellationToken) As Boolean Implements ISemanticFactsService.IsInsideNameOfExpression
Return node.FirstAncestorOrSelf(Of NameOfExpressionSyntax) IsNot Nothing
End Function
Private Function ISemanticFactsService_GenerateUniqueName(semanticModel As SemanticModel, location As SyntaxNode, containerOpt As SyntaxNode, baseName As String, filter As Func(Of ISymbol, Boolean), usedNames As IEnumerable(Of String), cancellationToken As CancellationToken) As SyntaxToken Implements ISemanticFactsService.GenerateUniqueName
Return MyBase.GenerateUniqueName(semanticModel, location, containerOpt, baseName, filter, usedNames, cancellationToken)
End Function
......
......@@ -28,7 +28,6 @@
<Compile Include="$(MSBuildThisFileDirectory)Extensions\NameSyntaxExtensions.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ObjectCreationExpressionExtensions.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\ParameterSyntaxExtensions.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SemanticModelExtensions.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SimpleNameSyntaxExtensions.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\StringExtensions.vb" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\SymbolDisplayPartExtensions.vb" />
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册