Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,23 @@ void Method()
}", new TestParameters(options: ExplicitTypeEverywhere()));
}

[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExplicitType)]
[WorkItem(27221, "https://github.com/dotnet/roslyn/issues/27221")]
public async Task NotIfRefTypeAlreadyExplicitlyTyped()
{
await TestMissingInRegularAndScriptAsync(
@"using System;

struct Program
{
void Method()
{
ref [|Program|] p = Ref();
}
ref Program Ref() => throw null;
}", new TestParameters(options: ExplicitTypeEverywhere()));
}

[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExplicitType)]
public async Task NotOnRHS()
{
Expand Down Expand Up @@ -297,6 +314,68 @@ void Method(int? x)
await TestInRegularAndScriptAsync(before, after, options: ExplicitTypeExceptWhereApparent());
}

[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExplicitType)]
[WorkItem(27221, "https://github.com/dotnet/roslyn/issues/27221")]
public async Task WithRefIntrinsicType()
{
var before = @"
class Program
{
void Method()
{
ref [|var|] y = Ref();
}
ref int Ref() => throw null;
}";
var after = @"
class Program
{
void Method()
{
ref int y = Ref();
}
ref int Ref() => throw null;
}";
// The type is intrinsic and not apparent
await TestInRegularAndScriptAsync(before, after, options: ExplicitTypeEverywhere());
await TestInRegularAndScriptAsync(before, after, options: ExplicitTypeForBuiltInTypesOnly());
await TestInRegularAndScriptAsync(before, after, options: ExplicitTypeExceptWhereApparent());
}

[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExplicitType)]
[WorkItem(27221, "https://github.com/dotnet/roslyn/issues/27221")]
public async Task WithRefIntrinsicTypeInForeach()
{
var before = @"
class E
{
public ref int Current => throw null;
public bool MoveNext() => throw null;
public E GetEnumerator() => throw null;

void M()
{
foreach (ref [|var|] x in this) { }
}
}";
var after = @"
class E
{
public ref int Current => throw null;
public bool MoveNext() => throw null;
public E GetEnumerator() => throw null;

void M()
{
foreach (ref int x in this) { }
}
}";
// The type is intrinsic and not apparent
await TestInRegularAndScriptAsync(before, after, options: ExplicitTypeEverywhere());
await TestInRegularAndScriptAsync(before, after, options: ExplicitTypeForBuiltInTypesOnly());
await TestInRegularAndScriptAsync(before, after, options: ExplicitTypeExceptWhereApparent());
}

[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseExplicitType)]
[WorkItem(23907, "https://github.com/dotnet/roslyn/issues/23907")]
public async Task InArrayOfNullableIntrinsicType()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -117,6 +117,21 @@ void Method()
}", new TestParameters(options: ImplicitTypeEverywhere()));
}

[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseImplicitType)]
[WorkItem(27221, "https://github.com/dotnet/roslyn/issues/27221")]
public async Task NotOnRefVar()
{
await TestMissingInRegularAndScriptAsync(@"
class Program
{
void Method()
{
ref [|var|] x = Method2();
}
ref int Method2() => throw null;
}", new TestParameters(options: ImplicitTypeEverywhere()));
}

[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseImplicitType)]
public async Task NotOnDynamic()
{
Expand Down Expand Up @@ -447,6 +462,64 @@ static void M()
}", options: ImplicitTypeEverywhere());
}

[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseImplicitType)]
[WorkItem(27221, "https://github.com/dotnet/roslyn/issues/27221")]
public async Task SuggestVarOnRefIntrinsicType()
{
await TestInRegularAndScriptAsync(
@"using System;

class C
{
static void M()
{
ref [|int|] s = Ref();
}
static ref int Ref() => throw null;
}",
@"using System;

class C
{
static void M()
{
ref var s = Ref();
}
static ref int Ref() => throw null;
}", options: ImplicitTypeEverywhere());
}

[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseImplicitType)]
[WorkItem(27221, "https://github.com/dotnet/roslyn/issues/27221")]
public async Task WithRefIntrinsicTypeInForeach()
{
var before = @"
class E
{
public ref int Current => throw null;
public bool MoveNext() => throw null;
public E GetEnumerator() => throw null;

void M()
{
foreach (ref [|int|] x in this) { }
}
}";
var after = @"
class E
{
public ref int Current => throw null;
public bool MoveNext() => throw null;
public E GetEnumerator() => throw null;

void M()
{
foreach (ref var x in this) { }
}
}";
await TestInRegularAndScriptAsync(before, after, options: ImplicitTypeEverywhere());
}

[WpfFact, Trait(Traits.Feature, Traits.Features.CodeActionsUseImplicitType)]
public async Task SuggestVarOnFrameworkType()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ private void HandleVariableDeclaration(SyntaxNodeAnalysisContext context)

// The severity preference is not Hidden, as indicated by IsStylePreferred.
var descriptor = GetDescriptorWithSeverity(typeStyle.Severity);
context.ReportDiagnostic(CreateDiagnostic(descriptor, declarationStatement, declaredType.Span));
context.ReportDiagnostic(CreateDiagnostic(descriptor, declarationStatement, declaredType.StripRefIfNeeded().Span));
}

private Diagnostic CreateDiagnostic(DiagnosticDescriptor descriptor, SyntaxNode declaration, TextSpan diagnosticSpan)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,11 @@ internal static async Task HandleDeclarationAsync(

TypeSyntax typeSyntax = null;
ParenthesizedVariableDesignationSyntax parensDesignation = null;
if (declarationContext is RefTypeSyntax refType)
{
declarationContext = declarationContext.Parent;
}

if (declarationContext is VariableDeclarationSyntax varDecl)
{
typeSyntax = varDecl.Type;
Expand All @@ -78,7 +83,7 @@ internal static async Task HandleDeclarationAsync(

if (parensDesignation is null)
{
var typeSymbol = semanticModel.GetTypeInfo(typeSyntax).ConvertedType;
var typeSymbol = semanticModel.GetTypeInfo(typeSyntax.StripRefIfNeeded()).ConvertedType;

// 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).
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@ public static NamespaceDeclarationSyntax GetInnermostNamespaceDeclarationWithUsi
}

public static IEnumerable<SyntaxTrivia> GetAllPrecedingTriviaToPreviousToken(
this SyntaxNode node, SourceText sourceText = null,
this SyntaxNode node, SourceText sourceText = null,
bool includePreviousTokenTrailingTriviaOnlyIfOnSameLine = false)
=> node.GetFirstToken().GetAllPrecedingTriviaToPreviousToken(
sourceText, includePreviousTokenTrailingTriviaOnlyIfOnSameLine);
Expand All @@ -210,7 +210,7 @@ public static IEnumerable<SyntaxTrivia> GetAllPrecedingTriviaToPreviousToken(
/// the previous token's trailing trivia and this token's leading trivia).
/// </summary>
public static IEnumerable<SyntaxTrivia> GetAllPrecedingTriviaToPreviousToken(
this SyntaxToken token, SourceText sourceText = null,
this SyntaxToken token, SourceText sourceText = null,
bool includePreviousTokenTrailingTriviaOnlyIfOnSameLine = false)
{
var prevToken = token.GetPreviousToken(includeSkipped: true);
Expand All @@ -219,7 +219,7 @@ public static IEnumerable<SyntaxTrivia> GetAllPrecedingTriviaToPreviousToken(
return token.LeadingTrivia;
}

if (includePreviousTokenTrailingTriviaOnlyIfOnSameLine &&
if (includePreviousTokenTrailingTriviaOnlyIfOnSameLine &&
!sourceText.AreOnSameLine(prevToken, token))
{
return token.LeadingTrivia;
Expand Down Expand Up @@ -735,7 +735,7 @@ node is UsingStatementSyntax ||
public static StatementSyntax GetEmbeddedStatement(this SyntaxNode node)
{
switch (node)
{
{
case DoStatementSyntax n: return n.Statement;
case ElseClauseSyntax n: return n.Statement;
case FixedStatementSyntax n: return n.Statement;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,5 +122,8 @@ public static TypeSyntax GenerateTypeSyntax(this IPropertySymbol property)
return property.Type.GenerateTypeSyntax();
}
}

public static TypeSyntax StripRefIfNeeded(this TypeSyntax type)
=> type is RefTypeSyntax refType ? refType.Type : type;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ private bool IsTypeApparentInDeclaration(VariableDeclarationSyntax variableDecla
}

var initializerExpression = CSharpUseImplicitTypeHelper.GetInitializerExpression(initializer.Value);
var declaredTypeSymbol = semanticModel.GetTypeInfo(variableDeclaration.Type, cancellationToken).Type;
var declaredTypeSymbol = semanticModel.GetTypeInfo(variableDeclaration.Type.StripRefIfNeeded(), cancellationToken).Type;
return TypeStyleHelper.IsTypeApparentInAssignmentExpression(stylePreferences, initializerExpression, semanticModel, cancellationToken, declaredTypeSymbol);
}

Expand All @@ -104,7 +104,7 @@ private bool IsPredefinedTypeInDeclaration(SyntaxNode declarationStatement, Sema
var typeSyntax = GetTypeSyntaxFromDeclaration(declarationStatement);

return typeSyntax != null
? IsMadeOfSpecialTypes(semanticModel.GetTypeInfo(typeSyntax).Type)
? IsMadeOfSpecialTypes(semanticModel.GetTypeInfo(typeSyntax.StripRefIfNeeded()).Type)
: false;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ protected override bool IsStylePreferred(

protected override bool ShouldAnalyzeVariableDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (!variableDeclaration.Type.IsVar)
if (!variableDeclaration.Type.StripRefIfNeeded().IsVar)
{
// If the type is not 'var', this analyze has no work to do
return false;
Expand All @@ -52,7 +52,7 @@ protected override bool ShouldAnalyzeVariableDeclaration(VariableDeclarationSynt

protected override bool ShouldAnalyzeForEachStatement(ForEachStatementSyntax forEachStatement, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (!forEachStatement.Type.IsVar)
if (!forEachStatement.Type.StripRefIfNeeded().IsVar)
{
// If the type is not 'var', this analyze has no work to do
return false;
Expand All @@ -79,7 +79,7 @@ internal override bool TryAnalyzeVariableDeclaration(

// 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))
if (!typeName.StripRefIfNeeded().IsTypeInferred(semanticModel))
{
return false;
}
Expand Down Expand Up @@ -141,7 +141,7 @@ protected override bool AssignmentSupportsStylePreference(
// 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;
var declaredType = semanticModel.GetTypeInfo(typeName.StripRefIfNeeded(), cancellationToken).Type;
if (declaredType.ContainsAnonymousType())
{
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public override TypeStyleResult AnalyzeTypeName(
TypeSyntax typeName, SemanticModel semanticModel,
OptionSet optionSet, CancellationToken cancellationToken)
{
if (typeName.IsVar)
if (typeName.StripRefIfNeeded().IsVar)
{
return default;
}
Expand All @@ -47,9 +47,10 @@ public override TypeStyleResult AnalyzeTypeName(

protected override bool ShouldAnalyzeVariableDeclaration(VariableDeclarationSyntax variableDeclaration, SemanticModel semanticModel, CancellationToken cancellationToken)
{
if (variableDeclaration.Type.IsVar)
var type = variableDeclaration.Type.StripRefIfNeeded();
if (type.IsVar)
{
// If the type is already 'var', this analyze has no work to do
// If the type is already 'var' or 'ref var', this analyzer has no work to do
return false;
}

Expand Down Expand Up @@ -94,7 +95,7 @@ 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.");
Debug.Assert(!typeName.StripRefIfNeeded().IsVar, "'var' special case should have prevented analysis of this variable.");

var candidateReplacementNode = SyntaxFactory.IdentifierName("var");

Expand Down Expand Up @@ -252,7 +253,7 @@ protected override bool AssignmentSupportsStylePreference(
}

// cannot use implicit typing on method group or on dynamic
var declaredType = semanticModel.GetTypeInfo(typeName, cancellationToken).Type;
var declaredType = semanticModel.GetTypeInfo(typeName.StripRefIfNeeded(), cancellationToken).Type;
if (declaredType != null && declaredType.TypeKind == TypeKind.Dynamic)
{
return false;
Expand Down