diff --git a/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs b/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs index d7175a7c5525d91a812d43b0a8b40f560bd5d590..5bcc7913ea206161584a5927f9d9ded637795b0a 100644 --- a/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs +++ b/src/Analyzers/CSharp/Analyzers/UseAutoProperty/CSharpUseAutoPropertyAnalyzer.cs @@ -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 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 analysisResults, HashSet 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(AccessorDeclarationSyntax accessorDeclaration) where T : StatementSyntax + private static T? GetSingleStatementFromAccessor(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(setAccessor)?.Expression; diff --git a/src/Analyzers/Core/Analyzers/UseAutoProperty/AbstractUseAutoPropertyAnalyzer.cs b/src/Analyzers/Core/Analyzers/UseAutoProperty/AbstractUseAutoPropertyAnalyzer.cs index eb72f60b482736737ad7315e210c87aa24c6fbc2..2d178f497df2c7650139bc660b425018384b41a6 100644 --- a/src/Analyzers/Core/Analyzers/UseAutoProperty/AbstractUseAutoPropertyAnalyzer.cs +++ b/src/Analyzers/Core/Analyzers/UseAutoProperty/AbstractUseAutoPropertyAnalyzer.cs @@ -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) diff --git a/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs b/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs index 1f70b3e4220d5a683e36374b579cc70ae2e33874..c8864e9b3c783fc2429f85d5eb139cc3781e29a6 100644 --- a/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/CSharp/Portable/UseAutoProperty/CSharpUseAutoPropertyCodeFixProvider.cs @@ -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; diff --git a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs index c7b1c88b43227154cbd013527681d67c8f9dac54..9521e13e93670de0abd9eefee51f6e4a838374de 100644 --- a/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs +++ b/src/Features/Core/Portable/UseAutoProperty/AbstractUseAutoPropertyCodeFixProvider.cs @@ -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 FixableDiagnosticIds public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer; + protected abstract TPropertyDeclaration GetPropertyDeclaration(SyntaxNode node); protected abstract SyntaxNode GetNodeToRemove(TVariableDeclarator declarator); protected abstract IEnumerable GetFormattingRules(Document document); @@ -69,21 +72,21 @@ private async Task ProcessResultAsync(CodeFixContext context, Diagnost var propertyLocation = locations[0]; var declaratorLocation = locations[1]; - var declarator = declaratorLocation.FindToken(cancellationToken).Parent.FirstAncestorOrSelf(); - 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(); - 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 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 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(); + property = GetPropertyDeclaration(await propertySymbol.DeclaringSyntaxReferences[0].GetSyntaxAsync(cancellationToken).ConfigureAwait(false)); var nodeToRemove = GetNodeToRemove(declarator); @@ -187,7 +190,7 @@ private async Task ProcessResultAsync(CodeFixContext context, Diagnost // because there's nothing to actually separate it from. if (fieldDocument == propertyDocument) { - var syntaxFacts = fieldDocument.GetLanguageService(); + var syntaxFacts = fieldDocument.GetRequiredLanguageService(); if (WillRemoveFirstFieldInTypeDirectlyAboveProperty(syntaxFacts, property, nodeToRemove) && syntaxFacts.GetLeadingBlankLines(nodeToRemove).Length == 0) { @@ -213,10 +216,11 @@ private async Task 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 FormatAsync(SyntaxNode newRoot, Document document return false; } - var syntaxFacts = solution.GetDocument(location.DocumentId).GetLanguageService(); + var syntaxFacts = solution.GetRequiredDocument(location.DocumentId).GetRequiredLanguageService(); var node = location.Location.FindToken(cancellationToken).Parent; while (node != null && !syntaxFacts.IsAnonymousOrLocalFunction(node)) diff --git a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb index 43cbbc02526325b6c2eb6ca14ce8b750a0feddf2..db6b731c01f905801de703a81e990e6c07e89ff6 100644 --- a/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb +++ b/src/Features/VisualBasic/Portable/UseAutoProperty/VisualBasicUseAutoPropertyCodeFixProvider.vb @@ -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