diff --git a/Makefile b/Makefile index 1de8d8341a..bf92314790 100644 --- a/Makefile +++ b/Makefile @@ -108,6 +108,9 @@ check: dependencies @echo @echo "Checking for explicit interface violations..." @mono --debug OpenRA.Utility.exe all --check-explicit-interfaces + @echo + @echo "Checking for incorrect conditional trait interface overrides..." + @mono --debug OpenRA.Utility.exe all --check-conditional-trait-interface-overrides NUNIT_CONSOLE := $(shell test -f thirdparty/download/nunit3-console.exe && echo mono thirdparty/download/nunit3-console.exe || \ diff --git a/OpenRA.Mods.Common/UtilityCommands/CheckConditionalTraitInterfaceOverrides.cs b/OpenRA.Mods.Common/UtilityCommands/CheckConditionalTraitInterfaceOverrides.cs new file mode 100644 index 0000000000..2d3989a566 --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/CheckConditionalTraitInterfaceOverrides.cs @@ -0,0 +1,77 @@ +#region Copyright & License Information +/* + * Copyright 2007-2019 The OpenRA Developers (see AUTHORS) + * This file is part of OpenRA, which is free software. It is made + * available to you under the terms of the GNU General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. For more + * information, see COPYING. + */ +#endregion + +using System; +using System.Linq; +using System.Reflection; +using OpenRA.Mods.Common.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + public class CheckConditionalTraitInterfaceOverrides : IUtilityCommand + { + string IUtilityCommand.Name { get { return "--check-conditional-trait-interface-overrides"; } } + + bool IUtilityCommand.ValidateArguments(string[] args) + { + return args.Length == 1; + } + + int violationCount; + + static bool IsConditionalTrait(Type type) + { + // Walk up the inheritance chain to check if any parent type is the generic ConditionalTrait type + while (type != null && type != typeof(object)) + { + if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(ConditionalTrait<>)) + return true; + + type = type.BaseType; + } + + return false; + } + + void CheckInterfaceViolation(Utility utility, Type interfaceType, string methodName) + { + var types = utility.ModData.ObjectCreator.GetTypes() + .Where(t => interfaceType.IsAssignableFrom(t) && !t.IsGenericType); + + foreach (var t in types) + { + if (!IsConditionalTrait(t)) + continue; + + var overridesCreated = t.GetMethod("{0}.{1}".F(interfaceType.FullName, methodName), BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.DeclaredOnly) != null; + if (overridesCreated) + { + Console.WriteLine("{0} must override ConditionalTrait's {1} method instead of implementing {2} directly", t.Name, methodName, interfaceType.Name); + violationCount++; + } + } + } + + [Desc("Check for incorrect interface overrides in conditional traits defined in all assemblies referenced by the specified mod.")] + void IUtilityCommand.Run(Utility utility, string[] args) + { + CheckInterfaceViolation(utility, typeof(INotifyCreated), "Created"); + CheckInterfaceViolation(utility, typeof(IObservesVariables), "GetVariableObservers"); + + if (violationCount > 0) + { + Console.WriteLine("Interface override violations: {0}", violationCount); + Environment.Exit(1); + } + } + } +} diff --git a/make.ps1 b/make.ps1 index 75fa57b022..7df8061c43 100644 --- a/make.ps1 +++ b/make.ps1 @@ -147,6 +147,9 @@ function Check-Command { Write-Host "Checking for explicit interface violations..." -ForegroundColor Cyan ./OpenRA.Utility.exe all --check-explicit-interfaces + + Write-Host "Checking for incorrect conditional trait interface overrides..." -ForegroundColor Cyan + ./OpenRA.Utility.exe all --check-conditional-trait-interface-overrides } }