Skip to content

Commit

Permalink
Fix RCS1176 (#1140)
Browse files Browse the repository at this point in the history
  • Loading branch information
josefpihrt committed Jul 28, 2023
1 parent 932e041 commit 5ebe544
Show file tree
Hide file tree
Showing 3 changed files with 150 additions and 1 deletion.
2 changes: 1 addition & 1 deletion ChangeLog.md
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
@@ -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 @@ bool IsLocalThatSupportsExplicitDeclaration(VariableDesignationSyntax variableDe
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
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;
}
}
");
}
}

0 comments on commit 5ebe544

Please sign in to comment.