提交 06393e4e 编写于 作者: C CyrusNajmabadi 提交者: Heejae Chang

Update the simplifier to respect and apply the 'use var' user preference. (#25865)

* Initial UseConditional work.

* Initial UseConditional work.

* Working replacement.

* Extract common code.

* Make 'var' work properly in the simplifier service.

* Move types to their own files.

* Provide a way to create types that are not simplified to 'var'.

* Update test options.

* Update test.

* Move CSharp code style code down to the Workspace layer.

* Rename config value.

* Add ui options.

* Add VB ui options.

* Add VB support

* Strip trivia

* Initial stubs for 'use conditional for returns'

* Add VB impl.

* Only support processing of real trees.

* Actually assign variable.

* Don't assume a single variable.

* Share more code.

* Fix shared code.

* Add check.

* Simplify code.

* Only the analyzer needs to check the severity.

* Generalize loc strings.

* remove static.

* Remove unneeded methods.

* Do cheap checks first.

* Handle more cases.

* Handle more cases.

* Add test.

* Add tests.

* Add test.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Fix tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Add tests.

* Format multiline conditions.

* Add multiline formatting for VB.

* Move code.

* Simpler formilization of casts.

* Extract out code.

* Remove file.

* Move files.

* Share more code.

* Share more code.

* Break out types.

* Share more code.

* Add more tests.

* Add VB tests.

* Simplify.

* Add comment.

* Remove newlines.

* Move method.

* Move method.

* Add comments.

* Simplify

* Update comment.

* Update comment.

* Remove line.

* Update comment.

* Update comment.

* Update comment.

* Fix comment.

* Wrap long lines.

* Wrap long lines.

* Wrap long lines.

* Handle trivia.

* Simplify.

* Fix check.

* better support for else-if chains.

* Add comments.

* Remove 'Use Conditional Expressoin'

* Remove more vestiges of
 feature.

* Remove code.

* Add back newline.
上级 817ade2b
......@@ -13,7 +13,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Simplification
<[UseExportProvider]>
Public MustInherit Class AbstractSimplificationTests
Protected Async Function TestAsync(definition As XElement, expected As XElement, Optional simplificationOptions As Dictionary(Of OptionKey, Object) = Nothing) As System.Threading.Tasks.Task
Protected Async Function TestAsync(definition As XElement, expected As XElement, Optional options As Dictionary(Of OptionKey, Object) = Nothing) As System.Threading.Tasks.Task
Using workspace = TestWorkspace.Create(definition)
Dim hostDocument = workspace.Documents.Single()
......@@ -32,7 +32,9 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Simplification
explicitSpanToSimplifyAnnotatedSpans.Single().Value,
Nothing)
Await TestAsync(workspace, spansToAddSimplifierAnnotation, explicitSpansToSimplifyWithin, expected, simplificationOptions)
Await TestAsync(
workspace, spansToAddSimplifierAnnotation,
explicitSpansToSimplifyWithin, expected, options)
End Using
End Function
......@@ -41,7 +43,7 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Simplification
listOfLabelToAddSimplifierAnnotationSpans As IEnumerable(Of KeyValuePair(Of String, ImmutableArray(Of TextSpan))),
explicitSpansToSimplifyWithin As ImmutableArray(Of TextSpan),
expected As XElement,
simplificationOptions As Dictionary(Of OptionKey, Object)) As System.Threading.Tasks.Task
options As Dictionary(Of OptionKey, Object)) As System.Threading.Tasks.Task
Dim document = workspace.CurrentSolution.Projects.Single().Documents.Single()
Dim root = Await document.GetSyntaxRootAsync()
......@@ -84,8 +86,8 @@ Namespace Microsoft.CodeAnalysis.Editor.UnitTests.Simplification
Next
Dim optionSet = workspace.Options
If simplificationOptions IsNot Nothing Then
For Each entry In simplificationOptions
If options IsNot Nothing Then
For Each entry In options
optionSet = optionSet.WithChangedOption(entry.Key, entry.Value)
Next
End If
......
......@@ -2,6 +2,7 @@
Imports System.Threading.Tasks
Imports Microsoft.CodeAnalysis.CodeStyle
Imports Microsoft.CodeAnalysis.CSharp.CodeStyle
Imports Microsoft.CodeAnalysis.Editor.UnitTests.Workspaces
Imports Microsoft.CodeAnalysis.Options
Imports Microsoft.CodeAnalysis.Simplification
......@@ -1467,9 +1468,93 @@ class C1
<WorkItem(649385, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/649385")>
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function TestCSharpSimplifyToVarCorrect() As Task
Public Async Function TestCSharpSimplifyToVarLocalDeclaration() As Task
Dim simplificationOption = New Dictionary(Of OptionKey, Object) From {
{SimplificationOptions.PreferImplicitTypeInLocalDeclaration, True},
{CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, CodeStyleOptions.TrueWithNoneEnforcement},
{CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, CodeStyleOptions.TrueWithNoneEnforcement},
{CSharpCodeStyleOptions.UseImplicitTypeWherePossible, CodeStyleOptions.TrueWithNoneEnforcement}
}
Dim simplificationOption = New Dictionary(Of OptionKey, Object) From {{SimplificationOptions.PreferImplicitTypeInLocalDeclaration, True}}
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
class Program
{
void Main()
{
{|Simplify:int|} i = 0;
}
}
</Document>
</Project>
</Workspace>
Dim expected =
<code>
class Program
{
void Main()
{
var i = 0;
}
}
</code>
Await TestAsync(input, expected, simplificationOption)
End Function
<WorkItem(649385, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/649385")>
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function TestCSharpSimplifyToVarForeachDecl() As Task
Dim simplificationOption = New Dictionary(Of OptionKey, Object) From {
{SimplificationOptions.PreferImplicitTypeInLocalDeclaration, True},
{CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, CodeStyleOptions.TrueWithNoneEnforcement},
{CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, CodeStyleOptions.TrueWithNoneEnforcement},
{CSharpCodeStyleOptions.UseImplicitTypeWherePossible, CodeStyleOptions.TrueWithNoneEnforcement}
}
Dim input =
<Workspace>
<Project Language="C#" CommonReferences="true">
<Document>
using System.Collections.Generic;
class Program
{
void Main()
{
foreach ({|Simplify:int|} item in new List&lt;int&gt;()) { }
}
}
</Document>
</Project>
</Workspace>
Dim expected =
<code>
using System.Collections.Generic;
class Program
{
void Main()
{
foreach (var item in new List&lt;int&gt;()) { }
}
}
</code>
Await TestAsync(input, expected, simplificationOption)
End Function
<WorkItem(649385, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/649385")>
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function TestCSharpSimplifyToVarCorrect() As Task
Dim simplificationOption = New Dictionary(Of OptionKey, Object) From {
{SimplificationOptions.PreferImplicitTypeInLocalDeclaration, True},
{CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, CodeStyleOptions.TrueWithNoneEnforcement},
{CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, CodeStyleOptions.TrueWithNoneEnforcement},
{CSharpCodeStyleOptions.UseImplicitTypeWherePossible, CodeStyleOptions.TrueWithNoneEnforcement}
}
Dim input =
<Workspace>
......@@ -1517,7 +1602,7 @@ class Program
var d = new D();
foreach (var item in new List&lt;int&gt;()) { }
foreach (int item in new List&lt;int&gt;()) { }
using (var file = new StreamReader("C:\\myfile.txt")) {}
......@@ -1535,8 +1620,12 @@ class Program
<WorkItem(649385, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/649385")>
<Fact, Trait(Traits.Feature, Traits.Features.Simplification)>
Public Async Function TestCSharpSimplifyToVarCorrect_QualifiedTypeNames() As Task
Dim simplificationOption = New Dictionary(Of OptionKey, Object) From {{SimplificationOptions.PreferImplicitTypeInLocalDeclaration, True}}
Dim simplificationOption = New Dictionary(Of OptionKey, Object) From {
{SimplificationOptions.PreferImplicitTypeInLocalDeclaration, True},
{CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, CodeStyleOptions.TrueWithNoneEnforcement},
{CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, CodeStyleOptions.TrueWithNoneEnforcement},
{CSharpCodeStyleOptions.UseImplicitTypeWherePossible, CodeStyleOptions.TrueWithNoneEnforcement}
}
Dim input =
<Workspace>
......
......@@ -557,7 +557,7 @@ internal class CSharpFeaturesResources {
}
}
/// <summary>
/// <summary>
/// Looks up a localized string similar to is pattern.
/// </summary>
internal static string is_pattern {
......
......@@ -2,27 +2,23 @@
using System;
using System.Composition;
using System.Diagnostics;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CodeRefactorings;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.CSharp.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Text;
using Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle;
using System.Diagnostics;
using static Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle.CSharpTypeStyleDiagnosticAnalyzerBase;
namespace Microsoft.CodeAnalysis.CSharp.CodeRefactorings.UseExplicitType
{
[ExportCodeRefactoringProvider(LanguageNames.CSharp, Name = PredefinedCodeRefactoringProviderNames.UseExplicitType), Shared]
internal partial class UseExplicitTypeCodeRefactoringProvider : CodeRefactoringProvider
{
private static readonly CSharpUseExplicitTypeHelper s_useExplicitTypeHelper = new CSharpUseExplicitTypeHelper();
public override async Task ComputeRefactoringsAsync(CodeRefactoringContext context)
{
var document = context.Document;
......@@ -51,7 +47,7 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
Debug.Assert(declaration.IsKind(SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement, SyntaxKind.DeclarationExpression));
var declaredType = s_useExplicitTypeHelper.FindAnalyzableType(declaration, semanticModel, cancellationToken);
var declaredType = CSharpUseExplicitTypeHelper.Instance.FindAnalyzableType(declaration, semanticModel, cancellationToken);
if (declaredType == null)
{
return;
......@@ -67,17 +63,15 @@ public override async Task ComputeRefactoringsAsync(CodeRefactoringContext conte
return;
}
var state = State.Generate(declaration, semanticModel, optionSet,
isVariableDeclarationContext: declaration.IsKind(SyntaxKind.VariableDeclaration), cancellationToken: cancellationToken);
// UseExplicitType analyzer/fixer already gives an action in this case
if (s_useExplicitTypeHelper.IsStylePreferred(semanticModel, optionSet, state, cancellationToken))
var typeStyle = CSharpUseExplicitTypeHelper.Instance.AnalyzeTypeName(
declaredType, semanticModel, optionSet, cancellationToken);
if (typeStyle.IsStylePreferred && typeStyle.Severity != DiagnosticSeverity.Hidden)
{
// the analyzer would handle this. So we do not.
return;
}
Debug.Assert(state != null, "analyzing a declaration and state is null.");
if (!s_useExplicitTypeHelper.TryAnalyzeVariableDeclaration(declaredType, semanticModel, optionSet, cancellationToken))
if (!typeStyle.CanConvert())
{
return;
}
......
......@@ -6,6 +6,7 @@
using Microsoft.CodeAnalysis.ConvertForToForEach;
using Microsoft.CodeAnalysis.CSharp.CodeGeneration;
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Text;
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Text;
using static Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle.CSharpTypeStyleDiagnosticAnalyzerBase;
namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle
{
internal abstract partial class CSharpTypeStyleHelper
{
internal abstract bool IsStylePreferred(SemanticModel semanticModel, OptionSet optionSet, State state, CancellationToken cancellationToken);
internal abstract bool TryAnalyzeVariableDeclaration(TypeSyntax typeName, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken);
protected abstract bool AssignmentSupportsStylePreference(SyntaxToken identifier, TypeSyntax typeName, ExpressionSyntax initializer, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken);
internal TypeSyntax FindAnalyzableType(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
{
Debug.Assert(node.IsKind(SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement, SyntaxKind.DeclarationExpression));
switch (node)
{
case VariableDeclarationSyntax variableDeclaration:
return ShouldAnalyzeVariableDeclaration(variableDeclaration, semanticModel, cancellationToken)
? variableDeclaration.Type
: null;
case ForEachStatementSyntax forEachStatement:
return ShouldAnalyzeForEachStatement(forEachStatement, semanticModel, cancellationToken)
? forEachStatement.Type
: null;
case DeclarationExpressionSyntax declarationExpression:
return ShouldAnalyzeDeclarationExpression(declarationExpression, semanticModel, cancellationToken)
? declarationExpression.Type
: null;
}
return null;
}
protected virtual bool ShouldAnalyzeVariableDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
// implict type is applicable only for local variables and
// such declarations cannot have multiple declarators and
// must have an initializer.
var isSupportedParentKind = variableDeclaration.IsParentKind(
SyntaxKind.LocalDeclarationStatement,
SyntaxKind.ForStatement,
SyntaxKind.UsingStatement);
return isSupportedParentKind &&
variableDeclaration.Variables.Count == 1 &&
variableDeclaration.Variables.Single().Initializer.IsKind(SyntaxKind.EqualsValueClause);
}
protected virtual bool ShouldAnalyzeForEachStatement(ForEachStatementSyntax forEachStatement, SemanticModel semanticModel, CancellationToken cancellationToken)
=> true;
protected virtual bool ShouldAnalyzeDeclarationExpression(DeclarationExpressionSyntax declaration, SemanticModel semanticModel, CancellationToken cancellationToken)
=> true;
}
internal abstract partial class CSharpTypeStyleDiagnosticAnalyzerBase :
AbstractCodeStyleDiagnosticAnalyzer
{
......@@ -112,31 +60,21 @@ private void HandleVariableDeclaration(SyntaxNodeAnalysisContext context)
return;
}
var state = State.Generate(declarationStatement, semanticModel, optionSet,
isVariableDeclarationContext: declarationStatement.IsKind(SyntaxKind.VariableDeclaration), cancellationToken: cancellationToken);
if (!Helper.IsStylePreferred(semanticModel, optionSet, state, cancellationToken))
{
return;
}
Debug.Assert(state != null, "analyzing a declaration and state is null.");
if (!Helper.TryAnalyzeVariableDeclaration(declaredType, semanticModel, optionSet, cancellationToken))
var typeStyle = Helper.AnalyzeTypeName(
declaredType, semanticModel, optionSet, cancellationToken);
if (!typeStyle.IsStylePreferred ||
typeStyle.Severity == DiagnosticSeverity.Hidden ||
!typeStyle.CanConvert())
{
return;
}
// The severity preference is not Hidden, as indicated by IsStylePreferred.
var descriptor = GetDescriptorWithSeverity(state.GetDiagnosticSeverityPreference());
var descriptor = GetDescriptorWithSeverity(typeStyle.Severity);
context.ReportDiagnostic(CreateDiagnostic(descriptor, declarationStatement, declaredType.Span));
}
internal static ExpressionSyntax GetInitializerExpression(ExpressionSyntax initializer) =>
initializer is CheckedExpressionSyntax
? ((CheckedExpressionSyntax)initializer).Expression.WalkDownParentheses()
: initializer.WalkDownParentheses();
private Diagnostic CreateDiagnostic(DiagnosticDescriptor descriptor, SyntaxNode declaration, TextSpan diagnosticSpan) =>
Diagnostic.Create(descriptor, declaration.SyntaxTree.GetLocation(diagnosticSpan));
private Diagnostic CreateDiagnostic(DiagnosticDescriptor descriptor, SyntaxNode declaration, TextSpan diagnosticSpan)
=> Diagnostic.Create(descriptor, declaration.SyntaxTree.GetLocation(diagnosticSpan));
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using static Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle.CSharpTypeStyleDiagnosticAnalyzerBase;
namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle
{
internal sealed class CSharpUseExplicitTypeHelper : CSharpTypeStyleHelper
{
protected override bool ShouldAnalyzeVariableDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (!variableDeclaration.Type.IsVar)
{
// If the type is not 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeVariableDeclaration(variableDeclaration, semanticModel, cancellationToken);
}
protected override bool ShouldAnalyzeForEachStatement(ForEachStatementSyntax forEachStatement, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (!forEachStatement.Type.IsVar)
{
// If the type is not 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeForEachStatement(forEachStatement, semanticModel, cancellationToken);
}
internal override bool IsStylePreferred(SemanticModel semanticModel, OptionSet optionSet, State state, CancellationToken cancellationToken)
{
var stylePreferences = state.TypeStylePreference;
var shouldNotify = state.ShouldNotify();
// If notification preference is None, don't offer the suggestion.
if (!shouldNotify)
{
return false;
}
if (state.IsInIntrinsicTypeContext)
{
return !stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeForIntrinsicTypes);
}
else if (state.IsTypeApparentInContext)
{
return !stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeWhereApparent);
}
else
{
return !stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeWherePossible);
}
}
internal override bool TryAnalyzeVariableDeclaration(TypeSyntax typeName, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken)
{
// var (x, y) = e;
// foreach (var (x, y) in e) ...
if (typeName.IsParentKind(SyntaxKind.DeclarationExpression))
{
var parent = (DeclarationExpressionSyntax)typeName.Parent;
if (parent.Designation.IsKind(SyntaxKind.ParenthesizedVariableDesignation))
{
return true;
}
}
// If it is currently not var, explicit typing exists, return.
// this also takes care of cases where var is mapped to a named type via an alias or a class declaration.
if (!typeName.IsTypeInferred(semanticModel))
{
return false;
}
if (typeName.Parent.IsKind(SyntaxKind.VariableDeclaration) &&
typeName.Parent.Parent.IsKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement))
{
// check assignment for variable declarations.
var variable = ((VariableDeclarationSyntax)typeName.Parent).Variables.First();
if (!AssignmentSupportsStylePreference(
variable.Identifier, typeName, variable.Initializer.Value,
semanticModel, optionSet, cancellationToken))
{
return false;
}
}
else if (typeName.Parent.IsKind(SyntaxKind.ForEachStatement))
{
var foreachStatement = (ForEachStatementSyntax)typeName.Parent;
if (!AssignmentSupportsStylePreference(
foreachStatement.Identifier, typeName, foreachStatement.Expression,
semanticModel, optionSet, cancellationToken))
{
return false;
}
}
return true;
}
protected override bool ShouldAnalyzeDeclarationExpression(DeclarationExpressionSyntax declaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (!declaration.Type.IsVar)
{
// If the type is not 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeDeclarationExpression(declaration, semanticModel, cancellationToken);
}
/// <summary>
/// Analyzes the assignment expression and rejects a given declaration if it is unsuitable for explicit typing.
/// </summary>
/// <returns>
/// false, if explicit typing cannot be used.
/// true, otherwise.
/// </returns>
protected override bool AssignmentSupportsStylePreference(
SyntaxToken identifier,
TypeSyntax typeName,
ExpressionSyntax initializer,
SemanticModel semanticModel,
OptionSet optionSet,
CancellationToken cancellationToken)
{
// is or contains an anonymous type
// cases :
// var anon = new { Num = 1 };
// var enumerableOfAnons = from prod in products select new { prod.Color, prod.Price };
var declaredType = semanticModel.GetTypeInfo(typeName, cancellationToken).Type;
if (declaredType.ContainsAnonymousType())
{
return false;
}
// cannot find type if initializer resolves to an ErrorTypeSymbol
var initializerTypeInfo = semanticModel.GetTypeInfo(initializer, cancellationToken);
return !initializerTypeInfo.Type.IsErrorType();
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpUseExplicitTypeDiagnosticAnalyzer : CSharpTypeStyleDiagnosticAnalyzerBase
{
......@@ -161,8 +17,7 @@ internal sealed class CSharpUseExplicitTypeDiagnosticAnalyzer : CSharpTypeStyleD
private static readonly LocalizableString s_Message =
new LocalizableResourceString(nameof(CSharpFeaturesResources.Use_explicit_type_instead_of_var), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources));
private static readonly CSharpUseExplicitTypeHelper s_Helper = new CSharpUseExplicitTypeHelper();
protected override CSharpTypeStyleHelper Helper => s_Helper;
protected override CSharpTypeStyleHelper Helper => CSharpUseExplicitTypeHelper.Instance;
public CSharpUseExplicitTypeDiagnosticAnalyzer()
: base(diagnosticId: IDEDiagnosticIds.UseExplicitTypeDiagnosticId,
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.CSharp.Utilities;
using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using static Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle.CSharpTypeStyleDiagnosticAnalyzerBase;
namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle
{
internal sealed class CSharpUseImplicitTypeHelper : CSharpTypeStyleHelper
{
protected override bool ShouldAnalyzeVariableDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (variableDeclaration.Type.IsVar)
{
// If the type is already 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeVariableDeclaration(variableDeclaration, semanticModel, cancellationToken);
}
protected override bool ShouldAnalyzeForEachStatement(ForEachStatementSyntax forEachStatement, SemanticModel semanticModel, CancellationToken cancellationToken)
{
var type = forEachStatement.Type;
if (type.IsVar || (type.Kind() == SyntaxKind.RefType && ((RefTypeSyntax)type).Type.IsVar))
{
// If the type is already 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeForEachStatement(forEachStatement, semanticModel, cancellationToken);
}
internal override bool IsStylePreferred(SemanticModel semanticModel, OptionSet optionSet, State state, CancellationToken cancellationToken)
{
var stylePreferences = state.TypeStylePreference;
var shouldNotify = state.ShouldNotify();
// If notification preference is None, don't offer the suggestion.
if (!shouldNotify)
{
return false;
}
if (state.IsInIntrinsicTypeContext)
{
return stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeForIntrinsicTypes);
}
else if (state.IsTypeApparentInContext)
{
return stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeWhereApparent);
}
else
{
return stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeWherePossible);
}
}
internal override bool TryAnalyzeVariableDeclaration(TypeSyntax typeName, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken)
{
Debug.Assert(!typeName.IsVar, "'var' special case should have prevented analysis of this variable.");
var candidateReplacementNode = SyntaxFactory.IdentifierName("var");
// If there exists a type named var, return.
var conflict = semanticModel.GetSpeculativeSymbolInfo(typeName.SpanStart, candidateReplacementNode, SpeculativeBindingOption.BindAsTypeOrNamespace).Symbol;
if (conflict?.IsKind(SymbolKind.NamedType) == true)
{
return false;
}
if (typeName.Parent.IsKind(SyntaxKind.VariableDeclaration) &&
typeName.Parent.IsParentKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement))
{
var variableDeclaration = (VariableDeclarationSyntax)typeName.Parent;
// implicitly typed variables cannot be constants.
if ((variableDeclaration.Parent as LocalDeclarationStatementSyntax)?.IsConst == true)
{
return false;
}
var variable = variableDeclaration.Variables.Single();
var initializer = variable.Initializer.Value;
// Do not suggest var replacement for stackalloc span expressions.
// This will change the bound type from a span to a pointer.
if (!variableDeclaration.Type.IsKind(SyntaxKind.PointerType))
{
var containsStackAlloc = initializer
.DescendantNodesAndSelf(descendIntoChildren: node => !node.IsAnyLambdaOrAnonymousMethod())
.Any(node => node.IsKind(SyntaxKind.StackAllocArrayCreationExpression));
if (containsStackAlloc)
{
return false;
}
}
if (AssignmentSupportsStylePreference(
variable.Identifier, typeName, initializer,
semanticModel, optionSet, cancellationToken))
{
return true;
}
}
else if (typeName.Parent is ForEachStatementSyntax foreachStatement)
{
var foreachStatementInfo = semanticModel.GetForEachStatementInfo(foreachStatement);
if (foreachStatementInfo.ElementConversion.IsIdentity)
{
return true;
}
}
else if (typeName.Parent is DeclarationExpressionSyntax declarationExpression &&
TryAnalyzeDeclarationExpression(declarationExpression, semanticModel, optionSet, cancellationToken))
{
return true;
}
return false;
}
private bool TryAnalyzeDeclarationExpression(
DeclarationExpressionSyntax declarationExpression,
SemanticModel semanticModel,
OptionSet optionSet,
CancellationToken cancellationToken)
{
// It's not always safe to convert a decl expression like "Method(out int i)" to
// "Method(out var i)". Changing to 'var' may cause overload resolution errors.
// Have to see if using 'var' means not resolving to the same type as before.
// Note: this is fairly expensive, so we try to avoid this if we can by seeing if
// there are multiple candidates with the original call. If not, then we don't
// have to do anything.
if (declarationExpression.Parent is ArgumentSyntax argument &&
argument.Parent is ArgumentListSyntax argumentList &&
argumentList.Parent is InvocationExpressionSyntax invocationExpression)
{
// If there was only one member in the group, and it was non-generic itself,
// then this change is safe to make without doing any complex analysis.
// Multiple methods mean that switching to 'var' might remove information
// that affects overload resolution. And if the method is generic, then
// switching to 'var' may mean that inference might not work properly.
var memberGroup = semanticModel.GetMemberGroup(invocationExpression.Expression, cancellationToken);
if (memberGroup.Length == 1 &&
memberGroup[0].GetTypeParameters().IsEmpty)
{
return true;
}
}
// Do the expensive check. Note: we can't use the SpeculationAnalyzer (or any
// speculative analyzers) here. This is due to https://github.com/dotnet/roslyn/issues/20724.
// Specifically, all the speculative helpers do not deal with with changes to code that
// introduces a variable (in this case, the declaration expression). The compiler sees
// this as an error because there are now two colliding variables, which causes all sorts
// of errors to be reported.
var tree = semanticModel.SyntaxTree;
var root = tree.GetRoot(cancellationToken);
var annotation = new SyntaxAnnotation();
var declarationTypeNode = declarationExpression.Type;
var declarationType = semanticModel.GetTypeInfo(declarationTypeNode, cancellationToken).Type;
var newRoot = root.ReplaceNode(
declarationTypeNode,
SyntaxFactory.IdentifierName("var").WithTriviaFrom(declarationTypeNode).WithAdditionalAnnotations(annotation));
var newTree = tree.WithRootAndOptions(newRoot, tree.Options);
var newSemanticModel = semanticModel.Compilation.ReplaceSyntaxTree(tree, newTree).GetSemanticModel(newTree);
var newDeclarationTypeNode = newTree.GetRoot(cancellationToken).GetAnnotatedNodes(annotation).Single();
var newDeclarationType = newSemanticModel.GetTypeInfo(newDeclarationTypeNode, cancellationToken).Type;
return SymbolEquivalenceComparer.Instance.Equals(declarationType, newDeclarationType);
}
/// <summary>
/// Analyzes the assignment expression and rejects a given declaration if it is unsuitable for implicit typing.
/// </summary>
/// <returns>
/// false, if implicit typing cannot be used.
/// true, otherwise.
/// </returns>
protected override bool AssignmentSupportsStylePreference(
SyntaxToken identifier,
TypeSyntax typeName,
ExpressionSyntax initializer,
SemanticModel semanticModel,
OptionSet optionSet,
CancellationToken cancellationToken)
{
var expression = CSharpTypeStyleDiagnosticAnalyzerBase.GetInitializerExpression(initializer);
// var cannot be assigned null
if (expression.IsKind(SyntaxKind.NullLiteralExpression))
{
return false;
}
// cannot use implicit typing on method group or on dynamic
var declaredType = semanticModel.GetTypeInfo(typeName, cancellationToken).Type;
if (declaredType != null && declaredType.TypeKind == TypeKind.Dynamic)
{
return false;
}
// variables declared using var cannot be used further in the same initialization expression.
if (initializer.DescendantNodesAndSelf()
.Where(n => (n as IdentifierNameSyntax)?.Identifier.ValueText.Equals(identifier.ValueText) == true)
.Any(n => semanticModel.GetSymbolInfo(n, cancellationToken).Symbol?.IsKind(SymbolKind.Local) == true))
{
return false;
}
// Get the conversion that occurred between the expression's type and type implied by the expression's context
// and filter out implicit conversions. If an implicit conversion (other than identity) exists
// and if we're replacing the declaration with 'var' we'd be changing the semantics by inferring type of
// initializer expression and thereby losing the conversion.
var conversion = semanticModel.GetConversion(expression, cancellationToken);
if (conversion.IsIdentity)
{
// final check to compare type information on both sides of assignment.
var initializerType = semanticModel.GetTypeInfo(expression, cancellationToken).Type;
return declaredType.Equals(initializerType);
}
return false;
}
protected override bool ShouldAnalyzeDeclarationExpression(DeclarationExpressionSyntax declaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (declaration.Type.IsVar)
{
// If the type is already 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeDeclarationExpression(declaration, semanticModel, cancellationToken);
}
}
[DiagnosticAnalyzer(LanguageNames.CSharp)]
internal sealed class CSharpUseImplicitTypeDiagnosticAnalyzer : CSharpTypeStyleDiagnosticAnalyzerBase
{
......@@ -262,8 +17,7 @@ internal sealed class CSharpUseImplicitTypeDiagnosticAnalyzer : CSharpTypeStyleD
private static readonly LocalizableString s_Message =
new LocalizableResourceString(nameof(CSharpFeaturesResources.use_var_instead_of_explicit_type), CSharpFeaturesResources.ResourceManager, typeof(CSharpFeaturesResources));
private static readonly CSharpUseImplicitTypeHelper s_Helper = new CSharpUseImplicitTypeHelper();
protected override CSharpTypeStyleHelper Helper => s_Helper;
protected override CSharpTypeStyleHelper Helper => CSharpUseImplicitTypeHelper.Instance;
public CSharpUseImplicitTypeDiagnosticAnalyzer()
: base(diagnosticId: IDEDiagnosticIds.UseImplicitTypeDiagnosticId,
......
......@@ -79,7 +79,10 @@ public override Task RegisterCodeFixesAsync(CodeFixContext context)
if (parensDesignation is null)
{
var typeSymbol = semanticModel.GetTypeInfo(typeSyntax).ConvertedType;
var typeName = typeSymbol.GenerateTypeSyntax()
// We're going to be passed through the simplifier. Tell it to not just convert
// this back to var (as that would defeat the purpose of this refactoring entirely).
var typeName = typeSymbol.GenerateTypeSyntax(allowVar: false)
.WithLeadingTrivia(node.GetLeadingTrivia())
.WithTrailingTrivia(node.GetTrailingTrivia());
Debug.Assert(!typeName.ContainsDiagnostics, "Explicit type replacement likely introduced an error in code");
......
......@@ -436,18 +436,16 @@ internal class FeaturesResources {
return ResourceManager.GetString("Adding_an_imported_method_will_prevent_the_debug_session_from_continuing", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Alias ambiguous type &apos;{0}&apos;.
/// </summary>
internal static string Alias_ambiguous_type_0
{
get
{
internal static string Alias_ambiguous_type_0 {
get {
return ResourceManager.GetString("Alias_ambiguous_type_0", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to All lowercase.
/// </summary>
......
......@@ -1507,7 +1507,7 @@ L'utente accetta le condizioni sopra riportate:</target>
<source>Manage naming styles</source>
<target state="new">Manage naming styles</target>
<note />
</trans-unit>
</trans-unit>
<trans-unit id="Field_preferences_colon">
<source>Field preferences:</source>
<target state="new">Field preferences:</target>
......
......@@ -3772,9 +3772,7 @@ public override SyntaxNode TryCastExpression(SyntaxNode expression, SyntaxNode t
}
public override SyntaxNode CastExpression(SyntaxNode type, SyntaxNode expression)
{
return SyntaxFactory.CastExpression((TypeSyntax)type, Parenthesize(expression)).WithAdditionalAnnotations(Simplifier.Annotation);
}
=> SyntaxFactory.CastExpression((TypeSyntax)type, Parenthesize(expression)).WithAdditionalAnnotations(Simplifier.Annotation);
public override SyntaxNode ConvertExpression(SyntaxNode type, SyntaxNode expression)
{
......
......@@ -14,9 +14,9 @@ namespace Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle
internal static class TypeStyleHelper
{
/// <summary>
/// Given an expression of assignment, answers whether the declaration
/// can use var keyword, by looking at the user's style preferences
/// obtained from options and the context obtained from the expression.
/// Given an expression or assignment, answers whether the declaration can use var keyword,
/// by looking at the user's style preferences obtained from options and the context
/// obtained from the expression.
/// </summary>
public static bool IsImplicitTypePreferred(
ExpressionSyntax initializerExpression,
......@@ -28,10 +28,8 @@ internal static class TypeStyleHelper
var isBuiltInTypeContext = IsBuiltInType(initializerExpression, semanticModel, cancellationToken);
var isTypeApparentContext = IsTypeApparentInAssignmentExpression(stylePreferences,
initializerExpression,
semanticModel,
cancellationToken);
var isTypeApparentContext = IsTypeApparentInAssignmentExpression(
stylePreferences, initializerExpression, semanticModel, cancellationToken);
return IsImplicitStylePreferred(stylePreferences, isBuiltInTypeContext, isTypeApparentContext);
}
......@@ -130,7 +128,7 @@ public static bool IsBuiltInType(ITypeSymbol type)
// other Conversion cases:
// a. conversion with helpers like: int.Parse methods
// b. types that implement IConvertible and then invoking .ToType()
// c. System.Convert.Totype()
// c. System.Convert.ToType()
var memberName = GetRightmostInvocationExpression(initializerExpression).GetRightmostName();
if (memberName == null)
{
......@@ -306,7 +304,7 @@ public static bool IsPredefinedType(TypeSyntax type)
}
else
{
return (TypeSyntax)generator.TypeExpression(type);
return type.GenerateTypeSyntax(allowVar: false);
}
}
}
......
......@@ -6,6 +6,7 @@
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions.ContextQuery;
using Microsoft.CodeAnalysis.CSharp.Simplification;
using Microsoft.CodeAnalysis.CSharp.Symbols;
......@@ -691,13 +692,20 @@ private static bool CanReplace(ISymbol symbol)
var memberAccess = (MemberAccessExpressionSyntax)expression;
return memberAccess.TryReduce(semanticModel, out replacementNode, out issueSpan, optionSet, cancellationToken);
}
else if (expression is NameSyntax name)
{
return name.TryReduce(semanticModel, out replacementNode, out issueSpan, optionSet, cancellationToken);
}
else if (expression is TypeSyntax typeName)
if (expression is TypeSyntax typeName)
{
return typeName.IsReplaceableByVar(semanticModel, out replacementNode, out issueSpan, optionSet, cancellationToken);
// First, see if we can replace this type with var if that's what the user prefers.
// That always overrides all other simplification.
if (typeName.IsReplaceableByVar(semanticModel, out replacementNode, out issueSpan, optionSet, cancellationToken))
{
return true;
}
if (expression is NameSyntax name)
{
return name.TryReduce(semanticModel, out replacementNode, out issueSpan, optionSet, cancellationToken);
}
}
return false;
......@@ -2269,75 +2277,21 @@ private static bool IsThisOrTypeOrNamespace(MemberAccessExpressionSyntax memberA
OptionSet optionSet,
CancellationToken cancellationToken)
{
replacementNode = null;
issueSpan = default;
var typeStyle = CSharpUseImplicitTypeHelper.Instance.AnalyzeTypeName(
simpleName, semanticModel, optionSet, cancellationToken);
if (!optionSet.GetOption(SimplificationOptions.PreferImplicitTypeInLocalDeclaration))
if (!typeStyle.IsStylePreferred || !typeStyle.CanConvert())
{
replacementNode = null;
issueSpan = default;
return false;
}
// If it is already var
if (simpleName.IsVar)
{
return false;
}
var candidateReplacementNode = SyntaxFactory.IdentifierName("var")
.WithLeadingTrivia(simpleName.GetLeadingTrivia())
.WithTrailingTrivia(simpleName.GetTrailingTrivia());
var candidateIssueSpan = simpleName.Span;
// If there exists a Type called var , fail.
var checkSymbol = semanticModel.GetSpeculativeSymbolInfo(simpleName.SpanStart, candidateReplacementNode, SpeculativeBindingOption.BindAsTypeOrNamespace).Symbol;
if (checkSymbol != null && checkSymbol.IsKind(SymbolKind.NamedType) && ((INamedTypeSymbol)checkSymbol).TypeKind == TypeKind.Class && checkSymbol.Name == "var")
{
return false;
}
// If the simpleName is the type of the Variable Declaration Syntax belonging to LocalDeclaration, For Statement or Using statement
if (simpleName.IsParentKind(SyntaxKind.VariableDeclaration) &&
((VariableDeclarationSyntax)simpleName.Parent).Type == simpleName &&
simpleName.Parent.Parent.IsKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement))
{
if (simpleName.Parent.IsParentKind(SyntaxKind.LocalDeclarationStatement) &&
((LocalDeclarationStatementSyntax)simpleName.Parent.Parent).Modifiers.Any(n => n.Kind() == SyntaxKind.ConstKeyword))
{
return false;
}
var variableDeclaration = (VariableDeclarationSyntax)simpleName.Parent;
// Check the Initialized Value to see if it is allowed to be in the Var initialization
if (variableDeclaration.Variables.Count != 1 ||
!variableDeclaration.Variables.Single().Initializer.IsKind(SyntaxKind.EqualsValueClause))
{
return false;
}
var variable = variableDeclaration.Variables.Single();
var initializer = variable.Initializer;
var identifier = variable.Identifier;
if (EqualsValueClauseNotSuitableForVar(identifier, simpleName, initializer, semanticModel, cancellationToken))
{
return false;
}
replacementNode = candidateReplacementNode;
issueSpan = candidateIssueSpan;
return true;
}
if (simpleName.IsParentKind(SyntaxKind.ForEachStatement) &&
((ForEachStatementSyntax)simpleName.Parent).Type == simpleName)
{
replacementNode = candidateReplacementNode;
issueSpan = candidateIssueSpan;
return true;
}
return false;
replacementNode = SyntaxFactory.IdentifierName("var")
.WithLeadingTrivia(simpleName.GetLeadingTrivia())
.WithTrailingTrivia(simpleName.GetTrailingTrivia());
issueSpan = simpleName.Span;
return true;
}
private static bool EqualsValueClauseNotSuitableForVar(
......
......@@ -26,22 +26,29 @@ internal static partial class ITypeSymbolExtensions
}
public static NameSyntax GenerateNameSyntax(
this INamespaceOrTypeSymbol symbol)
this INamespaceOrTypeSymbol symbol, bool allowVar = true)
{
return (NameSyntax)GenerateTypeSyntax(symbol, nameSyntax: true);
return (NameSyntax)GenerateTypeSyntax(symbol, nameSyntax: true, allowVar: allowVar);
}
public static TypeSyntax GenerateTypeSyntax(
this INamespaceOrTypeSymbol symbol)
this INamespaceOrTypeSymbol symbol, bool allowVar = true)
{
return GenerateTypeSyntax(symbol, nameSyntax: false);
return GenerateTypeSyntax(symbol, nameSyntax: false, allowVar: allowVar);
}
private static TypeSyntax GenerateTypeSyntax(
INamespaceOrTypeSymbol symbol, bool nameSyntax)
INamespaceOrTypeSymbol symbol, bool nameSyntax, bool allowVar = true)
{
return symbol.Accept(TypeSyntaxGeneratorVisitor.Create(nameSyntax))
.WithAdditionalAnnotations(Simplifier.Annotation);
var syntax = symbol.Accept(TypeSyntaxGeneratorVisitor.Create(nameSyntax))
.WithAdditionalAnnotations(Simplifier.Annotation);
if (!allowVar)
{
syntax = syntax.WithAdditionalAnnotations(DoNotAllowVarAnnotation.Annotation);
}
return syntax;
}
public static TypeSyntax GenerateRefTypeSyntax(
......
......@@ -1848,5 +1848,8 @@ public void GetPartsOfCastExpression(SyntaxNode node, out SyntaxNode type, out S
return null;
}
public bool SpansPreprocessorDirective(IEnumerable<SyntaxNode> nodes)
=> nodes.SpansPreprocessorDirective();
}
}
......@@ -10,11 +10,11 @@
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.TypeStyle
namespace Microsoft.CodeAnalysis.CSharp.Utilities
{
internal abstract partial class CSharpTypeStyleDiagnosticAnalyzerBase
internal partial class CSharpTypeStyleHelper
{
internal class State
protected class State
{
private readonly Dictionary<TypeStylePreference, DiagnosticSeverity> _styleToSeverityMap;
......@@ -23,14 +23,17 @@ internal class State
public bool IsTypeApparentInContext { get; private set; }
public bool IsInVariableDeclarationContext { get; }
public State(bool isVariableDeclarationContext)
private State(bool isVariableDeclarationContext)
{
this.IsInVariableDeclarationContext = isVariableDeclarationContext;
_styleToSeverityMap = new Dictionary<TypeStylePreference, DiagnosticSeverity>();
}
public static State Generate(SyntaxNode declaration, SemanticModel semanticModel, OptionSet optionSet, bool isVariableDeclarationContext, CancellationToken cancellationToken)
public static State Generate(
SyntaxNode declaration, SemanticModel semanticModel,
OptionSet optionSet, CancellationToken cancellationToken)
{
var isVariableDeclarationContext = declaration.IsKind(SyntaxKind.VariableDeclaration);
var state = new State(isVariableDeclarationContext);
state.Initialize(declaration, semanticModel, optionSet, cancellationToken);
return state;
......@@ -52,9 +55,6 @@ public DiagnosticSeverity GetDiagnosticSeverityPreference()
}
}
public bool ShouldNotify() =>
GetDiagnosticSeverityPreference() != DiagnosticSeverity.Hidden;
private void Initialize(SyntaxNode declaration, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken)
{
this.TypeStylePreference = GetCurrentTypeStylePreferences(optionSet);
......@@ -74,8 +74,18 @@ private void Initialize(SyntaxNode declaration, SemanticModel semanticModel, Opt
/// </summary>
private bool IsTypeApparentInDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, TypeStylePreference stylePreferences, CancellationToken cancellationToken)
{
var initializer = variableDeclaration.Variables.Single().Initializer;
var initializerExpression = GetInitializerExpression(initializer.Value);
if (variableDeclaration.Variables.Count != 1)
{
return false;
}
var initializer = variableDeclaration.Variables[0].Initializer;
if (initializer == null)
{
return false;
}
var initializerExpression = CSharpUseImplicitTypeHelper.GetInitializerExpression(initializer.Value);
var declaredTypeSymbol = semanticModel.GetTypeInfo(variableDeclaration.Type, cancellationToken).Type;
return TypeStyleHelper.IsTypeApparentInAssignmentExpression(stylePreferences, initializerExpression, semanticModel, cancellationToken, declaredTypeSymbol);
}
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Options;
namespace Microsoft.CodeAnalysis.CSharp.Utilities
{
internal struct TypeStyleResult
{
private readonly CSharpTypeStyleHelper _helper;
private readonly TypeSyntax _typeName;
private readonly SemanticModel _semanticModel;
private readonly OptionSet _optionSet;
private readonly CancellationToken _cancellationToken;
/// <summary>
/// Whether or not converting would transition the code to the style the user prefers. i.e.
/// if the user likes 'var' for everything, and you have 'int i = 0' then IsStylePreffered
/// will be true. however, if the user likes 'var' for everything and you have 'var i = 0',
/// then it's still possible to convert that, it would just be 'false' for IsStylePreferred
/// because it goes against the user's preferences.
///
/// In general, most features should only convert the type if IsStylePreferred is true. The
/// one exception is the refactoring, which is explicitly there to still let people convert
/// things quickly, even if it's going against their stated style.
/// </summary>
public readonly bool IsStylePreferred;
public readonly DiagnosticSeverity Severity;
public TypeStyleResult(CSharpTypeStyleHelper helper, TypeSyntax typeName, SemanticModel semanticModel, OptionSet optionSet, bool isStylePreferred, DiagnosticSeverity severity, CancellationToken cancellationToken) : this()
{
_helper = helper;
_typeName = typeName;
_semanticModel = semanticModel;
_optionSet = optionSet;
_cancellationToken = cancellationToken;
IsStylePreferred = isStylePreferred;
Severity = severity;
}
public bool CanConvert()
=> _helper.TryAnalyzeVariableDeclaration(_typeName, _semanticModel, _optionSet, _cancellationToken);
}
internal abstract partial class CSharpTypeStyleHelper
{
protected abstract bool IsStylePreferred(
SemanticModel semanticModel, OptionSet optionSet, State state, CancellationToken cancellationToken);
public virtual TypeStyleResult AnalyzeTypeName(
TypeSyntax typeName, SemanticModel semanticModel,
OptionSet optionSet, CancellationToken cancellationToken)
{
var declaration = typeName?.FirstAncestorOrSelf<SyntaxNode>(
a => a.IsKind(SyntaxKind.DeclarationExpression, SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement));
if (declaration == null)
{
return default;
}
var state = State.Generate(
declaration, semanticModel, optionSet, cancellationToken);
var isStylePreferred = this.IsStylePreferred(semanticModel, optionSet, state, cancellationToken);
var severity = state.GetDiagnosticSeverityPreference();
return new TypeStyleResult(
this, typeName, semanticModel, optionSet, isStylePreferred, severity, cancellationToken);
}
internal abstract bool TryAnalyzeVariableDeclaration(
TypeSyntax typeName, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken);
protected abstract bool AssignmentSupportsStylePreference(SyntaxToken identifier, TypeSyntax typeName, ExpressionSyntax initializer, SemanticModel semanticModel, OptionSet optionSet, CancellationToken cancellationToken);
internal TypeSyntax FindAnalyzableType(SyntaxNode node, SemanticModel semanticModel, CancellationToken cancellationToken)
{
Debug.Assert(node.IsKind(SyntaxKind.VariableDeclaration, SyntaxKind.ForEachStatement, SyntaxKind.DeclarationExpression));
switch (node)
{
case VariableDeclarationSyntax variableDeclaration:
return ShouldAnalyzeVariableDeclaration(variableDeclaration, semanticModel, cancellationToken)
? variableDeclaration.Type
: null;
case ForEachStatementSyntax forEachStatement:
return ShouldAnalyzeForEachStatement(forEachStatement, semanticModel, cancellationToken)
? forEachStatement.Type
: null;
case DeclarationExpressionSyntax declarationExpression:
return ShouldAnalyzeDeclarationExpression(declarationExpression, semanticModel, cancellationToken)
? declarationExpression.Type
: null;
}
return null;
}
protected virtual bool ShouldAnalyzeVariableDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
// implicit type is applicable only for local variables and
// such declarations cannot have multiple declarators and
// must have an initializer.
var isSupportedParentKind = variableDeclaration.IsParentKind(
SyntaxKind.LocalDeclarationStatement,
SyntaxKind.ForStatement,
SyntaxKind.UsingStatement);
return isSupportedParentKind &&
variableDeclaration.Variables.Count == 1 &&
variableDeclaration.Variables.Single().Initializer.IsKind(SyntaxKind.EqualsValueClause);
}
protected virtual bool ShouldAnalyzeForEachStatement(ForEachStatementSyntax forEachStatement, SemanticModel semanticModel, CancellationToken cancellationToken)
=> true;
protected virtual bool ShouldAnalyzeDeclarationExpression(DeclarationExpressionSyntax declaration, SemanticModel semanticModel, CancellationToken cancellationToken)
=> true;
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
namespace Microsoft.CodeAnalysis.CSharp.Utilities
{
internal sealed class CSharpUseExplicitTypeHelper : CSharpTypeStyleHelper
{
public static CSharpUseExplicitTypeHelper Instance = new CSharpUseExplicitTypeHelper();
private CSharpUseExplicitTypeHelper()
{
}
protected override bool IsStylePreferred(
SemanticModel semanticModel, OptionSet optionSet,
State state, CancellationToken cancellationToken)
{
var stylePreferences = state.TypeStylePreference;
if (state.IsInIntrinsicTypeContext)
{
return !stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeForIntrinsicTypes);
}
else if (state.IsTypeApparentInContext)
{
return !stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeWhereApparent);
}
else
{
return !stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeWherePossible);
}
}
protected override bool ShouldAnalyzeVariableDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (!variableDeclaration.Type.IsVar)
{
// If the type is not 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeVariableDeclaration(variableDeclaration, semanticModel, cancellationToken);
}
protected override bool ShouldAnalyzeForEachStatement(ForEachStatementSyntax forEachStatement, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (!forEachStatement.Type.IsVar)
{
// If the type is not 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeForEachStatement(forEachStatement, semanticModel, cancellationToken);
}
internal override bool TryAnalyzeVariableDeclaration(
TypeSyntax typeName, SemanticModel semanticModel,
OptionSet optionSet, CancellationToken cancellationToken)
{
// var (x, y) = e;
// foreach (var (x, y) in e) ...
if (typeName.IsParentKind(SyntaxKind.DeclarationExpression))
{
var parent = (DeclarationExpressionSyntax)typeName.Parent;
if (parent.Designation.IsKind(SyntaxKind.ParenthesizedVariableDesignation))
{
return true;
}
}
// If it is currently not var, explicit typing exists, return.
// this also takes care of cases where var is mapped to a named type via an alias or a class declaration.
if (!typeName.IsTypeInferred(semanticModel))
{
return false;
}
if (typeName.Parent.IsKind(SyntaxKind.VariableDeclaration) &&
typeName.Parent.Parent.IsKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement))
{
// check assignment for variable declarations.
var variable = ((VariableDeclarationSyntax)typeName.Parent).Variables.First();
if (!AssignmentSupportsStylePreference(
variable.Identifier, typeName, variable.Initializer.Value,
semanticModel, optionSet, cancellationToken))
{
return false;
}
}
else if (typeName.Parent.IsKind(SyntaxKind.ForEachStatement))
{
var foreachStatement = (ForEachStatementSyntax)typeName.Parent;
if (!AssignmentSupportsStylePreference(
foreachStatement.Identifier, typeName, foreachStatement.Expression,
semanticModel, optionSet, cancellationToken))
{
return false;
}
}
return true;
}
protected override bool ShouldAnalyzeDeclarationExpression(DeclarationExpressionSyntax declaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (!declaration.Type.IsVar)
{
// If the type is not 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeDeclarationExpression(declaration, semanticModel, cancellationToken);
}
/// <summary>
/// Analyzes the assignment expression and rejects a given declaration if it is unsuitable for explicit typing.
/// </summary>
/// <returns>
/// false, if explicit typing cannot be used.
/// true, otherwise.
/// </returns>
protected override bool AssignmentSupportsStylePreference(
SyntaxToken identifier,
TypeSyntax typeName,
ExpressionSyntax initializer,
SemanticModel semanticModel,
OptionSet optionSet,
CancellationToken cancellationToken)
{
// is or contains an anonymous type
// cases :
// var anon = new { Num = 1 };
// var enumerableOfAnons = from prod in products select new { prod.Color, prod.Price };
var declaredType = semanticModel.GetTypeInfo(typeName, cancellationToken).Type;
if (declaredType.ContainsAnonymousType())
{
return false;
}
// cannot find type if initializer resolves to an ErrorTypeSymbol
var initializerTypeInfo = semanticModel.GetTypeInfo(initializer, cancellationToken);
return !initializerTypeInfo.Type.IsErrorType();
}
}
}
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Diagnostics;
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
using Microsoft.CodeAnalysis.Shared.Utilities;
using Microsoft.CodeAnalysis.Simplification;
namespace Microsoft.CodeAnalysis.CSharp.Utilities
{
internal sealed class CSharpUseImplicitTypeHelper : CSharpTypeStyleHelper
{
public static readonly CSharpUseImplicitTypeHelper Instance = new CSharpUseImplicitTypeHelper();
private CSharpUseImplicitTypeHelper()
{
}
public override TypeStyleResult AnalyzeTypeName(
TypeSyntax typeName, SemanticModel semanticModel,
OptionSet optionSet, CancellationToken cancellationToken)
{
if (typeName.IsVar)
{
return default;
}
if (!optionSet.GetOption(SimplificationOptions.PreferImplicitTypeInLocalDeclaration))
{
return default;
}
if (typeName.HasAnnotation(DoNotAllowVarAnnotation.Annotation))
{
return default;
}
return base.AnalyzeTypeName(
typeName, semanticModel, optionSet, cancellationToken);
}
protected override bool ShouldAnalyzeVariableDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (variableDeclaration.Type.IsVar)
{
// If the type is already 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeVariableDeclaration(variableDeclaration, semanticModel, cancellationToken);
}
protected override bool ShouldAnalyzeForEachStatement(ForEachStatementSyntax forEachStatement, SemanticModel semanticModel, CancellationToken cancellationToken)
{
var type = forEachStatement.Type;
if (type.IsVar || (type.Kind() == SyntaxKind.RefType && ((RefTypeSyntax)type).Type.IsVar))
{
// If the type is already 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeForEachStatement(forEachStatement, semanticModel, cancellationToken);
}
protected override bool IsStylePreferred(
SemanticModel semanticModel, OptionSet optionSet,
State state, CancellationToken cancellationToken)
{
var stylePreferences = state.TypeStylePreference;
if (state.IsInIntrinsicTypeContext)
{
return stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeForIntrinsicTypes);
}
else if (state.IsTypeApparentInContext)
{
return stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeWhereApparent);
}
else
{
return stylePreferences.HasFlag(TypeStylePreference.ImplicitTypeWherePossible);
}
}
internal override bool TryAnalyzeVariableDeclaration(
TypeSyntax typeName, SemanticModel semanticModel,
OptionSet optionSet, CancellationToken cancellationToken)
{
Debug.Assert(!typeName.IsVar, "'var' special case should have prevented analysis of this variable.");
var candidateReplacementNode = SyntaxFactory.IdentifierName("var");
// If there exists a type named var, return.
var conflict = semanticModel.GetSpeculativeSymbolInfo(typeName.SpanStart, candidateReplacementNode, SpeculativeBindingOption.BindAsTypeOrNamespace).Symbol;
if (conflict?.IsKind(SymbolKind.NamedType) == true)
{
return false;
}
if (typeName.Parent.IsKind(SyntaxKind.VariableDeclaration) &&
typeName.Parent.IsParentKind(SyntaxKind.LocalDeclarationStatement, SyntaxKind.ForStatement, SyntaxKind.UsingStatement))
{
var variableDeclaration = (VariableDeclarationSyntax)typeName.Parent;
// implicitly typed variables cannot be constants.
if ((variableDeclaration.Parent as LocalDeclarationStatementSyntax)?.IsConst == true)
{
return false;
}
if (variableDeclaration.Variables.Count != 1)
{
return false;
}
var variable = variableDeclaration.Variables[0];
if (variable.Initializer == null)
{
return false;
}
var initializer = variable.Initializer.Value;
// Do not suggest var replacement for stackalloc span expressions.
// This will change the bound type from a span to a pointer.
if (!variableDeclaration.Type.IsKind(SyntaxKind.PointerType))
{
var containsStackAlloc = initializer
.DescendantNodesAndSelf(descendIntoChildren: node => !node.IsAnyLambdaOrAnonymousMethod())
.Any(node => node.IsKind(SyntaxKind.StackAllocArrayCreationExpression));
if (containsStackAlloc)
{
return false;
}
}
if (AssignmentSupportsStylePreference(
variable.Identifier, typeName, initializer,
semanticModel, optionSet, cancellationToken))
{
return true;
}
}
else if (typeName.Parent is ForEachStatementSyntax foreachStatement)
{
var foreachStatementInfo = semanticModel.GetForEachStatementInfo(foreachStatement);
if (foreachStatementInfo.ElementConversion.IsIdentity)
{
return true;
}
}
else if (typeName.Parent is DeclarationExpressionSyntax declarationExpression &&
TryAnalyzeDeclarationExpression(declarationExpression, semanticModel, optionSet, cancellationToken))
{
return true;
}
return false;
}
private bool TryAnalyzeDeclarationExpression(
DeclarationExpressionSyntax declarationExpression,
SemanticModel semanticModel,
OptionSet optionSet,
CancellationToken cancellationToken)
{
// It's not always safe to convert a decl expression like "Method(out int i)" to
// "Method(out var i)". Changing to 'var' may cause overload resolution errors.
// Have to see if using 'var' means not resolving to the same type as before.
// Note: this is fairly expensive, so we try to avoid this if we can by seeing if
// there are multiple candidates with the original call. If not, then we don't
// have to do anything.
if (declarationExpression.Parent is ArgumentSyntax argument &&
argument.Parent is ArgumentListSyntax argumentList &&
argumentList.Parent is InvocationExpressionSyntax invocationExpression)
{
// If there was only one member in the group, and it was non-generic itself,
// then this change is safe to make without doing any complex analysis.
// Multiple methods mean that switching to 'var' might remove information
// that affects overload resolution. And if the method is generic, then
// switching to 'var' may mean that inference might not work properly.
var memberGroup = semanticModel.GetMemberGroup(invocationExpression.Expression, cancellationToken);
if (memberGroup.Length == 1 &&
memberGroup[0].GetTypeParameters().IsEmpty)
{
return true;
}
}
if (!semanticModel.SyntaxTree.HasCompilationUnitRoot)
{
return false;
}
// Do the expensive check. Note: we can't use the SpeculationAnalyzer (or any
// speculative analyzers) here. This is due to
// https://github.com/dotnet/roslyn/issues/20724. Specifically, all the speculative
// helpers do not deal with changes to code that introduces a variable (in this case,
// the declaration expression). The compiler sees this as an error because there are
// now two colliding variables, which causes all sorts of errors to be reported.
var tree = semanticModel.SyntaxTree;
var root = tree.GetRoot(cancellationToken);
var annotation = new SyntaxAnnotation();
var declarationTypeNode = declarationExpression.Type;
var declarationType = semanticModel.GetTypeInfo(declarationTypeNode, cancellationToken).Type;
var newRoot = root.ReplaceNode(
declarationTypeNode,
SyntaxFactory.IdentifierName("var").WithTriviaFrom(declarationTypeNode).WithAdditionalAnnotations(annotation));
var newTree = tree.WithRootAndOptions(newRoot, tree.Options);
var newSemanticModel = semanticModel.Compilation.ReplaceSyntaxTree(tree, newTree).GetSemanticModel(newTree);
var newDeclarationTypeNode = newTree.GetRoot(cancellationToken).GetAnnotatedNodes(annotation).Single();
var newDeclarationType = newSemanticModel.GetTypeInfo(newDeclarationTypeNode, cancellationToken).Type;
return SymbolEquivalenceComparer.Instance.Equals(declarationType, newDeclarationType);
}
/// <summary>
/// Analyzes the assignment expression and rejects a given declaration if it is unsuitable for implicit typing.
/// </summary>
/// <returns>
/// false, if implicit typing cannot be used.
/// true, otherwise.
/// </returns>
protected override bool AssignmentSupportsStylePreference(
SyntaxToken identifier,
TypeSyntax typeName,
ExpressionSyntax initializer,
SemanticModel semanticModel,
OptionSet optionSet,
CancellationToken cancellationToken)
{
var expression = GetInitializerExpression(initializer);
// var cannot be assigned null
if (expression.IsKind(SyntaxKind.NullLiteralExpression))
{
return false;
}
// cannot use implicit typing on method group or on dynamic
var declaredType = semanticModel.GetTypeInfo(typeName, cancellationToken).Type;
if (declaredType != null && declaredType.TypeKind == TypeKind.Dynamic)
{
return false;
}
// variables declared using var cannot be used further in the same initialization expression.
if (initializer.DescendantNodesAndSelf()
.Where(n => (n as IdentifierNameSyntax)?.Identifier.ValueText.Equals(identifier.ValueText) == true)
.Any(n => semanticModel.GetSymbolInfo(n, cancellationToken).Symbol?.IsKind(SymbolKind.Local) == true))
{
return false;
}
// Get the conversion that occurred between the expression's type and type implied by the expression's context
// and filter out implicit conversions. If an implicit conversion (other than identity) exists
// and if we're replacing the declaration with 'var' we'd be changing the semantics by inferring type of
// initializer expression and thereby losing the conversion.
var conversion = semanticModel.GetConversion(expression, cancellationToken);
if (conversion.IsIdentity)
{
// final check to compare type information on both sides of assignment.
var initializerType = semanticModel.GetTypeInfo(expression, cancellationToken).Type;
return declaredType.Equals(initializerType);
}
return false;
}
internal static ExpressionSyntax GetInitializerExpression(ExpressionSyntax initializer)
=> initializer is CheckedExpressionSyntax
? ((CheckedExpressionSyntax)initializer).Expression.WalkDownParentheses()
: initializer.WalkDownParentheses();
protected override bool ShouldAnalyzeDeclarationExpression(DeclarationExpressionSyntax declaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (declaration.Type.IsVar)
{
// If the type is already 'var', this analyze has no work to do
return false;
}
// The base analyzer may impose further limitations
return base.ShouldAnalyzeDeclarationExpression(declaration, semanticModel, cancellationToken);
}
}
}
......@@ -348,6 +348,8 @@ internal interface ISyntaxFactsService : ILanguageService
Location GetDeconstructionReferenceLocation(SyntaxNode node);
SyntaxToken? GetDeclarationIdentifierIfOverride(SyntaxToken token);
bool SpansPreprocessorDirective(IEnumerable<SyntaxNode> nodes);
}
[Flags]
......
// Copyright (c) Microsoft. All Rights Reserved. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using Microsoft.CodeAnalysis.LanguageServices;
using Roslyn.Utilities;
namespace Microsoft.CodeAnalysis.LanguageServices
{
......@@ -39,9 +41,10 @@ public static bool IsRegularOrDocumentationComment(this ISyntaxFactsService synt
syntaxFacts.GetPartsOfAssignmentStatement(statement, out left, out _, out right);
}
public static bool SpansPreprocessorDirective(this ISyntaxFactsService service, SyntaxNode node)
=> service.SpansPreprocessorDirective(SpecializedCollections.SingletonEnumerable(node));
public static bool IsWhitespaceOrEndOfLineTrivia(this ISyntaxFactsService syntaxFacts, SyntaxTrivia trivia)
{
return syntaxFacts.IsWhitespaceTrivia(trivia) || syntaxFacts.IsEndOfLineTrivia(trivia);
}
=> syntaxFacts.IsWhitespaceTrivia(trivia) || syntaxFacts.IsEndOfLineTrivia(trivia);
}
}
......@@ -6,23 +6,20 @@
namespace Microsoft.CodeAnalysis.Simplification
{
// When applied to a SyntaxNode, prevents AbstractImportsAdder from
// adding imports for this node. Applied alongside SymbolAnnotation
// when a type should be simplified without adding a using for the type.
// When applied to a SyntaxNode, prevents AbstractImportsAdder from adding imports for this
// node. Applied alongside SymbolAnnotation when a type should be simplified without adding a
// using for the type.
//
// For example, override completion adds
// void goo() => throw new System.NotImplementedException()
// with a SymbolAnnotation and a DoNotAddImportsAnnotation.
// For example, override completion adds void goo() => throw new
// System.NotImplementedException() with a SymbolAnnotation and a DoNotAddImportsAnnotation.
//
// This allows the simplifier to remove the `System.` if
// `using System` is already in the file but prevents the addition
// of `using System` just for the NotImplementedException.
// This allows the simplifier to remove the `System.` if `using System` is already in the file
// but prevents the addition of `using System` just for the NotImplementedException.
//
// This could have been implemented as an additional bit serialized
// into the `Data` string of SymbolAnnotation. However that would
// require additional substring operations to retrieve SymbolAnnotation
// symbols even in the common case where we don't need to supress
// add imports. This is therefore implemented as a separate annnotation.
// This could have been implemented as an additional bit serialized into the `Data` string of
// SymbolAnnotation. However that would require additional substring operations to retrieve
// SymbolAnnotation symbols even in the common case where we don't need to suppress add imports.
// This is therefore implemented as a separate annotation.
internal class DoNotAddImportsAnnotation
{
public static readonly SyntaxAnnotation Annotation = new SyntaxAnnotation(Kind);
......
namespace Microsoft.CodeAnalysis.Simplification
{
/// <summary>
/// When applied to a SyntaxNode, prevents the simplifier from converting a type to 'var'.
/// </summary>
internal class DoNotAllowVarAnnotation
{
public static readonly SyntaxAnnotation Annotation = new SyntaxAnnotation(Kind);
public const string Kind = "DoNotAllowVar";
}
}
......@@ -34,7 +34,7 @@ public static class SimplificationOptions
/// <summary>
/// This option says if we should simplify the Explicit Type in Local Declarations
/// </summary>
public static Option<bool> PreferImplicitTypeInLocalDeclaration { get; } = new Option<bool>(nameof(SimplificationOptions), nameof(PreferImplicitTypeInLocalDeclaration), false,
public static Option<bool> PreferImplicitTypeInLocalDeclaration { get; } = new Option<bool>(nameof(SimplificationOptions), nameof(PreferImplicitTypeInLocalDeclaration), true,
storageLocations: new RoamingProfileStorageLocation("TextEditor.%LANGUAGE%.Specific.PreferImplicitTypeInLocalDeclaration"));
/// <summary>
......
......@@ -1788,5 +1788,9 @@ Namespace Microsoft.CodeAnalysis.VisualBasic
Return Nothing
End Function
Public Function SpansPreprocessorDirective(nodes As IEnumerable(Of SyntaxNode)) As Boolean Implements ISyntaxFactsService.SpansPreprocessorDirective
Return nodes.SpansPreprocessorDirective()
End Function
End Class
End Namespace
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册