├── .config
└── dotnet-tools.json
├── .gitattributes
├── .github
├── ISSUE_TEMPLATE
│ ├── bug-template.md
│ └── config.yml
└── pull-request-template.md
├── .gitignore
├── CHANGELOG.md
├── LICENSE.txt
├── README.md
├── appveyor.yml
├── build.csx
├── icon-small.png
├── icon.png
├── icon.svg
└── src
├── .editorconfig
├── PropertyChanged.SourceGenerator.Benchmarks
├── Program.cs
└── PropertyChanged.SourceGenerator.Benchmarks.csproj
├── PropertyChanged.SourceGenerator.UnitTests
├── AccessibilityTests.HandlesGetterAndSetterEquallyAccessible_SomeViewModel.verified.cs
├── AccessibilityTests.HandlesGetterLessAccessibleThanSetter_SomeViewModel.verified.cs
├── AccessibilityTests.HandlesGetterMoreAccessibleThanSetter_SomeViewModel.verified.cs
├── AccessibilityTests.HandlesInternalGetterProtectedSetter_SomeViewModel.verified.cs
├── AccessibilityTests.HandlesProtectedGetterInternalSetter_SomeViewModel.verified.cs
├── AccessibilityTests.RaisesIfBackingFieldIsReadOnly_SomeViewModel.verified.cs
├── AccessibilityTests.RaisesIfBackingPropertyIsGetterOnly_SomeViewModel.verified.cs
├── AccessibilityTests.RaisesIfBackingPropertyIsSetterOnly_SomeViewModel.verified.cs
├── AccessibilityTests.cs
├── AdditionalPropertyGenerationTests.AddsVirtual_SomeViewModel.verified.cs
├── AdditionalPropertyGenerationTests.cs
├── AlsoNotifyTests.HandlesPathologicalAttributeCases_SomeViewModel.verified.cs
├── AlsoNotifyTests.HandlesStandardNonPropertyNames_PropertyChanged.SourceGenerator.Internal.EventArgsCache.verified.cs
├── AlsoNotifyTests.HandlesStandardNonPropertyNames_SomeViewModel.verified.cs
├── AlsoNotifyTests.NotifiesGeneratedPropertyOnBaseClass_Derived.verified.cs
├── AlsoNotifyTests.NotifiesGeneratedProperty_SomeViewModel.verified.cs
├── AlsoNotifyTests.NotifiesPreExistingPropertyOnBaseClass_Derived.verified.cs
├── AlsoNotifyTests.NotifiesPreExistingProperty_SomeViewModel.verified.cs
├── AlsoNotifyTests.PassesNullForOldAndNewValueIfPropertyDoesNotExist_SomeViewModel.verified.cs
├── AlsoNotifyTests.PassesOldAndNewValue_SomeViewModel.verified.cs
├── AlsoNotifyTests.RaisesIfAutoNotifyingSelf_SomeViewModel.verified.cs
├── AlsoNotifyTests.RaisesIfPropertyDefinedOnChildClass_Base.verified.cs
├── AlsoNotifyTests.cs
├── AutoDependsOnTests.cs
├── DependsOnTests.HandlesDerivedTypeAutoDependsOn_Derived.verified.cs
├── DependsOnTests.HandlesDerivedTypeManualDependsOn_Derived.verified.cs
├── DependsOnTests.cs
├── DocTests.CopiesDocs_SomeViewModel.verified.cs
├── DocTests.GeneratesOnPropertyChangedOrChangingDocNoOldAndNew_SomeViewModel.verified.cs
├── DocTests.GeneratesOnPropertyChangedOrChangingDocOldAndNew_SomeViewModel.verified.cs
├── DocTests.HandlesMalformedXml_SomeViewModel.verified.cs
├── DocTests.cs
├── Framework
│ ├── DiagnosticResult.cs
│ ├── DiagnosticVerifier.cs
│ ├── RemoveDocumentationRewriter.cs
│ ├── RemoveInpcMembersRewriter.cs
│ ├── RemovePropertiesRewriter.cs
│ └── TestsBase.cs
├── IsChangedTests.GeneratesIsChangedOnBaseClass_Derived.verified.cs
├── IsChangedTests.GeneratesIsChangedToGeneratedProperty_SomeViewModel.verified.cs
├── IsChangedTests.GeneratesIsChangedToProperty_SomeViewModel.verified.cs
├── IsChangedTests.IgnoresIfGeneratedIsChangedPropertyOnBaseClassHasPrivateSetter_Derived.verified.cs
├── IsChangedTests.IgnoresIfIsChangedPropertyOnBaseClassHasPrivateSetter_Derived.verified.cs
├── IsChangedTests.cs
├── NullableTypeTests.DoesNotGenerateNullableEventIfInFileNullableContext_SomeViewModel.verified.cs
├── NullableTypeTests.GeneratesNullableEventIfInCompilationNullableContext_SomeViewModel.verified.cs
├── NullableTypeTests.GeneratesNullablePropertiesIfInCompilationNullableContext_SomeViewModel.verified.cs
├── NullableTypeTests.GeneratesNullablePropertiesIfInFilenNullableContext_SomeViewModel.verified.cs
├── NullableTypeTests.cs
├── OnAnyPropertyChangedTests.DoesNotGenerateForOverriddenMethod_Base.verified.cs
├── OnAnyPropertyChangedTests.DoesNotGenerateForOverriddenMethod_Derived.verified.cs
├── OnAnyPropertyChangedTests.GeneratesForShadowedMethod_Derived.verified.cs
├── OnAnyPropertyChangedTests.GeneratesOldAndNewWhenAlsoGeneratingRaisePropertyChanged_SomeViewModel.verified.cs
├── OnAnyPropertyChangedTests.GeneratesOldAndNewWhenOverridingOldAndNew_Derived.verified.cs
├── OnAnyPropertyChangedTests.GeneratesOldAndNewWhenOverridingParameterless_Derived.verified.cs
├── OnAnyPropertyChangedTests.GeneratesParameterlessWhenAlsoGeneratingRaisePropertyChanged_SomeViewModel.verified.cs
├── OnAnyPropertyChangedTests.GeneratesParameterlessWhenOverridingOldAndNew_Derived.verified.cs
├── OnAnyPropertyChangedTests.GeneratesParameterlessWhenOverridingParameterless_Derived.verified.cs
├── OnAnyPropertyChangedTests.cs
├── OnAnyPropertyChangingTests.DoesNotGenerateForOverriddenMethod_Base.verified.cs
├── OnAnyPropertyChangingTests.DoesNotGenerateForOverriddenMethod_Derived.verified.cs
├── OnAnyPropertyChangingTests.GeneratesForShadowedMethod_Derived.verified.cs
├── OnAnyPropertyChangingTests.GeneratesOldWhenAlsoGeneratingRaisePropertyChanging_SomeViewModel.verified.cs
├── OnAnyPropertyChangingTests.GeneratesOldWhenOverridingOld_Derived.verified.cs
├── OnAnyPropertyChangingTests.GeneratesOldWhenOverridingParameterless_Derived.verified.cs
├── OnAnyPropertyChangingTests.GeneratesParameterlessWhenAlsoGeneratingRaisePropertyChanging_SomeViewModel.verified.cs
├── OnAnyPropertyChangingTests.GeneratesParameterlessWhenOverridingOld_Derived.verified.cs
├── OnAnyPropertyChangingTests.GeneratesParameterlessWhenOverridingParameterless_Derived.verified.cs
├── OnAnyPropertyChangingTests.cs
├── OnPropertyNameChangedTests.DoesNotCallInaccessibleAlsoNotifyOnBaseClass_Derived.verified.cs
├── OnPropertyNameChangedTests.DoesNotGenerateAlsoNotifyNonCallable_SomeViewModel.verified.cs
├── OnPropertyNameChangedTests.DoesNotGenerateAlsoNotifyWithPropertyOnBaseClassAndMethodOnDerived_Derived.verified.cs
├── OnPropertyNameChangedTests.DoesNotMatchMethodWithDifferingParameterTypes_SomeViewModel.verified.cs
├── OnPropertyNameChangedTests.GenerateParameterlessRaise_SomeViewModel.verified.cs
├── OnPropertyNameChangedTests.GeneratesAlsoNotifyCallableParameterless_SomeViewModel.verified.cs
├── OnPropertyNameChangedTests.GeneratesAlsoNotifyCallableParameters_SomeViewModel.verified.cs
├── OnPropertyNameChangedTests.GeneratesAlsoNotifyOnBaseClass_Derived.verified.cs
├── OnPropertyNameChangedTests.GeneratesAutoDependsOn_SomeViewModel.verified.cs
├── OnPropertyNameChangedTests.GeneratesDependsOn_SomeViewModel.verified.cs
├── OnPropertyNameChangedTests.GeneratesOldAndNewWithMatchingDataType_SomeViewModel.verified.cs
├── OnPropertyNameChangedTests.GeneratesOldAndNewWithParentDataType_SomeViewModel.verified.cs
├── OnPropertyNameChangedTests.cs
├── OnPropertyNameChangingTests.DoesNotCallInaccessibleAlsoNotifyOnBaseClass_Derived.verified.cs
├── OnPropertyNameChangingTests.DoesNotGenerateAlsoNotifyWithPropertyOnBaseClassAndMethodOnDerived_Derived.verified.cs
├── OnPropertyNameChangingTests.GenerateParameterlessRaise_SomeViewModel.verified.cs
├── OnPropertyNameChangingTests.GeneratesAlsoNotifyCallableParameterless_SomeViewModel.verified.cs
├── OnPropertyNameChangingTests.GeneratesAlsoNotifyCallableParameters_SomeViewModel.verified.cs
├── OnPropertyNameChangingTests.GeneratesAlsoNotifyOnBaseClass_Derived.verified.cs
├── OnPropertyNameChangingTests.GeneratesAutoDependsOn_SomeViewModel.verified.cs
├── OnPropertyNameChangingTests.GeneratesDependsOn_SomeViewModel.verified.cs
├── OnPropertyNameChangingTests.GeneratesOldAndNewWithParentDataType_SomeViewModel.verified.cs
├── OnPropertyNameChangingTests.GeneratesOldWithMatchingDataType_SomeViewModel.verified.cs
├── OnPropertyNameChangingTests.cs
├── PropertyAttributeTests.AddsLeadingAndTrailingBracketsIfOmitted_SomeViewModel.verified.cs
├── PropertyAttributeTests.CopiesAttributeOntoGeneratedProperty_SomeViewModel.verified.cs
├── PropertyAttributeTests.cs
├── PropertyChanged.SourceGenerator.UnitTests.csproj
├── PropertyNameTests.ReportsCollisionWithExplicitNamesInBaseClass_Base.verified.cs
├── PropertyNameTests.ReportsCollisionWithExplicitNamesInBaseClass_Derived.verified.cs
├── PropertyNameTests.ReportsCollisionWithTwoExplicitNames_SomeViewModel.verified.cs
├── PropertyNameTests.ReportsNameCollisionExplicit_SomeViewModel.verified.cs
├── PropertyNameTests.ReportsNameCollisionInBaseClass_SomeViewModel.verified.cs
├── PropertyNameTests.ReportsNameCollisionInferred_SomeViewModel.verified.cs
├── PropertyNameTests.UsesExplicitNameIfGiven_SomeViewModel.verified.cs
├── PropertyNameTests.cs
├── RaisePropertyChangedCallTests.DoesNotGenerateInpcInterfaceIfAlreadySpecified_SomeViewModel.verified.cs
├── RaisePropertyChangedCallTests.FindsAndCallsMethodWithEventArgsAndOldAndNewValues_SomeViewModel.verified.cs
├── RaisePropertyChangedCallTests.FindsAndCallsMethodWithEventArgs_Derived.verified.cs
├── RaisePropertyChangedCallTests.FindsAndCallsMethodWithStringNameAndOldAndNewValues_SomeViewModel.verified.cs
├── RaisePropertyChangedCallTests.FindsAndCallsMethodWithStringName_SomeViewModel.verified.cs
├── RaisePropertyChangedCallTests.FindsExplicitlyImplementedEvent_SomeViewModel.verified.cs
├── RaisePropertyChangedCallTests.FindsGenericBaseClasses_B.verified.cs
├── RaisePropertyChangedCallTests.GeneratesInpcInterfaceIfNotSpecified_SomeViewModel.verified.cs
├── RaisePropertyChangedCallTests.SearchesForMethodFromTopOfTypeHierarchy_B.verified.cs
├── RaisePropertyChangedCallTests.SearchesForMethodFromTopOfTypeHierarchy_C.verified.cs
├── RaisePropertyChangedCallTests.TakesNameFromPropertyChangingIfPossible_SomeViewModel.verified.cs
├── RaisePropertyChangedCallTests.cs
├── RaisePropertyChangedDefinitionTests.CallsOnPropertyNameChangedNoOldAndNew_Derived.verified.cs
├── RaisePropertyChangedDefinitionTests.CallsOnPropertyNameChangedOldAndNew_Derived.verified.cs
├── RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=internal.verified.cs
├── RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=private protected.verified.cs
├── RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected internal.verified.cs
├── RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected.verified.cs
├── RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=public.verified.cs
├── RaisePropertyChangedDefinitionTests.DefinesOverrideEventArgsNoOldAndNew_Derived.verified.cs
├── RaisePropertyChangedDefinitionTests.DefinesOverrideEventArgsOldAndNew_Derived.verified.cs
├── RaisePropertyChangedDefinitionTests.DefinesOverrideStringNoOldAndNew_Derived.verified.cs
├── RaisePropertyChangedDefinitionTests.DefinesOverrideStringOldAndNew_Derived.verified.cs
├── RaisePropertyChangedDefinitionTests.DefinesVirtual_Derived.verified.cs
├── RaisePropertyChangedDefinitionTests.GeneratesEventAndRaisePropertyChangedIfNotDefined_SomeViewModel.verified.cs
├── RaisePropertyChangedDefinitionTests.GeneratesRaisePropertyChangedIfNotDefined_SomeViewModel.verified.cs
├── RaisePropertyChangedDefinitionTests.HandlesSealedClass_SomeViewModel.verified.cs
├── RaisePropertyChangedDefinitionTests.RaisesIfNonVirtualBaseMethodAndOverrideRequired_Derived.verified.cs
├── RaisePropertyChangedDefinitionTests.cs
├── RaisePropertyChangingCallTests.DoesNotGenerateInpcInterfaceIfAlreadySpecified_SomeViewModel.verified.cs
├── RaisePropertyChangingCallTests.DoesNotGenerateInpcInterfaceIfNotSpecified_SomeViewModel.verified.cs
├── RaisePropertyChangingCallTests.FindsAndCallsMethodWithEventArgsAndOldValue_SomeViewModel.verified.cs
├── RaisePropertyChangingCallTests.FindsAndCallsMethodWithEventArgs_Derived.verified.cs
├── RaisePropertyChangingCallTests.FindsAndCallsMethodWithStringNameAndOldValue_SomeViewModel.verified.cs
├── RaisePropertyChangingCallTests.FindsAndCallsMethodWithStringName_SomeViewModel.verified.cs
├── RaisePropertyChangingCallTests.FindsExplicitlyImplementedEvent_SomeViewModel.verified.cs
├── RaisePropertyChangingCallTests.FindsGenericBaseClasses_B.verified.cs
├── RaisePropertyChangingCallTests.SearchesForMethodFromTopOfTypeHierarchy_B.verified.cs
├── RaisePropertyChangingCallTests.SearchesForMethodFromTopOfTypeHierarchy_C.verified.cs
├── RaisePropertyChangingCallTests.TakesNameFromPropertyChangedIfPossible_SomeViewModel.verified.cs
├── RaisePropertyChangingCallTests.cs
├── RaisePropertyChangingDefinitionTests.CallsOnPropertyNameChangingNoOld_Derived.verified.cs
├── RaisePropertyChangingDefinitionTests.CallsOnPropertyNameChangingOld_Derived.verified.cs
├── RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=internal.verified.cs
├── RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=private protected.verified.cs
├── RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected internal.verified.cs
├── RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected.verified.cs
├── RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=public.verified.cs
├── RaisePropertyChangingDefinitionTests.DefinesOverrideEventArgsNoOld_Derived.verified.cs
├── RaisePropertyChangingDefinitionTests.DefinesOverrideEventArgsOld_Derived.verified.cs
├── RaisePropertyChangingDefinitionTests.DefinesOverrideStringNoOld_Derived.verified.cs
├── RaisePropertyChangingDefinitionTests.DefinesOverrideStringOld_Derived.verified.cs
├── RaisePropertyChangingDefinitionTests.DefinesVirtual_Derived.verified.cs
├── RaisePropertyChangingDefinitionTests.DoesNothingIfTypeDoesNotImplementInterface_SomeViewModel.verified.cs
├── RaisePropertyChangingDefinitionTests.GeneratesEventAndRaisePropertyChangingIfNotDefined_SomeViewModel.verified.cs
├── RaisePropertyChangingDefinitionTests.GeneratesRaisePropertyChangingIfNotDefined_SomeViewModel.verified.cs
├── RaisePropertyChangingDefinitionTests.RaisesIfNonVirtualBaseMethodAndOverrideRequired_Derived.verified.cs
├── RaisePropertyChangingDefinitionTests.cs
├── TypeGenerationTests.GeneratesNamespace_Test.Foo.SomeViewModel.verified.cs
├── TypeGenerationTests.HandlesBadlyNamedGenericTypes_SomeViewModel.verified.cs
├── TypeGenerationTests.HandlesGenericTypesWithConstraints_SomeViewModel.verified.cs
├── TypeGenerationTests.HandlesPartialNestedTypes_A.B.C.verified.cs
├── TypeGenerationTests.HandlesRecord_SomeViewModel.verified.cs
├── TypeGenerationTests.HandlesStruct_SomeViewModel.verified.cs
├── TypeGenerationTests.HandlesTwoClassesSameNameDifferentNamespaces_NS1.SomeViewModel.verified.cs
├── TypeGenerationTests.HandlesTwoClassesSameNameDifferentNamespaces_NS2.SomeViewModel.verified.cs
└── TypeGenerationTests.cs
├── PropertyChanged.SourceGenerator.Vsix
├── PropertyChanged.SourceGenerator.Vsix.csproj
└── source.extension.vsixmanifest
├── PropertyChanged.SourceGenerator.sln
├── PropertyChanged.SourceGenerator
├── AlwaysFalseEqualityComparer.cs
├── Analysis
│ ├── AlsoNotifyMember.cs
│ ├── Analyser.cs
│ ├── AnalyserInput.cs
│ ├── Analyser_AlsoNotify.cs
│ ├── Analyser_DependsOn.cs
│ ├── Analyser_IsChanged.cs
│ ├── IMember.cs
│ ├── InterfaceAnalyser.cs
│ ├── InterfaceAnalysis.cs
│ ├── MemberAnalysis.cs
│ ├── OnPropertyNameChangedInfo.cs
│ ├── PropertyChangedInterfaceAnalyser.cs
│ ├── PropertyChangingInterfaceAnalyser.cs
│ ├── RaisePropertyChangedMethodType.cs
│ ├── RaisePropertyChangedOrChangingMethodSignature.cs
│ ├── TypeAnalysis.cs
│ ├── TypeAnalysisLookups.cs
│ └── Utils.cs
├── BetterIndentedTextWriter.cs
├── Configuration.cs
├── ConfigurationParser.cs
├── DiagnosticReporter.cs
├── EquatableArray.cs
├── EventArgs
│ ├── EventArgsCache.cs
│ ├── EventArgsCacheBuilder.cs
│ ├── EventArgsCacheKey.cs
│ └── EventArgsCacheLookup.cs
├── EventArgsCacheGenerator.cs
├── Generator.cs
├── Polyfills
│ ├── InterpolatedStringHandlerArgumentAttribute.cs
│ ├── InterpolatedStringHandlerAttribute.cs
│ ├── NullableAttributes.cs
│ ├── RequiredMemberAttribute.cs
│ └── SetsRequiredMembersAttribute.cs
├── PropertyChanged.SourceGenerator.csproj
├── PropertyChangedSourceGenerator.cs
├── ReadOnlyEquatableList.cs
├── StringConstants.cs
└── SymbolDisplayFormats.cs
└── Sandbox
├── .editorconfig
├── Program.cs
└── Sandbox.csproj
/.config/dotnet-tools.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": 1,
3 | "isRoot": true,
4 | "tools": {
5 | "dotnet-reportgenerator-globaltool": {
6 | "version": "4.8.9",
7 | "commands": [
8 | "reportgenerator"
9 | ]
10 | }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/.gitattributes:
--------------------------------------------------------------------------------
1 | * text=auto
2 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-template.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Report a bug
3 | about: If you've definitely found something wrong, use this. Not sure? Open a discussion.
4 | ---
5 |
6 | **Description**
7 | A clear and concise description of what the bug is. Use screenshots as necessary.
8 |
9 | **To Reproduce**
10 | Code to reproduce the bug, which someone else can run.
11 |
12 | **Version Info**
13 | - PropertyChanged.SourceGenerator version: [e.g. 1.2.3]
14 | - Roslyn Version (add `#error version` to your code, and see what the error message is)
15 |
16 | **Additional Info**
17 | Add any other context about the problem here.
18 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | blank_issues_enabled: false
2 | contact_links:
3 | - name: Open a Discussion
4 | url: https://github.com/canton7/PropertyChanged.SourceGenerator/discussions/new
5 | about: If you've got a question, suggestion, or something you're not sure about, please open a discussion.
6 |
--------------------------------------------------------------------------------
/.github/pull-request-template.md:
--------------------------------------------------------------------------------
1 | **Checklist**
2 |
3 | Thanks for contributing! Before we start, there are a few things we need to check:
4 |
5 | 1. You've discussed your intention to work on this feature/bug fix.
6 | 2. This feature branch is based on develop (**not** master). The bar above should say "base: develop".
7 |
8 | Thanks!
9 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | coverage
2 | coverage.xml
3 | .vs
4 | bin
5 | obj
6 | NuGet
7 | *.received.cs
8 | BenchmarkDotNet.Artifacts
9 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | v1.1.1
2 | ------
3 |
4 | - Fix DependsOn not working when depending on a generated property from a base type (#47)
5 |
6 |
7 | v1.1.0
8 | ------
9 |
10 | - Restructure as an incremental source generator
11 |
12 |
13 | v1.0.8
14 | ------
15 |
16 | - Support the user explicitly implementing the `PropertyChanged`/`PropertyChanging` event (#38)
17 |
18 | v1.0.7
19 | ------
20 |
21 | - Allow automatic property depenency resolution to be disabled project-wide (#36)
22 | - Tweak what errors are raised when a suitable method to raise the PropertyChanged event could not be found (#33)
23 | - Properly support explicitly-implemented properties (#35)
24 | - Support generating virtual properties (#27)
25 | - Add `` to generated events (#29)
26 | - Fix README documentation on remove_{prefixes/suffixes} (#25)
27 |
28 | v1.0.6
29 | ------
30 |
31 | - Don't crash when analysing a property which refers to itself (#24)
32 |
33 | v1.0.5
34 | ------
35 |
36 | - Support placing attributes on generated properties with `[PropertyAttribute]`
37 |
38 | v1.0.4
39 | ------
40 |
41 | - Add support for `INotifyPropertyChanging`
42 | - Generate doc comments on `OnPropertyChanged`/`OnPropertyChanging`
43 |
44 | v1.0.3
45 | ------
46 |
47 | - Handle malformed XML doc comments
48 | - Don't propagate crashes to Roslyn, as it gets stuck in the "Generator is not generating files" state
49 | - Add README to NuGet package
50 |
51 | v1.0.2
52 | ------
53 |
54 | - Correctly handle sealed types (#11)
55 | - Copy XML documentation from the field to generated property
56 | - Support automatic/manual `DependsOn` properties which refer to other properties
57 | - Support automatic/manual `DependsOn` with properties defined in the base class
58 | - Introduce the `OnAnyPropertyChanged` hook: using this is now recommended above overriding `OnPropertyChanged`
59 |
60 | v1.0.1
61 | ------
62 |
63 | - Correctly detect generic base types (#3)
64 | - Fix handling partial inner classes (#2)
65 | - Fix crash when two types with the same name exist in different namespaces (#4)
66 | - README fixes and improvements
67 |
68 | v1.0.0
69 | ------
70 |
71 | - Initial release
72 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2021 Antony Male
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
23 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: 0.0.0.{build}
2 | image: Visual Studio 2022
3 | install:
4 | - ps: |
5 | $tagVersion = $($(git describe $(git rev-list --tags --max-count=1 origin/master)).TrimStart("v"))
6 | if ($env:appveyor_repo_tag -eq "true")
7 | {
8 | echo "Build triggered by a tag"
9 | $version = $tagVersion
10 | }
11 | else
12 | {
13 | $version = "$tagVersion.$env:appveyor_build_number"
14 | }
15 | echo $version
16 | Update-AppveyorBuild -Version $version
17 | & dotnet tool install -g dotnet-script
18 |
19 | build_script:
20 | - cmd: dotnet script build.csx -- package --version=%APPVEYOR_BUILD_VERSION%
21 | test_script:
22 | - cmd: dotnet script build.csx -- test
23 | artifacts:
24 | - path: '**/*.nupkg'
25 |
--------------------------------------------------------------------------------
/build.csx:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env dotnet-script
2 |
3 | #r "nuget:SimpleTasks, 0.9.4"
4 |
5 | using SimpleTasks;
6 | using static SimpleTasks.SimpleTask;
7 |
8 | #nullable enable
9 |
10 | string libDir = "src/PropertyChanged.SourceGenerator";
11 | string testsDir = "src/PropertyChanged.SourceGenerator.UnitTests";
12 |
13 | string nugetDir = "NuGet";
14 |
15 | string CommonFlags(string? version, string? configuration) =>
16 | $"--configuration={configuration ?? "Release"} -p:VersionPrefix=\"{version ?? "0.0.0"}\"";
17 |
18 | CreateTask("build").Run((string versionOpt, string configurationOpt) =>
19 | {
20 | var flags = CommonFlags(versionOpt, configurationOpt);
21 | Command.Run("dotnet", $"build {flags} \"{libDir}\"");
22 | });
23 |
24 | CreateTask("package").DependsOn("build").Run((string version, string configurationOpt) =>
25 | {
26 | var flags = CommonFlags(version, configurationOpt) + $" --no-build --output=\"{nugetDir}\"";
27 | Command.Run("dotnet", $"pack {flags} \"{libDir}\"");
28 | });
29 |
30 | CreateTask("test").Run(() =>
31 | {
32 | Command.Run("dotnet", $"test \"{testsDir}\"");
33 | });
34 |
35 | CreateTask("coverage").Run(() =>
36 | {
37 | Command.Run("dotnet", $"test -p:AltCover=true {testsDir}");
38 | Command.Run("dotnet", $"reportgenerator -reports:{testsDir}/coverage.xml -targetdir:coverage -assemblyfilters:+PropertyChanged.SourceGenerator");
39 | });
40 |
41 | CreateTask("accept-tests").Run(() =>
42 | {
43 | var files = Directory.GetFiles(testsDir, "*.received.cs", SearchOption.AllDirectories);
44 | foreach (var file in files)
45 | {
46 | var renamed = System.Text.RegularExpressions.Regex.Replace(file, @"\.received\.cs$", ".verified.cs");
47 | Console.WriteLine($"{file} -> {renamed}");
48 | File.Move(file, renamed, true);
49 | }
50 | });
51 |
52 | return InvokeTask(Args);
53 |
--------------------------------------------------------------------------------
/icon-small.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canton7/PropertyChanged.SourceGenerator/28d6a67c2b9a2af79d4bc5708366300bca83275c/icon-small.png
--------------------------------------------------------------------------------
/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/canton7/PropertyChanged.SourceGenerator/28d6a67c2b9a2af79d4bc5708366300bca83275c/icon.png
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.Benchmarks/Program.cs:
--------------------------------------------------------------------------------
1 |
2 | using Basic.Reference.Assemblies;
3 | using BenchmarkDotNet.Attributes;
4 | using BenchmarkDotNet.Configs;
5 | using BenchmarkDotNet.Diagnostics.Windows.Configs;
6 | using BenchmarkDotNet.Running;
7 | using Microsoft.CodeAnalysis;
8 | using Microsoft.CodeAnalysis.CSharp;
9 | using PropertyChanged.SourceGenerator;
10 | using PropertyChanged.SourceGenerator.UnitTests.Framework;
11 |
12 | [EtwProfiler]
13 | public class Benchmarks
14 | {
15 | private (GeneratorDriver driver, Compilation compilation) selfGeneratedCompilation = default;
16 | private (GeneratorDriver driver, Compilation compilation) baseClassCompilation = default;
17 |
18 | [GlobalSetup]
19 | public void SetUp()
20 | {
21 | var generator = new PropertyChangedSourceGenerator();
22 |
23 | this.selfGeneratedCompilation = Create(SelfGeneratedInput);
24 | this.baseClassCompilation = Create(BaseClassInput);
25 |
26 | (GeneratorDriver driver, Compilation compilation) Create(string input)
27 | {
28 | GeneratorDriver driver = CSharpGeneratorDriver.Create(new[] { generator.AsSourceGenerator() }, driverOptions: new GeneratorDriverOptions(IncrementalGeneratorOutputKind.None, true));
29 |
30 | // Run once first with an empty syntax tree, so that FAWMN gets a chance to set itself up, without the cost of that being counted
31 | var inputCompilation = CSharpCompilation.Create("TestCompilation",
32 | new[] { CSharpSyntaxTree.ParseText(input) },
33 | ReferenceAssemblies.Net50,
34 | new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary,
35 | nullableContextOptions: NullableContextOptions.Disable));
36 |
37 | driver = driver.RunGenerators(inputCompilation);
38 |
39 | return (driver, inputCompilation.AddSyntaxTrees(CSharpSyntaxTree.ParseText("")));
40 | }
41 | }
42 |
43 | private void Run((GeneratorDriver driver, Compilation compilation) pair)
44 | {
45 | pair.driver.RunGenerators(pair.compilation);
46 | }
47 |
48 | [Benchmark]
49 | public void SelfGenerated() => this.Run(this.selfGeneratedCompilation);
50 |
51 | [Benchmark]
52 | public void BaseClass() => this.Run(this.baseClassCompilation);
53 |
54 | public const string SelfGeneratedInput = """
55 | using PropertyChanged.SourceGenerator;
56 | public partial class A1
57 | {
58 | [Notify] private string _fooA;
59 | [Notify] private string _barA;
60 | [Notify] private int _bazA;
61 | }
62 | public partial class B1
63 | {
64 | [Notify] private string _fooB;
65 | [Notify] private string _barB;
66 | [Notify] private int _bazB;
67 |
68 | private void OnBazBChanged(int oldValue, int newValue) { }
69 | private void AnyProperyChanged(string propertyName, object oldValue, object newValue) { }
70 | }
71 | public partial class C1
72 | {
73 | [Notify] private string _fooC;
74 | [Notify] private string _barC;
75 | [Notify] private int _bazC;
76 | }
77 | public partial class C2 : C1
78 | {
79 | [Notify] private string _subC1;
80 | [Notify] private int _subC2;
81 | [Notify] private int _subC3;
82 | [Notify] private double _subC4;
83 | [Notify] private decumal _subC5;
84 | [Notify] private A1 _subC6;
85 | }
86 | """;
87 |
88 | private const string BaseClassInput = """
89 | using PropertyChanged.SourceGenerator;
90 | using System.ComponentModel;
91 | public class PropertyChangedBase : INotifyPropertyChanged
92 | {
93 | public event PropertyChangedEventHandler PropertyChanged;
94 | protected void OnPropertyChanged(string propertyName) => PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
95 | }
96 | public partial class A1 : PropertyChangedBase
97 | {
98 | [Notify] private string _fooA;
99 | [Notify] private string _barA;
100 | [Notify] private int _bazA;
101 | }
102 | public partial class B1 : PropertyChangedBase
103 | {
104 | [Notify] private string _fooB;
105 | [Notify] private string _barB;
106 | [Notify] private int _bazB;
107 |
108 | private void OnBazBChanged(int oldValue, int newValue) { }
109 | private void AnyProperyChanged(string propertyName, object oldValue, object newValue) { }
110 | }
111 | public partial class C1 : PropertyChangedBase
112 | {
113 | [Notify] private string _fooC;
114 | [Notify] private string _barC;
115 | [Notify] private int _bazC;
116 | }
117 | public partial class C2 : C1
118 | {
119 | [Notify] private string _subC1;
120 | [Notify] private int _subC2;
121 | [Notify] private int _subC3;
122 | [Notify] private double _subC4;
123 | [Notify] private decumal _subC5;
124 | [Notify] private A1 _subC6;
125 | }
126 | """;
127 | }
128 |
129 | public class Program
130 | {
131 | public static void Main()
132 | {
133 | BenchmarkRunner.Run();
134 | }
135 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.Benchmarks/PropertyChanged.SourceGenerator.Benchmarks.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net7
6 | enable
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.HandlesGetterAndSetterEquallyAccessible_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | internal string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.HandlesGetterLessAccessibleThanSetter_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | protected string Foo { private protected get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.HandlesGetterMoreAccessibleThanSetter_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | internal string Foo { get; private set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.HandlesInternalGetterProtectedSetter_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | protected internal string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.HandlesProtectedGetterInternalSetter_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | protected internal string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.RaisesIfBackingFieldIsReadOnly_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.RaisesIfBackingPropertyIsGetterOnly_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.RaisesIfBackingPropertyIsSetterOnly_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AccessibilityTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using NUnit.Framework;
7 | using PropertyChanged.SourceGenerator.UnitTests.Framework;
8 |
9 | namespace PropertyChanged.SourceGenerator.UnitTests;
10 |
11 | [TestFixture]
12 | public class AccessibilityTests : TestsBase
13 | {
14 | [Test]
15 | public void HandlesGetterMoreAccessibleThanSetter()
16 | {
17 | string input = """
18 | public partial class SomeViewModel
19 | {
20 | [Notify(Getter.Internal, Setter.Private)]
21 | private string _foo;
22 | }
23 | """;
24 |
25 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters));
26 | }
27 |
28 | [Test]
29 | public void HandlesGetterLessAccessibleThanSetter()
30 | {
31 | string input = """
32 | public partial class SomeViewModel
33 | {
34 | [Notify(Getter.PrivateProtected, Setter.Protected)]
35 | private string _foo;
36 | }
37 | """;
38 |
39 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters));
40 | }
41 |
42 | [Test]
43 | public void HandlesGetterAndSetterEquallyAccessible()
44 | {
45 | string input = """
46 | public partial class SomeViewModel
47 | {
48 | [Notify(Getter.Internal, Setter.Internal)]
49 | private string _foo;
50 | }
51 | """;
52 |
53 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters));
54 | }
55 |
56 | [Test]
57 | public void HandlesInternalGetterProtectedSetter()
58 | {
59 | string input = """
60 | public partial class SomeViewModel
61 | {
62 | [Notify(Getter.Internal, Setter.Protected)]
63 | private string _foo;
64 | }
65 | """;
66 |
67 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)
68 | .HasDiagnostics(
69 | // (3,6): Warning INPC005: C# propertes may not have an internal getter and protected setter, or protected setter and internal getter. Defaulting both to protected internal
70 | // Notify(Getter.Internal, Setter.Protected)
71 | Diagnostic("INPC005", @"Notify(Getter.Internal, Setter.Protected)").WithLocation(3, 6)
72 | ));
73 | }
74 |
75 | [Test]
76 | public void HandlesProtectedGetterInternalSetter()
77 | {
78 | string input = """
79 | public partial class SomeViewModel
80 | {
81 | [Notify(Getter.Internal, Setter.Protected)]
82 | private string _foo;
83 | }
84 | """;
85 |
86 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)
87 | .HasDiagnostics(
88 | // (3,6): Warning INPC005: C# propertes may not have an internal getter and protected setter, or protected setter and internal getter. Defaulting both to protected internal
89 | // Notify(Getter.Internal, Setter.Protected)
90 | Diagnostic("INPC005", @"Notify(Getter.Internal, Setter.Protected)").WithLocation(3, 6)
91 | ));
92 | }
93 |
94 | [Test]
95 | public void RaisesIfBackingFieldIsReadOnly()
96 | {
97 | string input = """
98 | public partial class SomeViewModel
99 | {
100 | [Notify]
101 | private readonly int _foo;
102 | }
103 | """;
104 |
105 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)
106 | .HasDiagnostics(
107 | // (4,26): Warning INPC018: Backing field '_foo' cannot be readonly. Skipping
108 | // _foo
109 | Diagnostic("INPC018", @"_foo").WithLocation(4, 26)
110 | ));
111 | }
112 |
113 | [Test]
114 | public void RaisesIfBackingPropertyIsGetterOnly()
115 | {
116 | string input = """
117 | public partial class SomeViewModel
118 | {
119 | [Notify]
120 | private int _foo { get; }
121 | }
122 | """;
123 |
124 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)
125 | .HasDiagnostics(
126 | // (4,17): Warning INPC019: Backing property '_foo' cannot be getter-only. Skipping
127 | // _foo
128 | Diagnostic("INPC019", @"_foo").WithLocation(4, 17)
129 | ));
130 | }
131 |
132 | [Test]
133 | public void RaisesIfBackingPropertyIsSetterOnly()
134 | {
135 | string input = """
136 | public partial class SomeViewModel
137 | {
138 | [Notify]
139 | private int _foo { set { } }
140 | }
141 | """;
142 |
143 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)
144 | .HasDiagnostics(
145 | // (4,17): Warning INPC019: Backing property '_foo' must have a getter and a setter. Skipping
146 | // _foo
147 | Diagnostic("INPC019", @"_foo").WithLocation(4, 17)
148 | ));
149 | }
150 | }
151 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AdditionalPropertyGenerationTests.AddsVirtual_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public virtual int Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AdditionalPropertyGenerationTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using NUnit.Framework;
7 | using PropertyChanged.SourceGenerator.UnitTests.Framework;
8 |
9 | namespace PropertyChanged.SourceGenerator.UnitTests;
10 |
11 | public class AdditionalPropertyGenerationTests : TestsBase
12 | {
13 | [Test]
14 | public void AddsVirtual()
15 | {
16 | string input = """
17 | public partial class SomeViewModel
18 | {
19 | [Notify(IsVirtual = true)]
20 | private int _foo;
21 | }
22 | """;
23 |
24 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters));
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.HandlesPathologicalAttributeCases_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Null);
13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Empty);
14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_NonExistent);
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.HandlesStandardNonPropertyNames_PropertyChanged.SourceGenerator.Internal.EventArgsCache.verified.cs:
--------------------------------------------------------------------------------
1 | namespace PropertyChanged.SourceGenerator.Internal
2 | {
3 | internal static class EventArgsCache
4 | {
5 | private static global::System.ComponentModel.PropertyChangedEventArgs _PropertyChanged_Null;
6 | public static global::System.ComponentModel.PropertyChangedEventArgs PropertyChanged_Null => _PropertyChanged_Null ??= new global::System.ComponentModel.PropertyChangedEventArgs(null);
7 | private static global::System.ComponentModel.PropertyChangedEventArgs _PropertyChanged_Empty;
8 | public static global::System.ComponentModel.PropertyChangedEventArgs PropertyChanged_Empty => _PropertyChanged_Empty ??= new global::System.ComponentModel.PropertyChangedEventArgs(@"");
9 | private static global::System.ComponentModel.PropertyChangedEventArgs _PropertyChanged_Foo;
10 | public static global::System.ComponentModel.PropertyChangedEventArgs PropertyChanged_Foo => _PropertyChanged_Foo ??= new global::System.ComponentModel.PropertyChangedEventArgs(@"Foo");
11 | private static global::System.ComponentModel.PropertyChangedEventArgs _PropertyChanged_Item;
12 | public static global::System.ComponentModel.PropertyChangedEventArgs PropertyChanged_Item => _PropertyChanged_Item ??= new global::System.ComponentModel.PropertyChangedEventArgs(@"Item[]");
13 | }
14 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.HandlesStandardNonPropertyNames_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Null);
13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Empty);
14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Item);
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.NotifiesGeneratedPropertyOnBaseClass_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar
4 | {
5 | get => this._bar;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._bar))
9 | {
10 | this._bar = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.NotifiesGeneratedProperty_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
13 | }
14 | }
15 | }
16 | public string Bar
17 | {
18 | get => this._bar;
19 | set
20 | {
21 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._bar))
22 | {
23 | this._bar = value;
24 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.NotifiesPreExistingPropertyOnBaseClass_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar
4 | {
5 | get => this._bar;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._bar))
9 | {
10 | this._bar = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.NotifiesPreExistingProperty_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.PassesNullForOldAndNewValueIfPropertyDoesNotExist_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | string old_Foo = this._foo;
11 | this._foo = value;
12 | string new_Foo = this._foo;
13 | this.OnPropertyChanged(@"Foo", old_Foo, new_Foo);
14 | this.OnPropertyChanged(@"", (object)null, (object)null);
15 | this.OnPropertyChanged(@"Item[]", (object)null, (object)null);
16 | this.OnPropertyChanged(@"NonExistent", (object)null, (object)null);
17 | }
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.PassesOldAndNewValue_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | string old_Foo = this._foo;
11 | global::System.Collections.Generic.List old_Bar = this.Bar;
12 | this._foo = value;
13 | string new_Foo = this._foo;
14 | global::System.Collections.Generic.List new_Bar = this.Bar;
15 | this.OnPropertyChanged(@"Foo", old_Foo, new_Foo);
16 | this.OnPropertyChanged(@"Bar", old_Bar, new_Bar);
17 | }
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.RaisesIfAutoNotifyingSelf_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/AlsoNotifyTests.RaisesIfPropertyDefinedOnChildClass_Base.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Base
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/DependsOnTests.HandlesDerivedTypeAutoDependsOn_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Baz { get; set; }
4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
5 | {
6 | base.OnPropertyChanged(eventArgs);
7 | switch (eventArgs.PropertyName)
8 | {
9 | case @"Selected":
10 | {
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Test);
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/DependsOnTests.HandlesDerivedTypeManualDependsOn_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Baz { get; set; }
4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
5 | {
6 | base.OnPropertyChanged(eventArgs);
7 | switch (eventArgs.PropertyName)
8 | {
9 | case @"Selected":
10 | {
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Test);
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/DocTests.CopiesDocs_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | ///
4 | /// The Summary
5 | ///
6 | ///
7 | /// The Description.
8 | /// Indented line
9 | ///
10 | public string Foo { get; set; }
11 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/DocTests.GeneratesOnPropertyChangedOrChangingDocNoOldAndNew_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | ///
4 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
5 | ///
6 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
7 | public int Foo { get; set; }
8 | ///
9 | /// Raises the PropertyChanged event
10 | ///
11 | /// The EventArgs to use to raise the event
12 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
13 | {
14 | this.PropertyChanged?.Invoke(this, eventArgs);
15 | }
16 | ///
17 | /// Raises the PropertyChanging event
18 | ///
19 | /// The EventArgs to use to raise the event
20 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs)
21 | {
22 | this.PropertyChanging?.Invoke(this, eventArgs);
23 | }
24 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/DocTests.GeneratesOnPropertyChangedOrChangingDocOldAndNew_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | ///
4 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
5 | ///
6 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
7 | public int Foo { get; set; }
8 | ///
9 | /// Raises the PropertyChanged event
10 | ///
11 | /// The EventArgs to use to raise the event
12 | /// Current value of the property
13 | /// New value of the property
14 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue)
15 | {
16 | this.OnAnyPropertyChanged(eventArgs.PropertyName, oldValue, newValue);
17 | this.PropertyChanged?.Invoke(this, eventArgs);
18 | }
19 | ///
20 | /// Raises the PropertyChanging event
21 | ///
22 | /// The EventArgs to use to raise the event
23 | /// Current value of the property
24 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue)
25 | {
26 | this.OnAnyPropertyChanging(eventArgs.PropertyName, oldValue);
27 | this.PropertyChanging?.Invoke(this, eventArgs);
28 | }
29 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/DocTests.HandlesMalformedXml_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/DocTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.CodeAnalysis.CSharp;
7 | using Microsoft.CodeAnalysis;
8 | using NUnit.Framework;
9 | using PropertyChanged.SourceGenerator.UnitTests.Framework;
10 |
11 | namespace PropertyChanged.SourceGenerator.UnitTests;
12 |
13 | [TestFixture]
14 | public class DocTests : TestsBase
15 | {
16 | private static readonly CSharpSyntaxVisitor[] rewriters = new CSharpSyntaxVisitor[]
17 | {
18 | RemovePropertiesRewriter.Instance, RemoveInpcMembersRewriter.All
19 | };
20 |
21 | [Test]
22 | public void CopiesDocs()
23 | {
24 | string input = """
25 | public partial class SomeViewModel
26 | {
27 | ///
28 | /// The Summary
29 | ///
30 | ///
31 | /// The Description.
32 | /// Indented line
33 | ///
34 | [Notify] private string _foo;
35 | }
36 | """;
37 |
38 | this.AssertThat(input, It.HasFile("SomeViewModel", rewriters));
39 | }
40 |
41 | [Test]
42 | public void HandlesMalformedXml()
43 | {
44 | string input = """
45 | public partial class SomeViewModel
46 | {
47 | ///
48 | /// Test
49 | ///
50 | [Notify] private string _foo;
51 | }
52 | """;
53 |
54 | this.AssertThat(input, It.HasFile("SomeViewModel", rewriters));
55 | }
56 |
57 | [Test]
58 | public void GeneratesOnPropertyChangedOrChangingDocNoOldAndNew()
59 | {
60 | string input = """
61 | using System.ComponentModel;
62 | public partial class SomeViewModel : INotifyPropertyChanging
63 | {
64 | [Notify] private int _foo;
65 | }
66 | """;
67 |
68 | this.AssertThat(input, It.HasFile("SomeViewModel", RemovePropertiesRewriter.Instance));
69 | }
70 |
71 | [Test]
72 | public void GeneratesOnPropertyChangedOrChangingDocOldAndNew()
73 | {
74 | string input = """
75 | using System.ComponentModel;
76 | public partial class SomeViewModel : INotifyPropertyChanging
77 | {
78 | [Notify] private int _foo;
79 | private void OnAnyPropertyChanged(string name, object oldValue, object newValue) { }
80 | private void OnAnyPropertyChanging(string name, object oldValue) { }
81 | }
82 | """;
83 |
84 | this.AssertThat(input, It.HasFile("SomeViewModel", RemovePropertiesRewriter.Instance));
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/Framework/DiagnosticResult.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.CodeAnalysis;
7 |
8 | namespace PropertyChanged.SourceGenerator.UnitTests.Framework;
9 |
10 | public class DiagnosticResult
11 | {
12 | public const DiagnosticSeverity DefaultSeverity = DiagnosticSeverity.Warning;
13 |
14 | public string Code { get; }
15 | public string SquiggledText { get; }
16 | public DiagnosticSeverity Severity { get; set; } = DefaultSeverity;
17 | public List Locations { get; } = new List();
18 |
19 | public DiagnosticResult(string code, string squiggledText)
20 | {
21 | this.Code = code;
22 | this.SquiggledText = squiggledText;
23 | }
24 |
25 | public DiagnosticResult WithLocation(int line, int column)
26 | {
27 | this.Locations.Add(new DiagnosticResultLocation(line, column));
28 | return this;
29 | }
30 |
31 | public DiagnosticResult WithSeverity(DiagnosticSeverity severity)
32 | {
33 | this.Severity = severity;
34 | return this;
35 | }
36 | }
37 |
38 | public readonly struct DiagnosticResultLocation
39 | {
40 | public int Line { get; }
41 | public int Column { get; }
42 |
43 | public DiagnosticResultLocation(int line, int column)
44 | {
45 | if (column < -1)
46 | {
47 | throw new ArgumentOutOfRangeException(nameof(column), "column must be >= -1");
48 | }
49 |
50 | this.Line = line;
51 | this.Column = column;
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/Framework/RemoveDocumentationRewriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 | using Microsoft.CodeAnalysis;
8 | using Microsoft.CodeAnalysis.CSharp;
9 | using Microsoft.CodeAnalysis.CSharp.Syntax;
10 |
11 | namespace PropertyChanged.SourceGenerator.UnitTests.Framework;
12 |
13 | public class RemoveDocumentationRewriter : CSharpSyntaxRewriter
14 | {
15 | public static RemoveDocumentationRewriter Instance { get; } = new();
16 |
17 | public override SyntaxNode? Visit(SyntaxNode? node) => base.Visit(RemoveDocComments(node));
18 |
19 | private static SyntaxNode? RemoveDocComments(SyntaxNode? node)
20 | {
21 | if (node == null)
22 | {
23 | return node;
24 | }
25 |
26 | // Need to remove the SingleLineDocumentationCommentTrivia, and the WhitespaceTrivia just before it
27 |
28 | var trivia = node.GetLeadingTrivia();
29 | return node.WithLeadingTrivia(Filter(trivia));
30 |
31 | static IEnumerable Filter(IEnumerable input)
32 | {
33 | SyntaxTrivia? previous = null;
34 | foreach (var item in input)
35 | {
36 | if (item.IsKind(SyntaxKind.SingleLineDocumentationCommentTrivia))
37 | {
38 | // Don't yield this, and don't yield previous either if it's whitespace
39 | if (previous != null && !previous.Value.IsKind(SyntaxKind.WhitespaceTrivia))
40 | {
41 | yield return previous.Value;
42 | }
43 | previous = null;
44 | }
45 | else
46 | {
47 | if (previous != null)
48 | {
49 | yield return previous.Value;
50 | }
51 | previous = item;
52 | }
53 | }
54 |
55 | if (previous != null)
56 | {
57 | yield return previous.Value;
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/Framework/RemoveInpcMembersRewriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.CodeAnalysis;
7 | using Microsoft.CodeAnalysis.CSharp;
8 | using Microsoft.CodeAnalysis.CSharp.Syntax;
9 |
10 | namespace PropertyChanged.SourceGenerator.UnitTests.Framework;
11 |
12 | public class RemoveInpcMembersRewriter : CSharpSyntaxRewriter
13 | {
14 | private static readonly Configuration config = new();
15 | private readonly bool removeChanged;
16 | private readonly bool removeChanging;
17 |
18 | public static RemoveInpcMembersRewriter All { get; } = new(true, true);
19 | public static RemoveInpcMembersRewriter Changed { get; } = new(true, false);
20 | public static RemoveInpcMembersRewriter Changing { get; } = new(false, true);
21 |
22 | private RemoveInpcMembersRewriter(bool removeChanged, bool removeChanging)
23 | {
24 | this.removeChanged = removeChanged;
25 | this.removeChanging = removeChanging;
26 | }
27 |
28 | public override SyntaxNode? VisitMethodDeclaration(MethodDeclarationSyntax node)
29 | {
30 | if (config.RaisePropertyChangedMethodNames.Contains(node.Identifier.ValueText))
31 | {
32 | return this.removeChanged ? null : node;
33 | }
34 | if (config.RaisePropertyChangingMethodNames.Contains(node.Identifier.ValueText))
35 | {
36 | return this.removeChanging ? null : node;
37 | }
38 |
39 | return base.VisitMethodDeclaration(node);
40 | }
41 |
42 | public override SyntaxNode? VisitEventFieldDeclaration(EventFieldDeclarationSyntax node)
43 | {
44 | if (this.removeChanged && node.Declaration.Variables.Any(x => x.Identifier.ValueText == "PropertyChanged"))
45 | {
46 | return null;
47 | }
48 | if (this.removeChanging && node.Declaration.Variables.Any(x => x.Identifier.ValueText == "PropertyChanging"))
49 | {
50 | return null;
51 | }
52 |
53 | return base.VisitEventFieldDeclaration(node);
54 | }
55 |
56 | public override SyntaxNode? VisitClassDeclaration(ClassDeclarationSyntax node) =>
57 | this.RewriteTypeDeclaration((TypeDeclarationSyntax)base.VisitClassDeclaration(node)!);
58 |
59 | public override SyntaxNode? VisitStructDeclaration(StructDeclarationSyntax node) =>
60 | this.RewriteTypeDeclaration((TypeDeclarationSyntax)base.VisitStructDeclaration(node)!);
61 |
62 | public override SyntaxNode? VisitRecordDeclaration(RecordDeclarationSyntax node) =>
63 | this.RewriteTypeDeclaration((TypeDeclarationSyntax)base.VisitRecordDeclaration(node)!);
64 |
65 | private TypeDeclarationSyntax RewriteTypeDeclaration(TypeDeclarationSyntax node)
66 | {
67 | if (node.BaseList?.Types.Count == 0)
68 | {
69 | // The newline is normally attached to the final item in the BaseList
70 | node = node.ReplaceNode(node.BaseList, node.BaseList.WithTrailingTrivia(SyntaxFactory.Whitespace("\r\n")));
71 | node = node.RemoveNode(node.BaseList!, SyntaxRemoveOptions.KeepTrailingTrivia)!;
72 | // Remove any trailing space from the class name
73 | node = node.WithIdentifier(node.Identifier.WithTrailingTrivia(default(SyntaxTriviaList)));
74 | if (node.TypeParameterList != null)
75 | {
76 | node = node.WithTypeParameterList(node.TypeParameterList.WithTrailingTrivia(default(SyntaxTriviaList)));
77 | }
78 | }
79 |
80 | return node;
81 | }
82 |
83 | public override SyntaxNode? VisitSimpleBaseType(SimpleBaseTypeSyntax node)
84 | {
85 | if (this.removeChanged && node.Type is QualifiedNameSyntax changedSyntax && changedSyntax.ToString() == "global::System.ComponentModel.INotifyPropertyChanged")
86 | {
87 | return null;
88 | }
89 | if (this.removeChanging && node.Type is QualifiedNameSyntax changingSyntax && changingSyntax.ToString() == "global::System.ComponentModel.INotifyPropertyChanging")
90 | {
91 | return null;
92 | }
93 |
94 | return base.VisitSimpleBaseType(node);
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/Framework/RemovePropertiesRewriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.CodeAnalysis;
7 | using Microsoft.CodeAnalysis.CSharp;
8 | using Microsoft.CodeAnalysis.CSharp.Syntax;
9 | using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
10 |
11 | namespace PropertyChanged.SourceGenerator.UnitTests.Framework;
12 |
13 | public class RemovePropertiesRewriter : CSharpSyntaxRewriter
14 | {
15 | public static RemovePropertiesRewriter Instance { get; } = new();
16 |
17 | public override SyntaxNode? VisitPropertyDeclaration(PropertyDeclarationSyntax node)
18 | {
19 | // Remove the whitespace around the leading { and trailing }
20 | return base.VisitPropertyDeclaration(node
21 | .WithIdentifier(
22 | node.Identifier.WithTrailingTrivia(Whitespace(" ")))
23 | .WithAccessorList(
24 | node.AccessorList!
25 | .WithOpenBraceToken(node.AccessorList.OpenBraceToken.WithoutTrivia())
26 | .WithCloseBraceToken(node.AccessorList.CloseBraceToken
27 | .WithLeadingTrivia(Whitespace(" "))
28 | .WithTrailingTrivia(LineFeed))));
29 | }
30 |
31 | public override SyntaxNode? VisitAccessorDeclaration(AccessorDeclarationSyntax node)
32 | {
33 | return base.VisitAccessorDeclaration(node
34 | .WithBody(null)
35 | .WithExpressionBody(null)
36 | .WithKeyword(node.Keyword.WithTrailingTrivia(null))
37 | .WithLeadingTrivia(Whitespace(" "))
38 | .WithSemicolonToken(Token(SyntaxKind.SemicolonToken)));
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/IsChangedTests.GeneratesIsChangedOnBaseClass_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | this.IsChanged = true;
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/IsChangedTests.GeneratesIsChangedToGeneratedProperty_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public bool IsChanged
4 | {
5 | get => this._isChanged;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._isChanged))
9 | {
10 | this._isChanged = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_IsChanged);
12 | }
13 | }
14 | }
15 | public string Foo
16 | {
17 | get => this._foo;
18 | set
19 | {
20 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
21 | {
22 | this._foo = value;
23 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
24 | this.IsChanged = true;
25 | }
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/IsChangedTests.GeneratesIsChangedToProperty_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | this.IsChanged = true;
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/IsChangedTests.IgnoresIfGeneratedIsChangedPropertyOnBaseClassHasPrivateSetter_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int? Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/IsChangedTests.IgnoresIfIsChangedPropertyOnBaseClassHasPrivateSetter_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int? Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/NullableTypeTests.DoesNotGenerateNullableEventIfInFileNullableContext_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
4 | #nullable enable annotations
5 | public string Foo { get; set; }
6 | #nullable disable
7 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
8 | {
9 | this.PropertyChanged?.Invoke(this, eventArgs);
10 | }
11 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/NullableTypeTests.GeneratesNullableEventIfInCompilationNullableContext_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged
3 | {
4 | public event global::System.ComponentModel.PropertyChangedEventHandler? PropertyChanged;
5 | public string Foo { get; set; }
6 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
7 | {
8 | this.PropertyChanged?.Invoke(this, eventArgs);
9 | }
10 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/NullableTypeTests.GeneratesNullablePropertiesIfInCompilationNullableContext_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | #nullable enable
2 | partial class SomeViewModel
3 | {
4 | public string? Nullable
5 | {
6 | get => this._nullable;
7 | set
8 | {
9 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._nullable))
10 | {
11 | this._nullable = value;
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Nullable);
13 | }
14 | }
15 | }
16 | public string NotNullable
17 | {
18 | get => this._notNullable;
19 | set
20 | {
21 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._notNullable))
22 | {
23 | this._notNullable = value;
24 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_NotNullable);
25 | }
26 | }
27 | }
28 | #nullable disable
29 | public string Oblivious
30 | {
31 | get => this._oblivious;
32 | set
33 | {
34 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._oblivious))
35 | {
36 | this._oblivious = value;
37 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Oblivious);
38 | }
39 | }
40 | }
41 | #nullable enable
42 | public int? NullableValue
43 | {
44 | get => this._nullableValue;
45 | set
46 | {
47 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._nullableValue))
48 | {
49 | this._nullableValue = value;
50 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_NullableValue);
51 | }
52 | }
53 | }
54 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/NullableTypeTests.GeneratesNullablePropertiesIfInFilenNullableContext_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | #nullable enable annotations
4 | public string? Nullable
5 | {
6 | get => this._nullable;
7 | set
8 | {
9 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._nullable))
10 | {
11 | this._nullable = value;
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Nullable);
13 | }
14 | }
15 | }
16 | #nullable disable
17 | #nullable enable annotations
18 | public string NotNullable
19 | {
20 | get => this._notNullable;
21 | set
22 | {
23 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._notNullable))
24 | {
25 | this._notNullable = value;
26 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_NotNullable);
27 | }
28 | }
29 | }
30 | #nullable disable
31 | public string Oblivious
32 | {
33 | get => this._oblivious;
34 | set
35 | {
36 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._oblivious))
37 | {
38 | this._oblivious = value;
39 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Oblivious);
40 | }
41 | }
42 | }
43 | public int? NullableValue
44 | {
45 | get => this._nullableValue;
46 | set
47 | {
48 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._nullableValue))
49 | {
50 | this._nullableValue = value;
51 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_NullableValue);
52 | }
53 | }
54 | }
55 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/NullableTypeTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using Microsoft.CodeAnalysis;
7 | using Microsoft.CodeAnalysis.CSharp;
8 | using NUnit.Framework;
9 | using PropertyChanged.SourceGenerator.UnitTests.Framework;
10 |
11 | namespace PropertyChanged.SourceGenerator.UnitTests;
12 |
13 | [TestFixture]
14 | public class NullableTypeTests : TestsBase
15 | {
16 | private static readonly CSharpSyntaxVisitor[] rewriters = new CSharpSyntaxVisitor[]
17 | {
18 | RemovePropertiesRewriter.Instance, RemoveDocumentationRewriter.Instance,
19 | };
20 |
21 | [Test]
22 | public void GeneratesNullableEventIfInCompilationNullableContext()
23 | {
24 | string input = """
25 | public partial class SomeViewModel
26 | {
27 | [Notify]
28 | private string _foo = "";
29 | }
30 | """;
31 |
32 | this.AssertThat(
33 | input,
34 | It.HasFile("SomeViewModel", rewriters),
35 | nullableContextOptions: NullableContextOptions.Enable);
36 | }
37 |
38 | [Test]
39 | public void DoesNotGenerateNullableEventIfInFileNullableContext()
40 | {
41 | string input = """
42 | #nullable enable
43 | public partial class SomeViewModel
44 | {
45 | [Notify]
46 | private string _foo = "";
47 | }
48 | """;
49 |
50 | this.AssertThat(
51 | input,
52 | It.HasFile("SomeViewModel", rewriters),
53 | nullableContextOptions: NullableContextOptions.Disable);
54 | ;
55 | }
56 |
57 | [Test]
58 | public void GeneratesNullablePropertiesIfInCompilationNullableContext()
59 | {
60 | string input = """
61 | public partial class SomeViewModel
62 | {
63 | [Notify]
64 | private string? _nullable;
65 | [Notify]
66 | private string _notNullable = "";
67 | #nullable disable
68 | [Notify]
69 | private string _oblivious;
70 | #nullable restore
71 | [Notify]
72 | private int? _nullableValue;
73 | }
74 | """;
75 |
76 | this.AssertThat(
77 | input,
78 | It.HasFile("SomeViewModel", RemoveInpcMembersRewriter.All),
79 | nullableContextOptions: NullableContextOptions.Enable);
80 | }
81 |
82 | [Test]
83 | public void GeneratesNullablePropertiesIfInFilenNullableContext()
84 | {
85 | string input = """
86 | #nullable enable
87 | public partial class SomeViewModel
88 | {
89 | [Notify]
90 | private string? _nullable;
91 | [Notify]
92 | private string _notNullable = "";
93 | #nullable disable
94 | [Notify]
95 | private string _oblivious;
96 | #nullable restore
97 | [Notify]
98 | private int? _nullableValue;
99 | }
100 | """;
101 |
102 | this.AssertThat(
103 | input,
104 | It.HasFile("SomeViewModel", RemoveInpcMembersRewriter.All),
105 | nullableContextOptions: NullableContextOptions.Disable);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.DoesNotGenerateForOverriddenMethod_Base.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Base : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
4 | public string Foo { get; set; }
5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
6 | {
7 | this.OnAnyPropertyChanged(eventArgs.PropertyName);
8 | this.PropertyChanged?.Invoke(this, eventArgs);
9 | }
10 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.DoesNotGenerateForOverriddenMethod_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesForShadowedMethod_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
5 | {
6 | this.OnAnyPropertyChanged(eventArgs.PropertyName);
7 | base.OnPropertyChanged(eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesOldAndNewWhenAlsoGeneratingRaisePropertyChanged_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
4 | public string Foo { get; set; }
5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue)
6 | {
7 | this.OnAnyPropertyChanged(eventArgs.PropertyName, oldValue, newValue);
8 | this.PropertyChanged?.Invoke(this, eventArgs);
9 | }
10 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesOldAndNewWhenOverridingOldAndNew_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public int Foo { get; set; }
4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue)
5 | {
6 | this.OnAnyPropertyChanged(eventArgs.PropertyName, oldValue, newValue);
7 | base.OnPropertyChanged(eventArgs, oldValue, newValue);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesOldAndNewWhenOverridingParameterless_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public int Foo { get; set; }
4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
5 | {
6 | this.OnAnyPropertyChanged(eventArgs.PropertyName, (object)null, (object)null);
7 | base.OnPropertyChanged(eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesParameterlessWhenAlsoGeneratingRaisePropertyChanged_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
4 | public string Foo { get; set; }
5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
6 | {
7 | this.OnAnyPropertyChanged(eventArgs.PropertyName);
8 | this.PropertyChanged?.Invoke(this, eventArgs);
9 | }
10 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesParameterlessWhenOverridingOldAndNew_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public int Foo { get; set; }
4 | protected override void OnPropertyChanged(string propertyName, object oldValue, object newValue)
5 | {
6 | this.OnAnyPropertyChanged(propertyName);
7 | base.OnPropertyChanged(propertyName, oldValue, newValue);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangedTests.GeneratesParameterlessWhenOverridingParameterless_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public int Foo { get; set; }
4 | protected override void OnPropertyChanged(string propertyName)
5 | {
6 | this.OnAnyPropertyChanged(propertyName);
7 | base.OnPropertyChanged(propertyName);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.DoesNotGenerateForOverriddenMethod_Base.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Base
2 | {
3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
4 | public string Foo { get; set; }
5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs)
6 | {
7 | this.OnAnyPropertyChanging(eventArgs.PropertyName);
8 | this.PropertyChanging?.Invoke(this, eventArgs);
9 | }
10 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.DoesNotGenerateForOverriddenMethod_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesForShadowedMethod_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs)
5 | {
6 | this.OnAnyPropertyChanging(eventArgs.PropertyName);
7 | base.OnPropertyChanging(eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesOldWhenAlsoGeneratingRaisePropertyChanging_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
4 | public string Foo { get; set; }
5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue)
6 | {
7 | this.OnAnyPropertyChanging(eventArgs.PropertyName, oldValue);
8 | this.PropertyChanging?.Invoke(this, eventArgs);
9 | }
10 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesOldWhenOverridingOld_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Foo { get; set; }
4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue)
5 | {
6 | this.OnAnyPropertyChanging(eventArgs.PropertyName, oldValue);
7 | base.OnPropertyChanging(eventArgs, oldValue);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesOldWhenOverridingParameterless_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Foo { get; set; }
4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs)
5 | {
6 | this.OnAnyPropertyChanging(eventArgs.PropertyName, (object)null);
7 | base.OnPropertyChanging(eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesParameterlessWhenAlsoGeneratingRaisePropertyChanging_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
4 | public string Foo { get; set; }
5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs)
6 | {
7 | this.OnAnyPropertyChanging(eventArgs.PropertyName);
8 | this.PropertyChanging?.Invoke(this, eventArgs);
9 | }
10 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesParameterlessWhenOverridingOld_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Foo { get; set; }
4 | protected override void OnPropertyChanging(string propertyName, object oldValue)
5 | {
6 | this.OnAnyPropertyChanging(propertyName);
7 | base.OnPropertyChanging(propertyName, oldValue);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnAnyPropertyChangingTests.GeneratesParameterlessWhenOverridingParameterless_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Foo { get; set; }
4 | protected override void OnPropertyChanging(string propertyName)
5 | {
6 | this.OnAnyPropertyChanging(propertyName);
7 | base.OnPropertyChanging(propertyName);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.DoesNotCallInaccessibleAlsoNotifyOnBaseClass_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.DoesNotGenerateAlsoNotifyNonCallable_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.DoesNotGenerateAlsoNotifyWithPropertyOnBaseClassAndMethodOnDerived_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.DoesNotMatchMethodWithDifferingParameterTypes_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GenerateParameterlessRaise_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnFooChanged();
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesAlsoNotifyCallableParameterless_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | this.OnBarChanged();
13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
14 | }
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesAlsoNotifyCallableParameters_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | int old_Bar = this.Bar;
11 | this._foo = value;
12 | int new_Bar = this.Bar;
13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
14 | this.OnBarChanged(old_Bar, new_Bar);
15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
16 | }
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesAlsoNotifyOnBaseClass_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | int old_Bar = this.Bar;
11 | this._foo = value;
12 | int new_Bar = this.Bar;
13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
14 | this.OnBarChanged(old_Bar, new_Bar);
15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
16 | }
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesAutoDependsOn_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | int old_Bar = this.Bar;
11 | string old_Baz = this.Baz;
12 | this._foo = value;
13 | int new_Bar = this.Bar;
14 | string new_Baz = this.Baz;
15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
16 | this.OnBarChanged(old_Bar, new_Bar);
17 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
18 | this.OnBazChanged(old_Baz, new_Baz);
19 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Baz);
20 | }
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesDependsOn_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | int old_Bar = this.Bar;
11 | this._foo = value;
12 | int new_Bar = this.Bar;
13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
14 | this.OnBarChanged(old_Bar, new_Bar);
15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
16 | }
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesOldAndNewWithMatchingDataType_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | string old_Foo = this._foo;
11 | this._foo = value;
12 | string new_Foo = this._foo;
13 | this.OnFooChanged(old_Foo, new_Foo);
14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangedTests.GeneratesOldAndNewWithParentDataType_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | string old_Foo = this._foo;
11 | this._foo = value;
12 | string new_Foo = this._foo;
13 | this.OnFooChanged(old_Foo, new_Foo);
14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.DoesNotCallInaccessibleAlsoNotifyOnBaseClass_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar);
12 | this._foo = value;
13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.DoesNotGenerateAlsoNotifyWithPropertyOnBaseClassAndMethodOnDerived_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar);
12 | this._foo = value;
13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GenerateParameterlessRaise_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this.OnFooChanging();
11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
12 | this._foo = value;
13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
14 | }
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesAlsoNotifyCallableParameterless_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
11 | this.OnBarChanging();
12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar);
13 | this._foo = value;
14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
16 | }
17 | }
18 | }
19 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesAlsoNotifyCallableParameters_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | int old_Bar = this.Bar;
11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
12 | this.OnBarChanging(old_Bar);
13 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar);
14 | this._foo = value;
15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
16 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
17 | }
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesAlsoNotifyOnBaseClass_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | int old_Bar = this.Bar;
11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
12 | this.OnBarChanging(old_Bar);
13 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar);
14 | this._foo = value;
15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
16 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
17 | }
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesAutoDependsOn_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | int old_Bar = this.Bar;
11 | string old_Baz = this.Baz;
12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
13 | this.OnBarChanging(old_Bar);
14 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar);
15 | this.OnBazChanging(old_Baz);
16 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Baz);
17 | this._foo = value;
18 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
19 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
20 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Baz);
21 | }
22 | }
23 | }
24 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesDependsOn_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public int Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | int old_Bar = this.Bar;
11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
12 | this.OnBarChanging(old_Bar);
13 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar);
14 | this._foo = value;
15 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
16 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
17 | }
18 | }
19 | }
20 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesOldAndNewWithParentDataType_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | string old_Foo = this._foo;
11 | this.OnFooChanging(old_Foo);
12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
13 | this._foo = value;
14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/OnPropertyNameChangingTests.GeneratesOldWithMatchingDataType_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | string old_Foo = this._foo;
11 | this.OnFooChanging(old_Foo);
12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
13 | this._foo = value;
14 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
15 | }
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyAttributeTests.AddsLeadingAndTrailingBracketsIfOmitted_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | [System.Xml.Serialization.XmlIgnore]
4 | public string Foo { get; set; }
5 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyAttributeTests.CopiesAttributeOntoGeneratedProperty_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | [System.Xml.Serialization.XmlIgnore]
4 | public string Foo { get; set; }
5 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyAttributeTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using System.Xml.Serialization;
7 | using NUnit.Framework;
8 | using PropertyChanged.SourceGenerator.UnitTests.Framework;
9 |
10 | namespace PropertyChanged.SourceGenerator.UnitTests;
11 |
12 | [TestFixture]
13 | public class PropertyAttributeTests : TestsBase
14 | {
15 | [Test]
16 | public void CopiesAttributeOntoGeneratedProperty()
17 | {
18 | string input = """
19 | public partial class SomeViewModel
20 | {
21 | [PropertyAttribute("[System.Xml.Serialization.XmlIgnore]")]
22 | [Notify] private string _foo;
23 | }
24 | """;
25 |
26 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters));
27 | }
28 |
29 | [Test]
30 | public void AddsLeadingAndTrailingBracketsIfOmitted()
31 | {
32 | string input = """
33 | public partial class SomeViewModel
34 | {
35 | [PropertyAttribute("System.Xml.Serialization.XmlIgnore")]
36 | [Notify] private string _foo;
37 | }
38 | """;
39 |
40 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters));
41 | }
42 |
43 | [Test]
44 | public void WarnsIfAttributePlacedOnMemberWithoutNotify()
45 | {
46 | string input = """
47 | public partial class SomeViewModel
48 | {
49 | [PropertyAttribute("System.Xml.Serialization.XmlIgnore")]
50 | private string _foo;
51 | }
52 | """;
53 | this.AssertThat(input, It.HasDiagnostics(
54 | // (3,6): Warning INPC008: [AlsoNotify] is only valid on members which also have [Notify]. Skipping
55 | // PropertyAttribute("System.Xml.Serialization.XmlIgnore")
56 | Diagnostic("INPC008", @"PropertyAttribute(""System.Xml.Serialization.XmlIgnore"")").WithLocation(3, 6)
57 | ));
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyChanged.SourceGenerator.UnitTests.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7
5 |
6 | false
7 | enable
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | all
24 | runtime; build; native; contentfiles; analyzers; buildtransitive
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsCollisionWithExplicitNamesInBaseClass_Base.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Base
2 | {
3 | public int Prop { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsCollisionWithExplicitNamesInBaseClass_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsCollisionWithTwoExplicitNames_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsNameCollisionExplicit_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsNameCollisionInBaseClass_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.ReportsNameCollisionInferred_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.UsesExplicitNameIfGiven_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Prop { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/PropertyNameTests.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using NUnit.Framework;
7 | using PropertyChanged.SourceGenerator.UnitTests.Framework;
8 |
9 | namespace PropertyChanged.SourceGenerator.UnitTests;
10 |
11 | [TestFixture]
12 | public class PropertyNameTests : TestsBase
13 | {
14 | [Test]
15 | public void UsesExplicitNameIfGiven()
16 | {
17 | string input = """
18 | public partial class SomeViewModel
19 | {
20 | [Notify("Prop")]
21 | private string _foo;
22 | }
23 | """;
24 |
25 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters));
26 | }
27 |
28 | [Test]
29 | public void ReportsNameCollisionInferred()
30 | {
31 | string input = """
32 | public partial class SomeViewModel
33 | {
34 | [Notify]
35 | private string _foo;
36 | public string Foo;
37 | }
38 | """;
39 |
40 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)
41 | .HasDiagnostics(
42 | // (4,20): Warning INPC003: Attempted to generate property 'Foo' for member '_foo', but a member with that name already exists. Skipping this property
43 | // _foo
44 | Diagnostic("INPC003", @"_foo").WithLocation(4, 20)
45 | ));
46 | }
47 |
48 | [Test]
49 | public void ReportsNameCollisionExplicit()
50 | {
51 | string input = """
52 | public partial class SomeViewModel
53 | {
54 | [Notify("Prop")]
55 | private string _foo;
56 | public string Prop;
57 | }
58 | """;
59 |
60 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)
61 | .HasDiagnostics(
62 | // (4,20): Warning INPC003: Attempted to generate property 'Prop' for member '_foo', but a member with that name already exists. Skipping this property
63 | // _foo
64 | Diagnostic("INPC003", @"_foo").WithLocation(4, 20)
65 | ));
66 | }
67 |
68 | [Test]
69 | public void ReportsNameCollisionInBaseClass()
70 | {
71 | string input = """
72 | public partial class SomeViewModelBase
73 | {
74 | public string Prop;
75 | }
76 | public partial class SomeViewModel : SomeViewModelBase
77 | {
78 | [Notify("Prop")]
79 | private string _foo;
80 | }
81 | """;
82 |
83 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)
84 | .HasDiagnostics(
85 | // (8,20): Warning INPC003: Attempted to generate property 'Prop' for member '_foo', but a member with that name already exists. Skipping this property
86 | // _foo
87 | Diagnostic("INPC003", @"_foo").WithLocation(8, 20)
88 | ));
89 | }
90 |
91 | [Test]
92 | public void ReportsCollisionWithTwoExplicitNames()
93 | {
94 | string input = """
95 | public partial class SomeViewModel
96 | {
97 | [Notify("Prop")]
98 | private string _foo;
99 | [Notify("Prop")]
100 | private string _bar;
101 | }
102 | """;
103 |
104 | this.AssertThat(input, It.HasFile("SomeViewModel", StandardRewriters)
105 | .HasDiagnostics(
106 | // (4,20): Warning INPC004: Member '_foo' will have the same generated property name 'Prop' as member '_bar'
107 | // _foo
108 | Diagnostic("INPC004", @"_foo").WithLocation(4, 20),
109 |
110 | // (6,20): Warning INPC004: Member '_bar' will have the same generated property name 'Prop' as member '_foo'
111 | // _bar
112 | Diagnostic("INPC004", @"_bar").WithLocation(6, 20)
113 | ));
114 | }
115 |
116 | [Test]
117 | public void ReportsCollisionWithExplicitNamesInBaseClass()
118 | {
119 | string input = """
120 | public partial class Base
121 | {
122 | [Notify("Prop")]
123 | private int _foo;
124 | }
125 | public partial class Derived : Base
126 | {
127 | [Notify("Prop")]
128 | private int _bar;
129 | }
130 | """;
131 |
132 | this.AssertThat(input, It
133 | .HasFile("Base", StandardRewriters)
134 | .HasFile("Derived", StandardRewriters)
135 | .HasDiagnostics(
136 | // (9,17): Warning INPC003: Attempted to generate property 'Prop' for member '_bar', but a member with that name already exists. Skipping this property
137 | // _bar
138 | Diagnostic("INPC003", @"_bar").WithLocation(9, 17)
139 | ));
140 | }
141 | }
142 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.DoesNotGenerateInpcInterfaceIfAlreadySpecified_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
4 | public string Foo { get; set; }
5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
6 | {
7 | this.PropertyChanged?.Invoke(this, eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsAndCallsMethodWithEventArgsAndOldAndNewValues_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | string old_Foo = this._foo;
11 | this._foo = value;
12 | string new_Foo = this._foo;
13 | this.NotifyPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo, old_Foo, new_Foo);
14 | }
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsAndCallsMethodWithEventArgs_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsAndCallsMethodWithStringNameAndOldAndNewValues_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | string old_Foo = this._foo;
11 | this._foo = value;
12 | string new_Foo = this._foo;
13 | this.NotifyPropertyChanged(@"Foo", old_Foo, new_Foo);
14 | }
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsAndCallsMethodWithStringName_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.NotifyPropertyChanged(@"Foo");
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsExplicitlyImplementedEvent_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.FindsGenericBaseClasses_B.verified.cs:
--------------------------------------------------------------------------------
1 | partial class B
2 | {
3 | public string Bar { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.GeneratesInpcInterfaceIfNotSpecified_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
4 | public string Foo { get; set; }
5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
6 | {
7 | this.PropertyChanged?.Invoke(this, eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.SearchesForMethodFromTopOfTypeHierarchy_B.verified.cs:
--------------------------------------------------------------------------------
1 | partial class B
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this._foo = value;
11 | this.NotifyOfPropertyChange(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.SearchesForMethodFromTopOfTypeHierarchy_C.verified.cs:
--------------------------------------------------------------------------------
1 | partial class C
2 | {
3 | public string Bar
4 | {
5 | get => this._bar;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._bar))
9 | {
10 | this._bar = value;
11 | this.OnPropertyChanged(@"Bar");
12 | }
13 | }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedCallTests.TakesNameFromPropertyChangingIfPossible_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
4 | public int Foo { get; set; }
5 | protected virtual void NotifyOfPropertyChange(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
6 | {
7 | this.PropertyChanged?.Invoke(this, eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CallsOnPropertyNameChangedNoOldAndNew_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue)
5 | {
6 | base.OnPropertyChanged(eventArgs, oldValue, newValue);
7 | switch (eventArgs.PropertyName)
8 | {
9 | case @"Foo":
10 | {
11 | string new_Bar = this.Bar;
12 | this.OnBarChanged();
13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar, (object)null, new_Bar);
14 | }
15 | break;
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CallsOnPropertyNameChangedOldAndNew_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue)
5 | {
6 | base.OnPropertyChanged(eventArgs, oldValue, newValue);
7 | switch (eventArgs.PropertyName)
8 | {
9 | case @"Foo":
10 | {
11 | string new_Bar = this.Bar;
12 | this.OnBarChanged(default(string), new_Bar);
13 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar, (object)null, new_Bar);
14 | }
15 | break;
16 | }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=internal.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | internal override void OnPropertyChanged(string propertyName)
5 | {
6 | base.OnPropertyChanged(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanged(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=private protected.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | private protected override void OnPropertyChanged(string propertyName)
5 | {
6 | base.OnPropertyChanged(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanged(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected internal.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected internal override void OnPropertyChanged(string propertyName)
5 | {
6 | base.OnPropertyChanged(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanged(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanged(string propertyName)
5 | {
6 | base.OnPropertyChanged(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanged(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=public.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | public override void OnPropertyChanged(string propertyName)
5 | {
6 | base.OnPropertyChanged(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanged(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.DefinesOverrideEventArgsNoOldAndNew_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
5 | {
6 | base.OnPropertyChanged(eventArgs);
7 | switch (eventArgs.PropertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.DefinesOverrideEventArgsOldAndNew_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs, object oldValue, object newValue)
5 | {
6 | base.OnPropertyChanged(eventArgs, oldValue, newValue);
7 | switch (eventArgs.PropertyName)
8 | {
9 | case @"Foo":
10 | {
11 | string new_Bar = this.Bar;
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar, (object)null, new_Bar);
13 | }
14 | break;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.DefinesOverrideStringNoOldAndNew_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanged(string propertyName)
5 | {
6 | base.OnPropertyChanged(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanged(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.DefinesOverrideStringOldAndNew_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanged(string propertyName, object oldValue, object newValue)
5 | {
6 | base.OnPropertyChanged(propertyName, oldValue, newValue);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | string new_Bar = this.Bar;
12 | this.OnPropertyChanged(@"Bar", (object)null, new_Bar);
13 | }
14 | break;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.DefinesVirtual_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
4 | public string Bar { get; set; }
5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
6 | {
7 | this.PropertyChanged?.Invoke(this, eventArgs);
8 | switch (eventArgs.PropertyName)
9 | {
10 | case @"Foo":
11 | {
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
13 | }
14 | break;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.GeneratesEventAndRaisePropertyChangedIfNotDefined_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
4 | public string Foo { get; set; }
5 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
6 | {
7 | this.PropertyChanged?.Invoke(this, eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.GeneratesRaisePropertyChangedIfNotDefined_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo { get; set; }
4 | protected virtual void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
5 | {
6 | this.PropertyChanged?.Invoke(this, eventArgs);
7 | }
8 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.HandlesSealedClass_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel : global::System.ComponentModel.INotifyPropertyChanged
2 | {
3 | public event global::System.ComponentModel.PropertyChangedEventHandler PropertyChanged;
4 | public string Foo { get; set; }
5 | private void OnPropertyChanged(global::System.ComponentModel.PropertyChangedEventArgs eventArgs)
6 | {
7 | this.PropertyChanged?.Invoke(this, eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangedDefinitionTests.RaisesIfNonVirtualBaseMethodAndOverrideRequired_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.DoesNotGenerateInpcInterfaceIfAlreadySpecified_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
4 | public string Foo { get; set; }
5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs)
6 | {
7 | this.PropertyChanging?.Invoke(this, eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.DoesNotGenerateInpcInterfaceIfNotSpecified_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsAndCallsMethodWithEventArgsAndOldValue_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | string old_Foo = this._foo;
11 | this.NotifyPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo, old_Foo);
12 | this._foo = value;
13 | this.NotifyPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
14 | }
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsAndCallsMethodWithEventArgs_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
11 | this._foo = value;
12 | this.OnPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsAndCallsMethodWithStringNameAndOldValue_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | string old_Foo = this._foo;
11 | this.NotifyPropertyChanging(@"Foo", old_Foo);
12 | this._foo = value;
13 | this.NotifyPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
14 | }
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsAndCallsMethodWithStringName_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this.NotifyPropertyChanging(@"Foo");
11 | this._foo = value;
12 | this.NotifyPropertyChanged(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsExplicitlyImplementedEvent_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.FindsGenericBaseClasses_B.verified.cs:
--------------------------------------------------------------------------------
1 | partial class B
2 | {
3 | public string Bar { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.SearchesForMethodFromTopOfTypeHierarchy_B.verified.cs:
--------------------------------------------------------------------------------
1 | partial class B
2 | {
3 | public string Foo
4 | {
5 | get => this._foo;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._foo))
9 | {
10 | this.NotifyOfPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Foo);
11 | this._foo = value;
12 | this.NotifyOfPropertyChange(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Foo);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.SearchesForMethodFromTopOfTypeHierarchy_C.verified.cs:
--------------------------------------------------------------------------------
1 | partial class C
2 | {
3 | public string Bar
4 | {
5 | get => this._bar;
6 | set
7 | {
8 | if (!global::System.Collections.Generic.EqualityComparer.Default.Equals(value, this._bar))
9 | {
10 | this.OnPropertyChanging(@"Bar");
11 | this._bar = value;
12 | this.NotifyOfPropertyChange(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanged_Bar);
13 | }
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingCallTests.TakesNameFromPropertyChangedIfPossible_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
4 | public int Foo { get; set; }
5 | protected virtual void RaisePropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs)
6 | {
7 | this.PropertyChanging?.Invoke(this, eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CallsOnPropertyNameChangingNoOld_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue)
5 | {
6 | base.OnPropertyChanging(eventArgs, oldValue);
7 | switch (eventArgs.PropertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnBarChanging();
12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar, (object)null);
13 | }
14 | break;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CallsOnPropertyNameChangingOld_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue)
5 | {
6 | base.OnPropertyChanging(eventArgs, oldValue);
7 | switch (eventArgs.PropertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnBarChanging(default(string));
12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar, (object)null);
13 | }
14 | break;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=internal.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | internal override void OnPropertyChanging(string propertyName)
5 | {
6 | base.OnPropertyChanging(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanging(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=private protected.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | private protected override void OnPropertyChanging(string propertyName)
5 | {
6 | base.OnPropertyChanging(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanging(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected internal.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected internal override void OnPropertyChanging(string propertyName)
5 | {
6 | base.OnPropertyChanging(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanging(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=protected.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanging(string propertyName)
5 | {
6 | base.OnPropertyChanging(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanging(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.CopiesBaseMethodAccessibility_Derived_accessibility=public.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | public override void OnPropertyChanging(string propertyName)
5 | {
6 | base.OnPropertyChanging(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanging(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DefinesOverrideEventArgsNoOld_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs)
5 | {
6 | base.OnPropertyChanging(eventArgs);
7 | switch (eventArgs.PropertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar);
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DefinesOverrideEventArgsOld_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs, object oldValue)
5 | {
6 | base.OnPropertyChanging(eventArgs, oldValue);
7 | switch (eventArgs.PropertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar, (object)null);
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DefinesOverrideStringNoOld_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanging(string propertyName)
5 | {
6 | base.OnPropertyChanging(propertyName);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanging(@"Bar");
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DefinesOverrideStringOld_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Bar { get; set; }
4 | protected override void OnPropertyChanging(string propertyName, object oldValue)
5 | {
6 | base.OnPropertyChanging(propertyName, oldValue);
7 | switch (propertyName)
8 | {
9 | case @"Foo":
10 | {
11 | this.OnPropertyChanging(@"Bar", (object)null);
12 | }
13 | break;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DefinesVirtual_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
4 | public string Bar { get; set; }
5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs)
6 | {
7 | this.PropertyChanging?.Invoke(this, eventArgs);
8 | switch (eventArgs.PropertyName)
9 | {
10 | case @"Foo":
11 | {
12 | this.OnPropertyChanging(global::PropertyChanged.SourceGenerator.Internal.EventArgsCache.PropertyChanging_Bar);
13 | }
14 | break;
15 | }
16 | }
17 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.DoesNothingIfTypeDoesNotImplementInterface_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.GeneratesEventAndRaisePropertyChangingIfNotDefined_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public event global::System.ComponentModel.PropertyChangingEventHandler PropertyChanging;
4 | public string Foo { get; set; }
5 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs)
6 | {
7 | this.PropertyChanging?.Invoke(this, eventArgs);
8 | }
9 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.GeneratesRaisePropertyChangingIfNotDefined_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo { get; set; }
4 | protected virtual void OnPropertyChanging(global::System.ComponentModel.PropertyChangingEventArgs eventArgs)
5 | {
6 | this.PropertyChanging?.Invoke(this, eventArgs);
7 | }
8 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/RaisePropertyChangingDefinitionTests.RaisesIfNonVirtualBaseMethodAndOverrideRequired_Derived.verified.cs:
--------------------------------------------------------------------------------
1 | partial class Derived
2 | {
3 | public string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.GeneratesNamespace_Test.Foo.SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | namespace Test.Foo
2 | {
3 | partial class SomeViewModel
4 | {
5 | public string Foo { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesBadlyNamedGenericTypes_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel<@class>
2 | {
3 | public string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesGenericTypesWithConstraints_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial class SomeViewModel
2 | {
3 | public string Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesPartialNestedTypes_A.B.C.verified.cs:
--------------------------------------------------------------------------------
1 | partial class A
2 | {
3 | partial class B
4 | {
5 | partial class C
6 | {
7 | public string Field { get; set; }
8 | }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesRecord_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial record SomeViewModel
2 | {
3 | public int Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesStruct_SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | partial struct SomeViewModel
2 | {
3 | public int Foo { get; set; }
4 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesTwoClassesSameNameDifferentNamespaces_NS1.SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | namespace NS1
2 | {
3 | partial class SomeViewModel
4 | {
5 | public string A { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.UnitTests/TypeGenerationTests.HandlesTwoClassesSameNameDifferentNamespaces_NS2.SomeViewModel.verified.cs:
--------------------------------------------------------------------------------
1 | namespace NS2
2 | {
3 | partial class SomeViewModel
4 | {
5 | public string A { get; set; }
6 | }
7 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.Vsix/PropertyChanged.SourceGenerator.Vsix.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | net472
7 | PropertyChanged.SourceGenerator.Vsix
8 | PropertyChanged.SourceGenerator.Vsix
9 |
10 |
11 |
12 | false
13 | false
14 | false
15 | false
16 | false
17 | false
18 | Roslyn
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | Program
27 | $(DevEnvDir)devenv.exe
28 | /rootsuffix $(VSSDKTargetPlatformRegRootSuffix)
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator.Vsix/source.extension.vsixmanifest:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | PropertyChanged.SourceGenerator
6 | This is a sample diagnostic extension for the .NET Compiler Platform ("Roslyn").
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/AlwaysFalseEqualityComparer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Runtime.CompilerServices;
4 | using System.Text;
5 |
6 | namespace PropertyChanged.SourceGenerator;
7 |
8 | public class AlwaysFalseEqualityComparer : IEqualityComparer
9 | {
10 | public static AlwaysFalseEqualityComparer Instance { get; } = new();
11 |
12 | public bool Equals(T x, T y) => false;
13 | public int GetHashCode(T obj) => RuntimeHelpers.GetHashCode(obj);
14 | }
15 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/AlsoNotifyMember.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.Text;
5 | using Microsoft.CodeAnalysis;
6 |
7 | namespace PropertyChanged.SourceGenerator.Analysis;
8 |
9 | public readonly record struct AlsoNotifyMember : IMember, IEquatable
10 | {
11 | public string? Name { get; }
12 | public string? FullyQualifiedTypeName { get; }
13 | [MemberNotNullWhen(true, nameof(FullyQualifiedTypeName))]
14 | public bool IsCallable => this.FullyQualifiedTypeName != null;
15 |
16 | public OnPropertyNameChangedInfo? OnPropertyNameChanged { get; }
17 | public OnPropertyNameChangedInfo? OnPropertyNameChanging { get; }
18 |
19 | private AlsoNotifyMember(
20 | string? name,
21 | ITypeSymbol? type,
22 | OnPropertyNameChangedInfo? onPropertyNameChanged,
23 | OnPropertyNameChangedInfo? onPropertyNameChanging)
24 | {
25 | this.Name = name;
26 | this.FullyQualifiedTypeName = type?.ToDisplayString(SymbolDisplayFormats.FullyQualifiedTypeName);
27 | this.OnPropertyNameChanged = onPropertyNameChanged;
28 | this.OnPropertyNameChanging = onPropertyNameChanging;
29 | }
30 |
31 | public static AlsoNotifyMember NonCallable(string? name) =>
32 | new(name, null, null, null);
33 |
34 | public static AlsoNotifyMember FromMemberAnalysis(MemberAnalysisBuilder memberAnalysis) =>
35 | new(memberAnalysis.Name, memberAnalysis.Type, memberAnalysis.OnPropertyNameChanged, memberAnalysis.OnPropertyNameChanging);
36 |
37 | public static AlsoNotifyMember FromProperty(
38 | IPropertySymbol property,
39 | (OnPropertyNameChangedInfo? onPropertyNameChanged, OnPropertyNameChangedInfo? onPropertyNameChanging) namedChangedInfo)
40 | {
41 | // If we have an explicitly-implemented property, use e.g. 'Foo' as the name, not ISomeInterface.Foo
42 | return new(
43 | property.ToDisplayString(SymbolDisplayFormats.SymbolName),
44 | property.Type,
45 | namedChangedInfo.onPropertyNameChanged,
46 | namedChangedInfo.onPropertyNameChanging);
47 | }
48 | }
49 |
50 | public class AlsoNotifyMemberNameOnlyComparer : IEqualityComparer
51 | {
52 | public static AlsoNotifyMemberNameOnlyComparer Instance { get; } = new();
53 |
54 | public bool Equals(AlsoNotifyMember x, AlsoNotifyMember y) => string.Equals(x.Name, y.Name, StringComparison.Ordinal);
55 | public int GetHashCode(AlsoNotifyMember obj) => obj.Name == null ? 0 : StringComparer.Ordinal.GetHashCode(obj.Name);
56 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/AnalyserInput.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.Immutable;
4 | using Microsoft.CodeAnalysis;
5 |
6 | namespace PropertyChanged.SourceGenerator.Analysis;
7 |
8 | public readonly struct AnalyserInput : IEquatable
9 | {
10 | public INamedTypeSymbol TypeSymbol { get; }
11 | // Member -> Attributes
12 | public Dictionary> Attributes { get; } = new(SymbolEqualityComparer.Default);
13 |
14 | public AnalyserInput(INamedTypeSymbol typeSymbol)
15 | {
16 | this.TypeSymbol = typeSymbol.OriginalDefinition;
17 | }
18 |
19 | public void Update(ISymbol member, ImmutableArray attributes)
20 | {
21 | if (!this.Attributes.TryGetValue(member, out var existingAttributes))
22 | {
23 | existingAttributes = new();
24 | this.Attributes.Add(member, existingAttributes);
25 | }
26 | // Avoid the box
27 | foreach (var attribute in attributes)
28 | {
29 | existingAttributes.Add(attribute);
30 | }
31 | }
32 |
33 | public override bool Equals(object obj) => obj is AnalyserInput other && this.Equals(other);
34 |
35 | public bool Equals(AnalyserInput other)
36 | {
37 | return SymbolEqualityComparer.Default.Equals(this.TypeSymbol, other.TypeSymbol);
38 | }
39 |
40 | public override int GetHashCode()
41 | {
42 | return SymbolEqualityComparer.Default.GetHashCode(this.TypeSymbol);
43 | }
44 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/Analyser_AlsoNotify.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Microsoft.CodeAnalysis;
6 | using static PropertyChanged.SourceGenerator.Analysis.Utils;
7 |
8 | namespace PropertyChanged.SourceGenerator.Analysis;
9 |
10 | public partial class Analyser
11 | {
12 | private void ResolveAlsoNotify(TypeAnalysisBuilder typeAnalysis, List baseTypeAnalyses)
13 | {
14 | // We've already warned if there are AlsoNotify attributes on members that we haven't analysed
15 | foreach (var member in typeAnalysis.Members)
16 | {
17 | var alsoNotifyAttributes = member.Attributes.Where(x => x.AttributeClass?.Name == "AlsoNotifyAttribute");
18 | foreach (var attribute in alsoNotifyAttributes)
19 | {
20 | var alsoNotifyValues = ExtractAttributeStringParams(attribute);
21 |
22 | // We only allow them to use the property name. If we didn't, consider:
23 | // 1. Derived class has the same property name as base class (shadowed)
24 | // 2. Derived class has the same field name as a base class (generating different properties)
25 | // 3. Derived class has a field with the same name as a base class' property
26 | // 4. Derived class has a property with the same name as a base class' field
27 |
28 | foreach (string? alsoNotify in alsoNotifyValues)
29 | {
30 | // Remember that we're probably, but not necessarily, notifying a property which we're also
31 | // generating.
32 | // Allow null and emptystring as special cases
33 | if (alsoNotify == member.Name)
34 | {
35 | this.diagnostics.ReportAlsoNotifyForSelf(alsoNotify, attribute, member.BackingMember);
36 | }
37 | else
38 | {
39 | AlsoNotifyMember? alsoNotifyMember = null;
40 | if (!string.IsNullOrEmpty(alsoNotify))
41 | {
42 | bool foundAlsoNotify = false;
43 | if (baseTypeAnalyses.Prepend(typeAnalysis)
44 | .SelectMany(x => x.Members)
45 | .FirstOrDefault(x => x.Name == alsoNotify)
46 | is { } foundMemberAnalysis)
47 | {
48 | foundAlsoNotify = true;
49 | alsoNotifyMember = AlsoNotifyMember.FromMemberAnalysis(foundMemberAnalysis);
50 | }
51 | else if (TypeAndBaseTypes(typeAnalysis.TypeSymbol)
52 | .SelectMany(x => x.GetMembers(alsoNotify!))
53 | .OfType()
54 | .FirstOrDefault(x => this.compilation.IsSymbolAccessibleWithin(x, typeAnalysis.TypeSymbol))
55 | is { } foundProperty)
56 | {
57 | foundAlsoNotify = true;
58 | alsoNotifyMember = AlsoNotifyMember.FromProperty(
59 | foundProperty,
60 | this.FindOnPropertyNameChangedMethod(typeAnalysis.TypeSymbol, foundProperty));
61 | }
62 | else if (alsoNotify!.EndsWith("[]"))
63 | {
64 | string indexerName = alsoNotify.Substring(0, alsoNotify.Length - "[]".Length);
65 | foundAlsoNotify = TypeAndBaseTypes(typeAnalysis.TypeSymbol)
66 | .SelectMany(x => x.GetMembers("this[]"))
67 | .OfType()
68 | .Any(x => x.IsIndexer && x.MetadataName == indexerName);
69 | }
70 |
71 | if (!foundAlsoNotify)
72 | {
73 | this.diagnostics.ReportAlsoNotifyPropertyDoesNotExist(alsoNotify!, attribute, member.BackingMember);
74 | }
75 | }
76 |
77 | alsoNotifyMember ??= AlsoNotifyMember.NonCallable(alsoNotify);
78 | member.AddAlsoNotify(alsoNotifyMember.Value);
79 | }
80 | }
81 | }
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/Analyser_IsChanged.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using Microsoft.CodeAnalysis;
6 |
7 | namespace PropertyChanged.SourceGenerator.Analysis;
8 |
9 | public partial class Analyser
10 | {
11 | public void ResoveInheritedIsChanged(TypeAnalysisBuilder typeAnalysis, List baseTypeAnalyses)
12 | {
13 | // Copy the parent's, if it's accessible
14 | if (baseTypeAnalyses.FirstOrDefault() is { IsChangedSetterIsPrivate: false } directParent)
15 | {
16 | typeAnalysis.IsChangedPropertyName = directParent.IsChangedPropertyName;
17 | typeAnalysis.IsChangedSetterIsPrivate = false;
18 | }
19 | }
20 |
21 | public void ResolveIsChangedMember(
22 | TypeAnalysisBuilder typeAnalysis,
23 | ISymbol member,
24 | IReadOnlyList attributes,
25 | MemberAnalysisBuilder? memberAnalysis)
26 | {
27 | if (attributes.FirstOrDefault(x => x.AttributeClass?.Name == "IsChangedAttribute") is { } attribute &&
28 | GetMemberType(member) is { } memberType)
29 | {
30 | // TODO: Think about if a derived class has an IsChanged property which shadows a base class?
31 | // We'll warn that we'll use the base one, but that's not true.
32 | // TODO: Think about overridden IsChanged properties?
33 | if (typeAnalysis.IsChangedPropertyName != null)
34 | {
35 | this.diagnostics.ReportMultipleIsChangedProperties(typeAnalysis.IsChangedPropertyName, attribute, member);
36 | }
37 | else if (memberType.SpecialType != SpecialType.System_Boolean)
38 | {
39 | this.diagnostics.ReportNonBooleanIsChangedProperty(member);
40 | }
41 | else if (memberAnalysis == null && member is IPropertySymbol { SetMethod: null })
42 | {
43 | this.diagnostics.ReportIsChangedDoesNotHaveSetter(member);
44 | }
45 | else
46 | {
47 | // If it's got [Notify] on it, use the generated property name
48 | if (memberAnalysis != null)
49 | {
50 | typeAnalysis.IsChangedPropertyName = memberAnalysis.Name;
51 | typeAnalysis.IsChangedSetterIsPrivate = memberAnalysis.SetterAccessibility == Accessibility.Private;
52 | }
53 | else
54 | {
55 | typeAnalysis.IsChangedPropertyName = member.Name;
56 | typeAnalysis.IsChangedSetterIsPrivate = member is IPropertySymbol { SetMethod.DeclaredAccessibility: Accessibility.Private };
57 | }
58 | }
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/IMember.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.Text;
5 | using Microsoft.CodeAnalysis;
6 |
7 | namespace PropertyChanged.SourceGenerator.Analysis;
8 |
9 | public interface IMember
10 | {
11 | [MemberNotNullWhen(true, nameof(FullyQualifiedTypeName))]
12 | [MemberNotNullWhen(true, nameof(Name))]
13 | string? FullyQualifiedTypeName { get; }
14 | string? Name { get; }
15 |
16 | OnPropertyNameChangedInfo? OnPropertyNameChanged { get; }
17 | OnPropertyNameChangedInfo? OnPropertyNameChanging { get; }
18 | }
19 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/InterfaceAnalysis.cs:
--------------------------------------------------------------------------------
1 | using System.Net.Cache;
2 | using Microsoft.CodeAnalysis;
3 |
4 | namespace PropertyChanged.SourceGenerator.Analysis;
5 |
6 | public record InterfaceAnalysis
7 | {
8 | public static InterfaceAnalysis Empty { get; } = new()
9 | {
10 | RequiresInterface = false,
11 | RequiresEvent = false,
12 | CanCallRaiseMethod = false,
13 | EventName = "",
14 | EventArgsFullyQualifiedTypeName = "",
15 | RaiseMethodType = default,
16 | RaiseMethodName = null,
17 | RaiseMethodSignature = default,
18 | OnAnyPropertyChangedOrChangingInfo = null,
19 | };
20 |
21 | ///
22 | /// True if we need to add this interface to the partial class's list
23 | ///
24 | public required bool RequiresInterface { get; init; }
25 |
26 | ///
27 | /// True if we need to add this event to the partial class
28 | ///
29 | public required bool RequiresEvent { get; init; }
30 |
31 | ///
32 | /// True if this method can be called, because either we're generating it, or because it's user-defined and accessible
33 | ///
34 | public required bool CanCallRaiseMethod { get; init; }
35 |
36 | ///
37 | /// The name of the event: PropertyChanged or PropertyChanging
38 | ///
39 | public required string EventName { get; init; }
40 |
41 | ///
42 | /// The fully-qualified type name of the EventArgs: global::System.ComponentModel.PropertyChangedEventArgs etc
43 | ///
44 | public required string EventArgsFullyQualifiedTypeName { get; init; }
45 |
46 | ///
47 | /// The type of method we're generating, or None if we're not generating it
48 | ///
49 | public required RaisePropertyChangedMethodType RaiseMethodType { get; init; }
50 |
51 | ///
52 | /// The name of the method to raise this event
53 | ///
54 | // TODO: Make this immutable
55 | public required string? RaiseMethodName { get; init; }
56 |
57 | ///
58 | /// The signature of the raise method
59 | ///
60 | public required RaisePropertyChangedOrChangingMethodSignature RaiseMethodSignature { get; init; }
61 |
62 | ///
63 | /// The signature of the 'OnAnyPropertyChanged' method, if any
64 | ///
65 | public required OnPropertyNameChangedInfo? OnAnyPropertyChangedOrChangingInfo { get; init; }
66 | }
67 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/MemberAnalysis.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using Microsoft.CodeAnalysis;
4 |
5 | namespace PropertyChanged.SourceGenerator.Analysis;
6 |
7 | public class MemberAnalysisBuilder
8 | {
9 | public ISymbol BackingMember { get; set; } = null!;
10 | public string Name { get; set; } = null!;
11 | public ITypeSymbol Type { get; set; } = null!;
12 | public bool IsVirtual { get; set; }
13 | public IReadOnlyList Attributes { get; set; } = null!;
14 | public NullableContextOptions? NullableContextOverride { get; set; }
15 | public Accessibility GetterAccessibility { get; set; }
16 | public Accessibility SetterAccessibility { get; set; }
17 | public OnPropertyNameChangedInfo? OnPropertyNameChanged { get; set; }
18 | public OnPropertyNameChangedInfo? OnPropertyNameChanging { get; set; }
19 | public List? AttributesForGeneratedProperty { get; set; }
20 |
21 | public string[]? DocComment { get; set; }
22 |
23 | private HashSet? alsoNotify;
24 |
25 | public void AddAlsoNotify(AlsoNotifyMember alsoNotify)
26 | {
27 | this.alsoNotify ??= new HashSet(AlsoNotifyMemberNameOnlyComparer.Instance);
28 | this.alsoNotify.Add(alsoNotify);
29 | }
30 |
31 | public MemberAnalysis Build()
32 | {
33 | return new MemberAnalysis()
34 | {
35 | Name = this.Name,
36 | FullyQualifiedTypeName = this.Type.ToDisplayString(SymbolDisplayFormats.FullyQualifiedTypeName),
37 | BackingMemberSymbolName = this.BackingMember.ToDisplayString(SymbolDisplayFormats.SymbolName),
38 | IsVirtual = this.IsVirtual,
39 | NullableContextOverride = this.NullableContextOverride,
40 | GetterAccessibility = this.GetterAccessibility,
41 | SetterAccessibility = this.SetterAccessibility,
42 | OnPropertyNameChanged = this.OnPropertyNameChanged,
43 | OnPropertyNameChanging = this.OnPropertyNameChanging,
44 | AttributesForGeneratedProperty = this.AttributesForGeneratedProperty == null ? ReadOnlyEquatableList.Empty : new ReadOnlyEquatableList(this.AttributesForGeneratedProperty),
45 | DocComment = this.DocComment == null ? ReadOnlyEquatableList.Empty : new ReadOnlyEquatableList(this.DocComment),
46 | AlsoNotify = this.alsoNotify == null
47 | ? ReadOnlyEquatableList.Empty
48 | : new ReadOnlyEquatableList(this.alsoNotify.OrderBy(x => x.Name).ToList()),
49 | };
50 | }
51 | }
52 |
53 | public class MemberAnalysis : IMember
54 | {
55 | public required string Name { get; init; }
56 | public required string FullyQualifiedTypeName { get; init; }
57 | public required string BackingMemberSymbolName { get; init; }
58 | public required bool IsVirtual { get; init; }
59 | public required NullableContextOptions? NullableContextOverride { get; init; }
60 | public required Accessibility GetterAccessibility { get; init; }
61 | public required Accessibility SetterAccessibility { get; init; }
62 | public required OnPropertyNameChangedInfo? OnPropertyNameChanged { get; init; }
63 | public required OnPropertyNameChangedInfo? OnPropertyNameChanging { get; init; }
64 | public required ReadOnlyEquatableList AttributesForGeneratedProperty { get; init; }
65 |
66 | public required ReadOnlyEquatableList DocComment { get; init; }
67 |
68 | public required ReadOnlyEquatableList AlsoNotify { get; init; }
69 | }
70 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/OnPropertyNameChangedInfo.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace PropertyChanged.SourceGenerator.Analysis;
6 |
7 | public record struct OnPropertyNameChangedInfo(
8 | string Name,
9 | bool HasOld,
10 | bool HasNew)
11 | {
12 | }
13 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/RaisePropertyChangedMethodType.cs:
--------------------------------------------------------------------------------
1 | namespace PropertyChanged.SourceGenerator.Analysis;
2 |
3 | public enum RaisePropertyChangedMethodType
4 | {
5 | None,
6 | Virtual,
7 | Override,
8 | NonVirtual,
9 | }
10 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/RaisePropertyChangedOrChangingMethodSignature.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using System.Threading;
5 | using Microsoft.CodeAnalysis;
6 |
7 | namespace PropertyChanged.SourceGenerator.Analysis;
8 |
9 | public enum RaisePropertyChangedOrChangingNameType
10 | {
11 | // The order here is used by RaisePropertyChangedOrChangingMethodSignature.CompareTo!
12 | String = 0,
13 | PropertyChangedEventArgs = 1,
14 | }
15 |
16 | public record struct RaisePropertyChangedOrChangingMethodSignature(
17 | RaisePropertyChangedOrChangingNameType NameType,
18 | bool HasOld,
19 | bool HasNew,
20 | Accessibility Accessibility)
21 | {
22 | }
23 |
24 | // This is a separate class because the comparison doesn't strictly align with RaisePropertyChangedOrChangingMethodSignature's Equals implementation
25 | public class RaisePropertyChangedOrChangingMethodSignatureBetternessComparer : IComparer
26 | {
27 | public static RaisePropertyChangedOrChangingMethodSignatureBetternessComparer Instance { get; } = new();
28 |
29 | public int Compare(RaisePropertyChangedOrChangingMethodSignature? left, RaisePropertyChangedOrChangingMethodSignature? right)
30 | {
31 | // Not-null is always better than null
32 | if (left == null)
33 | return right == null ? 0 : -1;
34 | if (right == null)
35 | return 1;
36 |
37 | var (x, y) = (left.Value, right.Value);
38 |
39 | // Methods which take PropertyChangedEventArgs are always better than those which don't
40 | int result = Comparer.Default.Compare(x.NameType, y.NameType);
41 | if (result != 0)
42 | {
43 | return result;
44 | }
45 |
46 | // Methods which take old and/or new are better than those that don't
47 | result = (x.HasOld | x.HasNew).CompareTo(y.HasOld | y.HasNew);
48 | if (result != 0)
49 | {
50 | return result;
51 | }
52 |
53 | // We don't care about accessibility when testing equality
54 | return 0;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/TypeAnalysis.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using Microsoft.CodeAnalysis;
5 |
6 | namespace PropertyChanged.SourceGenerator.Analysis;
7 |
8 | public class TypeAnalysisBuilder
9 | {
10 | public required INamedTypeSymbol TypeSymbol { get; init; }
11 |
12 | public bool CanGenerate { get; set; }
13 | public bool HadException { get; set; }
14 |
15 | public InterfaceAnalysis? INotifyPropertyChanged { get; set; }
16 | public InterfaceAnalysis? INotifyPropertyChanging { get; set; }
17 |
18 | public string? IsChangedPropertyName { get; set; }
19 | public bool IsChangedSetterIsPrivate { get; set; }
20 | public List Members { get; } = new();
21 | public NullableContextOptions NullableContext { get; set; }
22 |
23 | private HashSet<(string baseProperty, AlsoNotifyMember notifyProperty)>? baseDependsOn;
24 | public void AddDependsOn(string baseProperty, AlsoNotifyMember notifyProperty)
25 | {
26 | this.baseDependsOn ??= new();
27 | this.baseDependsOn.Add((baseProperty, notifyProperty));
28 | }
29 |
30 | public TypeAnalysis Build()
31 | {
32 | string? containingNamespace = null;
33 | if (this.TypeSymbol.ContainingNamespace is { IsGlobalNamespace: false } @namespace)
34 | {
35 | containingNamespace = @namespace.ToDisplayString(SymbolDisplayFormats.Namespace);
36 | }
37 |
38 | var outerTypes = new List();
39 | for (var outerType = this.TypeSymbol.ContainingType; outerType != null; outerType = outerType.ContainingType)
40 | {
41 | outerTypes.Add(outerType.ToDisplayString(SymbolDisplayFormats.TypeDeclaration));
42 | }
43 |
44 | return new()
45 | {
46 | TypeDeclaration = this.TypeSymbol.ToDisplayString(SymbolDisplayFormats.TypeDeclaration),
47 | TypeNameForGeneratedFileName = this.TypeSymbol.ToDisplayString(SymbolDisplayFormats.GeneratedFileName),
48 | ContainingNamespace = containingNamespace,
49 | OuterTypes = new ReadOnlyEquatableList(outerTypes),
50 | INotifyPropertyChanged = this.INotifyPropertyChanged ?? throw new ArgumentNullException(nameof(this.INotifyPropertyChanged)),
51 | INotifyPropertyChanging = this.INotifyPropertyChanging ?? throw new ArgumentNullException(nameof(this.INotifyPropertyChanging)),
52 | IsChangedPropertyName = this.IsChangedPropertyName,
53 | IsChangedSetterIsPrivate = this.IsChangedSetterIsPrivate,
54 | Members = new ReadOnlyEquatableList(this.Members.Select(x => x.Build()).ToList()),
55 | NullableContext = this.NullableContext,
56 | BaseDependsOn = this.baseDependsOn == null
57 | ? ReadOnlyEquatableList<(string, AlsoNotifyMember)>.Empty
58 | : new ReadOnlyEquatableList<(string, AlsoNotifyMember)>(this.baseDependsOn.OrderBy(x => x.baseProperty).ThenBy(x => x.notifyProperty.Name).ToList()),
59 | };
60 | }
61 | }
62 |
63 | public record TypeAnalysis
64 | {
65 | public required string TypeDeclaration { get; init; }
66 | public required string TypeNameForGeneratedFileName { get; init; }
67 | public required string? ContainingNamespace { get; init; }
68 | public required ReadOnlyEquatableList OuterTypes { get; init; }
69 |
70 | public required InterfaceAnalysis INotifyPropertyChanged { get; init; }
71 | public required InterfaceAnalysis INotifyPropertyChanging { get; init; }
72 |
73 | public required string? IsChangedPropertyName { get; init; }
74 | public required bool IsChangedSetterIsPrivate { get; init; }
75 | public required ReadOnlyEquatableList Members { get; init; }
76 | public required NullableContextOptions NullableContext { get; init; }
77 |
78 | public required ReadOnlyEquatableList<(string baseProperty, AlsoNotifyMember notifyProperty)> BaseDependsOn { get; init; }
79 | }
80 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/TypeAnalysisLookups.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics.CodeAnalysis;
4 | using System.Linq;
5 | using System.Text;
6 | using Microsoft.CodeAnalysis;
7 |
8 | namespace PropertyChanged.SourceGenerator.Analysis;
9 |
10 | public class TypeAnalysisLookups
11 | {
12 | private readonly TypeAnalysisBuilder typeAnalysis;
13 | private readonly List baseTypeAnalyses;
14 |
15 | private Dictionary? nameLookup;
16 | private Dictionary? symbolLookup;
17 |
18 | private Dictionary? baseNameLookup;
19 |
20 |
21 | public TypeAnalysisLookups(TypeAnalysisBuilder typeAnalysis, List baseTypeAnalyses)
22 | {
23 | this.typeAnalysis = typeAnalysis;
24 | this.baseTypeAnalyses = baseTypeAnalyses;
25 | }
26 |
27 | public bool TryGetThisType(string name, [NotNullWhen(true)] out MemberAnalysisBuilder? memberAnalysis)
28 | {
29 | this.nameLookup ??= this.typeAnalysis.Members.ToDictionary(x => x.Name, x => x, StringComparer.Ordinal);
30 | return this.nameLookup.TryGetValue(name, out memberAnalysis);
31 | }
32 |
33 | public bool TryGetThisType(ISymbol symbol, [NotNullWhen(true)] out MemberAnalysisBuilder? memberAnalysis)
34 | {
35 | this.symbolLookup ??= this.typeAnalysis.Members.ToDictionary(x => x.BackingMember, x => x, SymbolEqualityComparer.Default);
36 | return this.symbolLookup.TryGetValue(symbol, out memberAnalysis);
37 | }
38 |
39 | public bool TryGetBaseType(string name, [NotNullWhen(true)] out MemberAnalysisBuilder? memberAnalysis)
40 | {
41 | if (this.baseNameLookup == null)
42 | {
43 | this.baseNameLookup = new(StringComparer.Ordinal);
44 | foreach (var baseType in this.baseTypeAnalyses)
45 | {
46 | foreach (var member in baseType.Members)
47 | {
48 | this.baseNameLookup.Add(member.Name, member);
49 | }
50 | }
51 | }
52 | return this.baseNameLookup.TryGetValue(name, out memberAnalysis);
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Analysis/Utils.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Microsoft.CodeAnalysis;
5 |
6 | namespace PropertyChanged.SourceGenerator.Analysis;
7 | public static class Utils
8 | {
9 | public static IEnumerable TypeAndBaseTypes(INamedTypeSymbol type)
10 | {
11 | // Stop at 'object': no point in analysing that
12 | for (var t = type; t != null && t.SpecialType != SpecialType.System_Object; t = t.BaseType)
13 | {
14 | yield return t;
15 | }
16 | }
17 |
18 | public static bool IsAccessibleNormalMethod(IMethodSymbol method, ITypeSymbol typeSymbol, Compilation compilation) =>
19 | !method.IsGenericMethod &&
20 | method.ReturnsVoid &&
21 | compilation.IsSymbolAccessibleWithin(method, typeSymbol);
22 |
23 | public static bool IsNormalParameter(IParameterSymbol parameter) =>
24 | parameter.RefKind == RefKind.None;
25 | }
26 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/BetterIndentedTextWriter.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.CodeDom.Compiler;
3 | using System.IO;
4 | using System.Runtime.CompilerServices;
5 | using System.Text;
6 | using Microsoft.CodeAnalysis.Text;
7 |
8 | namespace PropertyChanged.SourceGenerator;
9 |
10 | public class BetterIndentedTextWriter : IndentedTextWriter
11 | {
12 | public BetterIndentedTextWriter() : base(new StringWriter())
13 | {
14 | }
15 |
16 | public void Write([InterpolatedStringHandlerArgument("")] ref AppendInterpolatedStringHandler handler) { }
17 | public void WriteLine([InterpolatedStringHandlerArgument("")] ref AppendInterpolatedStringHandler handler) => this.WriteLine();
18 |
19 | public SourceText ToSourceText()
20 | {
21 | var stringWriter = (StringWriter)this.InnerWriter;
22 | var stringBuilder = stringWriter.GetStringBuilder();
23 | stringWriter.Close(); // Attempts to interact with it after this point will fail
24 | return new StringBuilderText(stringBuilder);
25 | }
26 |
27 | private class StringBuilderText : SourceText
28 | {
29 | private readonly StringBuilder stringBuilder;
30 |
31 | public StringBuilderText(StringBuilder stringBuilder)
32 | {
33 | this.stringBuilder = stringBuilder;
34 | }
35 | public override Encoding? Encoding => Encoding.UTF8;
36 |
37 | public override char this[int position] => this.stringBuilder[position];
38 |
39 | public override int Length => this.stringBuilder.Length;
40 |
41 | public override void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) =>
42 | this.stringBuilder.CopyTo(sourceIndex, destination, destinationIndex, count);
43 |
44 | public override string ToString(TextSpan span)
45 | {
46 | if (span.End > this.stringBuilder.Length)
47 | {
48 | throw new ArgumentOutOfRangeException(nameof(span));
49 | }
50 |
51 | return this.stringBuilder.ToString(span.Start, span.Length);
52 | }
53 | }
54 | }
55 |
56 | [InterpolatedStringHandler]
57 | public struct AppendInterpolatedStringHandler
58 | {
59 | private readonly BetterIndentedTextWriter writer;
60 |
61 | public AppendInterpolatedStringHandler(int literalLength, int formattedCount, BetterIndentedTextWriter writer)
62 | {
63 | this.writer = writer;
64 | }
65 |
66 | public void AppendLiteral(string value) => this.writer.Write(value);
67 | public void AppendFormatted(string? value) => this.writer.Write(value);
68 |
69 | public void AppendFormatted(T value)
70 | {
71 | if (value is not null)
72 | {
73 | this.writer.Write(value.ToString());
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Configuration.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.Immutable;
4 | using System.Text;
5 |
6 | namespace PropertyChanged.SourceGenerator;
7 |
8 | public enum Capitalisation
9 | {
10 | None,
11 | Uppercase,
12 | Lowercase,
13 | }
14 |
15 | public class Configuration
16 | {
17 | public ImmutableArray RaisePropertyChangedMethodNames { get; set; } = ImmutableArray.Create(
18 | "OnPropertyChanged", "RaisePropertyChanged", "NotifyOfPropertyChange", "NotifyPropertyChanged"
19 | );
20 |
21 | public ImmutableArray RaisePropertyChangingMethodNames { get; set; } = ImmutableArray.Create(
22 | "OnPropertyChanging", "RaisePropertyChanging", "NotifyOfPropertyChanging", "NotifyPropertyChanging"
23 | );
24 |
25 | public ImmutableArray RemovePrefixes { get; set; } = ImmutableArray.Create("_");
26 | public ImmutableArray RemoveSuffixes { get; set; } = ImmutableArray.Empty;
27 | public string? AddPrefix { get; set; } = null;
28 | public string? AddSuffix { get; set; } = null;
29 | public Capitalisation FirstLetterCapitalisation { get; set; } = Capitalisation.Uppercase;
30 | public bool EnableAutoNotify { get; set; } = true;
31 | }
32 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/ConfigurationParser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Collections.Immutable;
4 | using System.Net;
5 | using System.Reflection.Metadata.Ecma335;
6 | using System.Text;
7 | using Microsoft.CodeAnalysis;
8 | using Microsoft.CodeAnalysis.CSharp.Syntax;
9 | using Microsoft.CodeAnalysis.Diagnostics;
10 |
11 | namespace PropertyChanged.SourceGenerator;
12 |
13 | public class ConfigurationParser
14 | {
15 | private readonly AnalyzerConfigOptionsProvider optionsProvider;
16 |
17 | public ConfigurationParser(AnalyzerConfigOptionsProvider optionsProvider)
18 | {
19 | this.optionsProvider = optionsProvider;
20 | }
21 |
22 | public Configuration Parse(SyntaxTree? syntaxTree, DiagnosticReporter diagnostics)
23 | {
24 | var options = syntaxTree == null
25 | ? this.optionsProvider.GlobalOptions
26 | : this.optionsProvider.GetOptions(syntaxTree);
27 | var config = new Configuration();
28 |
29 | if (options.TryGetValue("propertychanged.onpropertychanged_method_name", out string? changedMethodName))
30 | {
31 | config.RaisePropertyChangedMethodNames = changedMethodName.Split(';').ToImmutableArray();
32 | }
33 | if (options.TryGetValue("propertychanged.onpropertychanging_method_name", out string? changingMethodName))
34 | {
35 | config.RaisePropertyChangingMethodNames = changingMethodName.Split(';').ToImmutableArray();
36 | }
37 | if (options.TryGetValue("propertychanged.remove_prefixes", out string? removePrefixes))
38 | {
39 | config.RemovePrefixes = removePrefixes.Split(';').ToImmutableArray();
40 | }
41 | if (options.TryGetValue("propertychanged.remove_suffixes", out string? removeSuffixes))
42 | {
43 | config.RemoveSuffixes = removeSuffixes.Split(';').ToImmutableArray();
44 | }
45 | if (options.TryGetValue("propertychanged.add_prefix", out string? addPrefix))
46 | {
47 | config.AddPrefix = addPrefix;
48 | }
49 | if (options.TryGetValue("propertychanged.add_suffix", out string? addSuffix))
50 | {
51 | config.AddSuffix = addSuffix;
52 | }
53 | if (options.TryGetValue("propertychanged.first_letter_capitalization", out string? firstLetterCapitalisation))
54 | {
55 | if (string.Equals(firstLetterCapitalisation, "none", StringComparison.OrdinalIgnoreCase))
56 | {
57 | config.FirstLetterCapitalisation = Capitalisation.None;
58 | }
59 | else if (string.Equals(firstLetterCapitalisation, "upper_case", StringComparison.OrdinalIgnoreCase))
60 | {
61 | config.FirstLetterCapitalisation = Capitalisation.Uppercase;
62 | }
63 | else if (string.Equals(firstLetterCapitalisation, "lower_case", StringComparison.OrdinalIgnoreCase))
64 | {
65 | config.FirstLetterCapitalisation = Capitalisation.Lowercase;
66 | }
67 | else
68 | {
69 | diagnostics.ReportUnknownFirstLetterCapitalisation(firstLetterCapitalisation);
70 | }
71 | }
72 | config.EnableAutoNotify = this.ParseBool(options, diagnostics, "propertychanged.auto_notify", true);
73 |
74 | return config;
75 | }
76 |
77 | private bool ParseBool(AnalyzerConfigOptions options, DiagnosticReporter diagnostics, string key, bool defaultValue)
78 | {
79 | if (options.TryGetValue(key, out string? value))
80 | {
81 | if (string.Equals(value, "true", StringComparison.OrdinalIgnoreCase))
82 | {
83 | return true;
84 | }
85 | if (string.Equals(value, "false", StringComparison.OrdinalIgnoreCase))
86 | {
87 | return false;
88 | }
89 |
90 | diagnostics.ReportCannotParseConfigBool(key, value);
91 | return defaultValue;
92 | }
93 | else
94 | {
95 | return defaultValue;
96 | }
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/EquatableArray.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Collections.Immutable;
5 | using System.Runtime.CompilerServices;
6 |
7 | namespace PropertyChanged.SourceGenerator;
8 |
9 | // Adapted from https://github.com/CommunityToolkit/dotnet/blob/63ba418b6fc86c9c9098ad119bd5e1d82e82ea4b/src/CommunityToolkit.Mvvm.SourceGenerators/Helpers/EquatableArray%7BT%7D.cs
10 |
11 | public static class EquatableArray
12 | {
13 | public static EquatableArray AsEquatableArray(this ImmutableArray array) where T : IEquatable => new(array);
14 | }
15 |
16 | public readonly struct EquatableArray : IEquatable>, IEnumerable
17 | where T : IEquatable
18 | {
19 | private readonly T[]? array;
20 |
21 | public static EquatableArray Empty { get; } = new(ImmutableArray.Empty);
22 |
23 | public EquatableArray(ImmutableArray array)
24 | {
25 | this.array = Unsafe.As, T[]?>(ref array);
26 | }
27 |
28 | public bool Equals(EquatableArray array)
29 | {
30 | return this.AsSpan().SequenceEqual(array.AsSpan());
31 | }
32 |
33 | public override bool Equals(object obj) => obj is EquatableArray other && this.Equals(other);
34 |
35 | public override int GetHashCode()
36 | {
37 | if (this.array == null)
38 | {
39 | return 0;
40 | }
41 |
42 | int hashCode = -658986029;
43 |
44 | foreach (var item in this.array)
45 | {
46 | hashCode = hashCode * -1521134295 + item.GetHashCode();
47 | }
48 |
49 | return hashCode;
50 | }
51 |
52 | /// The from the current .
53 | [MethodImpl(MethodImplOptions.AggressiveInlining)]
54 | public ImmutableArray AsImmutableArray()
55 | {
56 | return Unsafe.As>(ref Unsafe.AsRef(in this.array));
57 | }
58 |
59 | public ReadOnlySpan AsSpan()
60 | {
61 | return this.AsImmutableArray().AsSpan();
62 | }
63 |
64 | public ImmutableArray.Enumerator GetEnumerator() => this.AsImmutableArray().GetEnumerator();
65 |
66 | IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.AsImmutableArray()).GetEnumerator();
67 |
68 | IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.AsImmutableArray()).GetEnumerator();
69 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/EventArgs/EventArgsCache.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Diagnostics.Tracing;
3 | using System.Linq;
4 | using System.Text;
5 | using Microsoft.CodeAnalysis;
6 |
7 | namespace PropertyChanged.SourceGenerator.EventArgs;
8 |
9 | public class EventArgsCache
10 | {
11 | private readonly SortedDictionary propertyNameToCacheName;
12 | private int? hashCode;
13 |
14 | public bool IsEmpty => this.propertyNameToCacheName.Count == 0;
15 |
16 | internal EventArgsCache(SortedDictionary propertyNameToCacheName)
17 | {
18 | this.propertyNameToCacheName = propertyNameToCacheName;
19 | }
20 |
21 | public IEnumerable<(string cacheName, string eventArgsTypeName, string? propertyName)> GetEntries()
22 | {
23 | foreach (var kvp in this.propertyNameToCacheName)
24 | {
25 | yield return (kvp.Value, kvp.Key.EventArgsTypeName, kvp.Key.PropertyName);
26 | }
27 | }
28 |
29 | public override bool Equals(object obj)
30 | {
31 | var other = obj as EventArgsCache;
32 | if (ReferenceEquals(this, other))
33 | return true;
34 | if (other is null)
35 | return false;
36 |
37 | // We cache hash codes, so this is cheap
38 | if (this.GetHashCode() != other.GetHashCode())
39 | return false;
40 |
41 | if (this.propertyNameToCacheName.Count != other.propertyNameToCacheName.Count)
42 | return false;
43 |
44 | foreach (var (a, b) in this.propertyNameToCacheName.Zip(other.propertyNameToCacheName, (a, b) => (a, b)))
45 | {
46 | if (a.Key != b.Key || a.Value != b.Value)
47 | {
48 | return false;
49 | }
50 | }
51 |
52 | return true;
53 | }
54 |
55 | public override int GetHashCode()
56 | {
57 | if (this.hashCode == null)
58 | {
59 | int hash = 17;
60 | foreach (var kvp in this.propertyNameToCacheName)
61 | {
62 | unchecked
63 | {
64 | hash = hash * 23 + kvp.Key.GetHashCode();
65 | hash = hash * 23 + kvp.Value.GetHashCode();
66 | }
67 | }
68 |
69 | this.hashCode = hash;
70 | }
71 |
72 | return this.hashCode.Value;
73 | }
74 | }
75 |
76 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/EventArgs/EventArgsCacheBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text.RegularExpressions;
4 |
5 | namespace PropertyChanged.SourceGenerator.EventArgs;
6 |
7 | public class EventArgsCacheBuilder
8 | {
9 | // propertyName: The string passed to PropertyChangedEventArgs being cached
10 | // cacheName: The name of the generated property on the EventArgs cache
11 |
12 | // Collection of all property names we need to add to the cache
13 | // We use a sorted dictionary to make equality comparisons easier
14 | private readonly SortedDictionary propertyNameToCacheName = new();
15 | private readonly HashSet cacheNames = new(StringComparer.Ordinal);
16 |
17 | // Collection of just the property names which collided with something else, so we needed to adjust them
18 | private SortedDictionary? collidedPropertyNameToCacheNames;
19 |
20 | public void Add(string? propertyName, string eventName, string eventArgsFullyQualifiedTypeName)
21 | {
22 | var key = new EventArgsCacheKey(propertyName, eventArgsFullyQualifiedTypeName);
23 | if (this.propertyNameToCacheName.ContainsKey(key))
24 | {
25 | return;
26 | }
27 |
28 | string safeName = EventArgsCacheLookup.Transform(propertyName, eventName);
29 |
30 | string cacheName = safeName;
31 | for (int i = 1; this.cacheNames.Contains(cacheName); i++)
32 | {
33 | cacheName = safeName + i;
34 | }
35 |
36 | if (cacheName != safeName)
37 | {
38 | this.collidedPropertyNameToCacheNames ??= new();
39 | this.collidedPropertyNameToCacheNames.Add(key, cacheName);
40 | }
41 |
42 | this.propertyNameToCacheName.Add(key, cacheName);
43 | this.cacheNames.Add(cacheName);
44 | }
45 |
46 | public (EventArgsCache cache, EventArgsCacheLookup lookup) ToCacheAndLookup() => (new(this.propertyNameToCacheName), new(this.collidedPropertyNameToCacheNames));
47 | }
48 |
49 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/EventArgs/EventArgsCacheKey.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace PropertyChanged.SourceGenerator.EventArgs;
4 |
5 | internal readonly struct EventArgsCacheKey : IEquatable, IComparable
6 | {
7 | public string? PropertyName { get; }
8 | public string EventArgsTypeName { get; }
9 |
10 | public EventArgsCacheKey(string? propertyName, string eventArgsTypeName)
11 | {
12 | this.PropertyName = propertyName;
13 | this.EventArgsTypeName = eventArgsTypeName;
14 | }
15 |
16 | public int CompareTo(EventArgsCacheKey other)
17 | {
18 | int result = StringComparer.Ordinal.Compare(this.PropertyName, other.PropertyName);
19 | if (result != 0)
20 | return result;
21 |
22 | result = string.Compare(this.EventArgsTypeName, other.EventArgsTypeName);
23 | return result;
24 | }
25 |
26 | public bool Equals(EventArgsCacheKey other)
27 | {
28 | return string.Equals(this.PropertyName, other.PropertyName, StringComparison.Ordinal) &&
29 | this.EventArgsTypeName == other.EventArgsTypeName;
30 | }
31 |
32 | public override int GetHashCode()
33 | {
34 | unchecked
35 | {
36 | int hash = 17;
37 | hash = hash * 23 + (this.PropertyName == null ? 0 : StringComparer.Ordinal.GetHashCode(this.PropertyName));
38 | hash = hash * 23 + this.EventArgsTypeName.GetHashCode();
39 | return hash;
40 | }
41 | }
42 |
43 | public override bool Equals(object? obj) => obj is EventArgsCacheKey other && this.Equals(other);
44 |
45 | public static bool operator ==(EventArgsCacheKey left, EventArgsCacheKey right) => left.Equals(right);
46 | public static bool operator !=(EventArgsCacheKey left, EventArgsCacheKey right) => !left.Equals(right);
47 | public static bool operator <(EventArgsCacheKey left, EventArgsCacheKey right) => left.CompareTo(right) < 0;
48 | public static bool operator <=(EventArgsCacheKey left, EventArgsCacheKey right) => left.CompareTo(right) <= 0;
49 | public static bool operator >(EventArgsCacheKey left, EventArgsCacheKey right) => left.CompareTo(right) > 0;
50 | public static bool operator >=(EventArgsCacheKey left, EventArgsCacheKey right) => left.CompareTo(right) >= 0;
51 | }
52 |
53 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/EventArgs/EventArgsCacheLookup.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Text.RegularExpressions;
7 |
8 | namespace PropertyChanged.SourceGenerator.EventArgs;
9 |
10 | public struct EventArgsCacheLookup : IEquatable
11 | {
12 | private static readonly Regex invalidCharsRegex = new(@"(^[^a-zA-Z])|([^a-zA-Z0-9])");
13 |
14 |
15 | private readonly SortedDictionary? collidedPropertyNameToCacheNames;
16 |
17 | internal EventArgsCacheLookup(SortedDictionary? collidedPropertyNameToCacheNames)
18 | {
19 | this.collidedPropertyNameToCacheNames = collidedPropertyNameToCacheNames;
20 | }
21 |
22 | public string Get(string? propertyName, string eventName, string eventArgsFullyQualifiedTypeName)
23 | {
24 | if (this.collidedPropertyNameToCacheNames != null &&
25 | this.collidedPropertyNameToCacheNames.TryGetValue(new EventArgsCacheKey(propertyName, eventArgsFullyQualifiedTypeName), out string cacheName))
26 | {
27 | return cacheName;
28 | }
29 |
30 | // Else, there were no collisions when using the default transformation
31 | return Transform(propertyName, eventName);
32 | }
33 |
34 | internal static string Transform(string? propertyName, string eventName)
35 | {
36 | return $"{eventName}_" + propertyName switch
37 | {
38 | null => "Null",
39 | "" => "Empty",
40 | "Item[]" => "Item",
41 | // Reserve the names 'Null' and 'Empty' for actual null/empty strings, and 'Item' for indexers
42 | "Null" => "Null1",
43 | "Empty" => "Empty1",
44 | "Item" => "Item1",
45 | _ => invalidCharsRegex.Replace(propertyName, "_"),
46 | };
47 | }
48 |
49 | public bool Equals(EventArgsCacheLookup other)
50 | {
51 | if (ReferenceEquals(this.collidedPropertyNameToCacheNames, other.collidedPropertyNameToCacheNames))
52 | return true;
53 | if ((this.collidedPropertyNameToCacheNames == null) != (other.collidedPropertyNameToCacheNames == null))
54 | return false;
55 |
56 | // If both or either are null, we'd have returned by now
57 | if (this.collidedPropertyNameToCacheNames!.Count != other.collidedPropertyNameToCacheNames!.Count)
58 | return false;
59 |
60 | foreach (var (a, b) in this.collidedPropertyNameToCacheNames.Zip(other.collidedPropertyNameToCacheNames, (a, b) => (a, b)))
61 | {
62 | if (a.Key != b.Key || a.Value != b.Value)
63 | {
64 | return false;
65 | }
66 | }
67 |
68 | return true;
69 | }
70 |
71 | public override bool Equals(object? obj) => obj is EventArgsCacheLookup other && this.Equals(other);
72 |
73 | public override int GetHashCode()
74 | {
75 | if (this.collidedPropertyNameToCacheNames == null)
76 | return 0;
77 |
78 | int hash = 17;
79 | foreach (var kvp in this.collidedPropertyNameToCacheNames)
80 | {
81 | unchecked
82 | {
83 | hash = hash * 23 + kvp.Key.GetHashCode();
84 | hash = hash * 23 + kvp.Value.GetHashCode();
85 | }
86 | }
87 | return hash;
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/EventArgsCacheGenerator.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.CodeDom.Compiler;
3 | using System.Collections.Generic;
4 | using System.IO;
5 | using System.Text;
6 | using Microsoft.CodeAnalysis.Text;
7 | using PropertyChanged.SourceGenerator;
8 | using PropertyChanged.SourceGenerator.EventArgs;
9 |
10 | public class EventArgsCacheGenerator
11 | {
12 | public const string EventArgsCacheName = "EventArgsCache";
13 |
14 | private readonly BetterIndentedTextWriter writer = new();
15 | private readonly EventArgsCache eventArgsCache;
16 |
17 | public EventArgsCacheGenerator(EventArgsCache eventArgsCache)
18 | {
19 | this.eventArgsCache = eventArgsCache;
20 | }
21 |
22 | public void GenerateNameCache()
23 | {
24 | this.writer.WriteLine("namespace PropertyChanged.SourceGenerator.Internal");
25 | this.writer.WriteLine("{");
26 | this.writer.Indent++;
27 |
28 | this.writer.WriteLine($"internal static class {EventArgsCacheName}");
29 | this.writer.WriteLine("{");
30 | this.writer.Indent++;
31 |
32 | var backingFieldNames = new HashSet();
33 | foreach (var (cacheName, eventArgsTypeName, propertyName) in this.eventArgsCache.GetEntries())
34 | {
35 | string backingFieldName = "_" + cacheName;
36 | for (int i = 0; !backingFieldNames.Add(backingFieldName); i++)
37 | {
38 | backingFieldName = $"_{cacheName}{i}";
39 | }
40 | this.writer.WriteLine($"private static {eventArgsTypeName} {backingFieldName};");
41 | this.writer.WriteLine($"public static {eventArgsTypeName} {cacheName} => " +
42 | $"{backingFieldName} ??= new {eventArgsTypeName}({EscapeString(propertyName)});");
43 | }
44 |
45 | this.writer.Indent--;
46 | this.writer.WriteLine("}");
47 |
48 | this.writer.Indent--;
49 | this.writer.WriteLine("}");
50 | }
51 |
52 | private static string EscapeString(string? str)
53 | {
54 | return str == null
55 | ? "null"
56 | : "@\"" + str.Replace("\"", "\"\"") + "\"";
57 | }
58 |
59 | public SourceText ToSourceText() => this.writer.ToSourceText();
60 | }
61 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Polyfills/InterpolatedStringHandlerArgumentAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace System.Runtime.CompilerServices;
6 |
7 | /// Indicates which arguments to a method involving an interpolated string handler should be passed to that handler.
8 | [AttributeUsage(AttributeTargets.Parameter, AllowMultiple = false, Inherited = false)]
9 | public sealed class InterpolatedStringHandlerArgumentAttribute : Attribute
10 | {
11 | /// Initializes a new instance of the class.
12 | /// The name of the argument that should be passed to the handler.
13 | /// The empty string may be used as the name of the receiver in an instance method.
14 | public InterpolatedStringHandlerArgumentAttribute(string argument) => Arguments = new string[] { argument };
15 |
16 | /// Initializes a new instance of the class.
17 | /// The names of the arguments that should be passed to the handler.
18 | /// The empty string may be used as the name of the receiver in an instance method.
19 | public InterpolatedStringHandlerArgumentAttribute(params string[] arguments) => Arguments = arguments;
20 |
21 | /// Gets the names of the arguments that should be passed to the handler.
22 | /// The empty string may be used as the name of the receiver in an instance method.
23 | public string[] Arguments { get; }
24 | }
25 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Polyfills/InterpolatedStringHandlerAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace System.Runtime.CompilerServices;
6 |
7 | /// Indicates the attributed type is to be used as an interpolated string handler.
8 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, AllowMultiple = false, Inherited = false)]
9 | public sealed class InterpolatedStringHandlerAttribute : Attribute
10 | {
11 | /// Initializes the .
12 | public InterpolatedStringHandlerAttribute() { }
13 | }
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Polyfills/NullableAttributes.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 |
5 | namespace System.Diagnostics.CodeAnalysis;
6 |
7 | [AttributeUsage(AttributeTargets.Parameter, Inherited = false)]
8 | internal sealed class NotNullWhenAttribute : Attribute
9 | {
10 | /// Initializes the attribute with the specified return value condition.
11 | ///
12 | /// The return value condition. If the method returns this value, the associated parameter will not be null.
13 | ///
14 | public NotNullWhenAttribute(bool returnValue) => this.ReturnValue = returnValue;
15 |
16 | /// Gets the return value condition.
17 | public bool ReturnValue { get; }
18 | }
19 |
20 | [AttributeUsage(AttributeTargets.Method | AttributeTargets.Property, Inherited = false, AllowMultiple = true)]
21 | internal sealed class MemberNotNullWhenAttribute : Attribute
22 | {
23 | /// Initializes the attribute with the specified return value condition and a field or property member.
24 | ///
25 | /// The return value condition. If the method returns this value, the associated parameter will not be null.
26 | ///
27 | ///
28 | /// The field or property member that is promised to be not-null.
29 | ///
30 | public MemberNotNullWhenAttribute(bool returnValue, string member)
31 | {
32 | this.ReturnValue = returnValue;
33 | this.Members = new[] { member };
34 | }
35 |
36 | /// Initializes the attribute with the specified return value condition and list of field and property members.
37 | ///
38 | /// The return value condition. If the method returns this value, the associated parameter will not be null.
39 | ///
40 | ///
41 | /// The list of field and property members that are promised to be not-null.
42 | ///
43 | public MemberNotNullWhenAttribute(bool returnValue, params string[] members)
44 | {
45 | this.ReturnValue = returnValue;
46 | this.Members = members;
47 | }
48 |
49 | /// Gets the return value condition.
50 | public bool ReturnValue { get; }
51 |
52 | /// Gets field or property member names.
53 | public string[] Members { get; }
54 | }
55 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Polyfills/RequiredMemberAttribute.cs:
--------------------------------------------------------------------------------
1 | #if !NET7_0_OR_GREATER
2 | namespace System.Runtime.CompilerServices;
3 | #endif
4 |
5 | #if !NET5_0_OR_GREATER
6 | internal static class IsExternalInit { }
7 | #endif
8 |
9 | #if !NET7_0_OR_GREATER
10 | [AttributeUsage(AttributeTargets.Class | AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Struct, Inherited = false)]
11 | internal sealed class RequiredMemberAttribute : Attribute { }
12 |
13 | [AttributeUsage(AttributeTargets.All, AllowMultiple = true, Inherited = false)]
14 | internal sealed class CompilerFeatureRequiredAttribute : Attribute
15 | {
16 | public CompilerFeatureRequiredAttribute(string featureName) => this.FeatureName = featureName;
17 |
18 | public string FeatureName { get; }
19 | public bool IsOptional { get; init; }
20 |
21 | public const string RefStructs = nameof(RefStructs);
22 | public const string RequiredMembers = nameof(RequiredMembers);
23 | }
24 | #endif
25 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/Polyfills/SetsRequiredMembersAttribute.cs:
--------------------------------------------------------------------------------
1 | namespace System.Diagnostics.CodeAnalysis;
2 |
3 | ///
4 | /// Specifies that this constructor sets all required members for the current type, and callers
5 | /// do not need to set any required members themselves.
6 | ///
7 | [AttributeUsage(AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
8 | internal sealed class SetsRequiredMembersAttribute : Attribute { }
9 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/PropertyChanged.SourceGenerator.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netstandard2.0
5 | 11.0
6 | enable
7 | false
8 | true
9 | true
10 |
11 | pdbonly
12 | true
13 |
14 | 0.0.0
15 | PropertyChanged.SourceGenerator
16 | INPC;INotifyPropertyChanged;INotifyPropertyChanging;SourceGenerator;MVVM;Binding
17 | Copyright © Antony Male 2021 - 2023
18 | icon.png
19 | README.md
20 | https://github.com/canton7/PropertyChanged.SourceGenerator
21 | MIT
22 | git
23 | https://github.com/canton7/PropertyChanged.SourceGenerator
24 | Antony Male
25 | Powerful INotifyPropertyChanged Source Generator, which generates INPC boilerplate for you as part of your build.
26 | Supports features such as automatic and manual dependencies between properties, notifications when specific properties change, and more.
27 | true
28 |
29 |
30 |
31 |
32 | all
33 | runtime; build; native; contentfiles; analyzers; buildtransitive
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/ReadOnlyEquatableList.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using Microsoft.CodeAnalysis.CSharp.Syntax;
7 |
8 | namespace PropertyChanged.SourceGenerator;
9 |
10 | public readonly struct ReadOnlyEquatableList : IEquatable>, IReadOnlyList
11 | {
12 | private readonly IReadOnlyList inner;
13 | private readonly IEqualityComparer comparer;
14 |
15 | public static ReadOnlyEquatableList Empty { get; } = new ReadOnlyEquatableList(Array.Empty());
16 |
17 | public ReadOnlyEquatableList(IReadOnlyList inner, IEqualityComparer? comparer = null)
18 | {
19 | this.inner = inner;
20 | this.comparer = comparer ?? EqualityComparer.Default;
21 | }
22 |
23 | public bool Equals(ReadOnlyEquatableList other)
24 | {
25 | return this.inner.SequenceEqual(other.inner, this.comparer);
26 | }
27 |
28 | public override bool Equals(object obj) => obj is ReadOnlyEquatableList other && this.Equals(other);
29 |
30 | public override int GetHashCode()
31 | {
32 | int hashCode = -658986029;
33 |
34 | foreach (var item in this.inner)
35 | {
36 | hashCode = hashCode * -1521134295 + this.comparer.GetHashCode(item);
37 | }
38 |
39 | return hashCode;
40 | }
41 |
42 | public T this[int index] => this.inner[index];
43 |
44 | public int Count => this.inner.Count;
45 |
46 | public IEnumerator GetEnumerator() => this.inner.GetEnumerator();
47 | IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this.inner).GetEnumerator();
48 |
49 | // Avoid a box
50 | public IEnumerable Reverse() => this.inner.Reverse();
51 | }
52 |
--------------------------------------------------------------------------------
/src/PropertyChanged.SourceGenerator/SymbolDisplayFormats.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Text;
4 | using Microsoft.CodeAnalysis;
5 |
6 | namespace PropertyChanged.SourceGenerator;
7 |
8 | public static class SymbolDisplayFormats
9 | {
10 | ///
11 | /// The name of a symbol, suitably escaped. E.g. "List" or "MethodName"
12 | ///
13 | public static SymbolDisplayFormat SymbolName { get; }
14 |
15 | ///
16 | /// The full name of a namespace, suitably escaped. E.g. "System.Collections.Generic"
17 | ///
18 | public static SymbolDisplayFormat Namespace { get; }
19 |
20 | ///
21 | /// A class declaration, but not including accessibility or modifiers, e.g. "class List<T>"
22 | ///
23 | public static SymbolDisplayFormat TypeDeclaration { get; }
24 |
25 | ///
26 | /// A string suitable for use as the return type from a property or method, suitably escaped.
27 | /// E.g. "global::System.Collections.Generic.IEnumerable<T>"
28 | ///
29 | public static SymbolDisplayFormat FullyQualifiedTypeName { get; }
30 |
31 | ///
32 | /// A string suitable for indicating what type an event is defined on, and whether it is implicitly
33 | /// or explicitly implemented
34 | ///
35 | public static SymbolDisplayFormat EventDefinition { get; }
36 |
37 | ///
38 | /// A string suitable for using as part of a generated file name
39 | ///
40 | public static SymbolDisplayFormat GeneratedFileName { get; }
41 |
42 | static SymbolDisplayFormats()
43 | {
44 | SymbolName = new SymbolDisplayFormat(
45 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers);
46 |
47 | Namespace = new SymbolDisplayFormat(
48 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
49 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers);
50 |
51 | TypeDeclaration = new SymbolDisplayFormat(
52 | genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
53 | kindOptions: SymbolDisplayKindOptions.IncludeTypeKeyword,
54 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers);
55 |
56 | FullyQualifiedTypeName = new SymbolDisplayFormat(
57 | globalNamespaceStyle: SymbolDisplayGlobalNamespaceStyle.Included,
58 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces,
59 | genericsOptions: SymbolDisplayGenericsOptions.IncludeTypeParameters,
60 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers
61 | | SymbolDisplayMiscellaneousOptions.UseSpecialTypes
62 | | SymbolDisplayMiscellaneousOptions.IncludeNullableReferenceTypeModifier);
63 |
64 | EventDefinition = new SymbolDisplayFormat(
65 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypes,
66 | propertyStyle: SymbolDisplayPropertyStyle.NameOnly,
67 | memberOptions: SymbolDisplayMemberOptions.IncludeContainingType
68 | | SymbolDisplayMemberOptions.IncludeExplicitInterface,
69 | miscellaneousOptions: SymbolDisplayMiscellaneousOptions.EscapeKeywordIdentifiers);
70 |
71 | GeneratedFileName = new SymbolDisplayFormat(
72 | typeQualificationStyle: SymbolDisplayTypeQualificationStyle.NameAndContainingTypesAndNamespaces);
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/src/Sandbox/.editorconfig:
--------------------------------------------------------------------------------
1 | ;[*.cs]
2 | ;propertychanged.add_prefix = Property
3 | ;propertychanged.first_letter_capitalization = lower_case
--------------------------------------------------------------------------------
/src/Sandbox/Program.cs:
--------------------------------------------------------------------------------
1 | using PropertyChanged.SourceGenerator;
2 | using System;
3 | using System.ComponentModel;
4 |
5 | namespace Sandbox;
6 |
7 | public class Program
8 | {
9 | public static void Main(string[] args)
10 | {
11 | Console.WriteLine("Hello World!");
12 | var vm = new Derived();
13 | }
14 | }
15 |
16 | internal partial class Base
17 | {
18 | [Notify]
19 | private bool _selected;
20 | }
21 |
22 | internal partial class Derived : Base
23 | {
24 | // [DependsOn(nameof(Selected))]
25 | public string Test => Selected ? "foo" : "bar";
26 |
27 | [Notify]
28 | private int _baz;
29 | }
30 |
--------------------------------------------------------------------------------
/src/Sandbox/Sandbox.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Exe
5 | net6
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------