Skip to content

Commit 3c05f3a

Browse files
authored
Do not return an top lever parameter analyzer warning for some types (#13496)
* Do not return an top lever parameter analyzer warning for some types Fixes #6945
1 parent 8430a30 commit 3c05f3a

File tree

4 files changed

+61
-2
lines changed

4 files changed

+61
-2
lines changed

src/Mvc/Mvc.Analyzers/src/DiagnosticDescriptors.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,8 @@ public static class DiagnosticDescriptors
4747
new DiagnosticDescriptor(
4848
"MVC1004",
4949
"Rename model bound parameter.",
50-
"Property on type '{0}' has the same name as parameter '{1}'. This may result in incorrect model binding. Consider renaming the parameter or using a model binding attribute to override the name.",
50+
"Property on type '{0}' has the same name as parameter '{1}'. This may result in incorrect model binding. " +
51+
"Consider renaming the parameter or the property to avoid conflicts. If the type '{0}' has a custom type converter or custom model binder, you can suppress this message.",
5152
"Naming",
5253
DiagnosticSeverity.Warning,
5354
isEnabledByDefault: true,

src/Mvc/Mvc.Analyzers/src/TopLevelParameterNameAnalyzer.cs

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -89,12 +89,17 @@ internal static bool IsProblematicParameter(in SymbolCache symbolCache, IParamet
8989
return false;
9090
}
9191

92-
if (SpecifiesModelType(symbolCache, parameter))
92+
if (SpecifiesModelType(in symbolCache, parameter))
9393
{
9494
// Ignore parameters that specify a model type.
9595
return false;
9696
}
9797

98+
if (!IsComplexType(parameter.Type))
99+
{
100+
return false;
101+
}
102+
98103
var parameterName = GetName(symbolCache, parameter);
99104

100105
var type = parameter.Type;
@@ -122,6 +127,26 @@ internal static bool IsProblematicParameter(in SymbolCache symbolCache, IParamet
122127
return false;
123128
}
124129

130+
private static bool IsComplexType(ITypeSymbol type)
131+
{
132+
// This analyzer should not apply to simple types. In MVC, a simple type is any type that has a type converter that returns true for TypeConverter.CanConvertFrom(typeof(string)).
133+
// Unfortunately there isn't a Roslyn way of determining if a TypeConverter exists for a given symbol or if the converter allows string conversions.
134+
// https://github.com/dotnet/corefx/blob/v3.0.0-preview8.19405.3/src/System.ComponentModel.TypeConverter/src/System/ComponentModel/ReflectTypeDescriptionProvider.cs#L103-L141
135+
// provides a list of types that have built-in converters.
136+
// We'll use a simpler heuristic in the analyzer: A type is simple if it's a value type or if it's in the "System.*" namespace hierarchy.
137+
138+
var @namespace = type.ContainingNamespace?.ToString();
139+
if (@namespace != null)
140+
{
141+
// Things in the System.* namespace hierarchy don't count as complex types. This workarounds
142+
// the problem of discovering type converters on types in mscorlib.
143+
return @namespace != "System" &&
144+
!@namespace.StartsWith("System.", StringComparison.Ordinal);
145+
}
146+
147+
return true;
148+
}
149+
125150
internal static string GetName(in SymbolCache symbolCache, ISymbol symbol)
126151
{
127152
foreach (var attribute in symbol.GetAttributes(symbolCache.IModelNameProvider))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
using System;
2+
3+
namespace Microsoft.AspNetCore.Mvc.Analyzers.TopLevelParameterNameAnalyzerTestFiles
4+
{
5+
public class IsProblematicParameter_ReturnsFalse_ForSimpleTypes
6+
{
7+
public void ActionMethod(DateTime date, DateTime? day, Uri absoluteUri, Version majorRevision, DayOfWeek sunday) { }
8+
}
9+
}

src/Mvc/Mvc.Analyzers/test/TopLevelParameterNameAnalyzerTest.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,30 @@ public async Task IsProblematicParameter_ReturnsFalse_ForParametersWithCustomMod
9595
Assert.False(result);
9696
}
9797

98+
// Test for https://github.com/aspnet/AspNetCore/issues/6945
99+
[Fact]
100+
public async Task IsProblematicParameter_ReturnsFalse_ForSimpleTypes()
101+
{
102+
var testName = nameof(IsProblematicParameter_ReturnsFalse_ForSimpleTypes);
103+
var testSource = MvcTestSource.Read(GetType().Name, testName);
104+
var project = DiagnosticProject.Create(GetType().Assembly, new[] { testSource.Source });
105+
106+
var compilation = await project.GetCompilationAsync();
107+
108+
var modelType = compilation.GetTypeByMetadataName($"Microsoft.AspNetCore.Mvc.Analyzers.TopLevelParameterNameAnalyzerTestFiles.{testName}");
109+
var method = (IMethodSymbol)modelType.GetMembers("ActionMethod").First();
110+
111+
Assert.True(TopLevelParameterNameAnalyzer.SymbolCache.TryCreate(compilation, out var symbolCache));
112+
113+
Assert.Collection(
114+
method.Parameters,
115+
p => Assert.False(TopLevelParameterNameAnalyzer.IsProblematicParameter(symbolCache, p)),
116+
p => Assert.False(TopLevelParameterNameAnalyzer.IsProblematicParameter(symbolCache, p)),
117+
p => Assert.False(TopLevelParameterNameAnalyzer.IsProblematicParameter(symbolCache, p)),
118+
p => Assert.False(TopLevelParameterNameAnalyzer.IsProblematicParameter(symbolCache, p)),
119+
p => Assert.False(TopLevelParameterNameAnalyzer.IsProblematicParameter(symbolCache, p)));
120+
}
121+
98122
[Fact]
99123
public async Task IsProblematicParameter_IgnoresStaticProperties()
100124
{

0 commit comments

Comments
 (0)