提交 71c79f22 编写于 作者: C Cyrus Najmabadi

Switch 'use auto prop' to use NRT and make us more null safe.

上级 41fa23e1
......@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System.Collections.Generic;
using System.Linq;
using System.Threading;
......@@ -47,18 +49,18 @@ protected override bool CanExplicitInterfaceImplementationsBeFixed()
MemberDeclarationSyntax member,
List<AnalysisResult> analysisResults)
{
if (member.IsKind(SyntaxKind.NamespaceDeclaration, out NamespaceDeclarationSyntax namespaceDeclaration))
if (member.IsKind(SyntaxKind.NamespaceDeclaration, out NamespaceDeclarationSyntax? namespaceDeclaration))
{
AnalyzeMembers(context, namespaceDeclaration.Members, analysisResults);
}
else if (member.IsKind(SyntaxKind.ClassDeclaration, out TypeDeclarationSyntax typeDeclaration) ||
else if (member.IsKind(SyntaxKind.ClassDeclaration, out TypeDeclarationSyntax? typeDeclaration) ||
member.IsKind(SyntaxKind.StructDeclaration, out typeDeclaration) ||
member.IsKind(SyntaxKindEx.RecordDeclaration, out typeDeclaration))
{
// If we have a class or struct, recurse inwards.
AnalyzeMembers(context, typeDeclaration.Members, analysisResults);
}
else if (member.IsKind(SyntaxKind.PropertyDeclaration, out PropertyDeclarationSyntax propertyDeclaration))
else if (member.IsKind(SyntaxKind.PropertyDeclaration, out PropertyDeclarationSyntax? propertyDeclaration))
{
AnalyzeProperty(context, propertyDeclaration, analysisResults);
}
......@@ -68,7 +70,7 @@ protected override bool CanExplicitInterfaceImplementationsBeFixed()
List<AnalysisResult> analysisResults, HashSet<IFieldSymbol> ineligibleFields,
Compilation compilation, CancellationToken cancellationToken)
{
var groups = analysisResults.Select(r => (typeDeclaration: (TypeDeclarationSyntax)r.PropertyDeclaration.Parent, r.SemanticModel))
var groups = analysisResults.Select(r => (typeDeclaration: (TypeDeclarationSyntax)r.PropertyDeclaration.Parent!, r.SemanticModel))
.Distinct()
.GroupBy(n => n.typeDeclaration.SyntaxTree);
......@@ -94,7 +96,7 @@ protected override bool CanExplicitInterfaceImplementationsBeFixed()
}
}
protected override ExpressionSyntax GetFieldInitializer(
protected override ExpressionSyntax? GetFieldInitializer(
VariableDeclaratorSyntax variable, CancellationToken cancellationToken)
{
return variable.Initializer?.Value;
......@@ -111,7 +113,7 @@ protected override bool CanExplicitInterfaceImplementationsBeFixed()
AddIneligibleField(symbol);
}
void AddIneligibleField(ISymbol symbol)
void AddIneligibleField(ISymbol? symbol)
{
if (symbol is IFieldSymbol field)
{
......@@ -122,7 +124,7 @@ void AddIneligibleField(ISymbol symbol)
private static bool CheckExpressionSyntactically(ExpressionSyntax expression)
{
if (expression.IsKind(SyntaxKind.SimpleMemberAccessExpression, out MemberAccessExpressionSyntax memberAccessExpression))
if (expression.IsKind(SyntaxKind.SimpleMemberAccessExpression, out MemberAccessExpressionSyntax? memberAccessExpression))
{
return memberAccessExpression.Expression.Kind() == SyntaxKind.ThisExpression &&
memberAccessExpression.Name.Kind() == SyntaxKind.IdentifierName;
......@@ -135,7 +137,7 @@ private static bool CheckExpressionSyntactically(ExpressionSyntax expression)
return false;
}
protected override ExpressionSyntax GetGetterExpression(IMethodSymbol getMethod, CancellationToken cancellationToken)
protected override ExpressionSyntax? GetGetterExpression(IMethodSymbol getMethod, CancellationToken cancellationToken)
{
// Getter has to be of the form:
// 1. Getter can be defined as accessor or expression bodied lambda
......@@ -154,7 +156,7 @@ protected override ExpressionSyntax GetGetterExpression(IMethodSymbol getMethod,
return CheckExpressionSyntactically(expr) ? expr : null;
}
private static ExpressionSyntax GetGetterExpressionFromSymbol(IMethodSymbol getMethod, CancellationToken cancellationToken)
private static ExpressionSyntax? GetGetterExpressionFromSymbol(IMethodSymbol getMethod, CancellationToken cancellationToken)
{
var declaration = getMethod.DeclaringSyntaxReferences[0].GetSyntax(cancellationToken);
switch (declaration)
......@@ -169,7 +171,7 @@ private static ExpressionSyntax GetGetterExpressionFromSymbol(IMethodSymbol getM
}
}
private static T GetSingleStatementFromAccessor<T>(AccessorDeclarationSyntax accessorDeclaration) where T : StatementSyntax
private static T? GetSingleStatementFromAccessor<T>(AccessorDeclarationSyntax? accessorDeclaration) where T : StatementSyntax
{
var statements = accessorDeclaration?.Body?.Statements;
if (statements?.Count == 1)
......@@ -181,7 +183,7 @@ private static ExpressionSyntax GetGetterExpressionFromSymbol(IMethodSymbol getM
return null;
}
protected override ExpressionSyntax GetSetterExpression(
protected override ExpressionSyntax? GetSetterExpression(
IMethodSymbol setMethod, SemanticModel semanticModel, CancellationToken cancellationToken)
{
// Setter has to be of the form:
......@@ -205,7 +207,7 @@ private static ExpressionSyntax GetGetterExpressionFromSymbol(IMethodSymbol getM
return null;
}
private static ExpressionSyntax GetExpressionFromSetter(AccessorDeclarationSyntax setAccessor)
private static ExpressionSyntax? GetExpressionFromSetter(AccessorDeclarationSyntax? setAccessor)
=> setAccessor?.ExpressionBody?.Expression ??
GetSingleStatementFromAccessor<ExpressionStatementSyntax>(setAccessor)?.Expression;
......
......@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Threading;
......@@ -13,7 +15,10 @@
namespace Microsoft.CodeAnalysis.UseAutoProperty
{
internal abstract class AbstractUseAutoPropertyAnalyzer<
TPropertyDeclaration, TFieldDeclaration, TVariableDeclarator, TExpression> : AbstractBuiltInCodeStyleDiagnosticAnalyzer
TPropertyDeclaration,
TFieldDeclaration,
TVariableDeclarator,
TExpression> : AbstractBuiltInCodeStyleDiagnosticAnalyzer
where TPropertyDeclaration : SyntaxNode
where TFieldDeclaration : SyntaxNode
where TVariableDeclarator : SyntaxNode
......@@ -34,9 +39,9 @@ protected AbstractUseAutoPropertyAnalyzer()
protected abstract bool SupportsReadOnlyProperties(Compilation compilation);
protected abstract bool SupportsPropertyInitializer(Compilation compilation);
protected abstract bool CanExplicitInterfaceImplementationsBeFixed();
protected abstract TExpression GetFieldInitializer(TVariableDeclarator variable, CancellationToken cancellationToken);
protected abstract TExpression GetGetterExpression(IMethodSymbol getMethod, CancellationToken cancellationToken);
protected abstract TExpression GetSetterExpression(IMethodSymbol setMethod, SemanticModel semanticModel, CancellationToken cancellationToken);
protected abstract TExpression? GetFieldInitializer(TVariableDeclarator variable, CancellationToken cancellationToken);
protected abstract TExpression? GetGetterExpression(IMethodSymbol getMethod, CancellationToken cancellationToken);
protected abstract TExpression? GetSetterExpression(IMethodSymbol setMethod, SemanticModel semanticModel, CancellationToken cancellationToken);
protected abstract SyntaxNode GetNodeToFade(TFieldDeclaration fieldDeclaration, TVariableDeclarator variableDeclarator);
protected abstract void RegisterIneligibleFieldsAction(
......@@ -213,7 +218,7 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
return;
}
if (!(variableDeclarator?.Parent?.Parent is TFieldDeclaration fieldDeclaration))
if (!(variableDeclarator.Parent?.Parent is TFieldDeclaration fieldDeclaration))
{
return;
}
......@@ -230,26 +235,27 @@ private void AnalyzeSemanticModel(SemanticModelAnalysisContext context)
}
// Looks like a viable property/field to convert into an auto property.
analysisResults.Add(new AnalysisResult(property, getterField, propertyDeclaration,
fieldDeclaration, variableDeclarator, semanticModel, property.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)));
analysisResults.Add(new AnalysisResult(
property, getterField, propertyDeclaration, fieldDeclaration, variableDeclarator, semanticModel,
property.ToDisplayString(SymbolDisplayFormat.FullyQualifiedFormat)));
}
protected virtual bool CanConvert(IPropertySymbol property)
=> true;
private IFieldSymbol GetSetterField(
private IFieldSymbol? GetSetterField(
SemanticModel semanticModel, IMethodSymbol setMethod, CancellationToken cancellationToken)
{
return CheckFieldAccessExpression(semanticModel, GetSetterExpression(setMethod, semanticModel, cancellationToken));
}
private IFieldSymbol GetGetterField(
private IFieldSymbol? GetGetterField(
SemanticModel semanticModel, IMethodSymbol getMethod, CancellationToken cancellationToken)
{
return CheckFieldAccessExpression(semanticModel, GetGetterExpression(getMethod, cancellationToken));
}
private static IFieldSymbol CheckFieldAccessExpression(SemanticModel semanticModel, TExpression expression)
private static IFieldSymbol? CheckFieldAccessExpression(SemanticModel semanticModel, TExpression? expression)
{
if (expression == null)
{
......@@ -310,7 +316,8 @@ private void Process(AnalysisResult result, SemanticModelAnalysisContext context
// an auto property. For each diagnostic store both location so we can easily retrieve
// them when performing the code fix.
var additionalLocations = ImmutableArray.Create(
propertyDeclaration.GetLocation(), variableDeclarator.GetLocation());
propertyDeclaration.GetLocation(),
variableDeclarator.GetLocation());
var option = context.GetOption(CodeStyleOptions2.PreferAutoProperties, propertyDeclaration.Language);
if (option.Notification.Severity == ReportDiagnostic.Suppress)
......
......@@ -27,6 +27,9 @@ public CSharpUseAutoPropertyCodeFixProvider()
{
}
protected override PropertyDeclarationSyntax GetPropertyDeclaration(SyntaxNode node)
=> (PropertyDeclarationSyntax)node;
protected override SyntaxNode GetNodeToRemove(VariableDeclaratorSyntax declarator)
{
var fieldDeclaration = (FieldDeclarationSyntax)declarator.Parent.Parent;
......
......@@ -2,6 +2,8 @@
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
#nullable enable
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
......@@ -36,6 +38,7 @@ public sealed override ImmutableArray<string> FixableDiagnosticIds
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
protected abstract TPropertyDeclaration GetPropertyDeclaration(SyntaxNode node);
protected abstract SyntaxNode GetNodeToRemove(TVariableDeclarator declarator);
protected abstract IEnumerable<AbstractFormattingRule> GetFormattingRules(Document document);
......@@ -69,21 +72,21 @@ private async Task<Solution> ProcessResultAsync(CodeFixContext context, Diagnost
var propertyLocation = locations[0];
var declaratorLocation = locations[1];
var declarator = declaratorLocation.FindToken(cancellationToken).Parent.FirstAncestorOrSelf<TVariableDeclarator>();
var fieldDocument = context.Document.Project.GetDocument(declarator.SyntaxTree);
var fieldSemanticModel = await fieldDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var solution = context.Document.Project.Solution;
var declarator = (TVariableDeclarator)declaratorLocation.FindNode(cancellationToken);
var fieldDocument = solution.GetRequiredDocument(declarator.SyntaxTree);
var fieldSemanticModel = await fieldDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var fieldSymbol = (IFieldSymbol)fieldSemanticModel.GetDeclaredSymbol(declarator, cancellationToken);
var property = propertyLocation.FindToken(cancellationToken).Parent.FirstAncestorOrSelf<TPropertyDeclaration>();
var propertyDocument = context.Document.Project.GetDocument(property.SyntaxTree);
var propertySemanticModel = await propertyDocument.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var property = GetPropertyDeclaration(propertyLocation.FindNode(cancellationToken));
var propertyDocument = solution.GetRequiredDocument(property.SyntaxTree);
var propertySemanticModel = await propertyDocument.GetRequiredSemanticModelAsync(cancellationToken).ConfigureAwait(false);
var propertySymbol = (IPropertySymbol)propertySemanticModel.GetDeclaredSymbol(property, cancellationToken);
Debug.Assert(fieldDocument.Project == propertyDocument.Project);
var project = fieldDocument.Project;
var compilation = await project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
var compilation = await project.GetRequiredCompilationAsync(cancellationToken).ConfigureAwait(false);
var solution = context.Document.Project.Solution;
var fieldLocations = await Renamer.FindRenameLocationsAsync(
solution, fieldSymbol, RenameOptionSet.From(solution), cancellationToken).ConfigureAwait(false);
......@@ -127,7 +130,8 @@ private async Task<Solution> ProcessResultAsync(CodeFixContext context, Diagnost
// same as the property we're trying to get the references pointing to.
var filteredLocations = fieldLocations.Filter(
location => !location.IntersectsWith(declaratorLocation) &&
location => location.SourceTree != null &&
!location.IntersectsWith(declaratorLocation) &&
CanEditDocument(solution, location.SourceTree, linkedFiles, canEdit));
var resolution = await filteredLocations.ResolveConflictsAsync(
......@@ -140,19 +144,18 @@ private async Task<Solution> ProcessResultAsync(CodeFixContext context, Diagnost
solution = resolution.NewSolution;
// Now find the field and property again post rename.
fieldDocument = solution.GetDocument(fieldDocument.Id);
propertyDocument = solution.GetDocument(propertyDocument.Id);
fieldDocument = solution.GetRequiredDocument(fieldDocument.Id);
propertyDocument = solution.GetRequiredDocument(propertyDocument.Id);
Debug.Assert(fieldDocument.Project == propertyDocument.Project);
compilation = await fieldDocument.Project.GetCompilationAsync(cancellationToken).ConfigureAwait(false);
fieldSymbol = (IFieldSymbol)fieldSymbol.GetSymbolKey(cancellationToken).Resolve(compilation, cancellationToken: cancellationToken).Symbol;
propertySymbol = (IPropertySymbol)propertySymbol.GetSymbolKey(cancellationToken).Resolve(compilation, cancellationToken: cancellationToken).Symbol;
Debug.Assert(fieldSymbol != null && propertySymbol != null);
Contract.ThrowIfTrue(fieldSymbol == null || propertySymbol == null);
declarator = (TVariableDeclarator)await fieldSymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
var temp = await propertySymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false);
property = temp.FirstAncestorOrSelf<TPropertyDeclaration>();
property = GetPropertyDeclaration(await propertySymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false));
var nodeToRemove = GetNodeToRemove(declarator);
......@@ -187,7 +190,7 @@ private async Task<Solution> ProcessResultAsync(CodeFixContext context, Diagnost
// because there's nothing to actually separate it from.
if (fieldDocument == propertyDocument)
{
var syntaxFacts = fieldDocument.GetLanguageService<ISyntaxFactsService>();
var syntaxFacts = fieldDocument.GetRequiredLanguageService<ISyntaxFactsService>();
if (WillRemoveFirstFieldInTypeDirectlyAboveProperty(syntaxFacts, property, nodeToRemove) &&
syntaxFacts.GetLeadingBlankLines(nodeToRemove).Length == 0)
{
......@@ -213,10 +216,11 @@ private async Task<Solution> ProcessResultAsync(CodeFixContext context, Diagnost
else
{
// In different files. Just update both files.
var fieldTreeRoot = await fieldDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var propertyTreeRoot = await propertyDocument.GetSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var fieldTreeRoot = await fieldDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var propertyTreeRoot = await propertyDocument.GetRequiredSyntaxRootAsync(cancellationToken).ConfigureAwait(false);
var newFieldTreeRoot = fieldTreeRoot.RemoveNode(nodeToRemove, syntaxRemoveOptions);
Contract.ThrowIfNull(newFieldTreeRoot);
var newPropertyTreeRoot = propertyTreeRoot.ReplaceNode(property, updatedProperty);
newFieldTreeRoot = await FormatAsync(newFieldTreeRoot, fieldDocument, cancellationToken).ConfigureAwait(false);
......@@ -311,7 +315,7 @@ private async Task<SyntaxNode> FormatAsync(SyntaxNode newRoot, Document document
return false;
}
var syntaxFacts = solution.GetDocument(location.DocumentId).GetLanguageService<ISyntaxFactsService>();
var syntaxFacts = solution.GetRequiredDocument(location.DocumentId).GetRequiredLanguageService<ISyntaxFactsService>();
var node = location.Location.FindToken(cancellationToken).Parent;
while (node != null && !syntaxFacts.IsAnonymousOrLocalFunction(node))
......
......@@ -21,6 +21,14 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.UseAutoProperty
Public Sub New()
End Sub
Protected Overrides Function GetPropertyDeclaration(node As SyntaxNode) As PropertyBlockSyntax
If TypeOf node Is PropertyStatementSyntax Then
node = node.Parent
End If
Return DirectCast(node, PropertyBlockSyntax)
End Function
Protected Overrides Function GetNodeToRemove(identifier As ModifiedIdentifierSyntax) As SyntaxNode
Return Utilities.GetNodeToRemove(identifier)
End Function
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册