提交 ab9cc113 编写于 作者: C Cyrus Najmabadi

Better handle C# 5 code.

上级 8b5da4b6
......@@ -14,7 +14,7 @@ namespace Microsoft.CodeAnalysis.Editor.CSharp.UseAutoProperty
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal class UseAutoPropertyAnalyzer : AbstractUseAutoPropertyAnalyzer<PropertyDeclarationSyntax, FieldDeclarationSyntax, VariableDeclaratorSyntax, ExpressionSyntax>
{
protected override bool IsLanguageVersionSupported(Compilation compilation)
protected override bool SupportsReadOnlyProperties(Compilation compilation)
{
return ((CSharpCompilation)compilation).LanguageVersion >= LanguageVersion.CSharp6;
}
......
......@@ -25,14 +25,14 @@ protected override SyntaxNode GetNodeToRemove(VariableDeclaratorSyntax declarato
}
protected override SyntaxNode UpdateProperty(
Project project, IFieldSymbol fieldSymbol, IPropertySymbol propertySymbol, PropertyDeclarationSyntax propertyDeclaration,
bool isWrittenOutsideOfConstructor, CancellationToken cancellationToken)
Project project, Compilation compilation, IFieldSymbol fieldSymbol, IPropertySymbol propertySymbol,
PropertyDeclarationSyntax propertyDeclaration, bool isWrittenOutsideOfConstructor, CancellationToken cancellationToken)
{
var updatedProperty = propertyDeclaration.WithAccessorList(UpdateAccessorList(propertyDeclaration.AccessorList));
// We may need to add a setter if the field is written to outside of the constructor
// of it's class.
if (NeedsSetter(propertyDeclaration, isWrittenOutsideOfConstructor))
if (NeedsSetter(compilation, propertyDeclaration, isWrittenOutsideOfConstructor))
{
var accessor = SyntaxFactory.AccessorDeclaration(SyntaxKind.SetAccessorDeclaration)
.WithSemicolonToken(SyntaxFactory.Token(SyntaxKind.SemicolonToken));
......@@ -49,9 +49,28 @@ protected override SyntaxNode GetNodeToRemove(VariableDeclaratorSyntax declarato
return updatedProperty;
}
private bool NeedsSetter(PropertyDeclarationSyntax propertyDeclaration, bool isWrittenOutsideOfConstructor)
private bool NeedsSetter(Compilation compilation, PropertyDeclarationSyntax propertyDeclaration, bool isWrittenOutsideOfConstructor)
{
return isWrittenOutsideOfConstructor && !propertyDeclaration.AccessorList.Accessors.Any(SyntaxKind.SetAccessorDeclaration);
if (propertyDeclaration.AccessorList.Accessors.Any(SyntaxKind.SetAccessorDeclaration))
{
// Already has a setter.
return false;
}
if (!SupportsReadOnlyProperties(compilation))
{
// If the language doesn't have readonly properties, then we'll need a
// setter here.
return true;
}
// If we're written outside a constructor we need a setter.
return isWrittenOutsideOfConstructor;
}
private bool SupportsReadOnlyProperties(Compilation compilation)
{
return ((CSharpCompilation)compilation).LanguageVersion >= LanguageVersion.CSharp6;
}
private AccessorListSyntax UpdateAccessorList(AccessorListSyntax accessorList)
......
......@@ -25,10 +25,19 @@ public void TestSingleGetter1()
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseAutoProperty)]
public void TestCSharp5()
public void TestCSharp5_1()
{
Test(
@"class Class { [|int i|]; public int P { get { return i; } } }",
@"class Class { public int P { get; private set; } }",
CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5));
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsUseAutoProperty)]
public void TestCSharp5_2()
{
TestMissing(
@"class Class { [|int i|]; int P { get { return i; } } }",
@"class Class { [|readonly int i|]; int P { get { return i; } } }",
CSharpParseOptions.Default.WithLanguageVersion(LanguageVersion.CSharp5));
}
......
......@@ -13,7 +13,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UseAutoProperty
Private ReadOnly semanticFacts As New VisualBasicSemanticFactsService()
Protected Overrides Function IsLanguageVersionSupported(compilation As Compilation) As Boolean
Protected Overrides Function SupportsReadOnlyProperties(compilation As Compilation) As Boolean
Return True
End Function
......
......@@ -16,6 +16,7 @@ Namespace Microsoft.CodeAnalysis.Editor.VisualBasic.UseAutoProperty
End Function
Protected Overrides Function UpdateProperty(project As Project,
compilation As Compilation,
fieldSymbol As IFieldSymbol,
propertySymbol As IPropertySymbol,
propertyDeclaration As PropertyBlockSyntax,
......
......@@ -27,15 +27,16 @@ internal abstract class AbstractUseAutoPropertyAnalyzer<TPropertyDeclaration, TF
public sealed override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => ImmutableArray.Create(Descriptor, FadedTokenDescriptor);
protected abstract void RegisterIneligibleFieldsAction(CompilationStartAnalysisContext context, ConcurrentBag<IFieldSymbol> ineligibleFields);
protected abstract bool SupportsReadOnlyProperties(Compilation compilation);
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);
public sealed override void Initialize(AnalysisContext context)
{
context.RegisterCompilationStartAction(csac =>
{
if (!IsLanguageVersionSupported(csac.Compilation))
{
return;
}
var analysisResults = new ConcurrentBag<AnalysisResult>();
var ineligibleFields = new ConcurrentBag<IFieldSymbol>();
......@@ -46,12 +47,6 @@ public sealed override void Initialize(AnalysisContext context)
});
}
protected abstract bool IsLanguageVersionSupported(Compilation compilation);
protected abstract void RegisterIneligibleFieldsAction(CompilationStartAnalysisContext context, ConcurrentBag<IFieldSymbol> ineligibleFields);
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);
private void AnalyzeProperty(ConcurrentBag<AnalysisResult> analysisResults, SymbolAnalysisContext symbolContext)
{
var property = (IPropertySymbol)symbolContext.Symbol;
......@@ -109,6 +104,13 @@ private void AnalyzeProperty(ConcurrentBag<AnalysisResult> analysisResults, Symb
return;
}
// If the user made the field readonly, we only want to convert it to a property if we
// can keep it readonly.
if (getterField.IsReadOnly && !SupportsReadOnlyProperties(symbolContext.Compilation))
{
return;
}
if (!containingType.Equals(getterField.ContainingType))
{
// Field and property have to be in the same type.
......
......@@ -30,8 +30,8 @@ internal abstract class AbstractUseAutoPropertyCodeFixProvider<TPropertyDeclarat
protected abstract SyntaxNode GetNodeToRemove(TVariableDeclarator declarator);
protected abstract SyntaxNode UpdateProperty(
Project project, IFieldSymbol fieldSymbol, IPropertySymbol propertySymbol, TPropertyDeclaration propertyDeclaration,
bool isWrittenOutsideConstructor, CancellationToken cancellationToken);
Project project, Compilation compilation, IFieldSymbol fieldSymbol, IPropertySymbol propertySymbol,
TPropertyDeclaration propertyDeclaration, bool isWrittenOutsideConstructor, CancellationToken cancellationToken);
public sealed override Task RegisterCodeFixesAsync(CodeFixContext context)
{
......@@ -74,7 +74,7 @@ private async Task<Solution> ProcessResult(CodeFixContext context, Diagnostic di
var fieldLocations = await Renamer.GetRenameLocationsAsync(solution, fieldSymbol, solution.Workspace.Options, cancellationToken).ConfigureAwait(false);
// First, create the updated property we want to replace the old property with
var updatedProperty = UpdateProperty(project, fieldSymbol, propertySymbol, property,
var updatedProperty = UpdateProperty(project, compilation, fieldSymbol, propertySymbol, property,
IsWrittenToOutsideOfConstructorOrProperty(fieldSymbol, fieldLocations, property, cancellationToken), cancellationToken);
// Now, rename all usages of the field to point at the property. Except don't actually
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册