Skip to content

Commit

Permalink
feat: generate diagnostic warning when ignored members are explicitly…
Browse files Browse the repository at this point in the history
… mapped (#708)
  • Loading branch information
mikeguta committed Sep 4, 2023
1 parent a63035b commit 4cd4e3a
Show file tree
Hide file tree
Showing 5 changed files with 113 additions and 17 deletions.
3 changes: 2 additions & 1 deletion src/Riok.Mapperly/AnalyzerReleases.Shipped.md
Expand Up @@ -114,4 +114,5 @@ RMG047 | Mapper | Error | Cannot map to member path due to modifying a tem
Rule ID | Category | Severity | Notes
--------|----------|----------|-------
RMG048 | Mapper | Error | Used mapper members cannot be nullable

RMG049 | Mapper | Warning | Source member is ignored and also explicitly mapped
RMG050 | Mapper | Warning | Target member is ignored and also explicitly mapped
Expand Up @@ -15,6 +15,8 @@ public abstract class MembersMappingBuilderContext<T> : IMembersBuilderContext<T
where T : IMapping
{
private readonly HashSet<string> _unmappedSourceMemberNames;
private readonly HashSet<string> _mappedAndIgnoredTargetMemberNames;
private readonly HashSet<string> _mappedAndIgnoredSourceMemberNames;
private readonly IReadOnlyCollection<string> _ignoredUnmatchedTargetMemberNames;
private readonly IReadOnlyCollection<string> _ignoredUnmatchedSourceMemberNames;

Expand Down Expand Up @@ -44,9 +46,19 @@ protected MembersMappingBuilderContext(MappingBuilderContext builderContext, T m

MemberConfigsByRootTargetName = GetMemberConfigurations();

// source and target properties may have been ignored and mapped explicitly
_mappedAndIgnoredSourceMemberNames = MemberConfigsByRootTargetName.Values
.SelectMany(v => v.Select(s => s.Source.Path.First()))
.ToHashSet();
_mappedAndIgnoredSourceMemberNames.IntersectWith(IgnoredSourceMemberNames);

_mappedAndIgnoredTargetMemberNames = new HashSet<string>(ignoredTargetMemberNames);
_mappedAndIgnoredTargetMemberNames.IntersectWith(MemberConfigsByRootTargetName.Keys);

// remove explicitly mapped ignored targets from ignoredTargetMemberNames
// then remove all ignored targets from TargetMembers, leaving unignored and explicitly mapped ignored members
ignoredTargetMemberNames.ExceptWith(MemberConfigsByRootTargetName.Keys);
ignoredTargetMemberNames.ExceptWith(_mappedAndIgnoredTargetMemberNames);

TargetMembers.RemoveRange(ignoredTargetMemberNames);
}

Expand All @@ -66,6 +78,8 @@ public void AddDiagnostics()
AddUnmatchedIgnoredSourceMembersDiagnostics();
AddUnmatchedTargetMembersDiagnostics();
AddUnmatchedSourceMembersDiagnostics();
AddMappedAndIgnoredSourceMembersDiagnostics();
AddMappedAndIgnoredTargetMembersDiagnostics();
}

protected void SetSourceMemberMapped(MemberPath sourcePath) => _unmappedSourceMemberNames.Remove(sourcePath.Path.First().Name);
Expand Down Expand Up @@ -162,4 +176,28 @@ private void AddUnmatchedSourceMembersDiagnostics()
);
}
}

private void AddMappedAndIgnoredTargetMembersDiagnostics()
{
foreach (var targetMemberName in _mappedAndIgnoredTargetMemberNames)
{
BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.IgnoredTargetMemberExplicitlyMapped,
targetMemberName,
Mapping.TargetType
);
}
}

private void AddMappedAndIgnoredSourceMembersDiagnostics()
{
foreach (var sourceMemberName in _mappedAndIgnoredSourceMemberNames)
{
BuilderContext.ReportDiagnostic(
DiagnosticDescriptors.IgnoredSourceMemberExplicitlyMapped,
sourceMemberName,
Mapping.SourceType
);
}
}
}
18 changes: 18 additions & 0 deletions src/Riok.Mapperly/Diagnostics/DiagnosticDescriptors.cs
Expand Up @@ -428,4 +428,22 @@ public static class DiagnosticDescriptors
DiagnosticSeverity.Error,
true
);

public static readonly DiagnosticDescriptor IgnoredSourceMemberExplicitlyMapped = new DiagnosticDescriptor(
"RMG049",
"Source member is ignored and also explicitly mapped",
"The source member {0} on {1} is ignored, but is also mapped by the " + nameof(MapPropertyAttribute),
DiagnosticCategories.Mapper,
DiagnosticSeverity.Warning,
true
);

public static readonly DiagnosticDescriptor IgnoredTargetMemberExplicitlyMapped = new DiagnosticDescriptor(
"RMG050",
"Target member is ignored and also explicitly mapped",
"The target member {0} on {1} is ignored, but is also mapped by the " + nameof(MapPropertyAttribute),
DiagnosticCategories.Mapper,
DiagnosticSeverity.Warning,
true
);
}
51 changes: 40 additions & 11 deletions test/Riok.Mapperly.Tests/Mapping/IgnoreObsoleteTest.cs
@@ -1,4 +1,4 @@
using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Abstractions;
using Riok.Mapperly.Diagnostics;

namespace Riok.Mapperly.Tests.Mapping;
Expand Down Expand Up @@ -245,7 +245,7 @@ public void MapPropertyOverridesIgnoreObsoleteBoth()
);

TestHelper
.GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
.Should()
.HaveSingleMethodBody(
"""
Expand All @@ -254,7 +254,16 @@ public void MapPropertyOverridesIgnoreObsoleteBoth()
target.Ignored = source.Ignored;
return target;
"""
);
)
.HaveDiagnostic(
DiagnosticDescriptors.IgnoredSourceMemberExplicitlyMapped,
"The source member Ignored on A is ignored, but is also mapped by the MapPropertyAttribute"
)
.HaveDiagnostic(
DiagnosticDescriptors.IgnoredTargetMemberExplicitlyMapped,
"The target member Ignored on B is ignored, but is also mapped by the MapPropertyAttribute"
)
.HaveAssertedAllDiagnostics();
}

[Fact]
Expand All @@ -271,7 +280,7 @@ public void MapPropertyOverridesIgnoreObsoleteSource()
);

TestHelper
.GenerateMapper(source, TestHelperOptions.AllowInfoDiagnostics)
.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
.Should()
.HaveSingleMethodBody(
"""
Expand All @@ -280,7 +289,12 @@ public void MapPropertyOverridesIgnoreObsoleteSource()
target.Ignored = source.Ignored;
return target;
"""
);
)
.HaveDiagnostic(
DiagnosticDescriptors.IgnoredSourceMemberExplicitlyMapped,
"The source member Ignored on A is ignored, but is also mapped by the MapPropertyAttribute"
)
.HaveAssertedAllDiagnostics();
}

[Fact]
Expand All @@ -297,7 +311,7 @@ public void MapPropertyOverridesIgnoreObsoleteTarget()
);

TestHelper
.GenerateMapper(source)
.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
.Should()
.HaveSingleMethodBody(
"""
Expand All @@ -306,7 +320,12 @@ public void MapPropertyOverridesIgnoreObsoleteTarget()
target.Ignored = source.Ignored;
return target;
"""
);
)
.HaveDiagnostic(
DiagnosticDescriptors.IgnoredTargetMemberExplicitlyMapped,
"The target member Ignored on B is ignored, but is also mapped by the MapPropertyAttribute"
)
.HaveAssertedAllDiagnostics();
}

[Fact]
Expand All @@ -331,7 +350,7 @@ class B
);

TestHelper
.GenerateMapper(source)
.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
.Should()
.HaveSingleMethodBody(
"""
Expand All @@ -342,7 +361,12 @@ class B
target.Value = source.Value;
return target;
"""
);
)
.HaveDiagnostic(
DiagnosticDescriptors.IgnoredTargetMemberExplicitlyMapped,
"The target member Ignored on B is ignored, but is also mapped by the MapPropertyAttribute"
)
.HaveAssertedAllDiagnostics();
}

[Fact]
Expand All @@ -367,7 +391,7 @@ class B
);

TestHelper
.GenerateMapper(source)
.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
.Should()
.HaveSingleMethodBody(
"""
Expand All @@ -378,6 +402,11 @@ class B
target.Value = source.Value;
return target;
"""
);
)
.HaveDiagnostic(
DiagnosticDescriptors.IgnoredTargetMemberExplicitlyMapped,
"The target member Ignored on B is ignored, but is also mapped by the MapPropertyAttribute"
)
.HaveAssertedAllDiagnostics();
}
}
18 changes: 14 additions & 4 deletions test/Riok.Mapperly.Tests/Mapping/ObjectPropertyInitPropertyTest.cs
Expand Up @@ -328,7 +328,7 @@ public void IgnoredTargetRequiredPropertyWithConfiguration()
);

TestHelper
.GenerateMapper(source)
.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
.Should()
.HaveSingleMethodBody(
"""
Expand All @@ -339,7 +339,12 @@ public void IgnoredTargetRequiredPropertyWithConfiguration()
target.IntValue = source.IntValue;
return target;
"""
);
)
.HaveDiagnostic(
DiagnosticDescriptors.IgnoredTargetMemberExplicitlyMapped,
"The target member StringValue on B is ignored, but is also mapped by the MapPropertyAttribute"
)
.HaveAssertedAllDiagnostics();
}

[Fact]
Expand All @@ -358,7 +363,7 @@ public void IgnoredTargetInitPropertyWithConfiguration()
);

TestHelper
.GenerateMapper(source)
.GenerateMapper(source, TestHelperOptions.AllowDiagnostics)
.Should()
.HaveSingleMethodBody(
"""
Expand All @@ -369,7 +374,12 @@ public void IgnoredTargetInitPropertyWithConfiguration()
target.IntValue = source.IntValue;
return target;
"""
);
)
.HaveDiagnostic(
DiagnosticDescriptors.IgnoredTargetMemberExplicitlyMapped,
"The target member StringValue on B is ignored, but is also mapped by the MapPropertyAttribute"
)
.HaveAssertedAllDiagnostics();
}

[Fact]
Expand Down

0 comments on commit 4cd4e3a

Please sign in to comment.