Feature: CallerArgumentExpressionAttribute#51952
Feature: CallerArgumentExpressionAttribute#51952333fred merged 44 commits intodotnet:features/caller-argument-expressionfrom
Conversation
src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs
Outdated
Show resolved
Hide resolved
50a83c5 to
cb0ab7d
Compare
0cd335f to
28f460a
Compare
|
Ready for initial review. |
|
@Youssef1313 I've created a feature branch for this work. Feel free to put your open questions as prototype comments in the source: the syntax is |
src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs
Outdated
Show resolved
Hide resolved
src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs
Outdated
Show resolved
Hide resolved
| { | ||
| arguments.GetOrCreateData<ParameterEarlyWellKnownAttributeData>().HasCallerMemberNameAttribute = true; | ||
| } | ||
| else if (CSharpAttributeData.IsTargetEarlyAttribute(arguments.AttributeType, arguments.AttributeSyntax, AttributeDescription.CallerArgumentExpressionAttribute)) |
There was a problem hiding this comment.
As the language exists today we can do this as an early attribute, but if we do implement the nameof proposal (allowing nameof(parameter) in parameters/attributes on methods) I don't believe it will end well (this will cause a loop or fail to bind entirely). Please add a prototype comment to consider moving binding of the actual attribute data into regular attribute binding.
There was a problem hiding this comment.
@333fred To make sure I understand this correctly, this is about nameof binding being done after we are trying to get the parameter name?
This also affects existing code, for example:
roslyn/src/Compilers/CSharp/Portable/Symbols/Source/SourceNamedTypeSymbol.cs
Lines 898 to 912 in 8408750
There was a problem hiding this comment.
To make sure I understand this correctly, this is about nameof binding being done after we are trying to get the parameter name?
I'm not sure I understand the question. My concern here is:
- We're attempting to bind the parameter
- The parameter has this attribute, and uses nameof on a parameter in this method (even itself!)
- Attempting to bind the nameof argument causes us to bind the method, which causes us to bind the nameof, which causes us to bind the method, which... etc.
Today, the parameter is not in scope for that nameof, so our standard early attribute handling will work correctly without a chance for a loop. I'm not certain it will work correctly in the face of an updated nameof, however, so to do the right thing we'd want to move binding of the attribute argument to the standard binding location, which isn't susceptible to this problem.
There was a problem hiding this comment.
I think I got the general idea here. I think (most likely) this will require fixes in other existing areas first (e.g, the ConditionalAttribute above), so I'll probably follow the same work and add tests.
| ErrorCode.WRN_AnalyzerReferencesFramework, | ||
| ErrorCode.WRN_UnreadRecordParameter, | ||
| ErrorCode.WRN_DoNotCompareFunctionPointers, | ||
| ErrorCode.WRN_CallerArgumentExpressionParamForUnconsumedLocation, |
There was a problem hiding this comment.
ErrorCode.WRN_CallerArgumentExpressionParamForUnconsumedLocation, [](start = 20, length = 65)
It looks like this test requires unnecessary maintenance. Perhap the check for the range of error codes at the beginning of the loop should be adjusted instead? Based on the comment only "Nullable-unrelated warnings in the C# 8 range should be added to this array." #Closed
There was a problem hiding this comment.
@AlekseyTs I guess you meant this to be a "general" feedback on this test, rather than this PR specifically. Is that right?
Should I target main for that change in a separate PR, or file an issue for discussion?
There was a problem hiding this comment.
I guess you meant this to be a "general" feedback on this test, rather than this PR specifically. Is that right?
It is specific to this PR because there are changes here.
Should I target main for that change in a separate PR, or file an issue for discussion?
I guess either way is fine as long as we either fix it in this PR, or make sure it will be fixed before merging the feature into main.
In reply to: 607929347 [](ancestors = 607929347)
There was a problem hiding this comment.
Sorry I initially misunderstood your comment. Fixed in 418ec88.
Will give that a look 👀 |
|
Done with review pass (commit 39) #Closed |
|
roslyn/src/Compilers/CSharp/Portable/Symbols/Metadata/PE/PEParameterSymbol.cs Lines 110 to 115 in 7becc52 If I understand this method correctly, it looks like both flags are needed. One flag specifies whether we can get the attribute data or not, and the other specifies the actual data. Both |
I believe that is not the issue I am referring to. See https://github.com/dotnet/roslyn/pull/51952/files#r600068668 #Closed |
Thanks. Does b99bb7a address that? |
| } | ||
| } | ||
|
|
||
| private bool HasCallerArgumentExpressionAttribute |
There was a problem hiding this comment.
HasCallerArgumentExpressionAttribute [](start = 21, length = 36)
This property feels unnecessary. Also, it feels like we should be able to avoid looking for the attribute twice. First in this property and then to extract parameter name from it. #Closed
There was a problem hiding this comment.
@AlekseyTs It feels much more simple if I didn't follow the existing pattern for other caller info, i.e, not even touch PackedFlags. I attempted to do that in e8023b1. Let me know if there are any concerns with the approach I took.
| } | ||
| } | ||
|
|
||
| private int? _cachedCallerArgumentExpressionParameterIndex; |
There was a problem hiding this comment.
private int? _cachedCallerArgumentExpressionParameterIndex; [](start = 8, length = 59)
Please place the field declaration at the beginning of the class declaration, next to other fields. Also, we use "lazy" prefix for fields with delayed initialization. #Closed
| bool isCallerArgumentExpression = !HasCallerLineNumberAttribute | ||
| && !HasCallerFilePathAttribute | ||
| && !HasCallerMemberNameAttribute | ||
| && info.HasValue |
There was a problem hiding this comment.
&& info.HasValue [](start = 20, length = 16)
Is this check cheap? Consider doing it first. #Closed
| { | ||
| if (_cachedCallerArgumentExpressionParameterIndex.HasValue) | ||
| { | ||
| return _cachedCallerArgumentExpressionParameterIndex.Value; |
There was a problem hiding this comment.
Value [](start = 73, length = 5)
GetValueOrDefault? It avoids another HasValue check. #Closed
| { | ||
| if (parameters[i].Name == parameterName) | ||
| { | ||
| _cachedCallerArgumentExpressionParameterIndex = i; |
There was a problem hiding this comment.
_cachedCallerArgumentExpressionParameterIndex = i; [](start = 28, length = 50)
This assignment is not guaranteed to be atomic, therefore, the implementation of this property is not thread safe. That is why we don't use Nullable<T> for "lazy" members. For example, we use ThreeState instead of bool?. It looks like we can use a simple int field with initial value (-2), for example, to indicate uninitialized state. #Closed
| } | ||
| } | ||
|
|
||
| _cachedCallerArgumentExpressionParameterIndex = -1; |
There was a problem hiding this comment.
_cachedCallerArgumentExpressionParameterIndex = -1; [](start = 16, length = 51)
Same comment #Closed
| private const int RefKindMask = 0x3; | ||
| private const int WellKnownAttributeDataMask = 0xFF; | ||
|
|
||
| private const int WellKnownAttributeDataMask = (0x1 << 8) - 1; |
There was a problem hiding this comment.
(0x1 << 8) - 1 [](start = 59, length = 14)
Consider reverting back to 0xFF literal. #Closed
|
Done with review pass (commit 43) #Closed |
|
@333fred Please review the latest revision. Please squash commits during merging. |
|
Thanks @Youssef1313! |
|
Thanks a lot for review @AlekseyTs and @333fred. |
Proposal: dotnet/csharplang#287
Test plan: #52745
TODO:
Add a warning if the parameter name specified in the attribute is incorrect.Done.Open questions:
Do we need to support VB?
Do we need to add a warning for pre-C# 10 using this feature?
What should happen for the following case:
What should happen for the following case, should this have a warning?