Skip to content
This repository was archived by the owner on Dec 14, 2018. It is now read-only.

Conversation

@kishanAnem
Copy link
Contributor

@kishanAnem kishanAnem commented Aug 18, 2018

#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.

…n top-level parameters #7415 Hi @dougbu #7415 this is only about Action parameter in next commit will work on Properties. Thanks @pranavkm, used your framework a lot.
Copy link
Contributor

@dougbu dougbu left a 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",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • change RequiredAttribute to {0}
  • "collections"
  • end the sentence with a period

private bool IsProblematicParameter(IParameterSymbol parameter)
{
return parameter.GetAttributes().Any(Attribute => Equals(Attribute.AttributeClass.MetadataName, SymbolNames.RequiredAttribute));
Copy link
Contributor

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";
Copy link
Contributor

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?

@pranavkm

Copy link
Contributor

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;
Copy link
Contributor

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.

Copy link
Contributor Author

@kishanAnem kishanAnem Aug 19, 2018

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)].",
Copy link
Contributor

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.
@kishanAnem
Copy link
Contributor Author

kishanAnem commented Aug 19, 2018

@kishanAnem are you planning to implement CodeFixProviders as well?

@dougbu Yes, I started working on it.

Copy link
Contributor

@dougbu dougbu left a 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";
Copy link
Contributor

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
Copy link
Contributor

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.

Copy link
Contributor Author

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.

Copy link
Contributor Author

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.

Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, i mean codefixer.

Copy link
Contributor

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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

OK, ☺️

Copy link
Contributor

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)
Copy link
Contributor

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).

Copy link
Contributor

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)
Copy link
Contributor

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")
Copy link
Contributor

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)

Copy link
Contributor Author

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";
Copy link
Contributor

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.

Copy link
Contributor Author

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
Copy link
Contributor

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;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

unused?

Copy link
Contributor Author

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));
Copy link
Contributor

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" />
Copy link
Contributor

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

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure

@kishanAnem kishanAnem closed this Aug 20, 2018
@kishanAnem kishanAnem reopened this Aug 20, 2018
@kishanAnem
Copy link
Contributor Author

sorry, I did accidently.

…n top-level parameters #7415 hi @dougbu @pranavkm #7415 I made changes and added new Analyzer for properties.
Copy link
Contributor

@dougbu dougbu left a 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}'.",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"made" -> "makes"

return false;
}


Copy link
Contributor

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?

Copy link
Contributor Author

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?

Copy link
Contributor

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 )
Copy link
Contributor

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))
{
{
Copy link
Contributor

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 =>
Copy link
Contributor

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)
Copy link
Contributor

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?

Copy link
Contributor Author

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.

Copy link
Contributor

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.

Copy link
Contributor Author

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)
Copy link
Contributor

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();
Copy link
Contributor Author

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

Copy link
Contributor

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>, got ActionResult<int>

Yes, that was a typo.

both are MethodKind.Ordinary

Please test this assumption. I'm not sure.

Copy link
Contributor Author

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)"));
Copy link
Contributor

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
Copy link
Contributor

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))
Copy link
Contributor

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));

@dougbu
Copy link
Contributor

dougbu commented Sep 2, 2018

@kishanAnem this branch has gotten stale and needs to be rebased on the current release/2.2.

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 up-for-grabs issue.

@kishanAnem
Copy link
Contributor Author

Please take over work on this issue

@kishanAnem kishanAnem closed this Sep 2, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

None yet

3 participants