Skip to content

Commit 86ee0e0

Browse files
committed
Fix filtering explicitly overridden targets in build components
1 parent 3f80440 commit 86ee0e0

File tree

3 files changed

+60
-12
lines changed

3 files changed

+60
-12
lines changed

source/Nuke.Common.Tests/Execution/DefaultInterfaceExecutionTest.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,16 @@ public void TestOverriddenDuplicatedTarget()
122122
targets.Count(x => x.Name == nameof(ITestBuild.D)).Should().Be(1);
123123
}
124124

125+
[Fact]
126+
public void TestDeclaringAndImplementingComponent()
127+
{
128+
var build = new TestBuildWithDeclaringAndImplementingComponent();
129+
var targets = ExecutableTargetFactory.CreateAll(build);
130+
131+
targets.Should().HaveCount(1);
132+
targets.Single().Member.DeclaringType.Should().Be(typeof(IImplementingComponent));
133+
}
134+
125135
private interface IParameterInterface
126136
: INukeBuild
127137
{
@@ -215,5 +225,19 @@ private interface IDuplicatedTargetBuild
215225
public Target D => _ => _
216226
.Executes(() => { });
217227
}
228+
229+
private class TestBuildWithDeclaringAndImplementingComponent : NukeBuild, IImplementingComponent
230+
{
231+
}
232+
233+
private interface IDeclaringComponent : INukeBuild
234+
{
235+
Target Foo { get; }
236+
}
237+
238+
private interface IImplementingComponent : IDeclaringComponent
239+
{
240+
Target IDeclaringComponent.Foo => _ => _;
241+
}
218242
}
219243
}

source/Nuke.Common/Execution/ExecutableTargetFactory.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,8 @@ internal static IEnumerable<PropertyInfo> GetTargetProperties(Type buildType)
9494
return buildType.GetAllMembers(
9595
x => x is PropertyInfo property && property.PropertyType == typeof(Target),
9696
bindingFlags: ReflectionUtility.Instance,
97-
allowAmbiguity: false).Cast<PropertyInfo>();
97+
allowAmbiguity: false,
98+
filterQuasiOverridden: true).Cast<PropertyInfo>();
9899
}
99100
}
100101
}

source/Nuke.Common/Utilities/ReflectionUtility.cs

Lines changed: 34 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,11 @@ public static bool IsExtensionParameter(this ParameterInfo parameter)
128128
method.GetParameters().First() == parameter;
129129
}
130130

131+
public static bool IsExplicit(this MemberInfo member)
132+
{
133+
return member.Name.Contains(".");
134+
}
135+
131136
public static string GetPossibleExplicitName(this MemberInfo member)
132137
{
133138
return $"{member.DeclaringType.NotNull().FullName.NotNull().Replace("+", ".")}.{member.Name}";
@@ -147,35 +152,53 @@ public static IEnumerable<MemberInfo> GetAllMembers(
147152
this Type buildType,
148153
Func<MemberInfo, bool> filter,
149154
BindingFlags bindingFlags,
150-
bool allowAmbiguity)
155+
bool allowAmbiguity,
156+
bool filterQuasiOverridden = false)
151157
{
152-
var interfaceMembers = buildType.GetInterfaces()
158+
var interfaceMembersByName = buildType.GetInterfaces()
153159
.SelectMany(x => x.GetMembers(bindingFlags))
154160
.Where(filter)
155-
.Where(x => buildType.GetMember(x.Name).SingleOrDefault() == null).ToLookup(x => x.Name);
161+
.Where(x => buildType.GetMember(x.Name).SingleOrDefault() == null).ToLookup(x => x.GetDisplayShortName());
156162
var classMembers = buildType
157163
.GetMembers(bindingFlags)
158164
.Where(filter)
159-
.Where(x => !x.Name.Contains(".")).ToDictionary(x => x.Name);
165+
.Where(x => !x.IsExplicit()).ToDictionary(x => x.Name);
166+
var removeMembers = new List<MemberInfo>();
160167

161-
foreach (var interfacePropertiesByName in interfaceMembers)
168+
foreach (var interfaceMembers in interfaceMembersByName)
162169
{
163-
var memberName = interfacePropertiesByName.Key;
164-
var memberType = interfacePropertiesByName.First().MemberType;
170+
var memberName = interfaceMembers.Key;
171+
var memberType = interfaceMembers.First().MemberType;
165172
var classMember = classMembers.GetValueOrDefault(memberName);
166173

167-
ControlFlow.Assert(allowAmbiguity || interfacePropertiesByName.Count() == 1 || classMember != null,
174+
if (filterQuasiOverridden && classMember == null && interfaceMembers.Count() > 1)
175+
{
176+
var orderedProperties = interfaceMembers
177+
.TSort(x => interfaceMembers.Where(y => y.DeclaringType.NotNull().IsAssignableFrom(x.DeclaringType))).ToList();
178+
179+
var mostBaseType = orderedProperties.First().DeclaringType.NotNull();
180+
var derivedTypes = orderedProperties.Skip(1).Select(x => x.DeclaringType);
181+
if (derivedTypes.All(x => mostBaseType.IsAssignableFrom(x)))
182+
{
183+
removeMembers.AddRange(orderedProperties.Take(orderedProperties.Count - 1));
184+
continue;
185+
}
186+
}
187+
188+
ControlFlow.Assert(allowAmbiguity || interfaceMembers.Count() == 1 || classMember != null,
168189
new[] { $"{memberType} '{memberName}' must be implemented explicitly because it is inherited from multiple interfaces:" }
169-
.Concat(interfacePropertiesByName.Select(x => $" - {x.DeclaringType.NotNull().Name}"))
190+
.Concat(interfaceMembers.Select(x => $" - {x.DeclaringType.NotNull().Name}"))
170191
.JoinNewLine());
192+
171193
ControlFlow.Assert(allowAmbiguity || classMember == null || classMember.IsPublic(),
172194
new[] { $"{memberType} '{memberName}' must be marked public to override inherited member from:" }
173-
.Concat(interfacePropertiesByName.Select(x => $" - {x.DeclaringType.NotNull().Name}"))
195+
.Concat(interfaceMembers.Select(x => $" - {x.DeclaringType.NotNull().Name}"))
174196
.JoinNewLine());
175197
}
176198

177199
return classMembers.Values
178-
.Concat(interfaceMembers.SelectMany(x => x).Select(x => x.GetImplementedOrInterfaceMember(buildType)))
200+
.Concat(interfaceMembersByName.SelectMany(x => x).Select(x => x.GetImplementedOrInterfaceMember(buildType)))
201+
.Except(removeMembers)
179202
.Where(filter).ToList();
180203
}
181204
}

0 commit comments

Comments
 (0)