From 1ed1337163b4239d9782e05997d563ee03bde661 Mon Sep 17 00:00:00 2001 From: Marek Safar Date: Fri, 25 Sep 2020 09:35:43 +0200 Subject: Enable some code analyzers and make the build pass (#1505) --- .editorconfig | 63 + Directory.Build.props | 1 + eng/Analyzers.props | 7 + eng/Versions.props | 1 + external/Mono.Options/Options.cs | 1815 ++++++++++++++++++++ .../System/Reflection/AssemblyNameFormatter.cs | 6 +- .../shared/System/Reflection/AssemblyNameParser.cs | 8 +- .../Reflection/Runtime/TypeParsing/TypeParser.cs | 1 + src/ILLink.Tasks/ILLink.Tasks.csproj | 1 + src/ILLink.Tasks/LinkTask.cs | 2 +- src/ILLink.Tasks/Utils.cs | 31 +- src/analyzer/analyzer.csproj | 4 + src/analyzer/common/Mono.Options/Options.cs | 1813 ------------------- src/linker/Linker.Dataflow/ValueNode.cs | 2 + src/linker/Linker.Steps/BodySubstituterStep.cs | 2 +- src/linker/Linker.Steps/LinkAttributesStep.cs | 20 +- src/linker/Linker.Steps/OutputStep.cs | 5 +- src/linker/Linker.Steps/ResolveFromXmlStep.cs | 6 +- src/linker/Linker/AssemblyResolver.cs | 4 +- src/linker/Linker/DirectoryAssemblyResolver.cs | 4 +- src/linker/Linker/Driver.cs | 4 +- src/linker/Linker/DynamicDependency.cs | 1 + src/linker/Linker/Inflater.cs | 97 -- src/linker/Linker/LinkContext.cs | 2 +- src/linker/Linker/MessageContainer.cs | 2 +- src/linker/Linker/MethodBodyScanner.cs | 2 +- src/linker/Linker/Tracer.cs | 3 - src/linker/Mono.Linker.csproj | 1 + test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.csproj | 1 + .../ExpectedExceptionHandlerSequenceAttribute.cs | 2 +- .../ExpectedInstructionSequenceAttribute.cs | 2 +- .../Assertions/ExpectedLocalsSequenceAttribute.cs | 4 +- .../Assertions/KeptInitializerData.cs | 2 +- .../Assertions/NoLinkedOutputAttribute.cs | 1 + .../Mono.Linker.Tests.Cases.Expectations.csproj | 1 + .../Support/PlatformAssemblies.cs | 11 +- .../Mono.Linker.Tests.Cases.csproj | 1 + test/Mono.Linker.Tests/Mono.Linker.Tests.csproj | 1 + 38 files changed, 1964 insertions(+), 1970 deletions(-) create mode 100644 eng/Analyzers.props create mode 100644 external/Mono.Options/Options.cs delete mode 100644 src/analyzer/common/Mono.Options/Options.cs delete mode 100644 src/linker/Linker/Inflater.cs diff --git a/.editorconfig b/.editorconfig index 6750e823a..526def7ad 100644 --- a/.editorconfig +++ b/.editorconfig @@ -39,3 +39,66 @@ csharp_style_conditional_delegate_call = true:suggestion # Avoid redundant accessibility modifiers when they're default dotnet_style_require_accessibility_modifiers = omit_if_default:suggestion + +### Code Style Analyzers + +# IDE0004: Remove unnecessary cast +#dotnet_diagnostic.IDE0004.severity = warning + +# IDE0005: Remove unnecessary usings/imports +dotnet_diagnostic.IDE0005.severity = warning + +# IDE0019: Use pattern matching +#dotnet_diagnostic.IDE0019.severity = warning + +# IDE0020: +dotnet_diagnostic.IDE0020.severity = warning + +# IDE0035: Remove unreachable code +dotnet_diagnostic.IDE0035.severity = warning + +# IDE0036: Order modifiers +#dotnet_diagnostic.IDE0036.severity = warning + +# IDE0043: Format string contains invalid placeholder +dotnet_diagnostic.IDE0043.severity = warning + +# IDE0044: Make field readonly +#dotnet_diagnostic.IDE0044.severity = warning + +# IDE0051: Remove unused private members (no reads or writes) +dotnet_diagnostic.IDE0051.severity = warning + +# IDE0052: Remove unread private member +dotnet_diagnostic.IDE0052.severity = warning + +# IDE0053: Prefer expression bodies for lambdas +dotnet_diagnostic.IDE0053.severity = warning + +# IDE0055: Fix formatting +dotnet_diagnostic.IDE0055.severity = none + +# IDE0059: Unnecessary assignment to a value +#dotnet_diagnostic.IDE0059.severity = warning + +# IDE0060: Remove unused parameter +#dotnet_diagnostic.IDE0060.severity = warning + +# IDE0073: File header +dotnet_diagnostic.IDE0073.severity = suggestion +file_header_template = Licensed to the .NET Foundation under one or more agreements.\nThe .NET Foundation licenses this file to you under the MIT license.\nSee the LICENSE file in the project root for more information. + +## CA analyzer rules +dotnet_analyzer_diagnostic.category-performance.severity = warning +dotnet_analyzer_diagnostic.category-maintainability.severity = warning +dotnet_analyzer_diagnostic.category-reliability.severity = warning + +# CA1834: Use 'StringBuilder.Append(char)' +dotnet_diagnostic.CA1834.severity = none + +# CA1822: Make member static +dotnet_diagnostic.CA1822.severity = none + +[external/**/*.cs] +dotnet_analyzer_diagnostic.severity = none +generated_code = true diff --git a/Directory.Build.props b/Directory.Build.props index e1fcd8960..4f3900653 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -20,6 +20,7 @@ + false MIT diff --git a/eng/Analyzers.props b/eng/Analyzers.props new file mode 100644 index 000000000..cf407bcfc --- /dev/null +++ b/eng/Analyzers.props @@ -0,0 +1,7 @@ + + + + + + + diff --git a/eng/Versions.props b/eng/Versions.props index 37b5bbcb8..a82a6535f 100644 --- a/eng/Versions.props +++ b/eng/Versions.props @@ -21,6 +21,7 @@ 15.4.8 15.4.8 5.0.0-beta.20467.6 + 5.0.0-beta.20471.1 0.11.2 true diff --git a/external/Mono.Options/Options.cs b/external/Mono.Options/Options.cs new file mode 100644 index 000000000..e37e6001d --- /dev/null +++ b/external/Mono.Options/Options.cs @@ -0,0 +1,1815 @@ +#pragma warning disable CA1507,CA1825,CA1834,CA2208 + +// +// Options.cs +// +// Authors: +// Jonathan Pryor , +// Federico Di Gregorio +// Rolf Bjarne Kvinge +// +// Copyright (C) 2008 Novell (http://www.novell.com) +// Copyright (C) 2009 Federico Di Gregorio. +// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com) +// Copyright (C) 2017 Microsoft Corporation (http://www.microsoft.com) +// +// Permission is hereby granted, free of charge, to any person obtaining +// a copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to +// permit persons to whom the Software is furnished to do so, subject to +// the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +// + +// Compile With: +// mcs -debug+ -r:System.Core Options.cs -o:Mono.Options.dll -t:library +// mcs -debug+ -d:LINQ -r:System.Core Options.cs -o:Mono.Options.dll -t:library +// +// The LINQ version just changes the implementation of +// OptionSet.Parse(IEnumerable), and confers no semantic changes. + +// +// A Getopt::Long-inspired option parsing library for C#. +// +// Mono.Options.OptionSet is built upon a key/value table, where the +// key is a option format string and the value is a delegate that is +// invoked when the format string is matched. +// +// Option format strings: +// Regex-like BNF Grammar: +// name: .+ +// type: [=:] +// sep: ( [^{}]+ | '{' .+ '}' )? +// aliases: ( name type sep ) ( '|' name type sep )* +// +// Each '|'-delimited name is an alias for the associated action. If the +// format string ends in a '=', it has a required value. If the format +// string ends in a ':', it has an optional value. If neither '=' or ':' +// is present, no value is supported. `=' or `:' need only be defined on one +// alias, but if they are provided on more than one they must be consistent. +// +// Each alias portion may also end with a "key/value separator", which is used +// to split option values if the option accepts > 1 value. If not specified, +// it defaults to '=' and ':'. If specified, it can be any character except +// '{' and '}' OR the *string* between '{' and '}'. If no separator should be +// used (i.e. the separate values should be distinct arguments), then "{}" +// should be used as the separator. +// +// Options are extracted either from the current option by looking for +// the option name followed by an '=' or ':', or is taken from the +// following option IFF: +// - The current option does not contain a '=' or a ':' +// - The current option requires a value (i.e. not a Option type of ':') +// +// The `name' used in the option format string does NOT include any leading +// option indicator, such as '-', '--', or '/'. All three of these are +// permitted/required on any named option. +// +// Option bundling is permitted so long as: +// - '-' is used to start the option group +// - all of the bundled options are a single character +// - at most one of the bundled options accepts a value, and the value +// provided starts from the next character to the end of the string. +// +// This allows specifying '-a -b -c' as '-abc', and specifying '-D name=value' +// as '-Dname=value'. +// +// Option processing is disabled by specifying "--". All options after "--" +// are returned by OptionSet.Parse() unchanged and unprocessed. +// +// Unprocessed options are returned from OptionSet.Parse(). +// +// Examples: +// int verbose = 0; +// OptionSet p = new OptionSet () +// .Add ("v", v => ++verbose) +// .Add ("name=|value=", v => Console.WriteLine (v)); +// p.Parse (new string[]{"-v", "--v", "/v", "-name=A", "/name", "B", "extra"}); +// +// The above would parse the argument string array, and would invoke the +// lambda expression three times, setting `verbose' to 3 when complete. +// It would also print out "A" and "B" to standard output. +// The returned array would contain the string "extra". +// +// C# 3.0 collection initializers are supported and encouraged: +// var p = new OptionSet () { +// { "h|?|help", v => ShowHelp () }, +// }; +// +// System.ComponentModel.TypeConverter is also supported, allowing the use of +// custom data types in the callback type; TypeConverter.ConvertFromString() +// is used to convert the value option to an instance of the specified +// type: +// +// var p = new OptionSet () { +// { "foo=", (Foo f) => Console.WriteLine (f.ToString ()) }, +// }; +// +// Random other tidbits: +// - Boolean options (those w/o '=' or ':' in the option format string) +// are explicitly enabled if they are followed with '+', and explicitly +// disabled if they are followed with '-': +// string a = null; +// var p = new OptionSet () { +// { "a", s => a = s }, +// }; +// p.Parse (new string[]{"-a"}); // sets v != null +// p.Parse (new string[]{"-a+"}); // sets v != null +// p.Parse (new string[]{"-a-"}); // sets v == null +// + +// +// Mono.Options.CommandSet allows easily having separate commands and +// associated command options, allowing creation of a *suite* along the +// lines of **git**(1), **svn**(1), etc. +// +// CommandSet allows intermixing plain text strings for `--help` output, +// Option values -- as supported by OptionSet -- and Command instances, +// which have a name, optional help text, and an optional OptionSet. +// +// var suite = new CommandSet ("suite-name") { +// // Use strings and option values, as with OptionSet +// "usage: suite-name COMMAND [OPTIONS]+", +// { "v:", "verbosity", (int? v) => Verbosity = v.HasValue ? v.Value : Verbosity+1 }, +// // Commands may also be specified +// new Command ("command-name", "command help") { +// Options = new OptionSet {/*...*/}, +// Run = args => { /*...*/}, +// }, +// new MyCommandSubclass (), +// }; +// return suite.Run (new string[]{...}); +// +// CommandSet provides a `help` command, and forwards `help COMMAND` +// to the registered Command instance by invoking Command.Invoke() +// with `--help` as an option. +// + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Globalization; +using System.IO; +#if PCL +using System.Reflection; +#else +using System.Runtime.Serialization; +using System.Security.Permissions; +#endif +using System.Text; +using System.Text.RegularExpressions; + +#if LINQ +using System.Linq; +#endif + +#if TEST +using NDesk.Options; +#endif + +#if PCL +using MessageLocalizerConverter = System.Func; +#else +using MessageLocalizerConverter = System.Converter; +#endif + +#if NDESK_OPTIONS +namespace NDesk.Options +#else +namespace Mono.Options +#endif +{ + static class StringCoda { + + public static IEnumerable WrappedLines (string self, params int[] widths) + { + IEnumerable w = widths; + return WrappedLines (self, w); + } + + public static IEnumerable WrappedLines (string self, IEnumerable widths) + { + if (widths == null) + throw new ArgumentNullException ("widths"); + return CreateWrappedLinesIterator (self, widths); + } + + private static IEnumerable CreateWrappedLinesIterator (string self, IEnumerable widths) + { + if (string.IsNullOrEmpty (self)) { + yield return string.Empty; + yield break; + } + using (IEnumerator ewidths = widths.GetEnumerator ()) { + bool? hw = null; + int width = GetNextWidth (ewidths, int.MaxValue, ref hw); + int start = 0, end; + do { + end = GetLineEnd (start, width, self); + char c = self [end-1]; + if (char.IsWhiteSpace (c)) + --end; + bool needContinuation = end != self.Length && !IsEolChar (c); + string continuation = ""; + if (needContinuation) { + --end; + continuation = "-"; + } + string line = self.Substring (start, end - start) + continuation; + yield return line; + start = end; + if (char.IsWhiteSpace (c)) + ++start; + width = GetNextWidth (ewidths, width, ref hw); + } while (start < self.Length); + } + } + + private static int GetNextWidth (IEnumerator ewidths, int curWidth, ref bool? eValid) + { + if (!eValid.HasValue || (eValid.HasValue && eValid.Value)) { + curWidth = (eValid = ewidths.MoveNext ()).Value ? ewidths.Current : curWidth; + // '.' is any character, - is for a continuation + const string minWidth = ".-"; + if (curWidth < minWidth.Length) + throw new ArgumentOutOfRangeException ("widths", + string.Format ("Element must be >= {0}, was {1}.", minWidth.Length, curWidth)); + return curWidth; + } + // no more elements, use the last element. + return curWidth; + } + + private static bool IsEolChar (char c) + { + return !char.IsLetterOrDigit (c); + } + + private static int GetLineEnd (int start, int length, string description) + { + int end = System.Math.Min (start + length, description.Length); + int sep = -1; + for (int i = start; i < end; ++i) { + if (description [i] == '\n') + return i+1; + if (IsEolChar (description [i])) + sep = i+1; + } + if (sep == -1 || end == description.Length) + return end; + return sep; + } + } + + public class OptionValueCollection : IList, IList { + + List values = new List (); + OptionContext c; + + internal OptionValueCollection (OptionContext c) + { + this.c = c; + } + + #region ICollection + void ICollection.CopyTo (Array array, int index) {(values as ICollection).CopyTo (array, index);} + bool ICollection.IsSynchronized {get {return (values as ICollection).IsSynchronized;}} + object ICollection.SyncRoot {get {return (values as ICollection).SyncRoot;}} + #endregion + + #region ICollection + public void Add (string item) {values.Add (item);} + public void Clear () {values.Clear ();} + public bool Contains (string item) {return values.Contains (item);} + public void CopyTo (string[] array, int arrayIndex) {values.CopyTo (array, arrayIndex);} + public bool Remove (string item) {return values.Remove (item);} + public int Count {get {return values.Count;}} + public bool IsReadOnly {get {return false;}} + #endregion + + #region IEnumerable + IEnumerator IEnumerable.GetEnumerator () {return values.GetEnumerator ();} + #endregion + + #region IEnumerable + public IEnumerator GetEnumerator () {return values.GetEnumerator ();} + #endregion + + #region IList + int IList.Add (object value) {return (values as IList).Add (value);} + bool IList.Contains (object value) {return (values as IList).Contains (value);} + int IList.IndexOf (object value) {return (values as IList).IndexOf (value);} + void IList.Insert (int index, object value) {(values as IList).Insert (index, value);} + void IList.Remove (object value) {(values as IList).Remove (value);} + void IList.RemoveAt (int index) {(values as IList).RemoveAt (index);} + bool IList.IsFixedSize {get {return false;}} + object IList.this [int index] {get {return this [index];} set {(values as IList)[index] = value;}} + #endregion + + #region IList + public int IndexOf (string item) {return values.IndexOf (item);} + public void Insert (int index, string item) {values.Insert (index, item);} + public void RemoveAt (int index) {values.RemoveAt (index);} + + private void AssertValid (int index) + { + if (c.Option == null) + throw new InvalidOperationException ("OptionContext.Option is null."); + if (index >= c.Option.MaxValueCount) + throw new ArgumentOutOfRangeException ("index"); + if (c.Option.OptionValueType == OptionValueType.Required && + index >= values.Count) + throw new OptionException (string.Format ( + c.OptionSet.MessageLocalizer ("Missing required value for option '{0}'."), c.OptionName), + c.OptionName); + } + + public string this [int index] { + get { + AssertValid (index); + return index >= values.Count ? null : values [index]; + } + set { + values [index] = value; + } + } + #endregion + + public List ToList () + { + return new List (values); + } + + public string[] ToArray () + { + return values.ToArray (); + } + + public override string ToString () + { + return string.Join (", ", values.ToArray ()); + } + } + + public class OptionContext { + private Option option; + private string name; + private int index; + private OptionSet set; + private OptionValueCollection c; + + public OptionContext (OptionSet set) + { + this.set = set; + this.c = new OptionValueCollection (this); + } + + public Option Option { + get {return option;} + set {option = value;} + } + + public string OptionName { + get {return name;} + set {name = value;} + } + + public int OptionIndex { + get {return index;} + set {index = value;} + } + + public OptionSet OptionSet { + get {return set;} + } + + public OptionValueCollection OptionValues { + get {return c;} + } + } + + public enum OptionValueType { + None, + Optional, + Required, + } + + public abstract class Option { + string prototype, description; + string[] names; + OptionValueType type; + int count; + string[] separators; + bool hidden; + + protected Option (string prototype, string description) + : this (prototype, description, 1, false) + { + } + + protected Option (string prototype, string description, int maxValueCount) + : this (prototype, description, maxValueCount, false) + { + } + + protected Option (string prototype, string description, int maxValueCount, bool hidden) + { + if (prototype == null) + throw new ArgumentNullException ("prototype"); + if (prototype.Length == 0) + throw new ArgumentException ("Cannot be the empty string.", "prototype"); + if (maxValueCount < 0) + throw new ArgumentOutOfRangeException ("maxValueCount"); + + this.prototype = prototype; + this.description = description; + this.count = maxValueCount; + this.names = (this is OptionSet.Category) + // append GetHashCode() so that "duplicate" categories have distinct + // names, e.g. adding multiple "" categories should be valid. + ? new[]{prototype + this.GetHashCode ()} + : prototype.Split ('|'); + + if (this is OptionSet.Category || this is CommandOption) + return; + + this.type = ParsePrototype (); + this.hidden = hidden; + + if (this.count == 0 && type != OptionValueType.None) + throw new ArgumentException ( + "Cannot provide maxValueCount of 0 for OptionValueType.Required or " + + "OptionValueType.Optional.", + "maxValueCount"); + if (this.type == OptionValueType.None && maxValueCount > 1) + throw new ArgumentException ( + string.Format ("Cannot provide maxValueCount of {0} for OptionValueType.None.", maxValueCount), + "maxValueCount"); + if (Array.IndexOf (names, "<>") >= 0 && + ((names.Length == 1 && this.type != OptionValueType.None) || + (names.Length > 1 && this.MaxValueCount > 1))) + throw new ArgumentException ( + "The default option handler '<>' cannot require values.", + "prototype"); + } + + public string Prototype {get {return prototype;}} + public string Description {get {return description;}} + public OptionValueType OptionValueType {get {return type;}} + public int MaxValueCount {get {return count;}} + public bool Hidden {get {return hidden;}} + + public string[] GetNames () + { + return (string[]) names.Clone (); + } + + public string[] GetValueSeparators () + { + if (separators == null) + return new string [0]; + return (string[]) separators.Clone (); + } + + protected static T Parse (string value, OptionContext c) + { + Type tt = typeof (T); +#if PCL + TypeInfo ti = tt.GetTypeInfo (); +#else + Type ti = tt; +#endif + bool nullable = + ti.IsValueType && + ti.IsGenericType && + !ti.IsGenericTypeDefinition && + ti.GetGenericTypeDefinition () == typeof (Nullable<>); +#if PCL + Type targetType = nullable ? tt.GenericTypeArguments [0] : tt; +#else + Type targetType = nullable ? tt.GetGenericArguments () [0] : tt; +#endif + T t = default (T); + try { + if (value != null) { +#if PCL + if (targetType.GetTypeInfo ().IsEnum) + t = (T) Enum.Parse (targetType, value, true); + else + t = (T) Convert.ChangeType (value, targetType); +#else + TypeConverter conv = TypeDescriptor.GetConverter (targetType); + t = (T) conv.ConvertFromString (value); +#endif + } + } + catch (Exception e) { + throw new OptionException ( + string.Format ( + c.OptionSet.MessageLocalizer ("Could not convert string `{0}' to type {1} for option `{2}'."), + value, targetType.Name, c.OptionName), + c.OptionName, e); + } + return t; + } + + internal string[] Names {get {return names;}} + internal string[] ValueSeparators {get {return separators;}} + + static readonly char[] NameTerminator = new char[]{'=', ':'}; + + private OptionValueType ParsePrototype () + { + char type = '\0'; + List seps = new List (); + for (int i = 0; i < names.Length; ++i) { + string name = names [i]; + if (name.Length == 0) + throw new ArgumentException ("Empty option names are not supported.", "prototype"); + + int end = name.IndexOfAny (NameTerminator); + if (end == -1) + continue; + names [i] = name.Substring (0, end); + if (type == '\0' || type == name [end]) + type = name [end]; + else + throw new ArgumentException ( + string.Format ("Conflicting option types: '{0}' vs. '{1}'.", type, name [end]), + "prototype"); + AddSeparators (name, end, seps); + } + + if (type == '\0') + return OptionValueType.None; + + if (count <= 1 && seps.Count != 0) + throw new ArgumentException ( + string.Format ("Cannot provide key/value separators for Options taking {0} value(s).", count), + "prototype"); + if (count > 1) { + if (seps.Count == 0) + this.separators = new string[]{":", "="}; + else if (seps.Count == 1 && seps [0].Length == 0) + this.separators = null; + else + this.separators = seps.ToArray (); + } + + return type == '=' ? OptionValueType.Required : OptionValueType.Optional; + } + + private static void AddSeparators (string name, int end, ICollection seps) + { + int start = -1; + for (int i = end+1; i < name.Length; ++i) { + switch (name [i]) { + case '{': + if (start != -1) + throw new ArgumentException ( + string.Format ("Ill-formed name/value separator found in \"{0}\".", name), + "prototype"); + start = i+1; + break; + case '}': + if (start == -1) + throw new ArgumentException ( + string.Format ("Ill-formed name/value separator found in \"{0}\".", name), + "prototype"); + seps.Add (name.Substring (start, i-start)); + start = -1; + break; + default: + if (start == -1) + seps.Add (name [i].ToString ()); + break; + } + } + if (start != -1) + throw new ArgumentException ( + string.Format ("Ill-formed name/value separator found in \"{0}\".", name), + "prototype"); + } + + public void Invoke (OptionContext c) + { + OnParseComplete (c); + c.OptionName = null; + c.Option = null; + c.OptionValues.Clear (); + } + + protected abstract void OnParseComplete (OptionContext c); + + internal void InvokeOnParseComplete (OptionContext c) + { + OnParseComplete (c); + } + + public override string ToString () + { + return Prototype; + } + } + + public abstract class ArgumentSource { + + protected ArgumentSource () + { + } + + public abstract string[] GetNames (); + public abstract string Description { get; } + public abstract bool GetArguments (string value, out IEnumerable replacement); + +#if !PCL || NETSTANDARD1_3 + public static IEnumerable GetArgumentsFromFile (string file) + { + return GetArguments (File.OpenText (file), true); + } +#endif + + public static IEnumerable GetArguments (TextReader reader) + { + return GetArguments (reader, false); + } + + // Cribbed from mcs/driver.cs:LoadArgs(string) + static IEnumerable GetArguments (TextReader reader, bool close) + { + try { + StringBuilder arg = new StringBuilder (); + + string line; + while ((line = reader.ReadLine ()) != null) { + int t = line.Length; + + for (int i = 0; i < t; i++) { + char c = line [i]; + + if (c == '"' || c == '\'') { + char end = c; + + for (i++; i < t; i++){ + c = line [i]; + + if (c == end) + break; + arg.Append (c); + } + } else if (c == ' ') { + if (arg.Length > 0) { + yield return arg.ToString (); + arg.Length = 0; + } + } else + arg.Append (c); + } + if (arg.Length > 0) { + yield return arg.ToString (); + arg.Length = 0; + } + } + } + finally { + if (close) + reader.Dispose (); + } + } + } + +#if !PCL || NETSTANDARD1_3 + public class ResponseFileSource : ArgumentSource { + + public override string[] GetNames () + { + return new string[]{"@file"}; + } + + public override string Description { + get {return "Read response file for more options.";} + } + + public override bool GetArguments (string value, out IEnumerable replacement) + { + if (string.IsNullOrEmpty (value) || !value.StartsWith ("@")) { + replacement = null; + return false; + } + replacement = ArgumentSource.GetArgumentsFromFile (value.Substring (1)); + return true; + } + } +#endif + +#if !PCL + [Serializable] +#endif + public class OptionException : Exception { + private string option; + + public OptionException () + { + } + + public OptionException (string message, string optionName) + : base (message) + { + this.option = optionName; + } + + public OptionException (string message, string optionName, Exception innerException) + : base (message, innerException) + { + this.option = optionName; + } + +#if !PCL + protected OptionException (SerializationInfo info, StreamingContext context) + : base (info, context) + { + this.option = info.GetString ("OptionName"); + } +#endif + + public string OptionName { + get {return this.option;} + } + +#if !PCL && !NETCOREAPP +#pragma warning disable 618 // SecurityPermissionAttribute is obsolete + [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)] +#pragma warning restore 618 + public override void GetObjectData (SerializationInfo info, StreamingContext context) + { + base.GetObjectData (info, context); + info.AddValue ("OptionName", option); + } +#endif + } + + public delegate void OptionAction (TKey key, TValue value); + + public class OptionSet : KeyedCollection + { + public OptionSet () + : this (null) + { + } + + public OptionSet (MessageLocalizerConverter localizer) + { + this.roSources = new ReadOnlyCollection (sources); + this.localizer = localizer; + if (this.localizer == null) { + this.localizer = delegate (string f) { + return f; + }; + } + } + + MessageLocalizerConverter localizer; + + public MessageLocalizerConverter MessageLocalizer { + get {return localizer;} + internal set {localizer = value;} + } + + List sources = new List (); + ReadOnlyCollection roSources; + + public ReadOnlyCollection ArgumentSources { + get {return roSources;} + } + + + protected override string GetKeyForItem (Option item) + { + if (item == null) + throw new ArgumentNullException ("option"); + if (item.Names != null && item.Names.Length > 0) + return item.Names [0]; + // This should never happen, as it's invalid for Option to be + // constructed w/o any names. + throw new InvalidOperationException ("Option has no names!"); + } + + [Obsolete ("Use KeyedCollection.this[string]")] + protected Option GetOptionForName (string option) + { + if (option == null) + throw new ArgumentNullException ("option"); + try { + return base [option]; + } + catch (KeyNotFoundException) { + return null; + } + } + + protected override void InsertItem (int index, Option item) + { + base.InsertItem (index, item); + AddImpl (item); + } + + protected override void RemoveItem (int index) + { + Option p = Items [index]; + base.RemoveItem (index); + // KeyedCollection.RemoveItem() handles the 0th item + for (int i = 1; i < p.Names.Length; ++i) { + Dictionary.Remove (p.Names [i]); + } + } + + protected override void SetItem (int index, Option item) + { + base.SetItem (index, item); + AddImpl (item); + } + + private void AddImpl (Option option) + { + if (option == null) + throw new ArgumentNullException ("option"); + List added = new List (option.Names.Length); + try { + // KeyedCollection.InsertItem/SetItem handle the 0th name. + for (int i = 1; i < option.Names.Length; ++i) { + Dictionary.Add (option.Names [i], option); + added.Add (option.Names [i]); + } + } + catch (Exception) { + foreach (string name in added) + Dictionary.Remove (name); + throw; + } + } + + public OptionSet Add (string header) + { + if (header == null) + throw new ArgumentNullException ("header"); + Add (new Category (header)); + return this; + } + + internal sealed class Category : Option { + + // Prototype starts with '=' because this is an invalid prototype + // (see Option.ParsePrototype(), and thus it'll prevent Category + // instances from being accidentally used as normal options. + public Category (string description) + : base ("=:Category:= " + description, description) + { + } + + protected override void OnParseComplete (OptionContext c) + { + throw new NotSupportedException ("Category.OnParseComplete should not be invoked."); + } + } + + + public new OptionSet Add (Option option) + { + base.Add (option); + return this; + } + + sealed class ActionOption : Option { + Action action; + + public ActionOption (string prototype, string description, int count, Action action) + : this (prototype, description, count, action, false) + { + } + + public ActionOption (string prototype, string description, int count, Action action, bool hidden) + : base (prototype, description, count, hidden) + { + if (action == null) + throw new ArgumentNullException ("action"); + this.action = action; + } + + protected override void OnParseComplete (OptionContext c) + { + action (c.OptionValues); + } + } + + public OptionSet Add (string prototype, Action action) + { + return Add (prototype, null, action); + } + + public OptionSet Add (string prototype, string description, Action action) + { + return Add (prototype, description, action, false); + } + + public OptionSet Add (string prototype, string description, Action action, bool hidden) + { + if (action == null) + throw new ArgumentNullException ("action"); + Option p = new ActionOption (prototype, description, 1, + delegate (OptionValueCollection v) { action (v [0]); }, hidden); + base.Add (p); + return this; + } + + public OptionSet Add (string prototype, OptionAction action) + { + return Add (prototype, null, action); + } + + public OptionSet Add (string prototype, string description, OptionAction action) + { + return Add (prototype, description, action, false); + } + + public OptionSet Add (string prototype, string description, OptionAction action, bool hidden) { + if (action == null) + throw new ArgumentNullException ("action"); + Option p = new ActionOption (prototype, description, 2, + delegate (OptionValueCollection v) {action (v [0], v [1]);}, hidden); + base.Add (p); + return this; + } + + sealed class ActionOption : Option { + Action action; + + public ActionOption (string prototype, string description, Action action) + : base (prototype, description, 1) + { + if (action == null) + throw new ArgumentNullException ("action"); + this.action = action; + } + + protected override void OnParseComplete (OptionContext c) + { + action (Parse (c.OptionValues [0], c)); + } + } + + sealed class ActionOption : Option { + OptionAction action; + + public ActionOption (string prototype, string description, OptionAction action) + : base (prototype, description, 2) + { + if (action == null) + throw new ArgumentNullException ("action"); + this.action = action; + } + + protected override void OnParseComplete (OptionContext c) + { + action ( + Parse (c.OptionValues [0], c), + Parse (c.OptionValues [1], c)); + } + } + + public OptionSet Add (string prototype, Action action) + { + return Add (prototype, null, action); + } + + public OptionSet Add (string prototype, string description, Action action) + { + return Add (new ActionOption (prototype, description, action)); + } + + public OptionSet Add (string prototype, OptionAction action) + { + return Add (prototype, null, action); + } + + public OptionSet Add (string prototype, string description, OptionAction action) + { + return Add (new ActionOption (prototype, description, action)); + } + + public OptionSet Add (ArgumentSource source) + { + if (source == null) + throw new ArgumentNullException ("source"); + sources.Add (source); + return this; + } + + protected virtual OptionContext CreateOptionContext () + { + return new OptionContext (this); + } + + public List Parse (IEnumerable arguments) + { + if (arguments == null) + throw new ArgumentNullException ("arguments"); + OptionContext c = CreateOptionContext (); + c.OptionIndex = -1; + bool process = true; + List unprocessed = new List (); + Option def = Contains ("<>") ? this ["<>"] : null; + ArgumentEnumerator ae = new ArgumentEnumerator (arguments); + foreach (string argument in ae) { + ++c.OptionIndex; + if (argument == "--") { + process = false; + continue; + } + if (!process) { + Unprocessed (unprocessed, def, c, argument); + continue; + } + if (AddSource (ae, argument)) + continue; + if (!Parse (argument, c)) + Unprocessed (unprocessed, def, c, argument); + } + if (c.Option != null) + c.Option.Invoke (c); + return unprocessed; + } + + class ArgumentEnumerator : IEnumerable { + List> sources = new List> (); + + public ArgumentEnumerator (IEnumerable arguments) + { + sources.Add (arguments.GetEnumerator ()); + } + + public void Add (IEnumerable arguments) + { + sources.Add (arguments.GetEnumerator ()); + } + + public IEnumerator GetEnumerator () + { + do { + IEnumerator c = sources [sources.Count-1]; + if (c.MoveNext ()) + yield return c.Current; + else { + c.Dispose (); + sources.RemoveAt (sources.Count-1); + } + } while (sources.Count > 0); + } + + IEnumerator IEnumerable.GetEnumerator () + { + return GetEnumerator (); + } + } + + bool AddSource (ArgumentEnumerator ae, string argument) + { + foreach (ArgumentSource source in sources) { + IEnumerable replacement; + if (!source.GetArguments (argument, out replacement)) + continue; + ae.Add (replacement); + return true; + } + return false; + } + + private static bool Unprocessed (ICollection extra, Option def, OptionContext c, string argument) + { + if (def == null) { + extra.Add (argument); + return false; + } + c.OptionValues.Add (argument); + c.Option = def; + c.Option.Invoke (c); + return false; + } + + private readonly Regex ValueOption = new Regex ( + @"^(?--|-|/)(?[^:=]+)((?[:=])(?.*))?$"); + + protected bool GetOptionParts (string argument, out string flag, out string name, out string sep, out string value) + { + if (argument == null) + throw new ArgumentNullException ("argument"); + + flag = name = sep = value = null; + Match m = ValueOption.Match (argument); + if (!m.Success) { + return false; + } + flag = m.Groups ["flag"].Value; + name = m.Groups ["name"].Value; + if (m.Groups ["sep"].Success && m.Groups ["value"].Success) { + sep = m.Groups ["sep"].Value; + value = m.Groups ["value"].Value; + } + return true; + } + + protected virtual bool Parse (string argument, OptionContext c) + { + if (c.Option != null) { + ParseValue (argument, c); + return true; + } + + string f, n, s, v; + if (!GetOptionParts (argument, out f, out n, out s, out v)) + return false; + + Option p; + if (Contains (n)) { + p = this [n]; + c.OptionName = f + n; + c.Option = p; + switch (p.OptionValueType) { + case OptionValueType.None: + c.OptionValues.Add (n); + c.Option.Invoke (c); + break; + case OptionValueType.Optional: + case OptionValueType.Required: + ParseValue (v, c); + break; + } + return true; + } + // no match; is it a bool option? + if (ParseBool (argument, n, c)) + return true; + // is it a bundled option? + if (ParseBundledValue (f, string.Concat (n + s + v), c)) + return true; + + return false; + } + + private void ParseValue (string option, OptionContext c) + { + if (option != null) + foreach (string o in c.Option.ValueSeparators != null + ? option.Split (c.Option.ValueSeparators, c.Option.MaxValueCount - c.OptionValues.Count, StringSplitOptions.None) + : new string[]{option}) { + c.OptionValues.Add (o); + } + if (c.OptionValues.Count == c.Option.MaxValueCount || + c.Option.OptionValueType == OptionValueType.Optional) + c.Option.Invoke (c); + else if (c.OptionValues.Count > c.Option.MaxValueCount) { + throw new OptionException (localizer (string.Format ( + "Error: Found {0} option values when expecting {1}.", + c.OptionValues.Count, c.Option.MaxValueCount)), + c.OptionName); + } + } + + private bool ParseBool (string option, string n, OptionContext c) + { + Option p; + string rn; + if (n.Length >= 1 && (n [n.Length-1] == '+' || n [n.Length-1] == '-') && + Contains ((rn = n.Substring (0, n.Length-1)))) { + p = this [rn]; + string v = n [n.Length-1] == '+' ? option : null; + c.OptionName = option; + c.Option = p; + c.OptionValues.Add (v); + p.Invoke (c); + return true; + } + return false; + } + + private bool ParseBundledValue (string f, string n, OptionContext c) + { + if (f != "-") + return false; + for (int i = 0; i < n.Length; ++i) { + Option p; + string opt = f + n [i].ToString (); + string rn = n [i].ToString (); + if (!Contains (rn)) { + if (i == 0) + return false; + throw new OptionException (string.Format (localizer ( + "Cannot use unregistered option '{0}' in bundle '{1}'."), rn, f + n), null); + } + p = this [rn]; + switch (p.OptionValueType) { + case OptionValueType.None: + Invoke (c, opt, n, p); + break; + case OptionValueType.Optional: + case OptionValueType.Required: { + string v = n.Substring (i+1); + c.Option = p; + c.OptionName = opt; + ParseValue (v.Length != 0 ? v : null, c); + return true; + } + default: + throw new InvalidOperationException ("Unknown OptionValueType: " + p.OptionValueType); + } + } + return true; + } + + private static void Invoke (OptionContext c, string name, string value, Option option) + { + c.OptionName = name; + c.Option = option; + c.OptionValues.Add (value); + option.Invoke (c); + } + + private const int OptionWidth = 29; + private const int Description_FirstWidth = 80 - OptionWidth; + private const int Description_RemWidth = 80 - OptionWidth - 2; + + static readonly string CommandHelpIndentStart = new string (' ', OptionWidth); + static readonly string CommandHelpIndentRemaining = new string (' ', OptionWidth + 2); + + public void WriteOptionDescriptions (TextWriter o) + { + foreach (Option p in this) { + int written = 0; + + if (p.Hidden) + continue; + + Category c = p as Category; + if (c != null) { + WriteDescription (o, p.Description, "", 80, 80); + continue; + } + CommandOption co = p as CommandOption; + if (co != null) { + WriteCommandDescription (o, co.Command); + continue; + } + + if (!WriteOptionPrototype (o, p, ref written)) + continue; + + if (written < OptionWidth) + o.Write (new string (' ', OptionWidth - written)); + else { + o.WriteLine (); + o.Write (new string (' ', OptionWidth)); + } + + WriteDescription (o, p.Description, new string (' ', OptionWidth+2), + Description_FirstWidth, Description_RemWidth); + } + + foreach (ArgumentSource s in sources) { + string[] names = s.GetNames (); + if (names == null || names.Length == 0) + continue; + + int written = 0; + + Write (o, ref written, " "); + Write (o, ref written, names [0]); + for (int i = 1; i < names.Length; ++i) { + Write (o, ref written, ", "); + Write (o, ref written, names [i]); + } + + if (written < OptionWidth) + o.Write (new string (' ', OptionWidth - written)); + else { + o.WriteLine (); + o.Write (new string (' ', OptionWidth)); + } + + WriteDescription (o, s.Description, new string (' ', OptionWidth+2), + Description_FirstWidth, Description_RemWidth); + } + } + + internal void WriteCommandDescription (TextWriter o, Command c) + { + var name = new string (' ', 8) + c.Name; + if (name.Length < OptionWidth - 1) { + WriteDescription (o, name + new string (' ', OptionWidth - name.Length) + c.Help, CommandHelpIndentRemaining, 80, Description_RemWidth); + } else { + WriteDescription (o, name, "", 80, 80); + WriteDescription (o, CommandHelpIndentStart + c.Help, CommandHelpIndentRemaining, 80, Description_RemWidth); + } + } + + void WriteDescription (TextWriter o, string value, string prefix, int firstWidth, int remWidth) + { + bool indent = false; + foreach (string line in GetLines (localizer (GetDescription (value)), firstWidth, remWidth)) { + if (indent) + o.Write (prefix); + o.WriteLine (line); + indent = true; + } + } + + bool WriteOptionPrototype (TextWriter o, Option p, ref int written) + { + string[] names = p.Names; + + int i = GetNextOptionIndex (names, 0); + if (i == names.Length) + return false; + + if (names [i].Length == 1) { + Write (o, ref written, " -"); + Write (o, ref written, names [0]); + } + else { + Write (o, ref written, " --"); + Write (o, ref written, names [0]); + } + + for ( i = GetNextOptionIndex (names, i+1); + i < names.Length; i = GetNextOptionIndex (names, i+1)) { + Write (o, ref written, ", "); + Write (o, ref written, names [i].Length == 1 ? "-" : "--"); + Write (o, ref written, names [i]); + } + + if (p.OptionValueType == OptionValueType.Optional || + p.OptionValueType == OptionValueType.Required) { + if (p.OptionValueType == OptionValueType.Optional) { + Write (o, ref written, localizer ("[")); + } + Write (o, ref written, localizer ("=" + GetArgumentName (0, p.MaxValueCount, p.Description))); + string sep = p.ValueSeparators != null && p.ValueSeparators.Length > 0 + ? p.ValueSeparators [0] + : " "; + for (int c = 1; c < p.MaxValueCount; ++c) { + Write (o, ref written, localizer (sep + GetArgumentName (c, p.MaxValueCount, p.Description))); + } + if (p.OptionValueType == OptionValueType.Optional) { + Write (o, ref written, localizer ("]")); + } + } + return true; + } + + static int GetNextOptionIndex (string[] names, int i) + { + while (i < names.Length && names [i] == "<>") { + ++i; + } + return i; + } + + static void Write (TextWriter o, ref int n, string s) + { + n += s.Length; + o.Write (s); + } + + private static string GetArgumentName (int index, int maxIndex, string description) + { + if (description == null) + return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1); + string[] nameStart; + if (maxIndex == 1) + nameStart = new string[]{"{0:", "{"}; + else + nameStart = new string[]{"{" + index + ":"}; + for (int i = 0; i < nameStart.Length; ++i) { + int start, j = 0; + do { + start = description.IndexOf (nameStart [i], j); + } while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false); + if (start == -1) + continue; + int end = description.IndexOf ("}", start); + if (end == -1) + continue; + return description.Substring (start + nameStart [i].Length, end - start - nameStart [i].Length); + } + return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1); + } + + private static string GetDescription (string description) + { + if (description == null) + return string.Empty; + StringBuilder sb = new StringBuilder (description.Length); + int start = -1; + for (int i = 0; i < description.Length; ++i) { + switch (description [i]) { + case '{': + if (i == start) { + sb.Append ('{'); + start = -1; + } + else if (start < 0) + start = i + 1; + break; + case '}': + if (start < 0) { + if ((i+1) == description.Length || description [i+1] != '}') + throw new InvalidOperationException ("Invalid option description: " + description); + ++i; + sb.Append ("}"); + } + else { + sb.Append (description.Substring (start, i - start)); + start = -1; + } + break; + case ':': + if (start < 0) + goto default; + start = i + 1; + break; + default: + if (start < 0) + sb.Append (description [i]); + break; + } + } + return sb.ToString (); + } + + private static IEnumerable GetLines (string description, int firstWidth, int remWidth) + { + return StringCoda.WrappedLines (description, firstWidth, remWidth); + } + } + + public class Command + { + public string Name {get;} + public string Help {get;} + + public OptionSet Options {get; set;} + public Action> Run {get; set;} + + public CommandSet CommandSet {get; internal set;} + + public Command (string name, string help = null) + { + if (string.IsNullOrEmpty (name)) + throw new ArgumentNullException (nameof (name)); + + Name = name; + Help = help; + } + + public virtual int Invoke (IEnumerable arguments) + { + var rest = Options?.Parse (arguments) ?? arguments; + Run?.Invoke (rest); + return 0; + } + } + + class CommandOption : Option + { + public Command Command {get;} + + // Prototype starts with '=' because this is an invalid prototype + // (see Option.ParsePrototype(), and thus it'll prevent Category + // instances from being accidentally used as normal options. + public CommandOption (Command command, bool hidden = false) + : base ("=:Command:= " + command?.Name, command?.Name, maxValueCount: 0, hidden: hidden) + { + if (command == null) + throw new ArgumentNullException (nameof (command)); + Command = command; + } + + protected override void OnParseComplete (OptionContext c) + { + throw new NotSupportedException ("CommandOption.OnParseComplete should not be invoked."); + } + } + + class HelpOption : Option + { + Option option; + CommandSet commands; + + public HelpOption (CommandSet commands, Option d) + : base (d.Prototype, d.Description, d.MaxValueCount, d.Hidden) + { + this.commands = commands; + this.option = d; + } + + protected override void OnParseComplete (OptionContext c) + { + commands.showHelp = true; + + option?.InvokeOnParseComplete (c); + } + } + + class CommandOptionSet : OptionSet + { + CommandSet commands; + + public CommandOptionSet (CommandSet commands, MessageLocalizerConverter localizer) + : base (localizer) + { + this.commands = commands; + } + + protected override void SetItem (int index, Option item) + { + if (ShouldWrapOption (item)) { + base.SetItem (index, new HelpOption (commands, item)); + return; + } + base.SetItem (index, item); + } + + bool ShouldWrapOption (Option item) + { + if (item == null) + return false; + var help = item as HelpOption; + if (help != null) + return false; + foreach (var n in item.Names) { + if (n == "help") + return true; + } + return false; + } + + protected override void InsertItem (int index, Option item) + { + if (ShouldWrapOption (item)) { + base.InsertItem (index, new HelpOption (commands, item)); + return; + } + base.InsertItem (index, item); + } + } + + public class CommandSet : KeyedCollection + { + readonly OptionSet options; + readonly TextWriter outWriter; + readonly TextWriter errorWriter; + readonly string suite; + + HelpCommand help; + + internal bool showHelp; + + internal OptionSet Options => options; + +#if !PCL || NETSTANDARD1_3 + public CommandSet(string suite, MessageLocalizerConverter localizer = null) + : this(suite, Console.Out, Console.Error, localizer) + { + } +#endif + + public CommandSet (string suite, TextWriter output, TextWriter error, MessageLocalizerConverter localizer = null) + { + if (suite == null) + throw new ArgumentNullException (nameof (suite)); + if (output == null) + throw new ArgumentNullException (nameof (output)); + if (error == null) + throw new ArgumentNullException (nameof (error)); + + this.suite = suite; + options = new CommandOptionSet (this, localizer); + outWriter = output; + errorWriter = error; + } + + public string Suite => suite; + public TextWriter Out => outWriter; + public TextWriter Error => errorWriter; + public MessageLocalizerConverter MessageLocalizer => options.MessageLocalizer; + + protected override string GetKeyForItem (Command item) + { + return item?.Name; + } + + public new CommandSet Add (Command value) + { + if (value == null) + throw new ArgumentNullException (nameof (value)); + AddCommand (value); + options.Add (new CommandOption (value)); + return this; + } + + void AddCommand (Command value) + { + if (value.CommandSet != null && value.CommandSet != this) { + throw new ArgumentException ("Command instances can only be added to a single CommandSet.", nameof (value)); + } + value.CommandSet = this; + if (value.Options != null) { + value.Options.MessageLocalizer = options.MessageLocalizer; + } + + base.Add (value); + + help = help ?? value as HelpCommand; + } + + public CommandSet Add (string header) + { + options.Add (header); + return this; + } + + public CommandSet Add (Option option) + { + options.Add (option); + return this; + } + + public CommandSet Add (string prototype, Action action) + { + options.Add (prototype, action); + return this; + } + + public CommandSet Add (string prototype, string description, Action action) + { + options.Add (prototype, description, action); + return this; + } + + public CommandSet Add (string prototype, string description, Action action, bool hidden) + { + options.Add (prototype, description, action, hidden); + return this; + } + + public CommandSet Add (string prototype, OptionAction action) + { + options.Add (prototype, action); + return this; + } + + public CommandSet Add (string prototype, string description, OptionAction action) + { + options.Add (prototype, description, action); + return this; + } + + public CommandSet Add (string prototype, string description, OptionAction action, bool hidden) + { + options.Add (prototype, description, action, hidden); + return this; + } + + public CommandSet Add (string prototype, Action action) + { + options.Add (prototype, null, action); + return this; + } + + public CommandSet Add (string prototype, string description, Action action) + { + options.Add (prototype, description, action); + return this; + } + + public CommandSet Add (string prototype, OptionAction action) + { + options.Add (prototype, action); + return this; + } + + public CommandSet Add (string prototype, string description, OptionAction action) + { + options.Add (prototype, description, action); + return this; + } + + public CommandSet Add (ArgumentSource source) + { + options.Add (source); + return this; + } + + public int Run (IEnumerable arguments) + { + if (arguments == null) + throw new ArgumentNullException (nameof (arguments)); + + this.showHelp = false; + if (help == null) { + help = new HelpCommand (); + AddCommand (help); + } + Action setHelp = v => showHelp = v != null; + if (!options.Contains ("help")) { + options.Add ("help", "", setHelp, hidden: true); + } + if (!options.Contains ("?")) { + options.Add ("?", "", setHelp, hidden: true); + } + var extra = options.Parse (arguments); + if (extra.Count == 0) { + if (showHelp) { + return help.Invoke (extra); + } + Out.WriteLine (options.MessageLocalizer ($"Use `{Suite} help` for usage.")); + return 1; + } + var command = Contains (extra [0]) ? this [extra [0]] : null; + if (command == null) { + help.WriteUnknownCommand (extra [0]); + return 1; + } + extra.RemoveAt (0); + if (showHelp) { + if (command.Options?.Contains ("help") ?? true) { + extra.Add ("--help"); + return command.Invoke (extra); + } + command.Options.WriteOptionDescriptions (Out); + return 0; + } + return command.Invoke (extra); + } + } + + public class HelpCommand : Command + { + public HelpCommand () + : base ("help", help: "Show this message and exit") + { + } + + public override int Invoke (IEnumerable arguments) + { + var extra = new List (arguments ?? new string [0]); + var _ = CommandSet.Options.MessageLocalizer; + if (extra.Count == 0) { + CommandSet.Options.WriteOptionDescriptions (CommandSet.Out); + return 0; + } + var command = CommandSet.Contains (extra [0]) + ? CommandSet [extra [0]] + : null; + if (command == this || extra [0] == "--help") { + CommandSet.Out.WriteLine (_ ($"Usage: {CommandSet.Suite} COMMAND [OPTIONS]")); + CommandSet.Out.WriteLine (_ ($"Use `{CommandSet.Suite} help COMMAND` for help on a specific command.")); + CommandSet.Out.WriteLine (); + CommandSet.Out.WriteLine (_ ($"Available commands:")); + CommandSet.Out.WriteLine (); + foreach (var c in CommandSet) { + CommandSet.Options.WriteCommandDescription (CommandSet.Out, c); + } + return 0; + } + if (command == null) { + WriteUnknownCommand (extra [0]); + return 1; + } + if (command.Options != null) { + command.Options.WriteOptionDescriptions (CommandSet.Out); + return 0; + } + return command.Invoke (new [] { "--help" }); + } + + internal void WriteUnknownCommand (string unknownCommand) + { + CommandSet.Error.WriteLine (CommandSet.Options.MessageLocalizer ($"{CommandSet.Suite}: Unknown command: {unknownCommand}")); + CommandSet.Error.WriteLine (CommandSet.Options.MessageLocalizer ($"{CommandSet.Suite}: Use `{CommandSet.Suite} help` for usage.")); + } + } +} + diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs index 821a8d3c8..4873318d3 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameFormatter.cs @@ -14,7 +14,7 @@ namespace System.Reflection { const int PUBLIC_KEY_TOKEN_LEN = 8; - if (a.Name == string.Empty) + if (string.IsNullOrEmpty(a.Name)) throw new FileLoadException(); StringBuilder sb = new StringBuilder(); @@ -54,7 +54,7 @@ namespace System.Reflection string cultureName = a.CultureName; if (cultureName != null) { - if (cultureName == string.Empty) + if (cultureName.Length == 0) cultureName = "neutral"; sb.Append(", Culture="); sb.AppendQuoted(cultureName); @@ -64,7 +64,7 @@ namespace System.Reflection if (pkt != null) { if (pkt.Length > PUBLIC_KEY_TOKEN_LEN) - throw new ArgumentException(); + throw new ArgumentException("Invalid token length", nameof(a)); sb.Append(", PublicKeyToken="); if (pkt.Length == 0) diff --git a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs index fdf5b8f06..7bb53199b 100644 --- a/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs +++ b/external/corert/src/System.Private.CoreLib/shared/System/Reflection/AssemblyNameParser.cs @@ -21,7 +21,7 @@ namespace System.Reflection if (indexOfNul != -1) s = s.Substring(0, indexOfNul); if (s.Length == 0) - throw new ArgumentException(); + throw new ArgumentException("Empty string", nameof(s)); AssemblyNameLexer lexer = new AssemblyNameLexer(s); @@ -31,7 +31,7 @@ namespace System.Reflection if (token != AssemblyNameLexer.Token.String) throw new FileLoadException(); - if (name == string.Empty || name.IndexOfAny(s_illegalCharactersInSimpleName) != -1) + if (string.IsNullOrEmpty(name) || name.IndexOfAny(s_illegalCharactersInSimpleName) != -1) throw new FileLoadException(); Version version = null; @@ -64,7 +64,7 @@ namespace System.Reflection if (token != AssemblyNameLexer.Token.String) throw new FileLoadException(); - if (attributeName == string.Empty) + if (String.IsNullOrEmpty(attributeName)) throw new FileLoadException(); for (int i = 0; i < alreadySeen.Count; i++) @@ -168,7 +168,7 @@ namespace System.Reflection private static byte[] ParsePKT(string attributeValue) { - if (attributeValue.Equals("null", StringComparison.OrdinalIgnoreCase) || attributeValue == string.Empty) + if (attributeValue.Equals("null", StringComparison.OrdinalIgnoreCase) || attributeValue.Length == 0) return Array.Empty(); if (attributeValue.Length != 8 * 2) diff --git a/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs b/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs index c06cf7667..218c25260 100644 --- a/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs +++ b/external/corert/src/System.Private.CoreLib/src/System/Reflection/Runtime/TypeParsing/TypeParser.cs @@ -1,6 +1,7 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +#pragma warning disable CA2208 using System.Collections.Generic; namespace System.Reflection.Runtime.TypeParsing diff --git a/src/ILLink.Tasks/ILLink.Tasks.csproj b/src/ILLink.Tasks/ILLink.Tasks.csproj index 36a49f016..dcff939a4 100644 --- a/src/ILLink.Tasks/ILLink.Tasks.csproj +++ b/src/ILLink.Tasks/ILLink.Tasks.csproj @@ -19,6 +19,7 @@ true true + true diff --git a/src/ILLink.Tasks/LinkTask.cs b/src/ILLink.Tasks/LinkTask.cs index a2960f1c4..a9b360fb6 100644 --- a/src/ILLink.Tasks/LinkTask.cs +++ b/src/ILLink.Tasks/LinkTask.cs @@ -200,7 +200,7 @@ namespace ILLink.Tasks /// public ITaskItem[] CustomSteps { get; set; } - private readonly static string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH"; + private const string DotNetHostPathEnvironmentName = "DOTNET_HOST_PATH"; private string _dotnetPath; diff --git a/src/ILLink.Tasks/Utils.cs b/src/ILLink.Tasks/Utils.cs index bbaf45613..afd544245 100644 --- a/src/ILLink.Tasks/Utils.cs +++ b/src/ILLink.Tasks/Utils.cs @@ -2,22 +2,25 @@ using System; using Mono.Cecil; using System.Linq; -public static class Utils +namespace ILLink.Tasks { - public static bool IsManagedAssembly (string fileName) + public static class Utils { - try { - ModuleDefinition module = ModuleDefinition.ReadModule (fileName); - return !IsCPPCLIAssembly (module); - } catch (BadImageFormatException) { - return false; + public static bool IsManagedAssembly (string fileName) + { + try { + ModuleDefinition module = ModuleDefinition.ReadModule (fileName); + return !IsCPPCLIAssembly (module); + } catch (BadImageFormatException) { + return false; + } } - } - private static bool IsCPPCLIAssembly (ModuleDefinition module) - { - return module.Types.Any (t => - t.Namespace == "" || - t.Namespace == ""); + private static bool IsCPPCLIAssembly (ModuleDefinition module) + { + return module.Types.Any (t => + t.Namespace == "" || + t.Namespace == ""); + } } -} +} \ No newline at end of file diff --git a/src/analyzer/analyzer.csproj b/src/analyzer/analyzer.csproj index 3166183ec..90eeaa7da 100644 --- a/src/analyzer/analyzer.csproj +++ b/src/analyzer/analyzer.csproj @@ -16,6 +16,10 @@ net471 + + + + diff --git a/src/analyzer/common/Mono.Options/Options.cs b/src/analyzer/common/Mono.Options/Options.cs deleted file mode 100644 index a289e6281..000000000 --- a/src/analyzer/common/Mono.Options/Options.cs +++ /dev/null @@ -1,1813 +0,0 @@ -// -// Options.cs -// -// Authors: -// Jonathan Pryor , -// Federico Di Gregorio -// Rolf Bjarne Kvinge -// -// Copyright (C) 2008 Novell (http://www.novell.com) -// Copyright (C) 2009 Federico Di Gregorio. -// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com) -// Copyright (C) 2017 Microsoft Corporation (http://www.microsoft.com) -// -// Permission is hereby granted, free of charge, to any person obtaining -// a copy of this software and associated documentation files (the -// "Software"), to deal in the Software without restriction, including -// without limitation the rights to use, copy, modify, merge, publish, -// distribute, sublicense, and/or sell copies of the Software, and to -// permit persons to whom the Software is furnished to do so, subject to -// the following conditions: -// -// The above copyright notice and this permission notice shall be -// included in all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -// - -// Compile With: -// mcs -debug+ -r:System.Core Options.cs -o:Mono.Options.dll -t:library -// mcs -debug+ -d:LINQ -r:System.Core Options.cs -o:Mono.Options.dll -t:library -// -// The LINQ version just changes the implementation of -// OptionSet.Parse(IEnumerable), and confers no semantic changes. - -// -// A Getopt::Long-inspired option parsing library for C#. -// -// Mono.Options.OptionSet is built upon a key/value table, where the -// key is a option format string and the value is a delegate that is -// invoked when the format string is matched. -// -// Option format strings: -// Regex-like BNF Grammar: -// name: .+ -// type: [=:] -// sep: ( [^{}]+ | '{' .+ '}' )? -// aliases: ( name type sep ) ( '|' name type sep )* -// -// Each '|'-delimited name is an alias for the associated action. If the -// format string ends in a '=', it has a required value. If the format -// string ends in a ':', it has an optional value. If neither '=' or ':' -// is present, no value is supported. `=' or `:' need only be defined on one -// alias, but if they are provided on more than one they must be consistent. -// -// Each alias portion may also end with a "key/value separator", which is used -// to split option values if the option accepts > 1 value. If not specified, -// it defaults to '=' and ':'. If specified, it can be any character except -// '{' and '}' OR the *string* between '{' and '}'. If no separator should be -// used (i.e. the separate values should be distinct arguments), then "{}" -// should be used as the separator. -// -// Options are extracted either from the current option by looking for -// the option name followed by an '=' or ':', or is taken from the -// following option IFF: -// - The current option does not contain a '=' or a ':' -// - The current option requires a value (i.e. not a Option type of ':') -// -// The `name' used in the option format string does NOT include any leading -// option indicator, such as '-', '--', or '/'. All three of these are -// permitted/required on any named option. -// -// Option bundling is permitted so long as: -// - '-' is used to start the option group -// - all of the bundled options are a single character -// - at most one of the bundled options accepts a value, and the value -// provided starts from the next character to the end of the string. -// -// This allows specifying '-a -b -c' as '-abc', and specifying '-D name=value' -// as '-Dname=value'. -// -// Option processing is disabled by specifying "--". All options after "--" -// are returned by OptionSet.Parse() unchanged and unprocessed. -// -// Unprocessed options are returned from OptionSet.Parse(). -// -// Examples: -// int verbose = 0; -// OptionSet p = new OptionSet () -// .Add ("v", v => ++verbose) -// .Add ("name=|value=", v => Console.WriteLine (v)); -// p.Parse (new string[]{"-v", "--v", "/v", "-name=A", "/name", "B", "extra"}); -// -// The above would parse the argument string array, and would invoke the -// lambda expression three times, setting `verbose' to 3 when complete. -// It would also print out "A" and "B" to standard output. -// The returned array would contain the string "extra". -// -// C# 3.0 collection initializers are supported and encouraged: -// var p = new OptionSet () { -// { "h|?|help", v => ShowHelp () }, -// }; -// -// System.ComponentModel.TypeConverter is also supported, allowing the use of -// custom data types in the callback type; TypeConverter.ConvertFromString() -// is used to convert the value option to an instance of the specified -// type: -// -// var p = new OptionSet () { -// { "foo=", (Foo f) => Console.WriteLine (f.ToString ()) }, -// }; -// -// Random other tidbits: -// - Boolean options (those w/o '=' or ':' in the option format string) -// are explicitly enabled if they are followed with '+', and explicitly -// disabled if they are followed with '-': -// string a = null; -// var p = new OptionSet () { -// { "a", s => a = s }, -// }; -// p.Parse (new string[]{"-a"}); // sets v != null -// p.Parse (new string[]{"-a+"}); // sets v != null -// p.Parse (new string[]{"-a-"}); // sets v == null -// - -// -// Mono.Options.CommandSet allows easily having separate commands and -// associated command options, allowing creation of a *suite* along the -// lines of **git**(1), **svn**(1), etc. -// -// CommandSet allows intermixing plain text strings for `--help` output, -// Option values -- as supported by OptionSet -- and Command instances, -// which have a name, optional help text, and an optional OptionSet. -// -// var suite = new CommandSet ("suite-name") { -// // Use strings and option values, as with OptionSet -// "usage: suite-name COMMAND [OPTIONS]+", -// { "v:", "verbosity", (int? v) => Verbosity = v.HasValue ? v.Value : Verbosity+1 }, -// // Commands may also be specified -// new Command ("command-name", "command help") { -// Options = new OptionSet {/*...*/}, -// Run = args => { /*...*/}, -// }, -// new MyCommandSubclass (), -// }; -// return suite.Run (new string[]{...}); -// -// CommandSet provides a `help` command, and forwards `help COMMAND` -// to the registered Command instance by invoking Command.Invoke() -// with `--help` as an option. -// - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.ComponentModel; -using System.Globalization; -using System.IO; -#if PCL -using System.Reflection; -#else -using System.Runtime.Serialization; -using System.Security.Permissions; -#endif -using System.Text; -using System.Text.RegularExpressions; - -#if LINQ -using System.Linq; -#endif - -#if TEST -using NDesk.Options; -#endif - -#if PCL -using MessageLocalizerConverter = System.Func; -#else -using MessageLocalizerConverter = System.Converter; -#endif - -#if NDESK_OPTIONS -namespace NDesk.Options -#else -namespace Mono.Options -#endif -{ - static class StringCoda { - - public static IEnumerable WrappedLines (string self, params int[] widths) - { - IEnumerable w = widths; - return WrappedLines (self, w); - } - - public static IEnumerable WrappedLines (string self, IEnumerable widths) - { - if (widths == null) - throw new ArgumentNullException ("widths"); - return CreateWrappedLinesIterator (self, widths); - } - - private static IEnumerable CreateWrappedLinesIterator (string self, IEnumerable widths) - { - if (string.IsNullOrEmpty (self)) { - yield return string.Empty; - yield break; - } - using (IEnumerator ewidths = widths.GetEnumerator ()) { - bool? hw = null; - int width = GetNextWidth (ewidths, int.MaxValue, ref hw); - int start = 0, end; - do { - end = GetLineEnd (start, width, self); - char c = self [end-1]; - if (char.IsWhiteSpace (c)) - --end; - bool needContinuation = end != self.Length && !IsEolChar (c); - string continuation = ""; - if (needContinuation) { - --end; - continuation = "-"; - } - string line = self.Substring (start, end - start) + continuation; - yield return line; - start = end; - if (char.IsWhiteSpace (c)) - ++start; - width = GetNextWidth (ewidths, width, ref hw); - } while (start < self.Length); - } - } - - private static int GetNextWidth (IEnumerator ewidths, int curWidth, ref bool? eValid) - { - if (!eValid.HasValue || (eValid.HasValue && eValid.Value)) { - curWidth = (eValid = ewidths.MoveNext ()).Value ? ewidths.Current : curWidth; - // '.' is any character, - is for a continuation - const string minWidth = ".-"; - if (curWidth < minWidth.Length) - throw new ArgumentOutOfRangeException ("widths", - string.Format ("Element must be >= {0}, was {1}.", minWidth.Length, curWidth)); - return curWidth; - } - // no more elements, use the last element. - return curWidth; - } - - private static bool IsEolChar (char c) - { - return !char.IsLetterOrDigit (c); - } - - private static int GetLineEnd (int start, int length, string description) - { - int end = System.Math.Min (start + length, description.Length); - int sep = -1; - for (int i = start; i < end; ++i) { - if (description [i] == '\n') - return i+1; - if (IsEolChar (description [i])) - sep = i+1; - } - if (sep == -1 || end == description.Length) - return end; - return sep; - } - } - - public class OptionValueCollection : IList, IList { - - List values = new List (); - OptionContext c; - - internal OptionValueCollection (OptionContext c) - { - this.c = c; - } - - #region ICollection - void ICollection.CopyTo (Array array, int index) {(values as ICollection).CopyTo (array, index);} - bool ICollection.IsSynchronized {get {return (values as ICollection).IsSynchronized;}} - object ICollection.SyncRoot {get {return (values as ICollection).SyncRoot;}} - #endregion - - #region ICollection - public void Add (string item) {values.Add (item);} - public void Clear () {values.Clear ();} - public bool Contains (string item) {return values.Contains (item);} - public void CopyTo (string[] array, int arrayIndex) {values.CopyTo (array, arrayIndex);} - public bool Remove (string item) {return values.Remove (item);} - public int Count {get {return values.Count;}} - public bool IsReadOnly {get {return false;}} - #endregion - - #region IEnumerable - IEnumerator IEnumerable.GetEnumerator () {return values.GetEnumerator ();} - #endregion - - #region IEnumerable - public IEnumerator GetEnumerator () {return values.GetEnumerator ();} - #endregion - - #region IList - int IList.Add (object value) {return (values as IList).Add (value);} - bool IList.Contains (object value) {return (values as IList).Contains (value);} - int IList.IndexOf (object value) {return (values as IList).IndexOf (value);} - void IList.Insert (int index, object value) {(values as IList).Insert (index, value);} - void IList.Remove (object value) {(values as IList).Remove (value);} - void IList.RemoveAt (int index) {(values as IList).RemoveAt (index);} - bool IList.IsFixedSize {get {return false;}} - object IList.this [int index] {get {return this [index];} set {(values as IList)[index] = value;}} - #endregion - - #region IList - public int IndexOf (string item) {return values.IndexOf (item);} - public void Insert (int index, string item) {values.Insert (index, item);} - public void RemoveAt (int index) {values.RemoveAt (index);} - - private void AssertValid (int index) - { - if (c.Option == null) - throw new InvalidOperationException ("OptionContext.Option is null."); - if (index >= c.Option.MaxValueCount) - throw new ArgumentOutOfRangeException ("index"); - if (c.Option.OptionValueType == OptionValueType.Required && - index >= values.Count) - throw new OptionException (string.Format ( - c.OptionSet.MessageLocalizer ("Missing required value for option '{0}'."), c.OptionName), - c.OptionName); - } - - public string this [int index] { - get { - AssertValid (index); - return index >= values.Count ? null : values [index]; - } - set { - values [index] = value; - } - } - #endregion - - public List ToList () - { - return new List (values); - } - - public string[] ToArray () - { - return values.ToArray (); - } - - public override string ToString () - { - return string.Join (", ", values.ToArray ()); - } - } - - public class OptionContext { - private Option option; - private string name; - private int index; - private OptionSet set; - private OptionValueCollection c; - - public OptionContext (OptionSet set) - { - this.set = set; - this.c = new OptionValueCollection (this); - } - - public Option Option { - get {return option;} - set {option = value;} - } - - public string OptionName { - get {return name;} - set {name = value;} - } - - public int OptionIndex { - get {return index;} - set {index = value;} - } - - public OptionSet OptionSet { - get {return set;} - } - - public OptionValueCollection OptionValues { - get {return c;} - } - } - - public enum OptionValueType { - None, - Optional, - Required, - } - - public abstract class Option { - string prototype, description; - string[] names; - OptionValueType type; - int count; - string[] separators; - bool hidden; - - protected Option (string prototype, string description) - : this (prototype, description, 1, false) - { - } - - protected Option (string prototype, string description, int maxValueCount) - : this (prototype, description, maxValueCount, false) - { - } - - protected Option (string prototype, string description, int maxValueCount, bool hidden) - { - if (prototype == null) - throw new ArgumentNullException ("prototype"); - if (prototype.Length == 0) - throw new ArgumentException ("Cannot be the empty string.", "prototype"); - if (maxValueCount < 0) - throw new ArgumentOutOfRangeException ("maxValueCount"); - - this.prototype = prototype; - this.description = description; - this.count = maxValueCount; - this.names = (this is OptionSet.Category) - // append GetHashCode() so that "duplicate" categories have distinct - // names, e.g. adding multiple "" categories should be valid. - ? new[]{prototype + this.GetHashCode ()} - : prototype.Split ('|'); - - if (this is OptionSet.Category || this is CommandOption) - return; - - this.type = ParsePrototype (); - this.hidden = hidden; - - if (this.count == 0 && type != OptionValueType.None) - throw new ArgumentException ( - "Cannot provide maxValueCount of 0 for OptionValueType.Required or " + - "OptionValueType.Optional.", - "maxValueCount"); - if (this.type == OptionValueType.None && maxValueCount > 1) - throw new ArgumentException ( - string.Format ("Cannot provide maxValueCount of {0} for OptionValueType.None.", maxValueCount), - "maxValueCount"); - if (Array.IndexOf (names, "<>") >= 0 && - ((names.Length == 1 && this.type != OptionValueType.None) || - (names.Length > 1 && this.MaxValueCount > 1))) - throw new ArgumentException ( - "The default option handler '<>' cannot require values.", - "prototype"); - } - - public string Prototype {get {return prototype;}} - public string Description {get {return description;}} - public OptionValueType OptionValueType {get {return type;}} - public int MaxValueCount {get {return count;}} - public bool Hidden {get {return hidden;}} - - public string[] GetNames () - { - return (string[]) names.Clone (); - } - - public string[] GetValueSeparators () - { - if (separators == null) - return new string [0]; - return (string[]) separators.Clone (); - } - - protected static T Parse (string value, OptionContext c) - { - Type tt = typeof (T); -#if PCL - TypeInfo ti = tt.GetTypeInfo (); -#else - Type ti = tt; -#endif - bool nullable = - ti.IsValueType && - ti.IsGenericType && - !ti.IsGenericTypeDefinition && - ti.GetGenericTypeDefinition () == typeof (Nullable<>); -#if PCL - Type targetType = nullable ? tt.GenericTypeArguments [0] : tt; -#else - Type targetType = nullable ? tt.GetGenericArguments () [0] : tt; -#endif - T t = default (T); - try { - if (value != null) { -#if PCL - if (targetType.GetTypeInfo ().IsEnum) - t = (T) Enum.Parse (targetType, value, true); - else - t = (T) Convert.ChangeType (value, targetType); -#else - TypeConverter conv = TypeDescriptor.GetConverter (targetType); - t = (T) conv.ConvertFromString (value); -#endif - } - } - catch (Exception e) { - throw new OptionException ( - string.Format ( - c.OptionSet.MessageLocalizer ("Could not convert string `{0}' to type {1} for option `{2}'."), - value, targetType.Name, c.OptionName), - c.OptionName, e); - } - return t; - } - - internal string[] Names {get {return names;}} - internal string[] ValueSeparators {get {return separators;}} - - static readonly char[] NameTerminator = new char[]{'=', ':'}; - - private OptionValueType ParsePrototype () - { - char type = '\0'; - List seps = new List (); - for (int i = 0; i < names.Length; ++i) { - string name = names [i]; - if (name.Length == 0) - throw new ArgumentException ("Empty option names are not supported.", "prototype"); - - int end = name.IndexOfAny (NameTerminator); - if (end == -1) - continue; - names [i] = name.Substring (0, end); - if (type == '\0' || type == name [end]) - type = name [end]; - else - throw new ArgumentException ( - string.Format ("Conflicting option types: '{0}' vs. '{1}'.", type, name [end]), - "prototype"); - AddSeparators (name, end, seps); - } - - if (type == '\0') - return OptionValueType.None; - - if (count <= 1 && seps.Count != 0) - throw new ArgumentException ( - string.Format ("Cannot provide key/value separators for Options taking {0} value(s).", count), - "prototype"); - if (count > 1) { - if (seps.Count == 0) - this.separators = new string[]{":", "="}; - else if (seps.Count == 1 && seps [0].Length == 0) - this.separators = null; - else - this.separators = seps.ToArray (); - } - - return type == '=' ? OptionValueType.Required : OptionValueType.Optional; - } - - private static void AddSeparators (string name, int end, ICollection seps) - { - int start = -1; - for (int i = end+1; i < name.Length; ++i) { - switch (name [i]) { - case '{': - if (start != -1) - throw new ArgumentException ( - string.Format ("Ill-formed name/value separator found in \"{0}\".", name), - "prototype"); - start = i+1; - break; - case '}': - if (start == -1) - throw new ArgumentException ( - string.Format ("Ill-formed name/value separator found in \"{0}\".", name), - "prototype"); - seps.Add (name.Substring (start, i-start)); - start = -1; - break; - default: - if (start == -1) - seps.Add (name [i].ToString ()); - break; - } - } - if (start != -1) - throw new ArgumentException ( - string.Format ("Ill-formed name/value separator found in \"{0}\".", name), - "prototype"); - } - - public void Invoke (OptionContext c) - { - OnParseComplete (c); - c.OptionName = null; - c.Option = null; - c.OptionValues.Clear (); - } - - protected abstract void OnParseComplete (OptionContext c); - - internal void InvokeOnParseComplete (OptionContext c) - { - OnParseComplete (c); - } - - public override string ToString () - { - return Prototype; - } - } - - public abstract class ArgumentSource { - - protected ArgumentSource () - { - } - - public abstract string[] GetNames (); - public abstract string Description { get; } - public abstract bool GetArguments (string value, out IEnumerable replacement); - -#if !PCL || NETSTANDARD1_3 - public static IEnumerable GetArgumentsFromFile (string file) - { - return GetArguments (File.OpenText (file), true); - } -#endif - - public static IEnumerable GetArguments (TextReader reader) - { - return GetArguments (reader, false); - } - - // Cribbed from mcs/driver.cs:LoadArgs(string) - static IEnumerable GetArguments (TextReader reader, bool close) - { - try { - StringBuilder arg = new StringBuilder (); - - string line; - while ((line = reader.ReadLine ()) != null) { - int t = line.Length; - - for (int i = 0; i < t; i++) { - char c = line [i]; - - if (c == '"' || c == '\'') { - char end = c; - - for (i++; i < t; i++){ - c = line [i]; - - if (c == end) - break; - arg.Append (c); - } - } else if (c == ' ') { - if (arg.Length > 0) { - yield return arg.ToString (); - arg.Length = 0; - } - } else - arg.Append (c); - } - if (arg.Length > 0) { - yield return arg.ToString (); - arg.Length = 0; - } - } - } - finally { - if (close) - reader.Dispose (); - } - } - } - -#if !PCL || NETSTANDARD1_3 - public class ResponseFileSource : ArgumentSource { - - public override string[] GetNames () - { - return new string[]{"@file"}; - } - - public override string Description { - get {return "Read response file for more options.";} - } - - public override bool GetArguments (string value, out IEnumerable replacement) - { - if (string.IsNullOrEmpty (value) || !value.StartsWith ("@")) { - replacement = null; - return false; - } - replacement = ArgumentSource.GetArgumentsFromFile (value.Substring (1)); - return true; - } - } -#endif - -#if !PCL - [Serializable] -#endif - public class OptionException : Exception { - private string option; - - public OptionException () - { - } - - public OptionException (string message, string optionName) - : base (message) - { - this.option = optionName; - } - - public OptionException (string message, string optionName, Exception innerException) - : base (message, innerException) - { - this.option = optionName; - } - -#if !PCL - protected OptionException (SerializationInfo info, StreamingContext context) - : base (info, context) - { - this.option = info.GetString ("OptionName"); - } -#endif - - public string OptionName { - get {return this.option;} - } - -#if !PCL && !NETCOREAPP -#pragma warning disable 618 // SecurityPermissionAttribute is obsolete - [SecurityPermission (SecurityAction.LinkDemand, SerializationFormatter = true)] -#pragma warning restore 618 - public override void GetObjectData (SerializationInfo info, StreamingContext context) - { - base.GetObjectData (info, context); - info.AddValue ("OptionName", option); - } -#endif - } - - public delegate void OptionAction (TKey key, TValue value); - - public class OptionSet : KeyedCollection - { - public OptionSet () - : this (null) - { - } - - public OptionSet (MessageLocalizerConverter localizer) - { - this.roSources = new ReadOnlyCollection (sources); - this.localizer = localizer; - if (this.localizer == null) { - this.localizer = delegate (string f) { - return f; - }; - } - } - - MessageLocalizerConverter localizer; - - public MessageLocalizerConverter MessageLocalizer { - get {return localizer;} - internal set {localizer = value;} - } - - List sources = new List (); - ReadOnlyCollection roSources; - - public ReadOnlyCollection ArgumentSources { - get {return roSources;} - } - - - protected override string GetKeyForItem (Option item) - { - if (item == null) - throw new ArgumentNullException ("option"); - if (item.Names != null && item.Names.Length > 0) - return item.Names [0]; - // This should never happen, as it's invalid for Option to be - // constructed w/o any names. - throw new InvalidOperationException ("Option has no names!"); - } - - [Obsolete ("Use KeyedCollection.this[string]")] - protected Option GetOptionForName (string option) - { - if (option == null) - throw new ArgumentNullException ("option"); - try { - return base [option]; - } - catch (KeyNotFoundException) { - return null; - } - } - - protected override void InsertItem (int index, Option item) - { - base.InsertItem (index, item); - AddImpl (item); - } - - protected override void RemoveItem (int index) - { - Option p = Items [index]; - base.RemoveItem (index); - // KeyedCollection.RemoveItem() handles the 0th item - for (int i = 1; i < p.Names.Length; ++i) { - Dictionary.Remove (p.Names [i]); - } - } - - protected override void SetItem (int index, Option item) - { - base.SetItem (index, item); - AddImpl (item); - } - - private void AddImpl (Option option) - { - if (option == null) - throw new ArgumentNullException ("option"); - List added = new List (option.Names.Length); - try { - // KeyedCollection.InsertItem/SetItem handle the 0th name. - for (int i = 1; i < option.Names.Length; ++i) { - Dictionary.Add (option.Names [i], option); - added.Add (option.Names [i]); - } - } - catch (Exception) { - foreach (string name in added) - Dictionary.Remove (name); - throw; - } - } - - public OptionSet Add (string header) - { - if (header == null) - throw new ArgumentNullException ("header"); - Add (new Category (header)); - return this; - } - - internal sealed class Category : Option { - - // Prototype starts with '=' because this is an invalid prototype - // (see Option.ParsePrototype(), and thus it'll prevent Category - // instances from being accidentally used as normal options. - public Category (string description) - : base ("=:Category:= " + description, description) - { - } - - protected override void OnParseComplete (OptionContext c) - { - throw new NotSupportedException ("Category.OnParseComplete should not be invoked."); - } - } - - - public new OptionSet Add (Option option) - { - base.Add (option); - return this; - } - - sealed class ActionOption : Option { - Action action; - - public ActionOption (string prototype, string description, int count, Action action) - : this (prototype, description, count, action, false) - { - } - - public ActionOption (string prototype, string description, int count, Action action, bool hidden) - : base (prototype, description, count, hidden) - { - if (action == null) - throw new ArgumentNullException ("action"); - this.action = action; - } - - protected override void OnParseComplete (OptionContext c) - { - action (c.OptionValues); - } - } - - public OptionSet Add (string prototype, Action action) - { - return Add (prototype, null, action); - } - - public OptionSet Add (string prototype, string description, Action action) - { - return Add (prototype, description, action, false); - } - - public OptionSet Add (string prototype, string description, Action action, bool hidden) - { - if (action == null) - throw new ArgumentNullException ("action"); - Option p = new ActionOption (prototype, description, 1, - delegate (OptionValueCollection v) { action (v [0]); }, hidden); - base.Add (p); - return this; - } - - public OptionSet Add (string prototype, OptionAction action) - { - return Add (prototype, null, action); - } - - public OptionSet Add (string prototype, string description, OptionAction action) - { - return Add (prototype, description, action, false); - } - - public OptionSet Add (string prototype, string description, OptionAction action, bool hidden) { - if (action == null) - throw new ArgumentNullException ("action"); - Option p = new ActionOption (prototype, description, 2, - delegate (OptionValueCollection v) {action (v [0], v [1]);}, hidden); - base.Add (p); - return this; - } - - sealed class ActionOption : Option { - Action action; - - public ActionOption (string prototype, string description, Action action) - : base (prototype, description, 1) - { - if (action == null) - throw new ArgumentNullException ("action"); - this.action = action; - } - - protected override void OnParseComplete (OptionContext c) - { - action (Parse (c.OptionValues [0], c)); - } - } - - sealed class ActionOption : Option { - OptionAction action; - - public ActionOption (string prototype, string description, OptionAction action) - : base (prototype, description, 2) - { - if (action == null) - throw new ArgumentNullException ("action"); - this.action = action; - } - - protected override void OnParseComplete (OptionContext c) - { - action ( - Parse (c.OptionValues [0], c), - Parse (c.OptionValues [1], c)); - } - } - - public OptionSet Add (string prototype, Action action) - { - return Add (prototype, null, action); - } - - public OptionSet Add (string prototype, string description, Action action) - { - return Add (new ActionOption (prototype, description, action)); - } - - public OptionSet Add (string prototype, OptionAction action) - { - return Add (prototype, null, action); - } - - public OptionSet Add (string prototype, string description, OptionAction action) - { - return Add (new ActionOption (prototype, description, action)); - } - - public OptionSet Add (ArgumentSource source) - { - if (source == null) - throw new ArgumentNullException ("source"); - sources.Add (source); - return this; - } - - protected virtual OptionContext CreateOptionContext () - { - return new OptionContext (this); - } - - public List Parse (IEnumerable arguments) - { - if (arguments == null) - throw new ArgumentNullException ("arguments"); - OptionContext c = CreateOptionContext (); - c.OptionIndex = -1; - bool process = true; - List unprocessed = new List (); - Option def = Contains ("<>") ? this ["<>"] : null; - ArgumentEnumerator ae = new ArgumentEnumerator (arguments); - foreach (string argument in ae) { - ++c.OptionIndex; - if (argument == "--") { - process = false; - continue; - } - if (!process) { - Unprocessed (unprocessed, def, c, argument); - continue; - } - if (AddSource (ae, argument)) - continue; - if (!Parse (argument, c)) - Unprocessed (unprocessed, def, c, argument); - } - if (c.Option != null) - c.Option.Invoke (c); - return unprocessed; - } - - class ArgumentEnumerator : IEnumerable { - List> sources = new List> (); - - public ArgumentEnumerator (IEnumerable arguments) - { - sources.Add (arguments.GetEnumerator ()); - } - - public void Add (IEnumerable arguments) - { - sources.Add (arguments.GetEnumerator ()); - } - - public IEnumerator GetEnumerator () - { - do { - IEnumerator c = sources [sources.Count-1]; - if (c.MoveNext ()) - yield return c.Current; - else { - c.Dispose (); - sources.RemoveAt (sources.Count-1); - } - } while (sources.Count > 0); - } - - IEnumerator IEnumerable.GetEnumerator () - { - return GetEnumerator (); - } - } - - bool AddSource (ArgumentEnumerator ae, string argument) - { - foreach (ArgumentSource source in sources) { - IEnumerable replacement; - if (!source.GetArguments (argument, out replacement)) - continue; - ae.Add (replacement); - return true; - } - return false; - } - - private static bool Unprocessed (ICollection extra, Option def, OptionContext c, string argument) - { - if (def == null) { - extra.Add (argument); - return false; - } - c.OptionValues.Add (argument); - c.Option = def; - c.Option.Invoke (c); - return false; - } - - private readonly Regex ValueOption = new Regex ( - @"^(?--|-|/)(?[^:=]+)((?[:=])(?.*))?$"); - - protected bool GetOptionParts (string argument, out string flag, out string name, out string sep, out string value) - { - if (argument == null) - throw new ArgumentNullException ("argument"); - - flag = name = sep = value = null; - Match m = ValueOption.Match (argument); - if (!m.Success) { - return false; - } - flag = m.Groups ["flag"].Value; - name = m.Groups ["name"].Value; - if (m.Groups ["sep"].Success && m.Groups ["value"].Success) { - sep = m.Groups ["sep"].Value; - value = m.Groups ["value"].Value; - } - return true; - } - - protected virtual bool Parse (string argument, OptionContext c) - { - if (c.Option != null) { - ParseValue (argument, c); - return true; - } - - string f, n, s, v; - if (!GetOptionParts (argument, out f, out n, out s, out v)) - return false; - - Option p; - if (Contains (n)) { - p = this [n]; - c.OptionName = f + n; - c.Option = p; - switch (p.OptionValueType) { - case OptionValueType.None: - c.OptionValues.Add (n); - c.Option.Invoke (c); - break; - case OptionValueType.Optional: - case OptionValueType.Required: - ParseValue (v, c); - break; - } - return true; - } - // no match; is it a bool option? - if (ParseBool (argument, n, c)) - return true; - // is it a bundled option? - if (ParseBundledValue (f, string.Concat (n + s + v), c)) - return true; - - return false; - } - - private void ParseValue (string option, OptionContext c) - { - if (option != null) - foreach (string o in c.Option.ValueSeparators != null - ? option.Split (c.Option.ValueSeparators, c.Option.MaxValueCount - c.OptionValues.Count, StringSplitOptions.None) - : new string[]{option}) { - c.OptionValues.Add (o); - } - if (c.OptionValues.Count == c.Option.MaxValueCount || - c.Option.OptionValueType == OptionValueType.Optional) - c.Option.Invoke (c); - else if (c.OptionValues.Count > c.Option.MaxValueCount) { - throw new OptionException (localizer (string.Format ( - "Error: Found {0} option values when expecting {1}.", - c.OptionValues.Count, c.Option.MaxValueCount)), - c.OptionName); - } - } - - private bool ParseBool (string option, string n, OptionContext c) - { - Option p; - string rn; - if (n.Length >= 1 && (n [n.Length-1] == '+' || n [n.Length-1] == '-') && - Contains ((rn = n.Substring (0, n.Length-1)))) { - p = this [rn]; - string v = n [n.Length-1] == '+' ? option : null; - c.OptionName = option; - c.Option = p; - c.OptionValues.Add (v); - p.Invoke (c); - return true; - } - return false; - } - - private bool ParseBundledValue (string f, string n, OptionContext c) - { - if (f != "-") - return false; - for (int i = 0; i < n.Length; ++i) { - Option p; - string opt = f + n [i].ToString (); - string rn = n [i].ToString (); - if (!Contains (rn)) { - if (i == 0) - return false; - throw new OptionException (string.Format (localizer ( - "Cannot use unregistered option '{0}' in bundle '{1}'."), rn, f + n), null); - } - p = this [rn]; - switch (p.OptionValueType) { - case OptionValueType.None: - Invoke (c, opt, n, p); - break; - case OptionValueType.Optional: - case OptionValueType.Required: { - string v = n.Substring (i+1); - c.Option = p; - c.OptionName = opt; - ParseValue (v.Length != 0 ? v : null, c); - return true; - } - default: - throw new InvalidOperationException ("Unknown OptionValueType: " + p.OptionValueType); - } - } - return true; - } - - private static void Invoke (OptionContext c, string name, string value, Option option) - { - c.OptionName = name; - c.Option = option; - c.OptionValues.Add (value); - option.Invoke (c); - } - - private const int OptionWidth = 29; - private const int Description_FirstWidth = 80 - OptionWidth; - private const int Description_RemWidth = 80 - OptionWidth - 2; - - static readonly string CommandHelpIndentStart = new string (' ', OptionWidth); - static readonly string CommandHelpIndentRemaining = new string (' ', OptionWidth + 2); - - public void WriteOptionDescriptions (TextWriter o) - { - foreach (Option p in this) { - int written = 0; - - if (p.Hidden) - continue; - - Category c = p as Category; - if (c != null) { - WriteDescription (o, p.Description, "", 80, 80); - continue; - } - CommandOption co = p as CommandOption; - if (co != null) { - WriteCommandDescription (o, co.Command); - continue; - } - - if (!WriteOptionPrototype (o, p, ref written)) - continue; - - if (written < OptionWidth) - o.Write (new string (' ', OptionWidth - written)); - else { - o.WriteLine (); - o.Write (new string (' ', OptionWidth)); - } - - WriteDescription (o, p.Description, new string (' ', OptionWidth+2), - Description_FirstWidth, Description_RemWidth); - } - - foreach (ArgumentSource s in sources) { - string[] names = s.GetNames (); - if (names == null || names.Length == 0) - continue; - - int written = 0; - - Write (o, ref written, " "); - Write (o, ref written, names [0]); - for (int i = 1; i < names.Length; ++i) { - Write (o, ref written, ", "); - Write (o, ref written, names [i]); - } - - if (written < OptionWidth) - o.Write (new string (' ', OptionWidth - written)); - else { - o.WriteLine (); - o.Write (new string (' ', OptionWidth)); - } - - WriteDescription (o, s.Description, new string (' ', OptionWidth+2), - Description_FirstWidth, Description_RemWidth); - } - } - - internal void WriteCommandDescription (TextWriter o, Command c) - { - var name = new string (' ', 8) + c.Name; - if (name.Length < OptionWidth - 1) { - WriteDescription (o, name + new string (' ', OptionWidth - name.Length) + c.Help, CommandHelpIndentRemaining, 80, Description_RemWidth); - } else { - WriteDescription (o, name, "", 80, 80); - WriteDescription (o, CommandHelpIndentStart + c.Help, CommandHelpIndentRemaining, 80, Description_RemWidth); - } - } - - void WriteDescription (TextWriter o, string value, string prefix, int firstWidth, int remWidth) - { - bool indent = false; - foreach (string line in GetLines (localizer (GetDescription (value)), firstWidth, remWidth)) { - if (indent) - o.Write (prefix); - o.WriteLine (line); - indent = true; - } - } - - bool WriteOptionPrototype (TextWriter o, Option p, ref int written) - { - string[] names = p.Names; - - int i = GetNextOptionIndex (names, 0); - if (i == names.Length) - return false; - - if (names [i].Length == 1) { - Write (o, ref written, " -"); - Write (o, ref written, names [0]); - } - else { - Write (o, ref written, " --"); - Write (o, ref written, names [0]); - } - - for ( i = GetNextOptionIndex (names, i+1); - i < names.Length; i = GetNextOptionIndex (names, i+1)) { - Write (o, ref written, ", "); - Write (o, ref written, names [i].Length == 1 ? "-" : "--"); - Write (o, ref written, names [i]); - } - - if (p.OptionValueType == OptionValueType.Optional || - p.OptionValueType == OptionValueType.Required) { - if (p.OptionValueType == OptionValueType.Optional) { - Write (o, ref written, localizer ("[")); - } - Write (o, ref written, localizer ("=" + GetArgumentName (0, p.MaxValueCount, p.Description))); - string sep = p.ValueSeparators != null && p.ValueSeparators.Length > 0 - ? p.ValueSeparators [0] - : " "; - for (int c = 1; c < p.MaxValueCount; ++c) { - Write (o, ref written, localizer (sep + GetArgumentName (c, p.MaxValueCount, p.Description))); - } - if (p.OptionValueType == OptionValueType.Optional) { - Write (o, ref written, localizer ("]")); - } - } - return true; - } - - static int GetNextOptionIndex (string[] names, int i) - { - while (i < names.Length && names [i] == "<>") { - ++i; - } - return i; - } - - static void Write (TextWriter o, ref int n, string s) - { - n += s.Length; - o.Write (s); - } - - private static string GetArgumentName (int index, int maxIndex, string description) - { - if (description == null) - return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1); - string[] nameStart; - if (maxIndex == 1) - nameStart = new string[]{"{0:", "{"}; - else - nameStart = new string[]{"{" + index + ":"}; - for (int i = 0; i < nameStart.Length; ++i) { - int start, j = 0; - do { - start = description.IndexOf (nameStart [i], j); - } while (start >= 0 && j != 0 ? description [j++ - 1] == '{' : false); - if (start == -1) - continue; - int end = description.IndexOf ("}", start); - if (end == -1) - continue; - return description.Substring (start + nameStart [i].Length, end - start - nameStart [i].Length); - } - return maxIndex == 1 ? "VALUE" : "VALUE" + (index + 1); - } - - private static string GetDescription (string description) - { - if (description == null) - return string.Empty; - StringBuilder sb = new StringBuilder (description.Length); - int start = -1; - for (int i = 0; i < description.Length; ++i) { - switch (description [i]) { - case '{': - if (i == start) { - sb.Append ('{'); - start = -1; - } - else if (start < 0) - start = i + 1; - break; - case '}': - if (start < 0) { - if ((i+1) == description.Length || description [i+1] != '}') - throw new InvalidOperationException ("Invalid option description: " + description); - ++i; - sb.Append ("}"); - } - else { - sb.Append (description.Substring (start, i - start)); - start = -1; - } - break; - case ':': - if (start < 0) - goto default; - start = i + 1; - break; - default: - if (start < 0) - sb.Append (description [i]); - break; - } - } - return sb.ToString (); - } - - private static IEnumerable GetLines (string description, int firstWidth, int remWidth) - { - return StringCoda.WrappedLines (description, firstWidth, remWidth); - } - } - - public class Command - { - public string Name {get;} - public string Help {get;} - - public OptionSet Options {get; set;} - public Action> Run {get; set;} - - public CommandSet CommandSet {get; internal set;} - - public Command (string name, string help = null) - { - if (string.IsNullOrEmpty (name)) - throw new ArgumentNullException (nameof (name)); - - Name = name; - Help = help; - } - - public virtual int Invoke (IEnumerable arguments) - { - var rest = Options?.Parse (arguments) ?? arguments; - Run?.Invoke (rest); - return 0; - } - } - - class CommandOption : Option - { - public Command Command {get;} - - // Prototype starts with '=' because this is an invalid prototype - // (see Option.ParsePrototype(), and thus it'll prevent Category - // instances from being accidentally used as normal options. - public CommandOption (Command command, bool hidden = false) - : base ("=:Command:= " + command?.Name, command?.Name, maxValueCount: 0, hidden: hidden) - { - if (command == null) - throw new ArgumentNullException (nameof (command)); - Command = command; - } - - protected override void OnParseComplete (OptionContext c) - { - throw new NotSupportedException ("CommandOption.OnParseComplete should not be invoked."); - } - } - - class HelpOption : Option - { - Option option; - CommandSet commands; - - public HelpOption (CommandSet commands, Option d) - : base (d.Prototype, d.Description, d.MaxValueCount, d.Hidden) - { - this.commands = commands; - this.option = d; - } - - protected override void OnParseComplete (OptionContext c) - { - commands.showHelp = true; - - option?.InvokeOnParseComplete (c); - } - } - - class CommandOptionSet : OptionSet - { - CommandSet commands; - - public CommandOptionSet (CommandSet commands, MessageLocalizerConverter localizer) - : base (localizer) - { - this.commands = commands; - } - - protected override void SetItem (int index, Option item) - { - if (ShouldWrapOption (item)) { - base.SetItem (index, new HelpOption (commands, item)); - return; - } - base.SetItem (index, item); - } - - bool ShouldWrapOption (Option item) - { - if (item == null) - return false; - var help = item as HelpOption; - if (help != null) - return false; - foreach (var n in item.Names) { - if (n == "help") - return true; - } - return false; - } - - protected override void InsertItem (int index, Option item) - { - if (ShouldWrapOption (item)) { - base.InsertItem (index, new HelpOption (commands, item)); - return; - } - base.InsertItem (index, item); - } - } - - public class CommandSet : KeyedCollection - { - readonly OptionSet options; - readonly TextWriter outWriter; - readonly TextWriter errorWriter; - readonly string suite; - - HelpCommand help; - - internal bool showHelp; - - internal OptionSet Options => options; - -#if !PCL || NETSTANDARD1_3 - public CommandSet(string suite, MessageLocalizerConverter localizer = null) - : this(suite, Console.Out, Console.Error, localizer) - { - } -#endif - - public CommandSet (string suite, TextWriter output, TextWriter error, MessageLocalizerConverter localizer = null) - { - if (suite == null) - throw new ArgumentNullException (nameof (suite)); - if (output == null) - throw new ArgumentNullException (nameof (output)); - if (error == null) - throw new ArgumentNullException (nameof (error)); - - this.suite = suite; - options = new CommandOptionSet (this, localizer); - outWriter = output; - errorWriter = error; - } - - public string Suite => suite; - public TextWriter Out => outWriter; - public TextWriter Error => errorWriter; - public MessageLocalizerConverter MessageLocalizer => options.MessageLocalizer; - - protected override string GetKeyForItem (Command item) - { - return item?.Name; - } - - public new CommandSet Add (Command value) - { - if (value == null) - throw new ArgumentNullException (nameof (value)); - AddCommand (value); - options.Add (new CommandOption (value)); - return this; - } - - void AddCommand (Command value) - { - if (value.CommandSet != null && value.CommandSet != this) { - throw new ArgumentException ("Command instances can only be added to a single CommandSet.", nameof (value)); - } - value.CommandSet = this; - if (value.Options != null) { - value.Options.MessageLocalizer = options.MessageLocalizer; - } - - base.Add (value); - - help = help ?? value as HelpCommand; - } - - public CommandSet Add (string header) - { - options.Add (header); - return this; - } - - public CommandSet Add (Option option) - { - options.Add (option); - return this; - } - - public CommandSet Add (string prototype, Action action) - { - options.Add (prototype, action); - return this; - } - - public CommandSet Add (string prototype, string description, Action action) - { - options.Add (prototype, description, action); - return this; - } - - public CommandSet Add (string prototype, string description, Action action, bool hidden) - { - options.Add (prototype, description, action, hidden); - return this; - } - - public CommandSet Add (string prototype, OptionAction action) - { - options.Add (prototype, action); - return this; - } - - public CommandSet Add (string prototype, string description, OptionAction action) - { - options.Add (prototype, description, action); - return this; - } - - public CommandSet Add (string prototype, string description, OptionAction action, bool hidden) - { - options.Add (prototype, description, action, hidden); - return this; - } - - public CommandSet Add (string prototype, Action action) - { - options.Add (prototype, null, action); - return this; - } - - public CommandSet Add (string prototype, string description, Action action) - { - options.Add (prototype, description, action); - return this; - } - - public CommandSet Add (string prototype, OptionAction action) - { - options.Add (prototype, action); - return this; - } - - public CommandSet Add (string prototype, string description, OptionAction action) - { - options.Add (prototype, description, action); - return this; - } - - public CommandSet Add (ArgumentSource source) - { - options.Add (source); - return this; - } - - public int Run (IEnumerable arguments) - { - if (arguments == null) - throw new ArgumentNullException (nameof (arguments)); - - this.showHelp = false; - if (help == null) { - help = new HelpCommand (); - AddCommand (help); - } - Action setHelp = v => showHelp = v != null; - if (!options.Contains ("help")) { - options.Add ("help", "", setHelp, hidden: true); - } - if (!options.Contains ("?")) { - options.Add ("?", "", setHelp, hidden: true); - } - var extra = options.Parse (arguments); - if (extra.Count == 0) { - if (showHelp) { - return help.Invoke (extra); - } - Out.WriteLine (options.MessageLocalizer ($"Use `{Suite} help` for usage.")); - return 1; - } - var command = Contains (extra [0]) ? this [extra [0]] : null; - if (command == null) { - help.WriteUnknownCommand (extra [0]); - return 1; - } - extra.RemoveAt (0); - if (showHelp) { - if (command.Options?.Contains ("help") ?? true) { - extra.Add ("--help"); - return command.Invoke (extra); - } - command.Options.WriteOptionDescriptions (Out); - return 0; - } - return command.Invoke (extra); - } - } - - public class HelpCommand : Command - { - public HelpCommand () - : base ("help", help: "Show this message and exit") - { - } - - public override int Invoke (IEnumerable arguments) - { - var extra = new List (arguments ?? new string [0]); - var _ = CommandSet.Options.MessageLocalizer; - if (extra.Count == 0) { - CommandSet.Options.WriteOptionDescriptions (CommandSet.Out); - return 0; - } - var command = CommandSet.Contains (extra [0]) - ? CommandSet [extra [0]] - : null; - if (command == this || extra [0] == "--help") { - CommandSet.Out.WriteLine (_ ($"Usage: {CommandSet.Suite} COMMAND [OPTIONS]")); - CommandSet.Out.WriteLine (_ ($"Use `{CommandSet.Suite} help COMMAND` for help on a specific command.")); - CommandSet.Out.WriteLine (); - CommandSet.Out.WriteLine (_ ($"Available commands:")); - CommandSet.Out.WriteLine (); - foreach (var c in CommandSet) { - CommandSet.Options.WriteCommandDescription (CommandSet.Out, c); - } - return 0; - } - if (command == null) { - WriteUnknownCommand (extra [0]); - return 1; - } - if (command.Options != null) { - command.Options.WriteOptionDescriptions (CommandSet.Out); - return 0; - } - return command.Invoke (new [] { "--help" }); - } - - internal void WriteUnknownCommand (string unknownCommand) - { - CommandSet.Error.WriteLine (CommandSet.Options.MessageLocalizer ($"{CommandSet.Suite}: Unknown command: {unknownCommand}")); - CommandSet.Error.WriteLine (CommandSet.Options.MessageLocalizer ($"{CommandSet.Suite}: Use `{CommandSet.Suite} help` for usage.")); - } - } -} - diff --git a/src/linker/Linker.Dataflow/ValueNode.cs b/src/linker/Linker.Dataflow/ValueNode.cs index 7ca6c09fc..7aec0bcac 100644 --- a/src/linker/Linker.Dataflow/ValueNode.cs +++ b/src/linker/Linker.Dataflow/ValueNode.cs @@ -954,6 +954,8 @@ namespace Mono.Linker.Dataflow /// The result of a Type.GetType. /// AssemblyIdentity is the scope in which to resolve if the type name string is not assembly-qualified. /// + +#pragma warning disable CA1812 // GetTypeFromStringValue is never instantiated class GetTypeFromStringValue : ValueNode { private readonly TypeResolver _resolver; diff --git a/src/linker/Linker.Steps/BodySubstituterStep.cs b/src/linker/Linker.Steps/BodySubstituterStep.cs index 773af56f6..e598b8ba9 100644 --- a/src/linker/Linker.Steps/BodySubstituterStep.cs +++ b/src/linker/Linker.Steps/BodySubstituterStep.cs @@ -85,7 +85,7 @@ namespace Mono.Linker.Steps return; case "stub": string value = GetAttribute (iterator.Current, "value"); - if (value != "") { + if (!string.IsNullOrEmpty (value)) { if (!TryConvertValue (value, method.ReturnType, out object res)) { Context.LogWarning ($"Invalid value for '{method.GetDisplayName ()}' stub", 2010, _xmlDocumentLocation); return; diff --git a/src/linker/Linker.Steps/LinkAttributesStep.cs b/src/linker/Linker.Steps/LinkAttributesStep.cs index 309535156..a874d3ddd 100644 --- a/src/linker/Linker.Steps/LinkAttributesStep.cs +++ b/src/linker/Linker.Steps/LinkAttributesStep.cs @@ -39,7 +39,7 @@ namespace Mono.Linker.Steps } string attributeFullName = GetFullName (iterator.Current); - if (attributeFullName == string.Empty) { + if (string.IsNullOrEmpty (attributeFullName)) { Context.LogWarning ($"'attribute' element does not contain attribute 'fullname' or it's empty", 2029, _xmlDocumentLocation); continue; } @@ -87,7 +87,7 @@ namespace Mono.Linker.Steps List attributeProperties = new List (); while (iterator.MoveNext ()) { string propertyName = GetName (iterator.Current); - if (propertyName == string.Empty) { + if (string.IsNullOrEmpty (propertyName)) { Context.LogWarning ($"Property element does not contain attribute 'name'", 2051, _xmlDocumentLocation); continue; } @@ -202,7 +202,7 @@ namespace Mono.Linker.Steps protected override void ProcessAssembly (AssemblyDefinition assembly, XPathNodeIterator iterator, bool warnOnUnresolvedTypes) { IEnumerable attributes = ProcessAttributes (iterator.Current, assembly); - if (attributes.Count () > 0) + if (attributes.Any ()) Context.CustomAttributes.AddCustomAttributes (assembly, attributes); ProcessTypes (assembly, iterator, warnOnUnresolvedTypes); } @@ -212,7 +212,7 @@ namespace Mono.Linker.Steps Debug.Assert (ShouldProcessElement (nav)); IEnumerable attributes = ProcessAttributes (nav, type); - if (attributes.Count () > 0) + if (attributes.Any ()) Context.CustomAttributes.AddCustomAttributes (type, attributes); ProcessTypeChildren (type, nav); @@ -231,14 +231,14 @@ namespace Mono.Linker.Steps protected override void ProcessField (TypeDefinition type, FieldDefinition field, XPathNavigator nav) { IEnumerable attributes = ProcessAttributes (nav, field); - if (attributes.Count () > 0) + if (attributes.Any ()) Context.CustomAttributes.AddCustomAttributes (field, attributes); } protected override void ProcessMethod (TypeDefinition type, MethodDefinition method, XPathNavigator nav, object customData) { IEnumerable attributes = ProcessAttributes (nav, method); - if (attributes.Count () > 0) + if (attributes.Any ()) Context.CustomAttributes.AddCustomAttributes (method, attributes); ProcessReturnParameters (method, nav); ProcessParameters (method, nav); @@ -249,7 +249,7 @@ namespace Mono.Linker.Steps var iterator = nav.SelectChildren ("parameter", string.Empty); while (iterator.MoveNext ()) { IEnumerable attributes = ProcessAttributes (iterator.Current, method); - if (attributes.Count () > 0) { + if (attributes.Any ()) { string paramName = GetAttribute (iterator.Current, "name"); foreach (ParameterDefinition parameter in method.Parameters) { if (paramName == parameter.Name) { @@ -273,7 +273,7 @@ namespace Mono.Linker.Steps if (firstAppearance) { firstAppearance = false; IEnumerable attributes = ProcessAttributes (iterator.Current, method.MethodReturnType); - if (attributes.Count () > 0) + if (attributes.Any ()) Context.CustomAttributes.AddCustomAttributes (method.MethodReturnType, attributes); } else { Context.LogWarning ( @@ -326,14 +326,14 @@ namespace Mono.Linker.Steps protected override void ProcessProperty (TypeDefinition type, PropertyDefinition property, XPathNavigator nav, object customData, bool fromSignature) { IEnumerable attributes = ProcessAttributes (nav, property); - if (attributes.Count () > 0) + if (attributes.Any ()) Context.CustomAttributes.AddCustomAttributes (property, attributes); } protected override void ProcessEvent (TypeDefinition type, EventDefinition @event, XPathNavigator nav, object customData) { IEnumerable attributes = ProcessAttributes (nav, @event); - if (attributes.Count () > 0) + if (attributes.Any ()) Context.CustomAttributes.AddCustomAttributes (@event, attributes); } diff --git a/src/linker/Linker.Steps/OutputStep.cs b/src/linker/Linker.Steps/OutputStep.cs index ebad3e1c4..da93461b0 100644 --- a/src/linker/Linker.Steps/OutputStep.cs +++ b/src/linker/Linker.Steps/OutputStep.cs @@ -169,10 +169,9 @@ namespace Mono.Linker.Steps return; using (var fs = File.Open (Path.Combine (Context.OutputDirectory, Context.PInvokesListFile), FileMode.Create)) { - Context.PInvokes.Distinct (); - Context.PInvokes.Sort (); + var values = Context.PInvokes.Distinct ().OrderBy (l => l); var jsonSerializer = new DataContractJsonSerializer (typeof (List)); - jsonSerializer.WriteObject (fs, Context.PInvokes); + jsonSerializer.WriteObject (fs, values); } } diff --git a/src/linker/Linker.Steps/ResolveFromXmlStep.cs b/src/linker/Linker.Steps/ResolveFromXmlStep.cs index d202c8ba2..f7cd9a5e5 100644 --- a/src/linker/Linker.Steps/ResolveFromXmlStep.cs +++ b/src/linker/Linker.Steps/ResolveFromXmlStep.cs @@ -41,9 +41,9 @@ namespace Mono.Linker.Steps { const string NamespaceElementName = "namespace"; - static readonly string _required = "required"; - static readonly string _preserve = "preserve"; - static readonly string _accessors = "accessors"; + const string _required = "required"; + const string _preserve = "preserve"; + const string _accessors = "accessors"; static readonly string[] _accessorsAll = new string[] { "all" }; static readonly char[] _accessorsSep = new char[] { ';' }; diff --git a/src/linker/Linker/AssemblyResolver.cs b/src/linker/Linker/AssemblyResolver.cs index 7ba22df57..5034a5344 100644 --- a/src/linker/Linker/AssemblyResolver.cs +++ b/src/linker/Linker/AssemblyResolver.cs @@ -124,9 +124,9 @@ namespace Mono.Linker { // Validate arguments, similarly to how the base class does it. if (name == null) - throw new ArgumentNullException ("name"); + throw new ArgumentNullException (nameof (name)); if (parameters == null) - throw new ArgumentNullException ("parameters"); + throw new ArgumentNullException (nameof (parameters)); if (!_assemblies.TryGetValue (name.Name, out AssemblyDefinition asm) && (_unresolvedAssemblies == null || !_unresolvedAssemblies.Contains (name.Name))) { try { diff --git a/src/linker/Linker/DirectoryAssemblyResolver.cs b/src/linker/Linker/DirectoryAssemblyResolver.cs index f6cb91d31..cd7e77137 100644 --- a/src/linker/Linker/DirectoryAssemblyResolver.cs +++ b/src/linker/Linker/DirectoryAssemblyResolver.cs @@ -71,9 +71,9 @@ namespace Mono.Linker { public virtual AssemblyDefinition Resolve (AssemblyNameReference name, ReaderParameters parameters) { if (name == null) - throw new ArgumentNullException ("name"); + throw new ArgumentNullException (nameof (name)); if (parameters == null) - throw new ArgumentNullException ("parameters"); + throw new ArgumentNullException (nameof (parameters)); var assembly = SearchDirectory (name, directories, parameters); if (assembly != null) diff --git a/src/linker/Linker/Driver.cs b/src/linker/Linker/Driver.cs index 84b204e3c..9da7cfd1b 100644 --- a/src/linker/Linker/Driver.cs +++ b/src/linker/Linker/Driver.cs @@ -43,10 +43,10 @@ namespace Mono.Linker #if FEATURE_ILLINK const string resolvers = "-a|-r|-x"; - static readonly string _linker = "IL Linker"; + const string _linker = "IL Linker"; #else const string resolvers = "-a|-i|-r|-x"; - static readonly string _linker = "Mono IL Linker"; + const string _linker = "Mono IL Linker"; #endif public static int Main (string[] args) diff --git a/src/linker/Linker/DynamicDependency.cs b/src/linker/Linker/DynamicDependency.cs index 2d162e8b6..25057a2c2 100644 --- a/src/linker/Linker/DynamicDependency.cs +++ b/src/linker/Linker/DynamicDependency.cs @@ -12,6 +12,7 @@ namespace Mono.Linker /// TypeReference instead of a Type, and it has a reference to the original /// CustomAttribute for dependency tracing. It is also a place for helper /// methods related to the attribute. + [System.AttributeUsage (System.AttributeTargets.Constructor | System.AttributeTargets.Field | System.AttributeTargets.Method, AllowMultiple = true, Inherited = false)] internal class DynamicDependency : Attribute { public CustomAttribute? OriginalAttribute { get; set; } diff --git a/src/linker/Linker/Inflater.cs b/src/linker/Linker/Inflater.cs deleted file mode 100644 index 9097cc1b9..000000000 --- a/src/linker/Linker/Inflater.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using Mono.Cecil; - -namespace Mono.Linker -{ - class Inflater - { - public static TypeReference InflateType (GenericContext context, TypeReference typeReference) - { - var typeDefinition = InflateTypeWithoutException (context, typeReference); - if (typeDefinition == null) - throw new InvalidOperationException ($"Unable to resolve a reference to the type '{typeReference.GetDisplayName ()}' in the assembly '{typeReference.Module.Assembly.FullName}'. Does this type exist in a different assembly in the project?"); - - return typeDefinition; - } - - public static GenericInstanceType InflateType (GenericContext context, TypeDefinition typeDefinition) - { - return ConstructGenericType (context, typeDefinition, typeDefinition.GenericParameters); - } - - public static GenericInstanceType InflateType (GenericContext context, GenericInstanceType genericInstanceType) - { - var inflatedType = ConstructGenericType (context, genericInstanceType.Resolve (), genericInstanceType.GenericArguments); - inflatedType.MetadataToken = genericInstanceType.MetadataToken; - return inflatedType; - } - - public static TypeReference InflateTypeWithoutException (GenericContext context, TypeReference typeReference) - { - if (typeReference is GenericParameter genericParameter) { - if (context.Method == null && genericParameter.Type != GenericParameterType.Type) { - // If no method is specified assume only partial inflation is desired. - return typeReference; - } - - var genericArgumentType = genericParameter.Type == GenericParameterType.Type - ? context.Type.GenericArguments[genericParameter.Position] - : context.Method.GenericArguments[genericParameter.Position]; - - var inflatedType = genericArgumentType; - return inflatedType; - } - if (typeReference is GenericInstanceType genericInstanceType) - return InflateType (context, genericInstanceType); - - if (typeReference is ArrayType arrayType) - return new ArrayType (InflateType (context, arrayType.ElementType), arrayType.Rank); - - if (typeReference is ByReferenceType byReferenceType) - return new ByReferenceType (InflateType (context, byReferenceType.ElementType)); - - if (typeReference is PointerType pointerType) - return new PointerType (InflateType (context, pointerType.ElementType)); - - if (typeReference is RequiredModifierType reqModType) - return InflateTypeWithoutException (context, reqModType.ElementType); - - if (typeReference is OptionalModifierType optModType) - return InflateTypeWithoutException (context, optModType.ElementType); - - return typeReference.Resolve (); - } - - static GenericInstanceType ConstructGenericType (GenericContext context, TypeDefinition typeDefinition, IEnumerable genericArguments) - { - var inflatedType = new GenericInstanceType (typeDefinition); - - foreach (var genericArgument in genericArguments) - inflatedType.GenericArguments.Add (InflateType (context, genericArgument)); - - return inflatedType; - } - - public class GenericContext - { - private readonly GenericInstanceType _type; - private readonly GenericInstanceMethod _method; - - public GenericContext (GenericInstanceType type, GenericInstanceMethod method) - { - _type = type; - _method = method; - } - - public GenericInstanceType Type { - get { return _type; } - } - - public GenericInstanceMethod Method { - get { return _method; } - } - } - } -} \ No newline at end of file diff --git a/src/linker/Linker/LinkContext.cs b/src/linker/Linker/LinkContext.cs index 013dcb7c8..bcaccbc64 100644 --- a/src/linker/Linker/LinkContext.cs +++ b/src/linker/Linker/LinkContext.cs @@ -348,7 +348,7 @@ namespace Mono.Linker return; if (_symbolReaderProvider == null) - throw new ArgumentNullException (nameof (_symbolReaderProvider)); + throw new InvalidOperationException ("Symbol provider is not set"); try { var symbolReader = _symbolReaderProvider.GetSymbolReader ( diff --git a/src/linker/Linker/MessageContainer.cs b/src/linker/Linker/MessageContainer.cs index fd81f732a..e42ed95ed 100644 --- a/src/linker/Linker/MessageContainer.cs +++ b/src/linker/Linker/MessageContainer.cs @@ -10,7 +10,7 @@ namespace Mono.Linker { public readonly struct MessageContainer { - public static readonly MessageContainer Empty = new MessageContainer (); + public static readonly MessageContainer Empty; /// /// Optional data with a filename, line and column that triggered the diff --git a/src/linker/Linker/MethodBodyScanner.cs b/src/linker/Linker/MethodBodyScanner.cs index e79426db1..1fe406fed 100644 --- a/src/linker/Linker/MethodBodyScanner.cs +++ b/src/linker/Linker/MethodBodyScanner.cs @@ -85,7 +85,7 @@ namespace Mono.Linker static HashSet AllPossibleStackTypes (MethodDefinition method) { if (!method.HasBody) - throw new ArgumentException (); + throw new ArgumentException ("Method does not have body", nameof (method)); var body = method.Body; var types = new HashSet (); diff --git a/src/linker/Linker/Tracer.cs b/src/linker/Linker/Tracer.cs index 43c8aa7df..32876937c 100644 --- a/src/linker/Linker/Tracer.cs +++ b/src/linker/Linker/Tracer.cs @@ -35,18 +35,15 @@ namespace Mono.Linker { protected readonly LinkContext context; - Stack dependency_stack; List recorders; public Tracer (LinkContext context) { this.context = context; - dependency_stack = new Stack (); } public void Finish () { - dependency_stack = null; if (recorders != null) { foreach (var recorder in recorders) { if (recorder is IDisposable disposableRecorder) diff --git a/src/linker/Mono.Linker.csproj b/src/linker/Mono.Linker.csproj index 471e6d0b3..4d4161e5f 100644 --- a/src/linker/Mono.Linker.csproj +++ b/src/linker/Mono.Linker.csproj @@ -3,6 +3,7 @@ Exe true + true diff --git a/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.csproj b/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.csproj index 871a186c3..d800ade3a 100644 --- a/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.csproj +++ b/test/ILLink.Tasks.Tests/ILLink.Tasks.Tests.csproj @@ -3,6 +3,7 @@ net5.0 false + false diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedExceptionHandlerSequenceAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedExceptionHandlerSequenceAttribute.cs index a90456dc6..0c9f10e4a 100644 --- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedExceptionHandlerSequenceAttribute.cs +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedExceptionHandlerSequenceAttribute.cs @@ -8,7 +8,7 @@ namespace Mono.Linker.Tests.Cases.Expectations.Assertions public ExpectedExceptionHandlerSequenceAttribute (string[] types) { if (types == null) - throw new ArgumentNullException (); + throw new ArgumentNullException (nameof (types)); } } } \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedInstructionSequenceAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedInstructionSequenceAttribute.cs index 5254351f5..7afdbb3b5 100644 --- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedInstructionSequenceAttribute.cs +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedInstructionSequenceAttribute.cs @@ -8,7 +8,7 @@ namespace Mono.Linker.Tests.Cases.Expectations.Assertions public ExpectedInstructionSequenceAttribute (string[] opCodes) { if (opCodes == null) - throw new ArgumentNullException (); + throw new ArgumentNullException (nameof (opCodes)); } } } \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedLocalsSequenceAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedLocalsSequenceAttribute.cs index 6eea5b561..17f9cae1c 100644 --- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedLocalsSequenceAttribute.cs +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/ExpectedLocalsSequenceAttribute.cs @@ -8,13 +8,13 @@ namespace Mono.Linker.Tests.Cases.Expectations.Assertions public ExpectedLocalsSequenceAttribute (string[] types) { if (types == null) - throw new ArgumentNullException (); + throw new ArgumentNullException (nameof (types)); } public ExpectedLocalsSequenceAttribute (Type[] types) { if (types == null) - throw new ArgumentNullException (); + throw new ArgumentNullException (nameof (types)); } } } \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptInitializerData.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptInitializerData.cs index b2c5647f0..6c448617c 100644 --- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptInitializerData.cs +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/KeptInitializerData.cs @@ -13,7 +13,7 @@ namespace Mono.Linker.Tests.Cases.Expectations.Assertions public KeptInitializerData (int occurrenceIndexInBody) { if (occurrenceIndexInBody < 0) - throw new ArgumentException (); + throw new ArgumentOutOfRangeException (nameof (occurrenceIndexInBody)); } } } \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/NoLinkedOutputAttribute.cs b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/NoLinkedOutputAttribute.cs index 0537a62e1..77574cad6 100644 --- a/test/Mono.Linker.Tests.Cases.Expectations/Assertions/NoLinkedOutputAttribute.cs +++ b/test/Mono.Linker.Tests.Cases.Expectations/Assertions/NoLinkedOutputAttribute.cs @@ -2,6 +2,7 @@ namespace Mono.Linker.Tests.Cases.Expectations.Assertions { + [AttributeUsage (AttributeTargets.Class)] public class NoLinkedOutputAttribute : Attribute { public NoLinkedOutputAttribute () { } diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj b/test/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj index fe28def77..211c1540b 100644 --- a/test/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj +++ b/test/Mono.Linker.Tests.Cases.Expectations/Mono.Linker.Tests.Cases.Expectations.csproj @@ -2,6 +2,7 @@ latest + false diff --git a/test/Mono.Linker.Tests.Cases.Expectations/Support/PlatformAssemblies.cs b/test/Mono.Linker.Tests.Cases.Expectations/Support/PlatformAssemblies.cs index 11c2f259c..d3c2dfcbd 100644 --- a/test/Mono.Linker.Tests.Cases.Expectations/Support/PlatformAssemblies.cs +++ b/test/Mono.Linker.Tests.Cases.Expectations/Support/PlatformAssemblies.cs @@ -1,8 +1,11 @@ -public static class PlatformAssemblies +namespace Mono.Linker.Tests.Cases.Expectations.Metadata { + public static class PlatformAssemblies + { #if NETCOREAPP - public const string CoreLib = "System.Private.CoreLib.dll"; + public const string CoreLib = "System.Private.CoreLib.dll"; #else - public const string CoreLib = "mscorlib.dll"; + public const string CoreLib = "mscorlib.dll"; #endif -} + } +} \ No newline at end of file diff --git a/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj b/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj index 645f3523e..6ebfb9890 100644 --- a/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj +++ b/test/Mono.Linker.Tests.Cases/Mono.Linker.Tests.Cases.csproj @@ -6,6 +6,7 @@ true false Latest + false diff --git a/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj b/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj index ff19e3e6e..56c70481a 100644 --- a/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj +++ b/test/Mono.Linker.Tests/Mono.Linker.Tests.csproj @@ -3,6 +3,7 @@ latest true + false -- cgit v1.2.3