Skip to content

Commit

Permalink
Allow parameter type name to be specified for WriteCodeFragment task (#…
Browse files Browse the repository at this point in the history
…6285)

Fixes #2281

Context
This change allows the WriteCodeFragment task to define assembly attributes that require parameters that are not of type System.String. For example, CSLCompliantAttribute can be generated with a parameter of true instead of "true".

Changes Made
Additional metadata can be defined on an AssemblyAttribute that specifies how to treat the parameters specified in the metadata. There are three different ways that the parameters can be treated.

Infer the Type
Without specifying any additional metadata, attributes that are defined in the mscorlib assembly (i.e. types that can be loaded via System.Type.GetType(string)) will have their parameter types inferred by finding the constructor where the parameter count matches the number of parameters specified in the metadata. For example, this:

<ItemGroup>
  <AssemblyAttribute Include="CLSCompliantAttribute">
    <_Parameter1>true</_Parameter1>
  </AssemblyAttribute>
</ItemGroup>
Will produce the code:

[assembly: CLSCompliantAttribute(true)]
For backward-compatibility, if the attribute cannot be found, or no matching constructor is found, the parameter is treated as a string.

Declare the Type
An additional metadata item can be used to specify the full name of the parameter type. To do this, add a metadata item that has the same name as the parameter with "_TypeName" appended to the end. For example, this:

<ItemGroup>
  <AssemblyAttribute Include="TestAttribute">
    <_Parameter1>True</_Parameter1>
    <_Parameter1_TypeName>System.Boolean</_Parameter1_TypeName>
  </AssemblyAttribute>
</ItemGroup>
Will produce the code:

[assembly: TestAttribute(true)]
This also works with named parameters:

<ItemGroup>
  <AssemblyAttribute Include="TestAttribute">
    <Foo>42</IdentifyLevel>
    <Foo_TypeName>System.Int32</Foo_TypeName>
  </AssemblyAttribute>
</ItemGroup>
[assembly: TestAttribute(42)]
All types that can be used as attribute parameters are supported, except for arrays.

For backward-compatibility, if a metadata item ends with "_TypeName", but there is no metadata item for the parameter with that name, then it will be treated as another named property. For example, this:

<ItemGroup>
  <AssemblyAttribute Include="TestAttribute">
    <Foo_TypeName>System.Int32</Foo_TypeName>
  </AssemblyAttribute>
</ItemGroup>
Would produce the code:

[assembly: TestAttribute(Foo_TypeName="System.Int32")]
Specify the Exact Code
For cases where declaring the type is insufficient (such as when the parameter is an array), you can specify the exact that that will be generated for the parameter by adding metadata that has the same name as the parameter with "_IsLiteral" appended to the end. For example, this:

<ItemGroup>
  <AssemblyAttribute Include="TestAttribute">
    <_Parameter1>new int[] { 1, 3, 5 } /* odd numbers */</_Parameter1>
    <_Parameter1_IsLiteral>true</_Parameter1_IsLiteral>
  </AssemblyAttribute>
</ItemGroup>
Will produce the code:

[assembly: TestAttribute(new int[] { 1, 3, 5 } /* odd numbers */)]
The limitation with this is that the code you provide is language-specific. For example, the literal value in the metadata above will only work in C#. If you used that same metadata in a VB.NET project, you would receive a compiler error.

This works with both positional and named parameters. As with the ..._TypeName metadata, if an ..._IsLiteral metadata does not have a corresponding parameter name, it will be treated as a named parameter for backward-compatibility.

Mixed Parameter Behavior
Because the additional metadata only applies to a specific parameter, you can choose to treat different parameters in different ways. For example, you can infer/use the default behavior for one parameter, specify the type for the second parameter and use a literal value for the third. For example:

<ItemGroup>
  <AssemblyAttribute Include="TestAttribute">
    <_Parameter1>This is a string</_Parameter1>
    <_Parameter2>42></Parameter2>
    <_Parameter2_TypeName>System.Int32</_Parameter2_TypeName>
    <_Parameter3>new int[] { 1 }</_Parameter3>
    <_Parameter3_IsLiteral>true</_Parameter3_IsLiteral>
  </AssemblyAttribute>
</ItemGroup>
  • Loading branch information
reduckted committed Apr 8, 2021
1 parent 7804350 commit c4cda20
Show file tree
Hide file tree
Showing 17 changed files with 1,161 additions and 12 deletions.
418 changes: 418 additions & 0 deletions src/Tasks.UnitTests/WriteCodeFragment_Tests.cs

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions src/Tasks/Resources/Strings.resx
Expand Up @@ -2378,6 +2378,14 @@
<value>MSB3714: The parameter "{0}" was supplied, but not all previously numbered parameters.</value>
<comment>{StrBegin="MSB3714: "}</comment>
</data>
<data name="WriteCodeFragment.ParameterTypeNotFound" xml:space="preserve">
<value>MSB3715: The type "{0}" was not found.</value>
<comment>{StrBegin="MSB3715: "}</comment>
</data>
<data name="WriteCodeFragment.CouldNotConvertValue" xml:space="preserve">
<value>MSB3716: The parameter value "{0}" could not be converted to "{1}". {2}</value>
<comment>{StrBegin="MSB3716: "}</comment>
</data>
<data name="WriteCodeFragment.NoWorkToDo" xml:space="preserve">
<value>No output file was written because no code was specified to create.</value>
</data>
Expand All @@ -2387,6 +2395,15 @@
<data name="WriteCodeFragment.Comment" xml:space="preserve">
<value>Generated by the MSBuild WriteCodeFragment class.</value>
</data>
<data name="WriteCodeFragment.CouldNotInferParameterType" xml:space="preserve">
<value>Could not infer the type of parameter "{0}" because the attribute type is unknown. The value will be treated as a string.</value>
</data>
<data name="WriteCodeFragment.CouldNotConvertToInferredType" xml:space="preserve">
<value>Could not convert the value for parameter "{0}" to the inferred type "{1}". The value will be treated as a string. {2}</value>
</data>
<data name="WriteCodeFragment.MultipleConstructorsFound" xml:space="preserve">
<value>Multiple attribute constructors were found for type inferencing.</value>
</data>


<!--
Expand Down
25 changes: 25 additions & 0 deletions src/Tasks/Resources/xlf/Strings.cs.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions src/Tasks/Resources/xlf/Strings.de.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions src/Tasks/Resources/xlf/Strings.en.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions src/Tasks/Resources/xlf/Strings.es.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions src/Tasks/Resources/xlf/Strings.fr.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions src/Tasks/Resources/xlf/Strings.it.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions src/Tasks/Resources/xlf/Strings.ja.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions src/Tasks/Resources/xlf/Strings.ko.xlf

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit c4cda20

Please sign in to comment.