Prevent member name collisions in PowerShell classes#8372
Prevent member name collisions in PowerShell classes#8372IISResetMe wants to merge 12 commits intoPowerShell:masterfrom
Conversation
As per @lzybkr's advice, duplicate member names should not be a parser concern since it's not a syntax violation. This also avoids LINQ queries in the parser.
|
Marked PR WIP to block merge since linked issue is still awaiting committee review |
|
@SteveL-MSFT can you include this (or the linked issue) in the next committee review? |
|
@IISResetMe to set expectation, due to the holidays and unavailability of many committee members, this won't be reviewed until after the new year (and probably 2nd week of Jan at the earliest) |
|
@PowerShell/powershell-committee reviewed this, although .Net allows this, no language supports it and PowerShell should not either. It is a technical breaking change although unlikely any customer is dependent on this since they would have defined a duplicate name member and not have used it. We agree PowerShell should prevent this and have a parse time error. |
Fix CodeFactor issue
|
Thanks @SteveL-MSFT, will add regression tests for this then and we can move ahead! :) |
The following class definition should still be valid (as in any other .NET language):
```
class A
{
$A
A(){}
}
```
| private void DefineProperty(PropertyMemberAst propertyMemberAst) | ||
| { | ||
| if (_definedProperties.ContainsKey(propertyMemberAst.Name)) | ||
| if (_definedProperties.ContainsKey(propertyMemberAst.Name) || _definedMethods.ContainsKey(propertyMemberAst.Name) && !_definedMethods[propertyMemberAst.Name].Any(m => m.Item1.IsConstructor)) |
There was a problem hiding this comment.
| if (_definedProperties.ContainsKey(propertyMemberAst.Name) || _definedMethods.ContainsKey(propertyMemberAst.Name) && !_definedMethods[propertyMemberAst.Name].Any(m => m.Item1.IsConstructor)) | |
| if (_definedProperties.ContainsKey(propertyMemberAst.Name) || (_definedMethods.ContainsKey(propertyMemberAst.Name) && !_definedMethods[propertyMemberAst.Name].Any(m => m.Item1.IsConstructor))) |
There was a problem hiding this comment.
Already fixed 😅
There was a problem hiding this comment.
We could move it into a separate method (like for the method overload check), but TBH I'm not so worried about the use of LINQ here since that code path is part of the compile-time routine, it's not triggered at parse-time
There was a problem hiding this comment.
Actually we can avoid LINQ altogether - IsConstructor is name-bound, so if any m satisfies IsConstructor == true, they all will. We can simply inspect the first item in the list
Co-Authored-By: IISResetMe <[email protected]>
Since IsConstructor is already tied to the method name, we can safely assume that either all or none of them satisfy IsConstructor == true, so only checking the first list item is sufficient
|
@iSazonov I think that was it! Do we need review from @daxian-dbw or @BrucePay still? |
|
Yes, we are waiting. |
|
Can we turn off the "variable name looks like hungarian notation" rule (SA1305) flagged by CodeFactor? It's super noisy, and a cursory glance at S.M.A shows that we have over 900 parameter names alone that look like hungarian notation to StyleCop, but I went through the list of name and only ~50 of them are actually named using hungarian notation: PS ~> $parameterNames = [psobject].Assembly.GetTypes().GetMethods().GetParameters().Name
PS ~> $looksHungarian = $parameterNames -cmatch '^[a-z]{2,3}[A-Z]+'
PS ~> $actuallyHungarian = $looksHungarian -notmatch '^(?:add|arg|ast|bad|do|eat|for|has|if|is|job|max|min|my|new|no|old|out|pre|ps|raw|run|set|sub|to|try|ui|use|yes)'
PS ~> $looksHungarian.Count
935
PS ~> $actuallyHungarian.Count
46 |
|
@IISResetMe We already have a PR to disable some CodeFactor rules. |
|
@iSazonov are we still waiting for the build pipeline to be fixed (CodeFactor issues)? Or do I need to address anything? |
|
@IISResetMe Feel free to ignore CodeFactor issues if they is not in your changed code. Maintainers will ask you to fix style issues before merge if they deem it necessary. |
|
This PR has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed if no further activity occurs within 10 days. |
|
@daxian-dbw can you review this? :) |
|
@IISResetMe Sure, will do the review today. |
| // $C | ||
| // } | ||
| // | ||
| if (_definedProperties.ContainsKey(propertyMemberAst.Name) || (_definedMethods.ContainsKey(propertyMemberAst.Name) && !_definedMethods[propertyMemberAst.Name][0].Item1.IsConstructor)) |
There was a problem hiding this comment.
nit: Can you please define a local variable to hold propertyMemberAst.Name and replace propertyMemberAst.Name with that local variable in this method?
That will make this check less cumbersome and a bit more readable.
|
@IISResetMe Your change doesn't fix the case where class A derives from class B: But to be honest, I don't know if it's worth the effort to fix that ... |
|
@IISResetMe are you gonna take that sitting down? 😄 That does look like a very interesting case to try to figure out, though. Kind of looks like it might be a bit of a maze of recursively checking inherited members... Fun times! |
|
@IISResetMe worked on something similar before -- flatten all implemented interfaces. |
|
Oh, that's right, he did. Should be a walk in the park, then. 😉 But I get where you're coming from too, it might not be worth it... but then again, it might end up being necessary -- after all, one of those members you're accidentally hiding might come from up the inheritance chain quite a ways, and it could potentially be very confusing if you hit that by accident. Could this happen if you attempted to inherit from two disparate classes? Do PS classes even support that? 🤔 |
No, and I can't think of a language that compiles against .NET that does... Multiple inheritance opens up a whole new can of worms wrt member resolution ambiguity, so maybe let's not try and solve that as part of this PR ^__^ @daxian-dbw we can indeed leverage the same approach as with the interface flattening code for this. The extra lookups would be dependent on the user specifying a base class so it doesn't have to be super expensive for types that just inherit from Object. Will take a stab at it over the weekend |
|
This PR has been automatically marked as stale because it has not had activity in the last 30 days. It will be closed if no further activity occurs within 10 days. |
|
This PR has been automatically closed because it is stale. If you wish to continue working on the PR, please first update the PR, then reopen it. |
|
This pull request has been automatically marked as Review Needed because it has been there has not been any activity for 7 days. |
PR Summary
Fix #8235, #7736
PowerShell currently allows definition of class with overlapping property and method names without warning, like so:
Unlike the C# compiler (which throws a compilation error), we allow compilation of the above type definition, and ETS then implicitly hides the property behind the method at runtime - making the property implementation completely useless.
This change fixes that by making duplicate member name checks mutual between
DefineMethod()/DefineProperty()- so that if a method with nameXis already defined, an attempt to define propertyXwill throw aParserError(and vice versa) just like we already do for duplicate member names of the same member typeTODO:
Regression testsPR Checklist
.h,.cpp,.cs,.ps1and.psm1files have the correct copyright headerWIP:to the beginning of the title and remove the prefix when the PR is ready.[feature]if the change is significant or affects feature testsI'd consider this a bucket 3 breaking change (we'll start failing on defining types that are already broken but partially functional).
Change is obviously user-facing but I don't believe it warrants a documentation update since the previous documentation doesn't address the existing buggy behavior