Skip to content

Commit 5ebe544

Browse files
authored
Fix RCS1176 (#1140)
1 parent 932e041 commit 5ebe544

File tree

3 files changed

+150
-1
lines changed

3 files changed

+150
-1
lines changed

ChangeLog.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3131
- Fix [RCS1154](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1154) ([#1105](https://github.com/JosefPihrt/Roslynator/pull/1105)).
3232
- Fix [RCS1211](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS1211) ([#1095](https://github.com/JosefPihrt/Roslynator/pull/1095)).
3333
- Fix [RCS0005](https://josefpihrt.github.io/docs/roslynator/analyzers/RCS0005) ([#1114](https://github.com/JosefPihrt/Roslynator/pull/1114)).
34-
- Fix [RCS1176](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1176.md) ([#1122](https://github.com/JosefPihrt/Roslynator/pull/1122)).
34+
- 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)).
3535
- Fix [RCS1085](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1085.md) ([#1120](https://github.com/josefpihrt/roslynator/pull/1120)).
3636
- Fix [RCS1208](https://github.com/JosefPihrt/Roslynator/blob/main/docs/analyzers/RCS1208.md) ([#1119](https://github.com/JosefPihrt/Roslynator/pull/1119)).
3737
- [CLI] Fix member full declaration in generated documentation (command `generate-doc`) ([#1130](https://github.com/josefpihrt/roslynator/pull/1130)).

src/CSharp/CSharp/CSharpTypeAnalysis.cs

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright (c) Josef Pihrt and Contributors. Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.
22

33
using System;
4+
using System.Collections.Immutable;
45
using System.Diagnostics;
56
using System.Threading;
67
using Microsoft.CodeAnalysis;
@@ -502,7 +503,68 @@ public static bool IsExplicitThatCanBeImplicit(
502503
if (typeSymbol.IsKind(SymbolKind.ErrorType, SymbolKind.DynamicType))
503504
return false;
504505

506+
if (declarationExpression.Parent is ArgumentSyntax argument)
507+
return AnalyzeArgument(argument, semanticModel, cancellationToken);
508+
505509
return true;
510+
511+
static bool AnalyzeArgument(ArgumentSyntax argument, SemanticModel semanticModel, CancellationToken cancellationToken)
512+
{
513+
IParameterSymbol parameterSymbol = semanticModel.DetermineParameter(argument, cancellationToken: cancellationToken);
514+
515+
if (parameterSymbol is null)
516+
return false;
517+
518+
if (SymbolEqualityComparer.Default.Equals(parameterSymbol.Type, parameterSymbol.OriginalDefinition.Type))
519+
return true;
520+
521+
if (parameterSymbol.ContainingSymbol is IMethodSymbol methodSymbol)
522+
{
523+
ImmutableArray<ITypeSymbol> typeParameterList = methodSymbol.TypeArguments;
524+
525+
ITypeParameterSymbol typeParameterSymbol = null;
526+
for (int i = 0; i < typeParameterList.Length; i++)
527+
{
528+
if (SymbolEqualityComparer.Default.Equals(typeParameterList[i], parameterSymbol.Type))
529+
{
530+
typeParameterSymbol = methodSymbol.TypeParameters[i];
531+
break;
532+
}
533+
}
534+
535+
if (typeParameterSymbol is not null
536+
&& argument.Parent is ArgumentListSyntax argumentList
537+
&& argumentList.Parent is InvocationExpressionSyntax invocation)
538+
{
539+
switch (invocation.Expression.Kind())
540+
{
541+
case SyntaxKind.IdentifierName:
542+
return false;
543+
544+
case SyntaxKind.GenericName:
545+
return true;
546+
547+
case SyntaxKind.SimpleMemberAccessExpression:
548+
var memberAccess = (MemberAccessExpressionSyntax)invocation.Expression;
549+
550+
if (memberAccess.Name.IsKind(SyntaxKind.IdentifierName))
551+
return false;
552+
553+
if (memberAccess.Name.IsKind(SyntaxKind.GenericName))
554+
return true;
555+
556+
Debug.Fail(memberAccess.Name.Kind().ToString());
557+
break;
558+
559+
default:
560+
Debug.Fail(invocation.Expression.Kind().ToString());
561+
break;
562+
}
563+
}
564+
}
565+
566+
return false;
567+
}
506568
}
507569

508570
public static bool IsExplicitThatCanBeImplicit(

src/Tests/Analyzers.Tests/RCS1176UseVarInsteadOfExplicitTypeWhenTypeIsNotObviousTests.cs

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,46 @@ void M()
116116
");
117117
}
118118

119+
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseVarInsteadOfExplicitTypeWhenTypeIsNotObvious)]
120+
public async Task Test_TryParse_GenericType()
121+
{
122+
await VerifyDiagnosticAndFixAsync(@"
123+
using System;
124+
#nullable enable
125+
126+
class C
127+
{
128+
void M()
129+
{
130+
bool TryParse<T>(string? s, out T t)
131+
{
132+
t = default!;
133+
return false;
134+
}
135+
136+
TryParse<IntPtr>(""wasted"", out [|IntPtr|] i);
137+
}
138+
}
139+
", @"
140+
using System;
141+
#nullable enable
142+
143+
class C
144+
{
145+
void M()
146+
{
147+
bool TryParse<T>(string? s, out T t)
148+
{
149+
t = default!;
150+
return false;
151+
}
152+
153+
TryParse<IntPtr>(""wasted"", out var i);
154+
}
155+
}
156+
");
157+
}
158+
119159
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseVarInsteadOfExplicitTypeWhenTypeIsNotObvious)]
120160
public async Task TestNoDiagnostic_ForEach_DeclarationExpression()
121161
{
@@ -237,6 +277,53 @@ void M()
237277
Type? nullableType = type;
238278
}
239279
}
280+
");
281+
}
282+
283+
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseVarInsteadOfExplicitTypeWhenTypeIsNotObvious)]
284+
public async Task TestNoDiagnostic_InferredType_Invocation_IdentifierName()
285+
{
286+
await VerifyNoDiagnosticAsync(@"
287+
using System;
288+
#nullable enable
289+
290+
class C
291+
{
292+
void M()
293+
{
294+
bool TryParse<T>(string? s, out T t)
295+
{
296+
t = default!;
297+
return false;
298+
}
299+
300+
TryParse(""wasted"", out IntPtr i);
301+
}
302+
}
303+
");
304+
}
305+
306+
[Fact, Trait(Traits.Analyzer, DiagnosticIdentifiers.UseVarInsteadOfExplicitTypeWhenTypeIsNotObvious)]
307+
public async Task TestNoDiagnostic_InferredType_Invocation_MemberAccessExpression()
308+
{
309+
await VerifyNoDiagnosticAsync(@"
310+
using System;
311+
#nullable enable
312+
313+
static class C
314+
{
315+
static void M()
316+
{
317+
318+
C.TryParse(""wasted"", out IntPtr i);
319+
}
320+
321+
static bool TryParse<T>(string? s, out T t)
322+
{
323+
t = default!;
324+
return false;
325+
}
326+
}
240327
");
241328
}
242329
}

0 commit comments

Comments
 (0)