Thanks to visit codestin.com
Credit goes to github.com

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
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,9 @@ public sealed class UsePartialPropertyForObservablePropertyCodeFixer : CodeFixPr
});

/// <inheritdoc/>
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(UseObservablePropertyOnPartialPropertyId);
public override ImmutableArray<string> FixableDiagnosticIds { get; } = ImmutableArray.Create(
UseObservablePropertyOnPartialPropertyId,
WinRTObservablePropertyOnFieldsIsNotAotCompatibleId);

/// <inheritdoc/>
public override FixAllProvider? GetFixAllProvider()
Expand All @@ -77,6 +79,14 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
return;
}

SemanticModel semanticModel = (await context.Document.GetSemanticModelAsync(context.CancellationToken).ConfigureAwait(false))!;

// If the language is not preview, we cannot apply this code fix (as it would generate invalid C# code)
if (!semanticModel.Compilation.IsLanguageVersionPreview())
{
return;
}

// Retrieve the properties passed by the analyzer
if (diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.FieldNameKey] is not string fieldName ||
diagnostic.Properties[FieldReferenceForObservablePropertyFieldAnalyzer.PropertyNameKey] is not string propertyName)
Expand All @@ -101,7 +111,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
context.RegisterCodeFix(
CodeAction.Create(
title: "Use a partial property",
createChangedDocument: token => ConvertToPartialProperty(context.Document, root, fieldDeclaration, fieldName, propertyName, context.CancellationToken),
createChangedDocument: token => ConvertToPartialProperty(context.Document, root, fieldDeclaration, semanticModel, fieldName, propertyName, context.CancellationToken),
equivalenceKey: "Use a partial property"),
diagnostic);
}
Expand All @@ -113,6 +123,7 @@ public override async Task RegisterCodeFixesAsync(CodeFixContext context)
/// <param name="document">The original document being fixed.</param>
/// <param name="root">The original tree root belonging to the current document.</param>
/// <param name="fieldDeclaration">The <see cref="FieldDeclarationSyntax"/> for the field being updated.</param>
/// <param name="semanticModel">The semantic model for <paramref name="document"/>.</param>
/// <param name="fieldName">The name of the annotated field.</param>
/// <param name="propertyName">The name of the generated property.</param>
/// <param name="cancellationToken">The cancellation token for the operation.</param>
Expand All @@ -121,11 +132,12 @@ private static async Task<Document> ConvertToPartialProperty(
Document document,
SyntaxNode root,
FieldDeclarationSyntax fieldDeclaration,
SemanticModel semanticModel,
string fieldName,
string propertyName,
CancellationToken cancellationToken)
{
SemanticModel semanticModel = (await document.GetSemanticModelAsync(cancellationToken).ConfigureAwait(false))!;
await Task.CompletedTask;

// Try to get all necessary type symbols to process the attributes
if (!semanticModel.Compilation.TryBuildNamedTypeSymbolMap(MvvmToolkitAttributeNamesToFullyQualifiedNamesMap, out ImmutableDictionary<string, INamedTypeSymbol>? toolkitTypeSymbols) ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -84,5 +84,9 @@ Rule ID | Category | Severity | Notes
--------|----------|----------|-------
MVVMTK0041 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0041
MVVMTK0042 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Info | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0042
MVVMTK0043| CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0043
MVVMTK0044| CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0043
MVVMTK0043 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0043
MVVMTK0044 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Error | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0044
MVVMTK0045 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0045
MVVMTK0046 | CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0046
MVVMTK0047 | CommunityToolkit.Mvvm.SourceGenerators.ObservablePropertyGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0047
MVVMTK0048 | CommunityToolkit.Mvvm.SourceGenerators.RelayCommandGenerator | Warning | See https://aka.ms/mvvmtoolkit/errors/mvvmtk0048
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,9 @@
<Compile Include="$(MSBuildThisFileDirectory)ComponentModel\TransitiveMembersGenerator.Execute.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\AsyncVoidReturningRelayCommandMethodAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\InvalidGeneratedPropertyObservablePropertyAttributeAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\WinRTRelayCommandIsNotGeneratedBindableCustomPropertyCompatibleAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\WinRTObservablePropertyOnFieldsIsNotAotCompatibleAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\PropertyNameCollisionObservablePropertyAttributeAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\InvalidTargetObservablePropertyAttributeAnalyzer.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Analyzers\InvalidClassLevelNotifyDataErrorInfoAttributeAnalyzer.cs" />
Expand All @@ -58,6 +61,7 @@
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\Suppressors\ObservablePropertyAttributeWithSupportedTargetDiagnosticSuppressor.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\DiagnosticDescriptors.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Diagnostics\SuppressionDescriptors.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\AnalyzerConfigOptionsExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\AccessibilityExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\AttributeDataExtensions.cs" />
<Compile Include="$(MSBuildThisFileDirectory)Extensions\CompilationExtensions.cs" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -153,8 +153,9 @@ public static bool TryGetInfo(

token.ThrowIfCancellationRequested();

// Override the property changing support if explicitly disabled
shouldInvokeOnPropertyChanging &= GetEnableINotifyPropertyChangingSupport(options);
// Override the property changing support if explicitly disabled.
// This setting is enabled by default, for backwards compatibility.
shouldInvokeOnPropertyChanging &= options.GetMSBuildBooleanPropertyValue("MvvmToolkitEnableINotifyPropertyChangingSupport", defaultValue: true);

token.ThrowIfCancellationRequested();

Expand Down Expand Up @@ -378,27 +379,6 @@ public static bool TryGetInfo(
return true;
}

/// <summary>
/// Gets the value for the "MvvmToolkitEnableINotifyPropertyChangingSupport" property.
/// </summary>
/// <param name="options">The options in use for the generator.</param>
/// <returns>The value for the "MvvmToolkitEnableINotifyPropertyChangingSupport" property.</returns>
public static bool GetEnableINotifyPropertyChangingSupport(AnalyzerConfigOptions options)
{
if (options.TryGetValue("build_property.MvvmToolkitEnableINotifyPropertyChangingSupport", out string? propertyValue))
{
if (bool.TryParse(propertyValue, out bool enableINotifyPropertyChangingSupport))
{
return enableINotifyPropertyChangingSupport;
}
}

// This setting is enabled by default, for backwards compatibility.
// Note that this path should never be reached, as the default
// value is also set in a .targets file bundled in the package.
return true;
}

/// <summary>
/// Validates the containing type for a given field being annotated.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,12 @@ public override void Initialize(AnalysisContext context)
return;
}

// If CsWinRT is in AOT-optimization mode, disable this analyzer, as the WinRT one will produce a warning instead
if (context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.IsCsWinRTAotOptimizerEnabled(context.Compilation))
{
return;
}

// Get the symbol for [ObservableProperty]
if (context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute") is not INamedTypeSymbol observablePropertySymbol)
{
Expand Down Expand Up @@ -73,7 +79,7 @@ public override void Initialize(AnalysisContext context)
.Add(FieldReferenceForObservablePropertyFieldAnalyzer.FieldNameKey, fieldSymbol.Name)
.Add(FieldReferenceForObservablePropertyFieldAnalyzer.PropertyNameKey, ObservablePropertyGenerator.Execute.GetGeneratedPropertyName(fieldSymbol)),
fieldSymbol.ContainingType,
fieldSymbol));
fieldSymbol.Name));
}, SymbolKind.Field);
});
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if ROSLYN_4_11_0_OR_GREATER

using System.Collections.Generic;
using System.Collections.Immutable;
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;

namespace CommunityToolkit.Mvvm.SourceGenerators;

/// <summary>
/// A diagnostic analyzer that generates an error when <c>[GeneratedBindableCustomProperty]</c> is used on types with invalid generated base members.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class WinRTGeneratedBindableCustomPropertyWithBasesMemberAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(
WinRTGeneratedBindableCustomPropertyWithBaseObservablePropertyOnField,
WinRTGeneratedBindableCustomPropertyWithBaseRelayCommand);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();

context.RegisterCompilationStartAction(static context =>
{
// This analyzer is only enabled when CsWinRT is also used
if (!context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.IsUsingWindowsRuntimePack())
{
return;
}

// Get the symbol for [ObservableProperty], [RelayCommand] and [GeneratedBindableCustomProperty]
if (context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute") is not INamedTypeSymbol observablePropertySymbol ||
context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.Input.RelayCommandAttribute") is not INamedTypeSymbol relayCommandSymbol ||
context.Compilation.GetTypeByMetadataName("WinRT.GeneratedBindableCustomPropertyAttribute") is not INamedTypeSymbol generatedBindableCustomPropertySymbol)
{
return;
}

context.RegisterSymbolAction(context =>
{
// Ensure we do have a valid type
if (context.Symbol is not INamedTypeSymbol typeSymbol)
{
return;
}

// We only care about it if it's using [GeneratedBindableCustomProperty]
if (!typeSymbol.TryGetAttributeWithType(generatedBindableCustomPropertySymbol, out AttributeData? generatedBindableCustomPropertyAttribute))
{
return;
}

// Warn on all [ObservableProperty] fields
foreach (IFieldSymbol fieldSymbol in FindObservablePropertyFields(typeSymbol, observablePropertySymbol))
{
context.ReportDiagnostic(Diagnostic.Create(
WinRTGeneratedBindableCustomPropertyWithBaseObservablePropertyOnField,
generatedBindableCustomPropertyAttribute.GetLocation(),
typeSymbol,
fieldSymbol.ContainingType,
fieldSymbol.Name));
}

// Warn on all [RelayCommand] methods
foreach (IMethodSymbol methodSymbol in FindRelayCommandMethods(typeSymbol, relayCommandSymbol))
{
context.ReportDiagnostic(Diagnostic.Create(
WinRTGeneratedBindableCustomPropertyWithBaseRelayCommand,
generatedBindableCustomPropertyAttribute.GetLocation(),
typeSymbol,
methodSymbol));
}
}, SymbolKind.NamedType);
});
}

/// <summary>
/// Finds all methods in the base types that have the <c>[RelayCommand]</c> attribute.
/// </summary>
/// <param name="typeSymbol">The <see cref="INamedTypeSymbol"/> instance to inspect.</param>
/// <param name="relayCommandSymbol">The symbol for the <c>[RelayCommand]</c></param>
/// <returns>All <see cref="IMethodSymbol"/> instances for matching members.</returns>
private static IEnumerable<IMethodSymbol> FindRelayCommandMethods(INamedTypeSymbol typeSymbol, INamedTypeSymbol relayCommandSymbol)
{
// Check whether the base type (if any) is from the same assembly, and stop if it isn't. We do not
// want to include methods from the same type, as those will already be caught by another analyzer.
if (!SymbolEqualityComparer.Default.Equals(typeSymbol.ContainingAssembly, typeSymbol.BaseType?.ContainingAssembly))
{
yield break;
}

foreach (ISymbol memberSymbol in typeSymbol.BaseType.GetAllMembersFromSameAssembly())
{
if (memberSymbol is IMethodSymbol methodSymbol &&
methodSymbol.HasAttributeWithType(relayCommandSymbol))
{
yield return methodSymbol;
}
}
}

/// <summary>
/// Finds all fields in the base types that have the <c>[ObservableProperty]</c> attribute.
/// </summary>
/// <param name="typeSymbol">The <see cref="INamedTypeSymbol"/> instance to inspect.</param>
/// <param name="observablePropertySymbol">The symbol for the <c>[ObservableProperty]</c></param>
/// <returns>All <see cref="IFieldSymbol"/> instances for matching members.</returns>
private static IEnumerable<IFieldSymbol> FindObservablePropertyFields(INamedTypeSymbol typeSymbol, INamedTypeSymbol observablePropertySymbol)
{
// Skip the base type if not from the same assembly, same as above
if (!SymbolEqualityComparer.Default.Equals(typeSymbol.ContainingAssembly, typeSymbol.BaseType?.ContainingAssembly))
{
yield break;
}

foreach (ISymbol memberSymbol in typeSymbol.BaseType.GetAllMembersFromSameAssembly())
{
if (memberSymbol is IFieldSymbol fieldSymbol &&
fieldSymbol.HasAttributeWithType(observablePropertySymbol))
{
yield return fieldSymbol;
}
}
}
}

#endif
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.

#if ROSLYN_4_11_0_OR_GREATER

using System.Collections.Immutable;
using CommunityToolkit.Mvvm.SourceGenerators.Extensions;
using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics;
using static CommunityToolkit.Mvvm.SourceGenerators.Diagnostics.DiagnosticDescriptors;

namespace CommunityToolkit.Mvvm.SourceGenerators;

/// <summary>
/// A diagnostic analyzer that generates an error when <c>[ObservableProperty]</c> is used on a field in a scenario where it wouldn't be AOT compatible.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public sealed class WinRTObservablePropertyOnFieldsIsNotAotCompatibleAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc/>
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics { get; } = ImmutableArray.Create(WinRTObservablePropertyOnFieldsIsNotAotCompatible);

/// <inheritdoc/>
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
context.EnableConcurrentExecution();

context.RegisterCompilationStartAction(static context =>
{
// This analyzer is only enabled in cases where CsWinRT is producing AOT-compatible code
if (!context.Options.AnalyzerConfigOptionsProvider.GlobalOptions.IsCsWinRTAotOptimizerEnabled(context.Compilation))
{
return;
}

// Get the symbol for [ObservableProperty]
if (context.Compilation.GetTypeByMetadataName("CommunityToolkit.Mvvm.ComponentModel.ObservablePropertyAttribute") is not INamedTypeSymbol observablePropertySymbol)
{
return;
}

context.RegisterSymbolAction(context =>
{
// Ensure we do have a valid field
if (context.Symbol is not IFieldSymbol fieldSymbol)
{
return;
}

// Emit a diagnostic if the field is using the [ObservableProperty] attribute
if (fieldSymbol.TryGetAttributeWithType(observablePropertySymbol, out AttributeData? observablePropertyAttribute))
{
context.ReportDiagnostic(Diagnostic.Create(
WinRTObservablePropertyOnFieldsIsNotAotCompatible,
observablePropertyAttribute.GetLocation(),
ImmutableDictionary.Create<string, string?>()
.Add(FieldReferenceForObservablePropertyFieldAnalyzer.FieldNameKey, fieldSymbol.Name)
.Add(FieldReferenceForObservablePropertyFieldAnalyzer.PropertyNameKey, ObservablePropertyGenerator.Execute.GetGeneratedPropertyName(fieldSymbol)),
fieldSymbol.ContainingType,
fieldSymbol.Name));
}
}, SymbolKind.Field);
});
}
}

#endif
Loading