diff --git a/OpenRA.Game/Traits/TraitsInterfaces.cs b/OpenRA.Game/Traits/TraitsInterfaces.cs index ffa931225a..04e860e609 100644 --- a/OpenRA.Game/Traits/TraitsInterfaces.cs +++ b/OpenRA.Game/Traits/TraitsInterfaces.cs @@ -19,6 +19,8 @@ using OpenRA.Primitives; namespace OpenRA.Traits { + public sealed class RequireExplicitImplementationAttribute : Attribute { } + public enum DamageState { Undamaged, Light, Medium, Heavy, Critical, Dead } public interface IHealth @@ -249,6 +251,8 @@ namespace OpenRA.Traits public interface ILoadsPlayerPalettes { void LoadPlayerPalettes(WorldRenderer wr, string playerName, HSLColor playerColor, bool replaceExisting); } public interface IPaletteModifier { void AdjustPalette(IReadOnlyDictionary b); } public interface IPips { IEnumerable GetPips(Actor self); } + + [RequireExplicitImplementation] public interface ISelectionBar { float GetValue(); Color GetColor(); } public interface IPositionableInfo : ITraitInfoInterface { } diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index 96efe75e0a..d79458f8e0 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -726,6 +726,7 @@ + diff --git a/OpenRA.Mods.Common/UtilityCommands/CheckExplicitInterfacesCommand.cs b/OpenRA.Mods.Common/UtilityCommands/CheckExplicitInterfacesCommand.cs new file mode 100644 index 0000000000..06af9d8350 --- /dev/null +++ b/OpenRA.Mods.Common/UtilityCommands/CheckExplicitInterfacesCommand.cs @@ -0,0 +1,122 @@ +#region Copyright & License Information +/* + * Copyright 2007-2015 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. For more information, + * see COPYING. + */ +#endregion + +using System; +using System.Linq; +using System.Reflection; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.UtilityCommands +{ + public class CheckExplicitInterfacesCommand : IUtilityCommand + { + string IUtilityCommand.Name { get { return "--check-explicit-interfaces"; } } + + bool IUtilityCommand.ValidateArguments(string[] args) + { + return args.Length == 1; + } + + int violationCount; + + [Desc("Check for explicit interface implementation violations in all assemblies referenced by the specified mod.")] + void IUtilityCommand.Run(ModData modData, string[] args) + { + var types = modData.ObjectCreator.GetTypes(); + + foreach (var implementingType in types.Where(t => !t.IsInterface)) + { + if (implementingType.IsEnum) + continue; + + var interfaces = implementingType.GetInterfaces(); + foreach (var interfaceType in interfaces) + { + if (!interfaceType.HasAttribute()) + continue; + + var interfaceMembers = interfaceType.GetMembers(); + foreach (var interfaceMember in interfaceMembers) + { + if (interfaceMember.Name.StartsWith("get_") || interfaceMember.Name.StartsWith("set_")) + continue; + + var interfaceMethod = interfaceMember as MethodInfo; + if (interfaceMethod != null) + { + var interfaceMethodParams = interfaceMethod.GetParameters(); + foreach (var implementingMethod in implementingType.GetMethods()) + { + if (implementingMethod.Name != interfaceMethod.Name + || implementingMethod.ReturnType != interfaceMethod.ReturnType) + continue; + + var implementingMethodParams = implementingMethod.GetParameters(); + var lenImpl = implementingMethodParams.Length; + if (lenImpl != interfaceMethodParams.Length) + continue; + + var allMatch = true; + for (var i = 0; i < lenImpl; i++) + { + var implementingParam = implementingMethodParams[i]; + var interfaceParam = interfaceMethodParams[i]; + if (implementingParam.ParameterType != interfaceParam.ParameterType + || implementingParam.Name != interfaceParam.Name + || implementingParam.IsOut != interfaceParam.IsOut) + { + allMatch = false; + break; + } + } + + // Explicitly implemented methods are never public in C#. + if (allMatch && implementingMethod.IsPublic) + OnViolation(implementingType, interfaceType, implementingMethod); + } + } + + var interfaceProperty = interfaceMember as PropertyInfo; + if (interfaceProperty != null) + { + var implementingProperties = implementingType.GetProperties(); + foreach (var implementingProperty in implementingProperties) + { + if (implementingProperty.PropertyType != interfaceProperty.PropertyType + || implementingProperty.Name != interfaceProperty.Name) + continue; + + if (!IsExplicitInterfaceProperty(implementingProperty)) + OnViolation(implementingType, interfaceType, implementingProperty); + } + } + } + } + } + + if (violationCount > 0) + { + Console.WriteLine("Explicit interface violations: {0}", violationCount); + Environment.Exit(1); + } + } + + static bool IsExplicitInterfaceProperty(PropertyInfo pi) + { + return pi.Name.Contains("."); + } + + void OnViolation(Type implementor, Type interfaceType, MemberInfo violator) + { + Console.WriteLine("{0} must explicitly implement the interface member {1}.{2}", implementor.Name, interfaceType.Name, violator.Name); + violationCount++; + } + } +}