diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index fab4718dc9..8998b359fa 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -44,8 +44,9 @@ jobs:
- if: failure()
uses: actions/upload-artifact@v4
with:
- name: verify-test-results
+ name: verify-test-results-package
path: '**/*.received.*'
+ retention-days: 3
validate-package:
needs: package
runs-on: ubuntu-latest
@@ -102,8 +103,9 @@ jobs:
- if: failure()
uses: actions/upload-artifact@v4
with:
- name: verify-test-results
+ name: verify-test-results-net${{ matrix.dotnet }}
path: '**/*.received.*'
+ retention-days: 3
integration-test-net-framework:
needs: package
runs-on: windows-latest
@@ -129,8 +131,9 @@ jobs:
- if: failure()
uses: actions/upload-artifact@v4
with:
- name: verify-test-results
+ name: verify-test-results-net-framework
path: '**/*.received.*'
+ retention-days: 3
sample:
runs-on: ubuntu-latest
needs: package
diff --git a/docs/docs/configuration/analyzer-diagnostics/RMG060.mdx b/docs/docs/configuration/analyzer-diagnostics/RMG060.mdx
new file mode 100644
index 0000000000..1c700883dc
--- /dev/null
+++ b/docs/docs/configuration/analyzer-diagnostics/RMG060.mdx
@@ -0,0 +1,61 @@
+---
+sidebar_label: RMG060
+description: 'Mapperly analyzer diagnostic RMG060 — Multiple user mappings discovered without specifying an explicit default'
+---
+
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# RMG060 — Multiple user-mappings discovered without specifying an explicit default
+
+Multiple user-mappings for the same type pair are discovered
+without specifying an explicit default.
+Mapperly needs to now which mapping it should use.
+
+To specify a default apply `[UserMapping(Default = true)]`.
+
+See also [user-implemented mappings](../user-implemented-methods.mdx#default-user-implemented-mapping-method).
+
+## Example
+
+Two mappings from `Car` to `CarDto` are defined.
+When Mapperly needs to map from `Car` to `CarDto` in `CarsToCarDtos`
+it needs to know whether to use `CarToCarDto` or `CarToCarDtoIgnoreId`.
+Apply `[UserMapping(Default = true)]` to the mapping which Mapperly should use
+in such cases.
+
+
+
+ ```csharp
+ [Mapper]
+ public partial class CarMapper
+ {
+ public paratial List CarsToCarDtos(List cars);
+
+ public partial CarDto CarToCarDto(Car car);
+
+ [MapperIgnoreSource(nameof(Car.Id))]
+ public partial CarDto CarToCarDtoIgnoreId(Car car);
+ }
+ ```
+
+
+
+ ```csharp
+ [Mapper]
+ public partial class CarMapper
+ {
+ public paratial List CarsToCarDtos(List cars);
+
+ // highlight-start
+ [UserMapping(Default = true)]
+ // highlight-end
+ public partial CarDto CarToCarDto(Car car);
+
+ [MapperIgnoreSource(nameof(Car.Id))]
+ public partial CarDto CarToCarDtoIgnoreId(Car car);
+ }
+ ```
+
+
+
diff --git a/docs/docs/configuration/user-implemented-methods.mdx b/docs/docs/configuration/user-implemented-methods.mdx
index dc40a83749..ec4b5f9889 100644
--- a/docs/docs/configuration/user-implemented-methods.mdx
+++ b/docs/docs/configuration/user-implemented-methods.mdx
@@ -89,7 +89,7 @@ the first user-implemented mapping which has an unspecified `Default` value is u
For each type-pair only one default mapping can exist.
If multiple mappings exist for the same type-pair without a default mapping, `RMG060` is reported.
-By default `RMG060` has a severity of info,
+By default `RMG060` has a severity of warning,
which can be changed using the [`.editorconfig`](./analyzer-diagnostics/index.mdx#editorconfig).
## Map properties using user-implemented mappings
diff --git a/src/Riok.Mapperly/AnalyzerReleases.Shipped.md b/src/Riok.Mapperly/AnalyzerReleases.Shipped.md
index d567dda277..d9b0e0820d 100644
--- a/src/Riok.Mapperly/AnalyzerReleases.Shipped.md
+++ b/src/Riok.Mapperly/AnalyzerReleases.Shipped.md
@@ -144,11 +144,11 @@ RMG018 | Mapper | Disabled | Partial static mapping method in an instance map
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
RMG059 | Mapper | Error | Multiple default user mappings found, only one is allowed
-RMG060 | Mapper | Info | Multiple user mappings discovered without specifying an explicit default
+RMG060 | Mapper | Warning | Multiple user mappings discovered without specifying an explicit default
RMG061 | Mapper | Error | The referenced mapping was not found
RMG062 | Mapper | Error | The referenced mapping name is ambiguous
RMG063 | Mapper | Error | Cannot configure an enum mapping on a non-enum mapping
RMG064 | Mapper | Error | Cannot configure an object mapping on a non-object mapping
RMG065 | Mapper | Warning | Cannot configure an object mapping on a queryable projection mapping, apply the configurations to an object mapping method instead
-RMG066 | Mapper | Warning | No members are mapped in an object mapping
+RMG066 | Mapper | Warning | No members are mapped in an object mapping
RMG067 | Mapper | Error | Invalid usage of the MapPropertyAttribute
diff --git a/src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs b/src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs
index 46fe1157b5..a05ccced7b 100644
--- a/src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs
+++ b/src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs
@@ -581,8 +581,9 @@ public static class DiagnosticDescriptors
"Multiple user mappings discovered without specifying an explicit default",
"Multiple user mappings discovered for the mapping from {0} to {1} without specifying an explicit default",
DiagnosticCategories.Mapper,
- DiagnosticSeverity.Info,
- true
+ DiagnosticSeverity.Warning,
+ true,
+ helpLinkUri: BuildHelpUri("RMG060")
);
public static readonly DiagnosticDescriptor ReferencedMappingNotFound =
diff --git a/test/Riok.Mapperly.IntegrationTests/Mapper/StaticTestMapper.cs b/test/Riok.Mapperly.IntegrationTests/Mapper/StaticTestMapper.cs
index 3010a0c28f..be95d5fec1 100644
--- a/test/Riok.Mapperly.IntegrationTests/Mapper/StaticTestMapper.cs
+++ b/test/Riok.Mapperly.IntegrationTests/Mapper/StaticTestMapper.cs
@@ -9,6 +9,7 @@ namespace Riok.Mapperly.IntegrationTests.Mapper
[Mapper(EnumMappingStrategy = EnumMappingStrategy.ByValue)]
public static partial class StaticTestMapper
{
+ [UserMapping(Default = true)]
public static partial int DirectInt(int value);
public static partial int? DirectIntNullable(int? value);
@@ -33,6 +34,7 @@ public static partial class StaticTestMapper
[MapperRequiredMapping(RequiredMappingStrategy.Target)]
public static partial TestObjectDto MapToDtoExt(this TestObject src);
+ [UserMapping(Default = true)]
public static TestObjectDto MapToDto(TestObject src)
{
var target = MapToDtoInternal(src);
@@ -120,6 +122,7 @@ public static void MapExistingList(List src, List dst)
[MapperIgnoreTargetValue(TestEnum.Value30)]
public static partial TestEnum MapToEnumByNameWithIgnored(TestEnumDtoAdditionalValue v);
+ [UserMapping(Default = true)]
[MapEnum(EnumMappingStrategy.ByValueCheckDefined)]
public static partial TestEnum MapToEnumByValueCheckDefined(TestEnumDtoByValue v);
diff --git a/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs b/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs
index ce61c87400..63fabfe8a8 100644
--- a/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs
+++ b/test/Riok.Mapperly.IntegrationTests/Mapper/TestMapper.cs
@@ -32,6 +32,7 @@ public TestMapper()
_formatEnUs.NumberFormat.CurrencyPositivePattern = 2;
}
+ [UserMapping(Default = true)]
public partial int DirectInt(int value);
public partial long ImplicitCastInt(int value);
@@ -48,6 +49,7 @@ public TestMapper()
public partial IEnumerable MapAllDtos(IEnumerable objects);
+ [UserMapping(Default = true)]
public TestObjectDto MapToDto(TestObject src)
{
var target = MapToDtoInternal(src);
diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt
index 3b351ccf72..af33555c26 100644
--- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt
+++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork.verified.txt
@@ -23,7 +23,7 @@
CtorValue: 5,
CtorValue2: 100,
RequiredValue: 4,
- StringValue: ,
+ StringValue: +after-map,
RenamedStringValue2: ,
Unflattening: {},
NestedNullableTargetNotNullable: {},
diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork_NET6_0.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork_NET6_0.verified.txt
index 17153c34e0..805c4557bb 100644
--- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork_NET6_0.verified.txt
+++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunExtensionMappingShouldWork_NET6_0.verified.txt
@@ -23,7 +23,7 @@
CtorValue: 5,
CtorValue2: 100,
RequiredValue: 4,
- StringValue: ,
+ StringValue: +after-map,
RenamedStringValue2: ,
Unflattening: {},
NestedNullableTargetNotNullable: {},
diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork.verified.txt
index a773be224e..ec9744f29b 100644
--- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork.verified.txt
+++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork.verified.txt
@@ -28,7 +28,7 @@
CtorValue: 5,
CtorValue2: 100,
RequiredValue: 4,
- StringValue: ,
+ StringValue: +after-map,
RenamedStringValue2: ,
Unflattening: {},
NestedNullableTargetNotNullable: {},
diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork_NET6_0.verified.txt b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork_NET6_0.verified.txt
index 4b1ab3647a..0c6249cf99 100644
--- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork_NET6_0.verified.txt
+++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.RunMappingShouldWork_NET6_0.verified.txt
@@ -28,7 +28,7 @@
CtorValue: 5,
CtorValue2: 100,
RequiredValue: 4,
- StringValue: ,
+ StringValue: +after-map,
RenamedStringValue2: ,
Unflattening: {},
NestedNullableTargetNotNullable: {},
diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource.verified.cs
index ebd0c3f0d6..e6597cc9c1 100644
--- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource.verified.cs
+++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource.verified.cs
@@ -1,4 +1,4 @@
-//
+//
#nullable enable
namespace Riok.Mapperly.IntegrationTests.Mapper
{
@@ -55,7 +55,7 @@ public static partial int ParseableInt(string value)
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")]
public static partial global::System.Collections.Generic.IEnumerable MapAllDtos(global::System.Collections.Generic.IEnumerable objects)
{
- return global::System.Linq.Enumerable.Select(objects, x => MapToDtoExt(x));
+ return global::System.Linq.Enumerable.Select(objects, x => MapToDto(x));
}
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")]
@@ -101,7 +101,7 @@ public static partial int ParseableInt(string value)
}
if (src.RecursiveObject != null)
{
- target.RecursiveObject = MapToDtoExt(src.RecursiveObject);
+ target.RecursiveObject = MapToDto(src.RecursiveObject);
}
else
{
@@ -214,7 +214,7 @@ public static partial int ParseableInt(string value)
}
if (testObject.RecursiveObject != null)
{
- target.RecursiveObject = MapToDtoExt(testObject.RecursiveObject);
+ target.RecursiveObject = MapToDto(testObject.RecursiveObject);
}
else
{
@@ -420,7 +420,7 @@ public static partial void UpdateDto(global::Riok.Mapperly.IntegrationTests.Mode
}
if (source.RecursiveObject != null)
{
- target.RecursiveObject = MapToDtoExt(source.RecursiveObject);
+ target.RecursiveObject = MapToDto(source.RecursiveObject);
}
else
{
diff --git a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs
index 34084e5f6b..5655aca0fc 100644
--- a/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs
+++ b/test/Riok.Mapperly.IntegrationTests/_snapshots/StaticMapperTest.SnapshotGeneratedSource_NET6_0.verified.cs
@@ -55,7 +55,7 @@ public static partial int ParseableInt(string value)
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")]
public static partial global::System.Collections.Generic.IEnumerable MapAllDtos(global::System.Collections.Generic.IEnumerable objects)
{
- return global::System.Linq.Enumerable.Select(objects, x => MapToDtoExt(x));
+ return global::System.Linq.Enumerable.Select(objects, x => MapToDto(x));
}
[global::System.CodeDom.Compiler.GeneratedCode("Riok.Mapperly", "0.0.1.0")]
@@ -101,7 +101,7 @@ public static partial int ParseableInt(string value)
}
if (src.RecursiveObject != null)
{
- target.RecursiveObject = MapToDtoExt(src.RecursiveObject);
+ target.RecursiveObject = MapToDto(src.RecursiveObject);
}
else
{
@@ -216,7 +216,7 @@ public static partial int ParseableInt(string value)
}
if (testObject.RecursiveObject != null)
{
- target.RecursiveObject = MapToDtoExt(testObject.RecursiveObject);
+ target.RecursiveObject = MapToDto(testObject.RecursiveObject);
}
else
{
@@ -427,7 +427,7 @@ public static partial void UpdateDto(global::Riok.Mapperly.IntegrationTests.Mode
}
if (source.RecursiveObject != null)
{
- target.RecursiveObject = MapToDtoExt(source.RecursiveObject);
+ target.RecursiveObject = MapToDto(source.RecursiveObject);
}
else
{
diff --git a/test/Riok.Mapperly.Tests/Mapping/UserMethodTest.cs b/test/Riok.Mapperly.Tests/Mapping/UserMethodTest.cs
index cd20e11e39..1dcd546764 100644
--- a/test/Riok.Mapperly.Tests/Mapping/UserMethodTest.cs
+++ b/test/Riok.Mapperly.Tests/Mapping/UserMethodTest.cs
@@ -654,7 +654,7 @@ public void UserImplementedWithDefaultFalseUserMappingsShouldUseFirstNonFalseDef
);
TestHelper
- .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
.Should()
.HaveDiagnostic(
DiagnosticDescriptors.MultipleUserMappingsWithoutDefault,
@@ -694,7 +694,7 @@ public void DisabledAutoUserMappingDiscoveryShouldUseFirstNonFalseDefault()
);
TestHelper
- .GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
+ .GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
.Should()
.HaveDiagnostic(
DiagnosticDescriptors.MultipleUserMappingsWithoutDefault,
diff --git a/test/Riok.Mapperly.Tests/_snapshots/ReferenceHandlingTest.ManuallyMappedPropertiesShouldWork.verified.txt b/test/Riok.Mapperly.Tests/_snapshots/ReferenceHandlingTest.ManuallyMappedPropertiesShouldWork.verified.txt
index 53e9d021c2..df4a69060a 100644
--- a/test/Riok.Mapperly.Tests/_snapshots/ReferenceHandlingTest.ManuallyMappedPropertiesShouldWork.verified.txt
+++ b/test/Riok.Mapperly.Tests/_snapshots/ReferenceHandlingTest.ManuallyMappedPropertiesShouldWork.verified.txt
@@ -23,9 +23,10 @@
{
Id: RMG060,
Title: Multiple user mappings discovered without specifying an explicit default,
- Severity: Info,
+ Severity: Warning,
WarningLevel: 1,
Location: : (12,0)-(12,71),
+ HelpLink: https://localhost:3000/docs/configuration/analyzer-diagnostics/RMG060,
MessageFormat: Multiple user mappings discovered for the mapping from {0} to {1} without specifying an explicit default,
Message: Multiple user mappings discovered for the mapping from A to B without specifying an explicit default,
Category: Mapper