-
Notifications
You must be signed in to change notification settings - Fork 4.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Feature: CallerArgumentExpressionAttribute #51952
Changes from all commits
15ea810
b480dd5
3ea7b29
28f460a
8085998
c1669d0
ac953bc
cab0727
a2b2607
1c49908
380edbc
6adaa54
c119c1b
174d1ca
75bcaa3
0e658e0
54afaec
4e34954
a8c9851
564993d
79cae72
b8ec8b3
31c72d9
0fe2481
4d0fe25
6f55195
9c8d283
1326603
a2055ec
670d405
765c6e3
4358fbb
8b5480a
416d995
9b1dd49
dba8bae
628dadb
c299f7c
e3b0894
418ec88
b99bb7a
e8023b1
3da9a8a
3e68c55
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -6600,4 +6600,43 @@ To remove the warning, you can use /reference instead (set the Embed Interop Typ | |||||||||||||
<data name="ERR_FunctionPointerTypesInAttributeNotSupported" xml:space="preserve"> | ||||||||||||||
<value>Using a function pointer type in a 'typeof' in an attribute is not supported.</value> | ||||||||||||||
</data> | ||||||||||||||
</root> | ||||||||||||||
<data name="ERR_BadCallerArgumentExpressionParamWithoutDefaultValue" xml:space="preserve"> | ||||||||||||||
<value>The CallerArgumentExpressionAttribute may only be applied to parameters with default values</value> | ||||||||||||||
</data> | ||||||||||||||
<data name="ERR_NoConversionForCallerArgumentExpressionParam" xml:space="preserve"> | ||||||||||||||
<value>CallerArgumentExpressionAttribute cannot be applied because there are no standard conversions from type '{0}' to type '{1}'</value> | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Should conversio be also implicit? #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I copied the exact same text from existing diagnostics. I'm not entirely sure about the correct terminology. Will need to go through the Standard conversions in spec. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AlekseyTs The following code compiles (standard explicit conversion from using System.Runtime.CompilerServices;
public class C
{
public void M([CallerMemberName] object name = null)
{
}
} So it seems standard conversion is correct There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Doesn't conversion in this scenario go from string to object instead? In reply to: 600250422 [](ancestors = 600250422) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AlekseyTs Consider the following code: using System.Runtime.CompilerServices;
public class C
{
public static void M([CallerMemberName] C name = null)
{
}
} The error message is:
Changing There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
The question is whether the standard conversion should also be implicit. In reply to: 600628537 [](ancestors = 600628537) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @AlekseyTs, Well, looking at the spec of CallerMemberName:
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ops I was swapping the source and destination for conversion in mind 😄 |
||||||||||||||
</data> | ||||||||||||||
<data name="WRN_CallerArgumentExpressionParamForUnconsumedLocation" xml:space="preserve"> | ||||||||||||||
<value>The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect because it applies to a member that is used in contexts that do not allow optional arguments</value> | ||||||||||||||
</data> | ||||||||||||||
<data name="WRN_CallerArgumentExpressionParamForUnconsumedLocation_Title" xml:space="preserve"> | ||||||||||||||
<value>The CallerArgumentExpressionAttribute will have no effect because it applies to a member that is used in contexts that do not allow optional arguments</value> | ||||||||||||||
</data> | ||||||||||||||
<data name="WRN_CallerFilePathPreferredOverCallerArgumentExpression" xml:space="preserve"> | ||||||||||||||
<value>The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerFilePathAttribute.</value> | ||||||||||||||
</data> | ||||||||||||||
<data name="WRN_CallerFilePathPreferredOverCallerArgumentExpression_Title" xml:space="preserve"> | ||||||||||||||
<value>The CallerArgumentExpressionAttribute will have no effect; it is overridden by the CallerFilePathAttribute</value> | ||||||||||||||
</data> | ||||||||||||||
<data name="WRN_CallerLineNumberPreferredOverCallerArgumentExpression" xml:space="preserve"> | ||||||||||||||
<value>The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is overridden by the CallerLineNumberAttribute.</value> | ||||||||||||||
</data> | ||||||||||||||
<data name="WRN_CallerLineNumberPreferredOverCallerArgumentExpression_Title" xml:space="preserve"> | ||||||||||||||
<value>The CallerArgumentExpressionAttribute will have no effect; it is overridden by the CallerLineNumberAttribute</value> | ||||||||||||||
</data> | ||||||||||||||
<data name="WRN_CallerMemberNamePreferredOverCallerArgumentExpression" xml:space="preserve"> | ||||||||||||||
<value>The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is overriden by the CallerMemberNameAttribute.</value> | ||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
English nit: remove There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I do the same with existing warnings for consistency? e.g:
or only remove "The" from the new warnings? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of course it is... I guess just leave it as is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What we should do, though, is add a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
FWIW, I am not aware of a requirement to add comments like that. Obviously, a developer can add remarks if he or she would like to do so. In this particular case, however, I believe that the suggested remark would state the obvious - type names are not translated and I personally wouldn't add it. It is quite obvious that "CallerArgumentExpressionAttribute" is not a word. In reply to: 605055933 [](ancestors = 605055933) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think there was some issues with translation for things that looks "trivial" that shouldn't be translated. See also #48364 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Also an example where "unmanaged" keyword was incorrectly translated, despite it's surrounded with single quotes which is a high indication it shouldn't be localized. roslyn/src/Compilers/CSharp/Portable/xlf/CSharpResources.ja.xlf Lines 170 to 174 in 02428e3
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
This is your PR, feel free to add remarks that you feel appropriate. In reply to: 605664888 [](ancestors = 605664888) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well, let's check with the loc team. @cristianosuzuki77 Is a "Locked" comment required for <trans-unit id="WRN_CallerMemberNamePreferredOverCallerArgumentExpression">
<source>The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is overriden by the CallerMemberNameAttribute.</source>
<note />
</trans-unit> |
||||||||||||||
</data> | ||||||||||||||
<data name="WRN_CallerMemberNamePreferredOverCallerArgumentExpression_Title" xml:space="preserve"> | ||||||||||||||
<value>The CallerArgumentExpressionAttribute will have no effect; it is overridden by the CallerMemberNameAttribute</value> | ||||||||||||||
</data> | ||||||||||||||
<data name="WRN_CallerArgumentExpressionAttributeHasInvalidParameterName" xml:space="preserve"> | ||||||||||||||
<value>The CallerArgumentExpressionAttribute applied to parameter '{0}' will have no effect. It is applied with an invalid parameter name.</value> | ||||||||||||||
</data> | ||||||||||||||
<data name="WRN_CallerArgumentExpressionAttributeHasInvalidParameterName_Title" xml:space="preserve"> | ||||||||||||||
<value>The CallerArgumentExpressionAttribute is applied with an invalid parameter name.</value> | ||||||||||||||
</data> | ||||||||||||||
<data name="IDS_FeatureCallerArgumentExpression" xml:space="preserve"> | ||||||||||||||
<value>caller argument expression</value> | ||||||||||||||
</data> | ||||||||||||||
</root> |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -216,6 +216,7 @@ internal enum MessageID | |
IDS_FeatureVarianceSafetyForStaticInterfaceMembers = MessageBase + 12791, | ||
IDS_FeatureConstantInterpolatedStrings = MessageBase + 12792, | ||
IDS_FeatureMixedDeclarationsAndExpressionsInDeconstruction = MessageBase + 12793, | ||
IDS_FeatureCallerArgumentExpression = MessageBase + 12794, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
It looks like we are missing the corresponding string in resourses #Closed There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done. |
||
} | ||
|
||
// Message IDs may refer to strings that need to be localized. | ||
|
@@ -324,6 +325,7 @@ internal static LanguageVersion RequiredVersion(this MessageID feature) | |
{ | ||
// C# preview features. | ||
case MessageID.IDS_FeatureMixedDeclarationsAndExpressionsInDeconstruction: | ||
case MessageID.IDS_FeatureCallerArgumentExpression: // semantic check | ||
return LanguageVersion.Preview; | ||
// C# 9.0 features. | ||
case MessageID.IDS_FeatureLambdaDiscardParameters: // semantic check | ||
|
Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -46,6 +46,7 @@ private struct PackedFlags | |
// r = RefKind. 2 bits. | ||
// n = hasNameInMetadata. 1 bit. | ||
// f = FlowAnalysisAnnotations. 9 bits (8 value bits + 1 completion bit). | ||
// Current total = 28 bits. | ||
|
||
private const int WellKnownAttributeDataOffset = 0; | ||
private const int WellKnownAttributeCompletionFlagOffset = 8; | ||
|
@@ -141,6 +142,12 @@ public bool TryGetFlowAnalysisAnnotations(out FlowAnalysisAnnotations value) | |
private ConstantValue _lazyDefaultValue = ConstantValue.Unset; | ||
private ThreeState _lazyIsParams; | ||
|
||
/// <summary> | ||
/// The index of a CallerArgumentExpression. The value -2 means uninitialized, -1 means | ||
/// not found. Otherwise, the index of the CallerArgumentExpression. | ||
/// </summary> | ||
private int _lazyCallerArgumentExpressionParameterIndex = -2; | ||
|
||
/// <summary> | ||
/// Attributes filtered out from m_lazyCustomAttributes, ParamArray, etc. | ||
/// </summary> | ||
|
@@ -656,6 +663,42 @@ internal override bool IsCallerMemberName | |
} | ||
} | ||
|
||
internal override int CallerArgumentExpressionParameterIndex | ||
{ | ||
get | ||
{ | ||
if (_lazyCallerArgumentExpressionParameterIndex != -2) | ||
{ | ||
return _lazyCallerArgumentExpressionParameterIndex; | ||
} | ||
|
||
var info = _moduleSymbol.Module.FindTargetAttribute(_handle, AttributeDescription.CallerArgumentExpressionAttribute); | ||
var discardedUseSiteInfo = CompoundUseSiteInfo<AssemblySymbol>.Discarded; | ||
bool isCallerArgumentExpression = info.HasValue | ||
&& !HasCallerLineNumberAttribute | ||
&& !HasCallerFilePathAttribute | ||
&& !HasCallerMemberNameAttribute | ||
&& new TypeConversions(ContainingAssembly).HasCallerInfoStringConversion(this.Type, ref discardedUseSiteInfo); | ||
|
||
if (isCallerArgumentExpression) | ||
{ | ||
_moduleSymbol.Module.TryExtractStringValueFromAttribute(info.Handle, out var parameterName); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It might make sense to move this logic into a helper method on PEModule so that it could be used by VB as well. #Closed |
||
var parameters = ContainingSymbol.GetParameters(); | ||
for (int i = 0; i < parameters.Length; i++) | ||
{ | ||
if (parameters[i].Name == parameterName) | ||
{ | ||
_lazyCallerArgumentExpressionParameterIndex = i; | ||
return i; | ||
} | ||
} | ||
} | ||
|
||
_lazyCallerArgumentExpressionParameterIndex = -1; | ||
return -1; | ||
} | ||
} | ||
|
||
internal override FlowAnalysisAnnotations FlowAnalysisAnnotations | ||
{ | ||
get | ||
|
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.
Is this condition ever true? #Closed
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.
@AlekseyTs Yes. When the arguments order matches the parameters order. I don't know the reason, only figured that by testing.
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.
Just to confirm. We have omitted arguments and argsToParamsOpt.IsDefault can still be true?
In reply to: 600234291 [](ancestors = 600234291)
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 sure what you mean by "We have omitted arguments". But yes I confirmed it can be true.
For example, for the following test: