-
Couldn't load subscription status.
- Fork 2.1k
Create an Analyzer to warn customers to not user Required attribute opn top-level parameters #7415 #8290
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kishanAnem are you planning to implement CodeFixProviders as well?
| public static readonly DiagnosticDescriptor MVC1005_ParameterAttributeAvoidUsingRequiredOncollections = | ||
| new DiagnosticDescriptor( | ||
| "MVC1005", | ||
| "Use of 'RequiredAttribute' should be avoided on collection", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- change
RequiredAttributeto{0} - "collections"
- end the sentence with a period
| | ||
| private bool IsProblematicParameter(IParameterSymbol parameter) | ||
| { | ||
| return parameter.GetAttributes().Any(Attribute => Equals(Attribute.AttributeClass.MetadataName, SymbolNames.RequiredAttribute)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
attribute (lowercase first character)
| | ||
| public const string RequiredAttribute = "RequiredAttribute"; | ||
| | ||
| public const string CollectionType= "SourceComplexParameterSymbol"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Doesn't this symbol both exclude SourceComplexParameterSymbolWithCustomModifiers and indicate only that the parameter is not simple e.g. that it has associated attributes? That is, what about this type indicates a collection?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you're using this anymore.
| { | ||
| public class DiagnosticsAreReturned_ForControllerActionsWithParametersWithRequiredAttributeForIEnumerable : Controller | ||
| { | ||
| public IActionResult Index([Required]IEnumerable<int> /*MM*/collection) => null; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IEnumerable<T> does not inherit from ICollection. So, I suspect the analyzer would also complain about a [Required] int nonCollection parameter.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So, I suspect the analyzer would also complain about a [Required] int nonCollection parameter.
Yes, you're right @dougbu will fix this.
| new DiagnosticDescriptor( | ||
| "MVC1005", | ||
| "Use of 'RequiredAttribute' should be avoided on collection", | ||
| "Use of '{0}' should be avoided. This always made model validation is true. Consider using [MinLength(1)].", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This advice works only for arrays in .NET Framework projects. Even in .NET Core, this advice doesn't work for IEnumerable<T> but IEnumerable<T> has the same issues w/ [Required]. Probably need an additional diagnostic without this advice for the excluded cases.
…pn top-level parameters #7415 #7415 I fixed all.
@dougbu Yes, I started working on it. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kishanAnem my earlier question about CodeFixProviders was just a question. We can file issues for fixes later. #7415 alone doesn't require them though they would be nice to have.
Initial priority is covering #7415 in full, especially covering all top-level models and providing correct advice. Well, the "might be worthwhile" point is optional.
| | ||
| public const string RequiredAttribute = "RequiredAttribute"; | ||
| | ||
| public const string CollectionType= "SourceComplexParameterSymbol"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think you're using this anymore.
| namespace Microsoft.AspNetCore.Mvc.Analyzers | ||
| { | ||
| [DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
| public class TopLevelParameterAttributeAnalyzer : DiagnosticAnalyzer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are you planning a separate analyzer or analyzer pair for [Required] on complex types? #7415 covers that case too.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes I'm planning seperate analyzer for that.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We'll writer analyzer as well in this issue. If it require please update issue.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@kishanAnem #7415 is already clear complex types must be covered. See https://github.com/aspnet/Mvc/issues/7415#issuecomment-369413264
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sorry, i mean codefixer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not doing fixers is fine for this PR. As I said earlier,
Initial priority is covering #7415 in full, especially covering all top-level models and providing correct advice. Well, the "might be worthwhile" point is optional.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK,
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we call this RequiredAttributeAnalyzer? There's already an analyzer by the same name in the Api.Analyzers package
| return false; | ||
| } | ||
| | ||
| public static bool IsCollection(this ITypeSymbol source) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IsEnumerable would be a better name. May also need IsCollection and IsArray to distinguish the cases where current MVC1005_ParameterAttributeAvoidUsingRequiredOncollections advice is actionable under .NET Core and .NET Framework (respectively).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we move this method to the analyzer? The methods on this type are meant to be more general purpose.
| return false; | ||
| } | ||
| | ||
| public static bool IsCollection(this ITypeSymbol source) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we move this method to the analyzer? The methods on this type are meant to be more general purpose.
| | ||
| foreach (var @interface in source.AllInterfaces) | ||
| { | ||
| if (@interface.MetadataName == "IEnumerable") |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd recommend checking the actual type @interface == symbolCache.IEnumerable. You can get to IEnumerable using
Compilation.GetSpecialType(SpecialType.System_Collections_IEnumerable)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure
| | ||
| public const string RenderPartialMethod = "RenderPartial"; | ||
| | ||
| public const string RequiredAttribute = "RequiredAttribute"; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fully qualify this and use that to look up the type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will change
| namespace Microsoft.AspNetCore.Mvc.Analyzers | ||
| { | ||
| [DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
| public class TopLevelParameterAttributeAnalyzer : DiagnosticAnalyzer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could we call this RequiredAttributeAnalyzer? There's already an analyzer by the same name in the Api.Analyzers package
| var location = parameter.Locations.Length != 0 ? | ||
| parameter.Locations[0] : | ||
| Location.None; | ||
| var descriptor = DiagnosticDescriptors.MVC1005_ParameterAttributeAvoidUsingRequiredOncollections; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unused?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. I will remove that.
| | ||
| private bool IsProblematicParameter(IParameterSymbol parameter) | ||
| { | ||
| return parameter.GetAttributes().Any(attribute => Equals(attribute.AttributeClass.MetadataName, SymbolNames.RequiredAttribute)); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parameter.GetAttributes(symbolCache.RequiredAttribute).Any()
| </PropertyGroup> | ||
| | ||
| <ItemGroup> | ||
| <Compile Remove="TestFiles\TopLevelParameterAttributeAnalyzerTest\DiagnosticsAreReturned_ForControllerActionsWithParametersWithOutRequiredAttributeForIEnumerable.cs" /> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Undo changes to the file
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure
| sorry, I did accidently. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
More comments though I believe you're not done yet. Please let us know when you're code complete.
| new DiagnosticDescriptor( | ||
| "MVC1005", | ||
| "Use of '{0}' should be avoided on collections.", | ||
| "Use of '{0}' should be avoided. This always made model validation is true. Consider using '{1}'.", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"made" -> "makes"
| return false; | ||
| } | ||
| | ||
| |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this file changing at all?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
should we keep this method here?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What method? I see only two new blank lines in this file.
| var property = (IPropertySymbol)symbolAnalysisContext.Symbol; | ||
| | ||
| if (property.DeclaredAccessibility != Accessibility.Public || | ||
| property.IsStatic ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Odd indentation here and in a few other places. Please avoid over-indentation because it is just extra work to maintain.
(In this particular case, the whole condition fits easily on one line.)
| | ||
| if (IsNonNullableValueType(property.Type, typeCache.SystemNullableType) && property.HasAttribute(typeCache.RequiredAttribute, inherit: false)) | ||
| { | ||
| { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this statement wrapped in an additional block?
| | ||
| private void InitializeWorker(CompilationStartAnalysisContext compilationStartAnalysisContext) | ||
| { | ||
| compilationStartAnalysisContext.RegisterSymbolAction(symbolAnalysisContext => |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
While #7415 mentions users continue to confuse [BindRequired] and [Required] on int and other value types, let's focus on top-level properties (and parameters) in this PR.
| context.RegisterCompilationStartAction(compilationStartAnalysisContext => | ||
| { | ||
| var typeCache = new SymbolCache(compilationStartAnalysisContext.Compilation); | ||
| if (typeCache.ControllerAttribute == null || typeCache.ControllerAttribute.TypeKind == TypeKind.Error) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I may be missing something. Why doesn't this case exist in ModelPropertyRequiredAttribueBindRequiredAttributeAnalyzer.Initialize?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we're checking type is the Controller, in my understanding whether it is controller or Model on properties we should advice.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Here we're checking type is the Controller
As far as I know, this checks the SymbolCache, not the specific symbol. My question was why this check doesn't exist in ModelPropertyRequiredAttribueBindRequiredAttributeAnalyzer because that also has a SymbolCache.
whether it is controller or Model on properties we should advice.
No, #7415 is specifically about top-level parameters and properties. Specifically
- model-bound properties in a controller
- parameters to an action
- model-bound properties in a page model
- parameters to a page handler
Again, ignore my optional "might be worthwhile warning" point in #7415 at least until the core work is done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
then it should has this there as well. i will add.
| { | ||
| | ||
| var method = (IMethodSymbol)symbolAnalysisContext.Symbol; | ||
| if (method.MethodKind != MethodKind.Ordinary) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is MethodKind.LambdaMethod unsupported? Customers often write actions as lambda methods e.g.
public IActionResult<int> First([Required] int[] numbers) => numbers.FirstOrDefault();There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
add this case as well in the next commit, with current implementation this test case got success. I didn't find IActionResult<int>, got ActionResult<int>.
Please correct If am wrong, up to my understanding following both are same .
public ActionResult<int> First([Required] int[] numbers) => numbers.FirstOrDefault();
and
public ActionResult<int> First([Required] int[] numbers) { return numbers.FirstOrDefault(); }
And both are MethodKind.Ordinary
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I didn't find
IActionResult<int>, gotActionResult<int>
Yes, that was a typo.
both are
MethodKind.Ordinary
Please test this assumption. I'm not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
sure
| Diagnostic.Create( | ||
| DiagnosticDescriptors.MVC1005_AttributeAvoidUsingRequiredAndBindRequired, | ||
| location, | ||
| "RequiredAttribute","MinLength(1)")); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MinLength(1)will only work in a subset of cases where a problem is detected i.e. arrays in the .NET Framework case and ICollection implementations in .NET Core. Should avoid giving incorrect advice otherwise.
| namespace Microsoft.AspNetCore.Mvc.Analyzers | ||
| { | ||
| [DiagnosticAnalyzer(LanguageNames.CSharp)] | ||
| public class ModelPropertyRequiredAttribueBindRequiredAttributeAnalyzer : DiagnosticAnalyzer |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggest creating a small base or helpers class that inspects an individual top-level parameter or property symbol. This would reduce the duplication between TopLevelParameterRequiredAttributeAnalyzer and TopLevelPropertyRequiredAttributeAnalyzer. (Yes, I suggest renaming this analyzer and narrowing its behaviour to match TopLevelParameterRequiredAttributeAnalyzer.)
As a smaller detail, Attribue -> Attribute.
| { | ||
| var parameter = method.Parameters[i]; | ||
| | ||
| if (IsEnumerable(parameter.Type, symbolCache.IEnumerableInterface)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Need to warn about [Required] on non-Nullable<T> value types and on complex types too.
Separately, MVC treats all types with conversions from string as "simple" types. Treating all classes as "complex" may result in false warnings. And, user-defined value types will not necessarily have conversions from string -- leading to missed warnings. @pranavkm does Roslyn support anything similar to the following (in ModelMetadata?
| IsComplexType = !TypeDescriptor.GetConverter(ModelType).CanConvertFrom(typeof(string)); |
| @kishanAnem this branch has gotten stale and needs to be rebased on the current More generally, what are your thoughts, given this PR has grown larger and more complicated than you likely expected? Might frustrate you less for us to take over work on this issue and for you (if you want) to pick up an |
| Please take over work on this issue |
#7415
Hi @dougbu
#7415 this is only about Action parameter in next commit will work on Properties. Please let me know, is this is fine?
Thanks @pranavkm, used your framework a lot.