提交 133d04c0 编写于 作者: B Balaji Krishnan

Merge pull request #9326 from balajikris/UseVar-CodeGenParity

Consume new use var options from CodeGen features

This is part of #9155

Since, we replaced an existing user option about using var in local declarations with 3 new codestyle options, we need to update code generation features that were directly consuming these affected options.

This PR, selectively handles updating the consumers of the obsolete option and moving them over to use the new options.
......@@ -2,6 +2,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeRefactorings.IntroduceVariable;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.Options;
......@@ -17,6 +18,21 @@ protected override object CreateCodeRefactoringProvider(Workspace workspace)
return new IntroduceVariableCodeRefactoringProvider();
}
private readonly SimpleCodeStyleOption onWithInfo = new SimpleCodeStyleOption(true, NotificationOption.Info);
// specify all options explicitly to override defaults.
private IDictionary<OptionKey, object> ImplicitTypingEverywhere() =>
OptionSet(CSharpCodeStyleOptions.UseImplicitTypeWherePossible, onWithInfo)
.With(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, onWithInfo)
.With(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, onWithInfo);
internal IDictionary<OptionKey, object> OptionSet(OptionKey option, object value)
{
var options = new Dictionary<OptionKey, object>();
options.Add(option, value);
return options;
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)]
public async Task TestMethodFix1()
{
......@@ -261,7 +277,8 @@ public async Task TestMethodFixComplexName1()
await TestAsync(
@"class C { static int Baz; void Foo() { Bar([|C.Baz|]); Bar(1 + 1); } }",
@"class C { static int Baz; void Foo() { var {|Rename:baz|} = C.Baz; Bar(baz); Bar(1 + 1); } }",
index: 0);
index: 0,
options: ImplicitTypingEverywhere());
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)]
......@@ -289,7 +306,8 @@ public async Task TestNameConflict2()
await TestAsync(
@"using System ; class Program { private static int v = 5 ; static void Main ( string [ ] args ) { Func < int , int > d = ( x ) => { return [|x * v|] ; } ; d . Invoke ( v ) ; } } ",
@"using System ; class Program { private static int v = 5 ; static void Main ( string [ ] args ) { Func < int , int > d = ( x ) => { var {|Rename:v1|} = x * v; return v1 ; } ; d . Invoke ( v ) ; } } ",
index: 0);
index: 0,
options: ImplicitTypingEverywhere());
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)]
......@@ -308,7 +326,8 @@ public async Task TestNameVerbatimIdentifier1()
await TestAsync(
@"static class G<T> { public class @class { } public static void Add(object t) { } } class Program { static void Main() { G<int>.Add([|new G<int>.@class()|]); } }",
@"static class G<T> { public class @class { } public static void Add(object t) { } } class Program { static void Main() { var {|Rename:@class|} = new G<int>.@class(); G<int>.Add(@class); } }",
index: 0);
index: 0,
options: ImplicitTypingEverywhere());
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)]
......@@ -326,7 +345,8 @@ public async Task TestNameVerbatimIdentifier2()
{
await TestAsync(
@"static class G<T> { public class @class { } public static void Add(object t) { } static void Main() { G<int>.Add([|new G<int>.@class()|]); } }",
@"static class G<T> { public class @class { } public static void Add(object t) { } static void Main() { var {|Rename:class1|} = new G<int>.@class(); G<int>.Add(class1); } }");
@"static class G<T> { public class @class { } public static void Add(object t) { } static void Main() { var {|Rename:class1|} = new G<int>.@class(); G<int>.Add(class1); } }",
options: ImplicitTypingEverywhere());
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsIntroduceVariable)]
......@@ -377,7 +397,8 @@ public async Task TestCantExtractMethodTypeParameterToField()
{
await TestAsync(
@"using System ; using System . Collections . Generic ; using System . Linq ; class Program { static void Main < T > ( string [ ] args ) { Foo ( [|( T ) 2 . ToString ( )|] ) ; } } ",
@"using System ; using System . Collections . Generic ; using System . Linq ; class Program { static void Main < T > ( string [ ] args ) { var {|Rename:t|} = ( T ) 2 . ToString ( ) ; Foo ( t ) ; } } ");
@"using System ; using System . Collections . Generic ; using System . Linq ; class Program { static void Main < T > ( string [ ] args ) { var {|Rename:t|} = ( T ) 2 . ToString ( ) ; Foo ( t ) ; } } ",
options: ImplicitTypingEverywhere());
}
[WorkItem(540468, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/540468")]
......@@ -554,7 +575,8 @@ public async Task TestImplicitlyTypedArraysUsedInCheckedExpression()
{
await TestAsync(
@"class Program { static void Main ( string [ ] args ) { int [ ] a = null ; int [ ] temp = checked ( [|a = new [ ] { 1 , 2 , 3 }|] ) ; } } ",
@"class Program { static void Main ( string [ ] args ) { int [ ] a = null ; var {|Rename:v|} = a = new [ ] { 1 , 2 , 3 } ; int [ ] temp = checked ( v ) ; } } ");
@"class Program { static void Main ( string [ ] args ) { int [ ] a = null ; var {|Rename:v|} = a = new [ ] { 1 , 2 , 3 } ; int [ ] temp = checked ( v ) ; } } ",
options: ImplicitTypingEverywhere());
}
[WorkItem(543832, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/543832")]
......@@ -1084,7 +1106,7 @@ static void Main()
}
}",
compareTokens: false);
compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(606347, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/606347")]
......@@ -1186,7 +1208,8 @@ public async Task TestLambdaParameter1()
{
await TestAsync(
@"using System ; class Program { static void Main ( string [ ] args ) { Func < int , int > f = x => [|x + 1|] ; } } ",
@"using System ; class Program { static void Main ( string [ ] args ) { Func < int , int > f = x => { var {|Rename:v|} = x + 1 ; return v; }; } } ");
@"using System ; class Program { static void Main ( string [ ] args ) { Func < int , int > f = x => { var {|Rename:v|} = x + 1 ; return v; }; } } ",
options: ImplicitTypingEverywhere());
}
[WorkItem(530480, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530480")]
......@@ -1195,7 +1218,8 @@ public async Task TestLambdaParameter2()
{
await TestAsync(
@"using System ; class Program { static void Main ( string [ ] args ) { Func < int , Func < int , int > > f = x => y => [|x + 1|] ; } } ",
@"using System ; class Program { static void Main ( string [ ] args ) { Func < int , Func < int , int > > f = x => { var {|Rename:v|} = x + 1 ; return y => v; }; } } ");
@"using System ; class Program { static void Main ( string [ ] args ) { Func < int , Func < int , int > > f = x => { var {|Rename:v|} = x + 1 ; return y => v; }; } } ",
options: ImplicitTypingEverywhere());
}
[WorkItem(530480, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530480")]
......@@ -1204,7 +1228,8 @@ public async Task TestLambdaParameter3()
{
await TestAsync(
@"using System ; class Program { static void Main ( string [ ] args ) { Func < int , Func < int , int > > f = x => y => [|y + 1|] ; } } ",
@"using System ; class Program { static void Main ( string [ ] args ) { Func < int , Func < int , int > > f = x => y =>{ var {|Rename:v|} = y + 1 ; return v; }; } } ");
@"using System ; class Program { static void Main ( string [ ] args ) { Func < int , Func < int , int > > f = x => y =>{ var {|Rename:v|} = y + 1 ; return v; }; } } ",
options: ImplicitTypingEverywhere());
}
[WorkItem(530480, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530480")]
......@@ -1240,7 +1265,8 @@ public async Task TestNullableOfPointerType()
{
await TestAsync(
@"using System ; class Program { static void Main ( ) { [|new Nullable < int * > ( )|] . GetValueOrDefault ( ) ; } } ",
@"using System ; class Program { static void Main ( ) { var {|Rename:v|} = new Nullable < int * > ( ) ; v . GetValueOrDefault ( ) ; } } ");
@"using System ; class Program { static void Main ( ) { var {|Rename:v|} = new Nullable < int * > ( ) ; v . GetValueOrDefault ( ) ; } } ",
options: ImplicitTypingEverywhere());
}
[WorkItem(530919, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/530919")]
......@@ -1259,7 +1285,8 @@ public async Task TestIntroduceLocalRemovesUnnecessaryCast()
{
await TestAsync(
@"using System.Collections.Generic; class C { static void Main(string[] args) { var set = new HashSet<string>(); set.Add([|set.ToString()|]); } } ",
@"using System.Collections.Generic; class C { static void Main(string[] args) { var set = new HashSet<string>(); var {|Rename:v|} = set.ToString(); set.Add(v); } } ");
@"using System.Collections.Generic; class C { static void Main(string[] args) { var set = new HashSet<string>(); var {|Rename:v|} = set.ToString(); set.Add(v); } } ",
options: ImplicitTypingEverywhere());
}
[WorkItem(655498, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/655498")]
......@@ -1357,7 +1384,7 @@ static void Main(string[] args)
d.Add(""a"", exception);
}
}",
compareTokens: false);
compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(884961, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/884961")]
......@@ -1384,7 +1411,7 @@ void M()
var l = new List<int>() { tickCount };
}
}",
compareTokens: false);
compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(884961, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/884961")]
......@@ -1443,7 +1470,7 @@ static int Main(string[] args)
return new Program { A = { { v, 0 } } }.A.Count;
}
}",
compareTokens: false);
compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(884961, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/884961")]
......@@ -1470,7 +1497,7 @@ void M()
var a = new int[] { tickCount };
}
}",
compareTokens: false);
compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(884961, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/884961")]
......@@ -1534,7 +1561,8 @@ void M()
}
}",
index: 1,
compareTokens: false);
compareTokens: false,
options: ImplicitTypingEverywhere());
}
[WorkItem(939259, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/939259")]
......@@ -1620,7 +1648,7 @@ void Foo()
}
}";
await TestAsync(code, expected, index: 0, compareTokens: false);
await TestAsync(code, expected, index: 0, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(1037057, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1037057")]
......@@ -1650,7 +1678,7 @@ void M()
int y = v * (x + 5);
}
}
", index: 0, compareTokens: false);
", index: 0, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(1065661, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1065661")]
......@@ -1697,7 +1725,7 @@ static void Foo(string s)
}
}";
await TestAsync(code, expected, index: 0, compareTokens: false);
await TestAsync(code, expected, index: 0, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(1097147, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097147")]
......@@ -1725,7 +1753,7 @@ static void Foo(string s)
}
}";
await TestAsync(code, expected, index: 0, compareTokens: false);
await TestAsync(code, expected, index: 0, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(1097147, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097147")]
......@@ -1771,7 +1799,7 @@ class B
public int Length { get; set; }
}";
await TestAsync(code, expected, index: 0, compareTokens: false);
await TestAsync(code, expected, index: 0, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(1097147, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1097147")]
......@@ -1819,7 +1847,7 @@ class B
public int GetAge() { return age; }
}";
await TestAsync(code, expected, index: 0, compareTokens: false);
await TestAsync(code, expected, index: 0, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(528, "http://github.com/dotnet/roslyn/issues/528")]
......@@ -1907,7 +1935,7 @@ private Complex Add(int b)
}
}";
await TestAsync(code, expected, index: 0, compareTokens: false);
await TestAsync(code, expected, index: 0, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(528, "http://github.com/dotnet/roslyn/issues/528")]
......@@ -2084,7 +2112,7 @@ class SampleCollection<T>
}
}";
await TestAsync(code, expected, index: 0, compareTokens: false);
await TestAsync(code, expected, index: 0, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(528, "http://github.com/dotnet/roslyn/issues/528")]
......@@ -2385,7 +2413,7 @@ static void Test(string[] args)
}
}";
await TestAsync(code, expected, index: 0, compareTokens: false);
await TestAsync(code, expected, index: 0, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(976, "https://github.com/dotnet/roslyn/issues/976")]
......@@ -2415,7 +2443,7 @@ static void Test(string[] args)
}
}";
await TestAsync(code, expected, index: 1, compareTokens: false);
await TestAsync(code, expected, index: 1, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(909152, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/909152")]
......@@ -2463,7 +2491,7 @@ public T F<T>(T x)
}
}";
await TestAsync(code, expected, index: 0, compareTokens: false);
await TestAsync(code, expected, index: 0, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(1130990, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1130990")]
......@@ -2493,7 +2521,7 @@ public T F<T>(T x)
}
}";
await TestAsync(code, expected, index: 0, compareTokens: false);
await TestAsync(code, expected, index: 0, compareTokens: false, options: ImplicitTypingEverywhere());
}
[WorkItem(1130990, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/1130990")]
......
......@@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CodeFixes;
using Microsoft.CodeAnalysis.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeFixes.GenerateVariable;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.Diagnostics;
......@@ -21,6 +22,21 @@ public class GenerateVariableTests : AbstractCSharpDiagnosticProviderBasedUserDi
null, new GenerateVariableCodeFixProvider());
}
private readonly SimpleCodeStyleOption onWithInfo = new SimpleCodeStyleOption(true, NotificationOption.Info);
// specify all options explicitly to override defaults.
private IDictionary<OptionKey, object> ImplicitTypingEverywhere() =>
OptionSet(CSharpCodeStyleOptions.UseImplicitTypeWherePossible, onWithInfo)
.With(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent, onWithInfo)
.With(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes, onWithInfo);
internal IDictionary<OptionKey, object> OptionSet(OptionKey option, object value)
{
var options = new Dictionary<OptionKey, object>();
options.Add(option, value);
return options;
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
public async Task TestSimpleLowercaseIdentifier1()
{
......@@ -1771,7 +1787,7 @@ public async Task TestGenerateFromAssign1()
await TestAsync(
@"class Program { void Main ( ) { [|undefined|] = 1 ; } } ",
@"class Program { void Main ( ) { var undefined = 1 ; } } ",
index: 2);
index: 2, options: ImplicitTypingEverywhere());
}
[Fact, Trait(Traits.Feature, Traits.Features.CodeActionsGenerateVariable)]
......@@ -1853,7 +1869,7 @@ void Foo()
#line default
#line hidden
}
");
", options: ImplicitTypingEverywhere());
}
[WorkItem(546027, "http://vstfdevdiv:8080/DevDiv2/DevDiv/_workitems/edit/546027")]
......
......@@ -123,7 +123,7 @@ public static void Main()
{
for (int i = 0; i < 10; i++)
{
var {|Rename:v|} = i + 1;
int {|Rename:v|} = i + 1;
Console.WriteLine(v);
}
}
......
......@@ -108,6 +108,8 @@
<Compile Include="CodeRefactorings\MoveDeclarationNearReference\MoveDeclarationNearReferenceCodeRefactoringProvider.State.cs" />
<Compile Include="CodeStyle\CSharpCodeStyleOptions.cs" />
<Compile Include="CodeStyle\CSharpCodeStyleOptionsProvider.cs" />
<Compile Include="CodeStyle\TypeStyle\TypeStyle.cs" />
<Compile Include="CodeStyle\TypeStyle\TypeStyleHelper.cs" />
<Compile Include="Completion\CompletionProviders\AttributeNamedParameterCompletionProvider.ItemRules.cs" />
<Compile Include="Completion\CompletionProviders\AttributeNamedParameterCompletionProvider.cs" />
<Compile Include="Completion\CompletionProviders\CompletionUtilities.cs" />
......
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Syntax;
namespace Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle
{
[Flags]
internal enum TypeStyle
{
None = 0,
ImplicitTypeForIntrinsicTypes = 1 << 0,
ImplicitTypeWhereApparent = 1 << 1,
ImplicitTypeWherePossible = 1 << 2,
}
}
\ No newline at end of file
// 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;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Options;
using Microsoft.CodeAnalysis.Shared.Extensions;
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.
/// </summary>
public static bool IsImplicitTypePreferred(
ExpressionSyntax initializerExpression,
SemanticModel semanticModel,
OptionSet optionSet,
CancellationToken cancellationToken)
{
var stylePreferences = GetCurrentTypeStylePreferences(optionSet);
var isBuiltInTypeContext = IsBuiltInType(initializerExpression, semanticModel, cancellationToken);
var isTypeApparentContext = IsTypeApparentInAssignmentExpression(stylePreferences,
initializerExpression,
semanticModel,
cancellationToken);
return IsImplicitStylePreferred(stylePreferences, isBuiltInTypeContext, isTypeApparentContext);
}
/// <summary>
/// Given an expression of assignment, answers if its type is
/// considered an intrinsic predefined type.
/// </summary>
private static bool IsBuiltInType(
ExpressionSyntax initializerExpression,
SemanticModel semanticModel,
CancellationToken cancellationToken) =>
semanticModel.GetTypeInfo(initializerExpression, cancellationToken).Type?.IsSpecialType() == true;
private static bool IsImplicitStylePreferred(TypeStyle stylePreferences,
bool isBuiltInTypeContext,
bool isTypeApparentContext)
{
return isBuiltInTypeContext
? stylePreferences.HasFlag(TypeStyle.ImplicitTypeForIntrinsicTypes)
: isTypeApparentContext
? stylePreferences.HasFlag(TypeStyle.ImplicitTypeWhereApparent)
: stylePreferences.HasFlag(TypeStyle.ImplicitTypeWherePossible);
}
/// <summary>
/// Analyzes if type information is obvious to the reader by simply looking at the assignment expression.
/// </summary>
/// <remarks>
/// <paramref name="typeInDeclaration"/> accepts null, to be able to cater to codegen features
/// that are about to generate a local declaration and do not have this information to pass in.
/// Things (like analyzers) that do have a local declaration already, should pass this in.
/// </remarks>
public static bool IsTypeApparentInAssignmentExpression(
TypeStyle stylePreferences,
ExpressionSyntax initializerExpression,
SemanticModel semanticModel,
CancellationToken cancellationToken,
ITypeSymbol typeInDeclaration = null)
{
// default(type)
if (initializerExpression.IsKind(SyntaxKind.DefaultExpression))
{
return true;
}
// literals, use var if options allow usage here.
if (initializerExpression.IsAnyLiteralExpression())
{
return stylePreferences.HasFlag(TypeStyle.ImplicitTypeForIntrinsicTypes);
}
// constructor invocations cases:
// = new type();
if (initializerExpression.IsKind(SyntaxKind.ObjectCreationExpression) &&
!initializerExpression.IsKind(SyntaxKind.AnonymousObjectCreationExpression))
{
return true;
}
// explicit conversion cases:
// (type)expr, expr is type, expr as type
if (initializerExpression.IsKind(SyntaxKind.CastExpression) ||
initializerExpression.IsKind(SyntaxKind.IsExpression) ||
initializerExpression.IsKind(SyntaxKind.AsExpression))
{
return true;
}
// other Conversion cases:
// a. conversion with helpers like: int.Parse methods
// b. types that implement IConvertible and then invoking .ToType()
// c. System.Convert.Totype()
var memberName = GetRightmostInvocationExpression(initializerExpression).GetRightmostName();
if (memberName == null)
{
return false;
}
var methodSymbol = semanticModel.GetSymbolInfo(memberName, cancellationToken).Symbol as IMethodSymbol;
if (methodSymbol == null)
{
return false;
}
if (memberName.IsRightSideOfDot())
{
var containingTypeName = memberName.GetLeftSideOfDot();
return IsPossibleCreationOrConversionMethod(methodSymbol, typeInDeclaration, semanticModel, containingTypeName, cancellationToken);
}
return false;
}
private static bool IsPossibleCreationOrConversionMethod(IMethodSymbol methodSymbol,
ITypeSymbol typeInDeclaration,
SemanticModel semanticModel,
ExpressionSyntax containingTypeName,
CancellationToken cancellationToken)
{
if (methodSymbol.ReturnsVoid)
{
return false;
}
var containingType = semanticModel.GetTypeInfo(containingTypeName, cancellationToken).Type;
return IsPossibleCreationMethod(methodSymbol, typeInDeclaration, containingType)
|| IsPossibleConversionMethod(methodSymbol, typeInDeclaration, containingType, semanticModel, cancellationToken);
}
/// <summary>
/// Looks for types that have static methods that return the same type as the container.
/// e.g: int.Parse, XElement.Load, Tuple.Create etc.
/// </summary>
private static bool IsPossibleCreationMethod(IMethodSymbol methodSymbol,
ITypeSymbol typeInDeclaration,
ITypeSymbol containingType)
{
if (!methodSymbol.IsStatic)
{
return false;
}
return IsContainerTypeEqualToReturnType(methodSymbol, typeInDeclaration, containingType);
}
/// <summary>
/// If we have a method ToXXX and its return type is also XXX, then type name is apparent
/// e.g: Convert.ToString.
/// </summary>
private static bool IsPossibleConversionMethod(IMethodSymbol methodSymbol,
ITypeSymbol typeInDeclaration,
ITypeSymbol containingType,
SemanticModel semanticModel,
CancellationToken cancellationToken)
{
var returnType = methodSymbol.ReturnType;
var returnTypeName = returnType.IsNullable()
? returnType.GetTypeArguments().First().Name
: returnType.Name;
return methodSymbol.Name.Equals("To" + returnTypeName, StringComparison.Ordinal);
}
/// <remarks>
/// If there are type arguments on either side of assignment, we match type names instead of type equality
/// to account for inferred generic type arguments.
/// e.g: Tuple.Create(0, true) returns Tuple&lt;X,y&gt; which isn't the same as type Tuple.
/// otherwise, we match for type equivalence
/// </remarks>
private static bool IsContainerTypeEqualToReturnType(IMethodSymbol methodSymbol,
ITypeSymbol typeInDeclaration,
ITypeSymbol containingType)
{
var returnType = methodSymbol.ReturnType;
if (typeInDeclaration?.GetTypeArguments().Length > 0 ||
containingType.GetTypeArguments().Length > 0)
{
return containingType.Name.Equals(returnType.Name);
}
else
{
return containingType.Equals(returnType);
}
}
private static ExpressionSyntax GetRightmostInvocationExpression(ExpressionSyntax node)
{
var awaitExpression = node as AwaitExpressionSyntax;
if (awaitExpression != null && awaitExpression.Expression != null)
{
return GetRightmostInvocationExpression(awaitExpression.Expression);
}
var invocationExpression = node as InvocationExpressionSyntax;
if (invocationExpression != null && invocationExpression.Expression != null)
{
return GetRightmostInvocationExpression(invocationExpression.Expression);
}
var conditional = node as ConditionalAccessExpressionSyntax;
if (conditional != null)
{
return GetRightmostInvocationExpression(conditional.WhenNotNull);
}
return node;
}
private static TypeStyle GetCurrentTypeStylePreferences(OptionSet optionSet)
{
var stylePreferences = TypeStyle.None;
var styleForIntrinsicTypes = optionSet.GetOption(CSharpCodeStyleOptions.UseImplicitTypeForIntrinsicTypes);
var styleForApparent = optionSet.GetOption(CSharpCodeStyleOptions.UseImplicitTypeWhereApparent);
var styleForElsewhere = optionSet.GetOption(CSharpCodeStyleOptions.UseImplicitTypeWherePossible);
if (styleForIntrinsicTypes.IsChecked)
{
stylePreferences |= TypeStyle.ImplicitTypeForIntrinsicTypes;
}
if (styleForApparent.IsChecked)
{
stylePreferences |= TypeStyle.ImplicitTypeWhereApparent;
}
if (styleForElsewhere.IsChecked)
{
stylePreferences |= TypeStyle.ImplicitTypeWherePossible;
}
return stylePreferences;
}
}
}
......@@ -5,6 +5,7 @@
using System.Linq;
using System.Threading;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Options;
......@@ -71,90 +72,14 @@ private void Initialize(SyntaxNode declaration, SemanticModel semanticModel, Opt
/// <summary>
/// Returns true if type information could be gleaned by simply looking at the given statement.
/// This typically means that the type name occurs in either left hand or right hand side of an assignment.
/// This typically means that the type name occurs in right hand side of an assignment.
/// </summary>
private bool IsTypeApparentInDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, TypeStyle stylePreferences, CancellationToken cancellationToken)
{
var initializer = variableDeclaration.Variables.Single().Initializer;
var initializerExpression = GetInitializerExpression(initializer);
// default(type)
if (initializerExpression.IsKind(SyntaxKind.DefaultExpression))
{
return true;
}
// literals, use var if options allow usage here.
if (initializerExpression.IsAnyLiteralExpression())
{
return stylePreferences.HasFlag(TypeStyle.ImplicitTypeForIntrinsicTypes);
}
// constructor invocations cases:
// = new type();
if (initializerExpression.IsKind(SyntaxKind.ObjectCreationExpression) &&
!initializerExpression.IsKind(SyntaxKind.AnonymousObjectCreationExpression))
{
return true;
}
// explicit conversion cases:
// (type)expr, expr is type, expr as type
if (initializerExpression.IsKind(SyntaxKind.CastExpression) ||
initializerExpression.IsKind(SyntaxKind.IsExpression) ||
initializerExpression.IsKind(SyntaxKind.AsExpression))
{
return true;
}
// other Conversion cases:
// a. conversion with helpers like: int.Parse methods
// b. types that implement IConvertible and then invoking .ToType()
// c. System.Convert.Totype()
var declaredTypeSymbol = semanticModel.GetTypeInfo(variableDeclaration.Type, cancellationToken).Type;
var memberName = GetRightmostInvocationExpression(initializerExpression).GetRightmostName();
if (memberName == null)
{
return false;
}
var methodSymbol = semanticModel.GetSymbolInfo(memberName, cancellationToken).Symbol as IMethodSymbol;
if (methodSymbol == null)
{
return false;
}
if (memberName.IsRightSideOfDot())
{
var typeName = memberName.GetLeftSideOfDot();
return IsPossibleCreationOrConversionMethod(methodSymbol, declaredTypeSymbol, semanticModel, typeName, cancellationToken);
}
return false;
}
private ExpressionSyntax GetRightmostInvocationExpression(ExpressionSyntax node)
{
var awaitExpression = node as AwaitExpressionSyntax;
if (awaitExpression != null && awaitExpression.Expression != null)
{
return GetRightmostInvocationExpression(awaitExpression.Expression);
}
var invocationExpression = node as InvocationExpressionSyntax;
if (invocationExpression != null && invocationExpression.Expression != null)
{
return GetRightmostInvocationExpression(invocationExpression.Expression);
}
var conditional = node as ConditionalAccessExpressionSyntax;
if (conditional != null)
{
return GetRightmostInvocationExpression(conditional.WhenNotNull);
}
return node;
return TypeStyleHelper.IsTypeApparentInAssignmentExpression(stylePreferences, initializerExpression, semanticModel,cancellationToken, declaredTypeSymbol);
}
/// <summary>
......@@ -199,75 +124,6 @@ private TypeSyntax GetTypeSyntaxFromDeclaration(SyntaxNode declarationStatement)
return null;
}
private bool IsPossibleCreationOrConversionMethod(IMethodSymbol methodSymbol, ITypeSymbol declaredType, SemanticModel semanticModel, ExpressionSyntax typeName, CancellationToken cancellationToken)
{
if (methodSymbol.ReturnsVoid)
{
return false;
}
var typeInInvocation = semanticModel.GetTypeInfo(typeName, cancellationToken).Type;
return IsPossibleCreationMethod(methodSymbol, declaredType, typeInInvocation)
|| IsPossibleConversionMethod(methodSymbol, declaredType, typeInInvocation, semanticModel, cancellationToken);
}
/// <summary>
/// Looks for types that have static methods that return the same type as the container.
/// e.g: int.Parse, XElement.Load, Tuple.Create etc.
/// </summary>
private bool IsPossibleCreationMethod(IMethodSymbol methodSymbol, ITypeSymbol declaredType, ITypeSymbol typeInInvocation)
{
if (!methodSymbol.IsStatic)
{
return false;
}
return IsDeclaredTypeEqualToReturnType(methodSymbol, declaredType, typeInInvocation);
}
/// <summary>
/// If we have a method ToXXX and its return type is also XXX, then type name is apparent
/// e.g: Convert.ToString.
/// </summary>
private bool IsPossibleConversionMethod(IMethodSymbol methodSymbol, ITypeSymbol declaredType, ITypeSymbol typeInInvocation, SemanticModel semanticModel, CancellationToken cancellationToken)
{
// take `char` from `char? c = `
var declaredTypeName = declaredType.IsNullable()
? declaredType.GetTypeArguments().First().Name
: declaredType.Name;
var returnType = methodSymbol.ReturnType;
if (methodSymbol.Name.Equals("To" + declaredTypeName, StringComparison.Ordinal))
{
return IsDeclaredTypeEqualToReturnType(methodSymbol, declaredType, typeInInvocation);
}
return false;
}
/// <remarks>
/// If there are type arguments on either side of assignment, we match type names instead of type equality
/// to account for inferred generic type arguments.
/// e.g: Tuple.Create(0, true) returns Tuple&lt;X,y&gt; which isn't the same as type Tuple.
/// otherwise, we match for type equivalence
/// </remarks>
private static bool IsDeclaredTypeEqualToReturnType(IMethodSymbol methodSymbol, ITypeSymbol declaredType, ITypeSymbol typeInInvocation)
{
var returnType = methodSymbol.ReturnType;
if (declaredType.GetTypeArguments().Length > 0 ||
typeInInvocation.GetTypeArguments().Length > 0)
{
return declaredType.Name.Equals(returnType.Name);
}
else
{
return declaredType.Equals(returnType);
}
}
private TypeStyle GetCurrentTypingStylePreferences(OptionSet optionSet)
{
var stylePreferences = TypeStyle.None;
......
......@@ -16,15 +16,6 @@ namespace Microsoft.CodeAnalysis.CSharp.Diagnostics.TypingStyles
{
internal abstract partial class CSharpTypingStyleDiagnosticAnalyzerBase : DiagnosticAnalyzer, IBuiltInAnalyzer
{
[Flags]
internal enum TypeStyle
{
None = 0,
ImplicitTypeForIntrinsicTypes = 1 << 0,
ImplicitTypeWhereApparent = 1 << 1,
ImplicitTypeWherePossible = 1 << 2,
}
private readonly string _diagnosticId;
private readonly LocalizableString _title;
private readonly LocalizableString _message;
......
......@@ -2,6 +2,7 @@
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.Diagnostics;
......
......@@ -3,6 +3,7 @@
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.Diagnostics;
......
......@@ -5,6 +5,7 @@
using System.Threading;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Symbols;
using Microsoft.CodeAnalysis.CSharp.Syntax;
......@@ -129,7 +130,7 @@ protected override bool ContainingTypesOrSelfHasUnsafeKeyword(INamedTypeSymbol c
return expression.CanReplaceWithLValue(document.SemanticModel, cancellationToken);
}
protected override bool TryConvertToLocalDeclaration(ITypeSymbol type, SyntaxToken identifierToken, OptionSet options, out SyntaxNode newRoot)
protected override bool TryConvertToLocalDeclaration(ITypeSymbol type, SyntaxToken identifierToken, OptionSet options, SemanticModel semanticModel, CancellationToken cancellationToken, out SyntaxNode newRoot)
{
var token = identifierToken;
var node = identifierToken.Parent as IdentifierNameSyntax;
......@@ -140,7 +141,7 @@ protected override bool TryConvertToLocalDeclaration(ITypeSymbol type, SyntaxTok
var declarationStatement = SyntaxFactory.LocalDeclarationStatement(
SyntaxFactory.VariableDeclaration(
GenerateTypeSyntax(type, options),
GenerateTypeSyntax(type, options, assignExpression, semanticModel, cancellationToken),
SyntaxFactory.SingletonSeparatedList(
SyntaxFactory.VariableDeclarator(token, null, SyntaxFactory.EqualsValueClause(
assignExpression.OperatorToken, assignExpression.Right)))));
......@@ -156,11 +157,24 @@ protected override bool TryConvertToLocalDeclaration(ITypeSymbol type, SyntaxTok
return false;
}
private static TypeSyntax GenerateTypeSyntax(ITypeSymbol type, OptionSet options)
private static TypeSyntax GenerateTypeSyntax(ITypeSymbol type, OptionSet options, ExpressionSyntax expression, SemanticModel semanticModel, CancellationToken cancellationToken)
{
return type.ContainsAnonymousType() || (options.GetOption(CSharpCodeStyleOptions.UseVarWhenDeclaringLocals) && type.TypeKind != TypeKind.Delegate)
? SyntaxFactory.IdentifierName("var")
: type.GenerateTypeSyntax();
// if there isn't a semantic model, we cannot perform further analysis.
if (semanticModel != null)
{
if (type.ContainsAnonymousType())
{
return SyntaxFactory.IdentifierName("var");
}
if (type.TypeKind != TypeKind.Delegate &&
TypeStyleHelper.IsImplicitTypePreferred(expression, semanticModel, options, cancellationToken))
{
return SyntaxFactory.IdentifierName("var");
}
}
return type.GenerateTypeSyntax();
}
}
}
......@@ -10,6 +10,7 @@
using Microsoft.CodeAnalysis.CodeActions;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.CodeStyle;
using Microsoft.CodeAnalysis.CSharp.CodeStyle.TypeStyle;
using Microsoft.CodeAnalysis.CSharp.Extensions;
using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Formatting;
......@@ -127,7 +128,9 @@ private TypeSyntax GetTypeSyntax(SemanticDocument document, ExpressionSyntax exp
return SyntaxFactory.IdentifierName("var");
}
if (!isConstant && options.GetOption(CSharpCodeStyleOptions.UseVarWhenDeclaringLocals) && CanUseVar(typeSymbol))
if (!isConstant &&
CanUseVar(typeSymbol) &&
TypeStyleHelper.IsImplicitTypePreferred(expression, document.SemanticModel, options, cancellationToken))
{
return SyntaxFactory.IdentifierName("var");
}
......
......@@ -39,18 +39,21 @@ public override string Title
}
}
protected override Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
protected async override Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
{
var newRoot = GetNewRoot(cancellationToken);
var newRoot = await GetNewRoot(cancellationToken).ConfigureAwait(false);
var newDocument = _document.WithSyntaxRoot(newRoot);
return Task.FromResult(newDocument);
return newDocument;
}
private SyntaxNode GetNewRoot(CancellationToken cancellationToken)
private async Task<SyntaxNode> GetNewRoot(CancellationToken cancellationToken)
{
SyntaxNode newRoot;
if (_service.TryConvertToLocalDeclaration(_state.LocalType, _state.IdentifierToken, _document.Project.Solution.Workspace.Options, out newRoot))
var semanticModel = await _document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false);
if (_service.TryConvertToLocalDeclaration(_state.LocalType, _state.IdentifierToken, _document.Project.Solution.Workspace.Options, semanticModel, cancellationToken, out newRoot))
{
return newRoot;
}
......
......@@ -28,7 +28,7 @@ protected AbstractGenerateVariableService()
protected abstract bool TryInitializeExplicitInterfaceState(SemanticDocument document, SyntaxNode node, CancellationToken cancellationToken, out SyntaxToken identifierToken, out IPropertySymbol propertySymbol, out INamedTypeSymbol typeToGenerateIn);
protected abstract bool TryInitializeIdentifierNameState(SemanticDocument document, TSimpleNameSyntax identifierName, CancellationToken cancellationToken, out SyntaxToken identifierToken, out TExpressionSyntax simpleNameOrMemberAccessExpression, out bool isInExecutableBlock, out bool isinConditionalAccessExpression);
protected abstract bool TryConvertToLocalDeclaration(ITypeSymbol type, SyntaxToken identifierToken, OptionSet options, out SyntaxNode newRoot);
protected abstract bool TryConvertToLocalDeclaration(ITypeSymbol type, SyntaxToken identifierToken, OptionSet options, SemanticModel semanticModel, CancellationToken cancellationToken, out SyntaxNode newRoot);
public async Task<IEnumerable<CodeAction>> GenerateVariableAsync(
Document document,
......
......@@ -112,7 +112,7 @@ Namespace Microsoft.CodeAnalysis.VisualBasic.GenerateMember.GenerateVariable
Return expression.CanReplaceWithLValue(semanticModel, cancellationToken)
End Function
Protected Overrides Function TryConvertToLocalDeclaration(type As ITypeSymbol, identifierToken As SyntaxToken, options As OptionSet, ByRef newRoot As SyntaxNode) As Boolean
Protected Overrides Function TryConvertToLocalDeclaration(type As ITypeSymbol, identifierToken As SyntaxToken, options As OptionSet, semanticModel As SemanticModel, cancellationToken As CancellationToken, ByRef newRoot As SyntaxNode) As Boolean
Return False
End Function
End Class
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册