Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 41 additions & 37 deletions Directory.Build.props
Original file line number Diff line number Diff line change
@@ -1,37 +1,41 @@
<Project>
<PropertyGroup>
<MajorVersion>0</MajorVersion>
<MinorVersion>1</MinorVersion>

<PackageIdPrefix>CommunityToolkit.Labs</PackageIdPrefix>
<RepositoryDirectory>$([MSBuild]::EnsureTrailingSlash('$(MSBuildThisFileDirectory)'))</RepositoryDirectory>
<ToolingDirectory>$(RepositoryDirectory)tooling</ToolingDirectory>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);Uno0001</NoWarn>

<!-- See https://github.com/CommunityToolkit/Labs-Windows/pull/605#issuecomment-2498743676 -->
<WarningsNotAsErrors>NU1901;NU1902;NU1903;NU1904;</WarningsNotAsErrors>

<!-- Labs only: No error for 'Missing XML comment for publicly visible type or member' -->
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS1591;CS1574;</WarningsNotAsErrors>

<!-- See https://github.com/CommunityToolkit/Windows/pull/609#issuecomment-2613505591 -->
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS0419;CS1570;</WarningsNotAsErrors>
</PropertyGroup>

<Import Project="Windows.Toolkit.Common.props" />

<PropertyGroup>
<DebugType>Embedded</DebugType>
<PackageOutputPath>bin\Package</PackageOutputPath>
<AllowedOutputExtensionsInPackageBuildOutputFolder>.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

</Project>
<Project>
<PropertyGroup>
<MajorVersion>0</MajorVersion>
<MinorVersion>1</MinorVersion>

<PackageIdPrefix>CommunityToolkit.Labs</PackageIdPrefix>
<RepositoryDirectory>$([MSBuild]::EnsureTrailingSlash('$(MSBuildThisFileDirectory)'))</RepositoryDirectory>
<ToolingDirectory>$(RepositoryDirectory)tooling</ToolingDirectory>
<EnableNETAnalyzers>true</EnableNETAnalyzers>
</PropertyGroup>

<PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
<EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<NoWarn>$(NoWarn);Uno0001</NoWarn>


<!-- See https://github.com/CommunityToolkit/Labs-Windows/pull/605#issuecomment-2498743676 -->
<WarningsNotAsErrors>NU1901;NU1902;NU1903;NU1904;</WarningsNotAsErrors>

<!-- Labs only: No error for 'Missing XML comment for publicly visible type or member' -->
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS1591;CS1574;</WarningsNotAsErrors>

<!-- See https://github.com/CommunityToolkit/Windows/pull/609#issuecomment-2613505591 -->
<WarningsNotAsErrors>$(WarningsNotAsErrors);CS0419;CS1570;</WarningsNotAsErrors>

<!-- Don't block Labs builds just because samples are underdeveloped -->
<WarningsNotAsErrors>$(WarningsNotAsErrors);TKSMPL0014;TKSMPL0013;</WarningsNotAsErrors>
</PropertyGroup>

<Import Project="Windows.Toolkit.Common.props" />

<PropertyGroup>
<DebugType>Embedded</DebugType>
<PackageOutputPath>bin\Package</PackageOutputPath>
<AllowedOutputExtensionsInPackageBuildOutputFolder>.pdb</AllowedOutputExtensionsInPackageBuildOutputFolder>
<PublishRepositoryUrl>true</PublishRepositoryUrl>
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>

</Project>
2 changes: 1 addition & 1 deletion ReadMe.md
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ Otherwise, you can clone the repo, open the `components` directory, navigate wit
- [CanvasView](https://github.com/CommunityToolkit/Labs-Windows/blob/main/components/CanvasView/samples/CanvasView.md)
- [ColorAnalyzer](https://github.com/CommunityToolkit/Labs-Windows/tree/main/components/ColorAnalyzer)
- [DataTable](https://github.com/CommunityToolkit/Labs-Windows/blob/main/components/DataTable/samples/DataTable.md)
- [DependencyPropertyGenerator](https://github.com/CommunityToolkit/Labs-Windows/tree/main/components/DependencyPropertyGenerator)
- [DependencyPropertyGenerator](https://github.com/CommunityToolkit/Labs-Windows/blob/main/components/DependencyPropertyGenerator/samples/DependencyPropertyGenerator.md)
- [Extensions.DependencyInjection](https://github.com/CommunityToolkit/Labs-Windows/tree/main/components/Extensions.DependencyInjection)
- [MarkdownTextBlock](https://github.com/CommunityToolkit/Labs-Windows/blob/main/components/MarkdownTextBlock/samples/MarkdownTextBlock.md)
- [Marquee](https://github.com/CommunityToolkit/Labs-Windows/blob/main/components/Marquee/samples/Marquee.md)
Expand Down
3 changes: 2 additions & 1 deletion components/DependencyPropertyGenerator/.gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,5 @@
*.cs text eol=lf
*.csproj text eol=lf
*.projitems text eol=lf
*.shprroj text eol=lf
*.shprroj text eol=lf
*.xaml text eol=crlf
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ public static DependencyPropertyDefaultValue GetDefaultValue(
// Validate the method has a valid signature as well
if (InvalidPropertyDefaultValueCallbackTypeAnalyzer.IsDefaultValueCallbackValid(propertySymbol, methodSymbol))
{
return new DependencyPropertyDefaultValue.Callback(methodName);
return new DependencyPropertyDefaultValue.Callback(methodName, RequiresBoxing: !methodSymbol.ReturnType.IsReferenceType);
}
}
}
Expand Down Expand Up @@ -690,33 +690,33 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility)
// Shared codegen
{ DefaultValue: DependencyPropertyDefaultValue.Null or DependencyPropertyDefaultValue.Default(_, true), IsPropertyChangedCallbackImplemented: false, IsSharedPropertyChangedCallbackImplemented: false }
=> "null",
{ DefaultValue: DependencyPropertyDefaultValue.Callback(string methodName), IsPropertyChangedCallbackImplemented: false, IsSharedPropertyChangedCallbackImplemented: false }
{ DefaultValue: DependencyPropertyDefaultValue.Callback callback, IsPropertyChangedCallbackImplemented: false, IsSharedPropertyChangedCallbackImplemented: false }
=> $"""
global::{WellKnownTypeNames.PropertyMetadata(propertyInfo.UseWindowsUIXaml)}.Create(
createDefaultValueCallback: new {WellKnownTypeNames.CreateDefaultValueCallback(propertyInfo.UseWindowsUIXaml)}({methodName}))
createDefaultValueCallback: new {WellKnownTypeNames.CreateDefaultValueCallback(propertyInfo.UseWindowsUIXaml)}({callback}))
""",
{ DefaultValue: { } defaultValue, IsPropertyChangedCallbackImplemented: false, IsSharedPropertyChangedCallbackImplemented: false }
=> $"new global::{WellKnownTypeNames.PropertyMetadata(propertyInfo.UseWindowsUIXaml)}({defaultValue})",

// Codegen for legacy UWP
{ IsAdditionalTypesGenerationSupported: false } => propertyInfo switch
{
{ DefaultValue: DependencyPropertyDefaultValue.Callback(string methodName), IsPropertyChangedCallbackImplemented: true, IsSharedPropertyChangedCallbackImplemented: false }
{ DefaultValue: DependencyPropertyDefaultValue.Callback callback, IsPropertyChangedCallbackImplemented: true, IsSharedPropertyChangedCallbackImplemented: false }
=> $"""
global::{WellKnownTypeNames.PropertyMetadata(propertyInfo.UseWindowsUIXaml)}.Create(
createDefaultValueCallback: new {WellKnownTypeNames.CreateDefaultValueCallback(propertyInfo.UseWindowsUIXaml)}({methodName}),
createDefaultValueCallback: new {WellKnownTypeNames.CreateDefaultValueCallback(propertyInfo.UseWindowsUIXaml)}({callback}),
propertyChangedCallback: static (d, e) => (({typeQualifiedName})d).On{propertyInfo.PropertyName}PropertyChanged(e))
""",
{ DefaultValue: DependencyPropertyDefaultValue.Callback(string methodName), IsPropertyChangedCallbackImplemented: false, IsSharedPropertyChangedCallbackImplemented: true }
{ DefaultValue: DependencyPropertyDefaultValue.Callback callback, IsPropertyChangedCallbackImplemented: false, IsSharedPropertyChangedCallbackImplemented: true }
=> $"""
global::{WellKnownTypeNames.PropertyMetadata(propertyInfo.UseWindowsUIXaml)}.Create(
createDefaultValueCallback: new {WellKnownTypeNames.CreateDefaultValueCallback(propertyInfo.UseWindowsUIXaml)}({methodName}),
createDefaultValueCallback: new {WellKnownTypeNames.CreateDefaultValueCallback(propertyInfo.UseWindowsUIXaml)}({callback}),
propertyChangedCallback: static (d, e) => (({typeQualifiedName})d).OnPropertyChanged(e))
""",
{ DefaultValue: DependencyPropertyDefaultValue.Callback(string methodName), IsPropertyChangedCallbackImplemented: true, IsSharedPropertyChangedCallbackImplemented: true }
{ DefaultValue: DependencyPropertyDefaultValue.Callback callback, IsPropertyChangedCallbackImplemented: true, IsSharedPropertyChangedCallbackImplemented: true }
=> $$"""
global::{{WellKnownTypeNames.PropertyMetadata(propertyInfo.UseWindowsUIXaml)}}.Create(
createDefaultValueCallback: new {{WellKnownTypeNames.CreateDefaultValueCallback(propertyInfo.UseWindowsUIXaml)}}({{methodName}}),
createDefaultValueCallback: new {{WellKnownTypeNames.CreateDefaultValueCallback(propertyInfo.UseWindowsUIXaml)}}({{callback}}),
propertyChangedCallback: static (d, e) => { (({{typeQualifiedName}})d).On{{propertyInfo.PropertyName}}PropertyChanged(e); (({{typeQualifiedName}})d).OnPropertyChanged(e); })
""",
{ DefaultValue: { } defaultValue, IsPropertyChangedCallbackImplemented: true, IsSharedPropertyChangedCallbackImplemented: false }
Expand Down Expand Up @@ -753,10 +753,10 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility)
defaultValue: null,
propertyChangedCallback: global::{GeneratorName}.PropertyChangedCallbacks.{propertyInfo.PropertyName}())
""",
{ DefaultValue: DependencyPropertyDefaultValue.Callback(string methodName) }
{ DefaultValue: DependencyPropertyDefaultValue.Callback callback }
=> $"""
global::{WellKnownTypeNames.PropertyMetadata(propertyInfo.UseWindowsUIXaml)}.Create(
createDefaultValueCallback: new {WellKnownTypeNames.CreateDefaultValueCallback(propertyInfo.UseWindowsUIXaml)}({methodName}),
createDefaultValueCallback: new {WellKnownTypeNames.CreateDefaultValueCallback(propertyInfo.UseWindowsUIXaml)}({callback}),
propertyChangedCallback: global::{GeneratorName}.PropertyChangedCallbacks.{propertyInfo.PropertyName}())
""",
{ DefaultValue: { } defaultValue } and ({ IsPropertyChangedCallbackImplemented: true } or { IsSharedPropertyChangedCallbackImplemented: true })
Expand Down Expand Up @@ -818,8 +818,11 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility)
// We will never have the 'partial' modifier in the set of property modifiers processed above.
writer.WriteLine($"partial {propertyInfo.TypeNameWithNullabilityAnnotations} {propertyInfo.PropertyName}");

using (writer.WriteBlock())
writer.WriteLine("{");

{
writer.IncreaseIndent();

// We need very different codegen depending on whether local caching is enabled or not
if (propertyInfo.IsLocalCachingEnabled)
{
Expand Down Expand Up @@ -894,13 +897,6 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility)
""", isMultiline: true);
}

// If the default value is not what the default field value would be, add an initializer
if (propertyInfo.DefaultValue is not (DependencyPropertyDefaultValue.Null or DependencyPropertyDefaultValue.Default or DependencyPropertyDefaultValue.Callback))
{
writer.Write($" = {propertyInfo.DefaultValue};");
}

// Always leave a newline after the end of the property declaration, in either case
writer.WriteLine();
}
else if (propertyInfo.TypeName == "object")
Expand Down Expand Up @@ -1010,7 +1006,20 @@ static string GetExpressionWithTrailingSpace(Accessibility accessibility)
}
""", isMultiline: true);
}

writer.DecreaseIndent();
}

writer.Write("}");

// The initializer follows the property's closing brace, not the setter's
if (propertyInfo.IsLocalCachingEnabled &&
propertyInfo.DefaultValue is not (DependencyPropertyDefaultValue.Null or DependencyPropertyDefaultValue.Default or DependencyPropertyDefaultValue.Callback))
{
writer.Write($" = {propertyInfo.DefaultValue};");
}

writer.WriteLine();
}

// Next, emit all partial method declarations at the bottom of the file
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,12 +90,14 @@ public override string ToString()
/// A <see cref="DependencyPropertyDefaultValue"/> type representing a callback.
/// </summary>
/// <param name="MethodName">The name of the callback method to invoke.</param>
public sealed record Callback(string MethodName) : DependencyPropertyDefaultValue
/// <param name="RequiresBoxing">Whether the callback return value requires a boxing conversion to <see cref="object"/>.</param>
public sealed record Callback(string MethodName, bool RequiresBoxing) : DependencyPropertyDefaultValue
{
/// <inheritdoc/>
public override string ToString()
{
return MethodName;
// Method-group conversions cannot box return values, including unconstrained type parameters
return RequiresBoxing ? $"static () => {MethodName}()" : MethodName;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
using CommunityToolkit.WinUI;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Emit;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Windows.Foundation;
using Windows.UI.ViewManagement;
Expand Down Expand Up @@ -55,6 +56,7 @@ public static void VerifyDiagnostics(string source, params string[] diagnosticsI
/// <param name="source">The input source to process.</param>
/// <param name="result">The expected source to be generated.</param>
/// <param name="languageVersion">The language version to use to run the test.</param>
/// <remarks>This also supports invalid inputs. Use <see cref="VerifyCompiles"/> to validate compilation.</remarks>
public static void VerifySources(string source, (string Filename, string Source) result, LanguageVersion languageVersion = LanguageVersion.CSharp13)
{
RunGenerator(source, out Compilation compilation, out ImmutableArray<Diagnostic> diagnostics, languageVersion);
Expand All @@ -70,6 +72,27 @@ public static void VerifySources(string source, (string Filename, string Source)
Assert.AreEqual(expectedText, actualText);
}

/// <summary>
/// Verifies that the resulting sources produced by a source generator can be emitted.
/// </summary>
/// <param name="source">The input source to process.</param>
/// <param name="languageVersion">The language version to use to run the test.</param>
/// <returns>The resulting compilation, for further assertions on the generated sources.</returns>
public static Compilation VerifyCompiles(string source, LanguageVersion languageVersion = LanguageVersion.CSharp13)
{
RunGenerator(source, out Compilation compilation, out ImmutableArray<Diagnostic> diagnostics, languageVersion);

CollectionAssert.AreEquivalent(Array.Empty<Diagnostic>(), diagnostics);

using MemoryStream stream = new();

EmitResult result = compilation.Emit(stream);

Assert.IsTrue(result.Success, $"Failed to emit generated sources:{Environment.NewLine}{string.Join(Environment.NewLine, result.Diagnostics)}");

return compilation;
}

/// <summary>
/// Verifies the incremental generator steps for a given source generator.
/// </summary>
Expand Down
Loading
Loading