From f787f275db740e27544472f1c1d30b38d98e8af3 Mon Sep 17 00:00:00 2001 From: Paul Chote Date: Wed, 14 Dec 2016 19:35:52 +0000 Subject: [PATCH] Add actor conditions lint rule. --- OpenRA.Mods.Common/Lint/CheckConditions.cs | 72 ++++++++++++++++++++ OpenRA.Mods.Common/Lint/LintExts.cs | 30 ++++++-- OpenRA.Mods.Common/OpenRA.Mods.Common.csproj | 1 + 3 files changed, 97 insertions(+), 6 deletions(-) create mode 100644 OpenRA.Mods.Common/Lint/CheckConditions.cs diff --git a/OpenRA.Mods.Common/Lint/CheckConditions.cs b/OpenRA.Mods.Common/Lint/CheckConditions.cs new file mode 100644 index 0000000000..2a2b54c640 --- /dev/null +++ b/OpenRA.Mods.Common/Lint/CheckConditions.cs @@ -0,0 +1,72 @@ +#region Copyright & License Information +/* + * Copyright 2007-2016 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.Collections.Generic; +using System.Linq; +using OpenRA.Mods.Common.Traits; +using OpenRA.Traits; + +namespace OpenRA.Mods.Common.Lint +{ + public class CheckConditions : ILintRulesPass + { + public void Run(Action emitError, Action emitWarning, Ruleset rules) + { + foreach (var actorInfo in rules.Actors) + { + if (actorInfo.Key.StartsWith("^", StringComparison.Ordinal)) + continue; + + var granted = new HashSet(); + var consumed = new HashSet(); + + foreach (var trait in actorInfo.Value.TraitInfos()) + { + var fieldConsumed = trait.GetType().GetFields() + .Where(x => x.HasAttribute()) + .SelectMany(f => LintExts.GetFieldValues(trait, f, emitError)); + + var propertyConsumed = trait.GetType().GetProperties() + .Where(x => x.HasAttribute()) + .SelectMany(p => LintExts.GetPropertyValues(trait, p, emitError)); + + var fieldGranted = trait.GetType().GetFields() + .Where(x => x.HasAttribute()) + .SelectMany(f => LintExts.GetFieldValues(trait, f, emitError)); + + var propertyGranted = trait.GetType().GetProperties() + .Where(x => x.HasAttribute()) + .SelectMany(f => LintExts.GetPropertyValues(trait, f, emitError)); + + foreach (var c in fieldConsumed.Concat(propertyConsumed)) + if (!string.IsNullOrEmpty(c)) + consumed.Add(c); + + foreach (var g in fieldGranted.Concat(propertyGranted)) + if (!string.IsNullOrEmpty(g)) + granted.Add(g); + } + + var unconsumed = granted.Except(consumed); + if (unconsumed.Any()) + emitWarning("Actor type `{0}` grants conditions that are not consumed: {1}".F(actorInfo.Key, unconsumed.JoinWith(", "))); + + var ungranted = consumed.Except(granted); + if (ungranted.Any()) + emitError("Actor type `{0}` consumes conditions that are not granted: {1}".F(actorInfo.Key, ungranted.JoinWith(", "))); + + if ((consumed.Any() || granted.Any()) && actorInfo.Value.TraitInfoOrDefault() == null) + emitError("Actor type `{0}` defines conditions but does not include an UpgradeManager".F(actorInfo.Key)); + } + } + } +} \ No newline at end of file diff --git a/OpenRA.Mods.Common/Lint/LintExts.cs b/OpenRA.Mods.Common/Lint/LintExts.cs index 582573790e..f5420e2d54 100644 --- a/OpenRA.Mods.Common/Lint/LintExts.cs +++ b/OpenRA.Mods.Common/Lint/LintExts.cs @@ -10,6 +10,7 @@ #endregion using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; @@ -24,20 +25,37 @@ namespace OpenRA.Mods.Common.Lint var type = fieldInfo.FieldType; if (type == typeof(string)) return new[] { (string)fieldInfo.GetValue(ruleInfo) }; - if (type == typeof(string[])) - return (string[])fieldInfo.GetValue(ruleInfo); - if (type == typeof(HashSet)) - return (HashSet)fieldInfo.GetValue(ruleInfo); + + if (typeof(IEnumerable).IsAssignableFrom(type)) + return fieldInfo.GetValue(ruleInfo) as IEnumerable; + if (type == typeof(BooleanExpression)) { var expr = (BooleanExpression)fieldInfo.GetValue(ruleInfo); return expr != null ? expr.Variables : Enumerable.Empty(); } - emitError("Bad type for reference on {0}.{1}. Supported types: string, string[], HashSet, BooleanExpression" + throw new InvalidOperationException("Bad type for reference on {0}.{1}. Supported types: string, IEnumerable, BooleanExpression" .F(ruleInfo.GetType().Name, fieldInfo.Name)); + } - return new string[] { }; + public static IEnumerable GetPropertyValues(object ruleInfo, PropertyInfo propertyInfo, Action emitError) + { + var type = propertyInfo.PropertyType; + if (type == typeof(string)) + return new[] { (string)propertyInfo.GetValue(ruleInfo) }; + + if (typeof(IEnumerable).IsAssignableFrom(type)) + return (IEnumerable)propertyInfo.GetValue(ruleInfo); + + if (type == typeof(BooleanExpression)) + { + var expr = (BooleanExpression)propertyInfo.GetValue(ruleInfo); + return expr != null ? expr.Variables : Enumerable.Empty(); + } + + throw new InvalidOperationException("Bad type for reference on {0}.{1}. Supported types: string, IEnumerable, BooleanExpression" + .F(ruleInfo.GetType().Name, propertyInfo.Name)); } } } diff --git a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj index debbc18184..7aea862391 100644 --- a/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj +++ b/OpenRA.Mods.Common/OpenRA.Mods.Common.csproj @@ -181,6 +181,7 @@ +