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
2 changes: 1 addition & 1 deletion ChangeLog.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Fix [RCS1154](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1154) ([#1105](https://github.com/JosefPihrt/Roslynator/pull/1105)).
- Fix [RCS1211](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1211) ([#1095](https://github.com/JosefPihrt/Roslynator/pull/1095)).
- Fix [RCS0005](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS0005) ([#1114](https://github.com/JosefPihrt/Roslynator/pull/1114)).
- Fix [RCS1176](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1176.md) ([#1122](https://github.com/JosefPihrt/Roslynator/pull/1122)).
- Fix [RCS1176](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1176.md) ([#1122](https://github.com/JosefPihrt/Roslynator/pull/1122), [#1140](https://github.com/JosefPihrt/Roslynator/pull/1140)).
- Fix [RCS1085](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1085.md) ([#1120](https://github.com/josefpihrt/roslynator/pull/1120)).
- Fix [RCS1208](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1208.md) ([#1119](https://github.com/JosefPihrt/Roslynator/pull/1119)).
- [CLI] Fix member full declaration in generated documentation (command `generate-doc`) ([#1130](https://github.com/josefpihrt/roslynator/pull/1130)).
Expand Down
62 changes: 62 additions & 0 deletions src/CSharp/CSharp/CSharpTypeAnalysis.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System;
using System.Collections.Immutable;
using System.Diagnostics;
using System.Threading;
using Microsoft.CodeAnalysis;
Expand Down Expand Up @@ -502,7 +503,68 @@ public static bool IsExplicitThatCanBeImplicit(
if (typeSymbol.IsKind(SymbolKind.ErrorType, SymbolKind.DynamicType))
return false;

if (declarationExpression.Parent is ArgumentSyntax argument)
return AnalyzeArgument(argument, semanticModel, cancellationToken);

return true;

static bool AnalyzeArgument(ArgumentSyntax argument, SemanticModel semanticModel, CancellationToken cancellationToken)
{
IParameterSymbol parameterSymbol = semanticModel.DetermineParameter(argument, cancellationToken: cancellationToken);

if (parameterSymbol is null)
return false;

if (SymbolEqualityComparer.Default.Equals(parameterSymbol.Type, parameterSymbol.OriginalDefinition.Type))
return true;

if (parameterSymbol.ContainingSymbol is IMethodSymbol methodSymbol)
{
ImmutableArray<ITypeSymbol> typeParameterList = methodSymbol.TypeArguments;

ITypeParameterSymbol typeParameterSymbol = null;
for (int i = 0; i < typeParameterList.Length; i++)
{
if (SymbolEqualityComparer.Default.Equals(typeParameterList[i], parameterSymbol.Type))
{
typeParameterSymbol = methodSymbol.TypeParameters[i];
break;
}
}

if (typeParameterSymbol is not null
&& argument.Parent is ArgumentListSyntax argumentList
&& argumentList.Parent is InvocationExpressionSyntax invocation)
{
switch (invocation.Expression.Kind())
{
case SyntaxKind.IdentifierName:
return false;

case SyntaxKind.GenericName:
return true;

case SyntaxKind.SimpleMemberAccessExpression:
var memberAccess = (MemberAccessExpressionSyntax)invocation.Expression;

if (memberAccess.Name.IsKind(SyntaxKind.IdentifierName))
return false;

if (memberAccess.Name.IsKind(SyntaxKind.GenericName))
return true;

Debug.Fail(memberAccess.Name.Kind().ToString());
break;

default:
Debug.Fail(invocation.Expression.Kind().ToString());
break;
}
}
}

return false;
}
}

public static bool IsExplicitThatCanBeImplicit(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,46 @@ void M()
");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseVarInsteadOfExplicitTypeWhenTypeIsNotObvious)]
public async Task Test_TryParse_GenericType()
{
await VerifyDiagnosticAndFixAsync(@"
using System;
#nullable enable

class C
{
void M()
{
bool TryParse<T>(string? s, out T t)
{
t = default!;
return false;
}

TryParse<IntPtr>(""wasted"", out [|IntPtr|] i);
}
}
", @"
using System;
#nullable enable

class C
{
void M()
{
bool TryParse<T>(string? s, out T t)
{
t = default!;
return false;
}

TryParse<IntPtr>(""wasted"", out var i);
}
}
");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseVarInsteadOfExplicitTypeWhenTypeIsNotObvious)]
public async Task TestNoDiagnostic_ForEach_DeclarationExpression()
{
Expand Down Expand Up @@ -237,6 +277,53 @@ void M()
Type? nullableType = type;
}
}
");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseVarInsteadOfExplicitTypeWhenTypeIsNotObvious)]
public async Task TestNoDiagnostic_InferredType_Invocation_IdentifierName()
{
await VerifyNoDiagnosticAsync(@"
using System;
#nullable enable

class C
{
void M()
{
bool TryParse<T>(string? s, out T t)
{
t = default!;
return false;
}

TryParse(""wasted"", out IntPtr i);
}
}
");
}

[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseVarInsteadOfExplicitTypeWhenTypeIsNotObvious)]
public async Task TestNoDiagnostic_InferredType_Invocation_MemberAccessExpression()
{
await VerifyNoDiagnosticAsync(@"
using System;
#nullable enable

static class C
{
static void M()
{

C.TryParse(""wasted"", out IntPtr i);
}

static bool TryParse<T>(string? s, out T t)
{
t = default!;
return false;
}
}
");
}
}