blob: 2946de2c4b5bb74298cf86db26199b21c74b37e0 [file] [log] [blame]
using Lucene.Net.Support;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using Assert = Lucene.Net.TestFramework.Assert;
using Console = Lucene.Net.Util.SystemConsole;
namespace Lucene.Net.Util
{
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/// <summary>
/// LUCENENET specific - functionality for scanning the API to ensure
/// naming and .NET conventions are followed consistently. Not for use
/// by end users.
/// </summary>
public abstract class ApiScanTestBase : LuceneTestCase
#if TESTFRAMEWORK_XUNIT
, Xunit.IClassFixture<BeforeAfterClass>
{
internal ApiScanTestBase(BeforeAfterClass beforeAfter)
: base(beforeAfter)
{
}
#else
{
internal ApiScanTestBase() { } // LUCENENET: Not for use by end users
#endif
/// <summary>
/// Private fields must be upper case separated with underscores,
/// must be camelCase (optionally may be prefixed with underscore,
/// but it is preferred not to use the underscore to match Lucene).
/// </summary>
private static readonly Regex PrivateFieldName = new Regex("^_?[a-z][a-zA-Z0-9_]*$|^[A-Z0-9_]+$", RegexOptions.Compiled);
/// <summary>
/// Protected fields must either be upper case separated with underscores or
/// must be prefixed with m_ (to avoid naming conflicts with properties).
/// </summary>
private static readonly Regex ProtectedFieldName = new Regex("^m_[a-z][a-zA-Z0-9_]*$|^[A-Z0-9_]+$", RegexOptions.Compiled);
/// <summary>
/// Method parameters must be camelCase and not begin or end with underscore.
/// </summary>
private static readonly Regex MethodParameterName = new Regex("^[a-z](?:[a-zA-Z0-9_]*[a-zA-Z0-9])?$", RegexOptions.Compiled);
/// <summary>
/// Interfaces must begin with "I" followed by another captial letter. Note this includes a
/// fix for generic interface names, that end with `{number}.
/// </summary>
private static readonly Regex InterfaceName = new Regex("^I[A-Z][a-zA-Z0-9_]*(?:`\\d+)?$", RegexOptions.Compiled);
/// <summary>
/// Class names must be pascal case and not use the interface naming convention.
/// </summary>
private static readonly Regex ClassName = new Regex("^[A-Z][a-zA-Z0-9_]*(?:`\\d+)?$", RegexOptions.Compiled);
/// <summary>
/// Public members should not contain the word "Comparer". In .NET, these should be named "Comparer".
/// </summary>
private static readonly Regex ContainsComparer = new Regex("[Cc]omparator", RegexOptions.Compiled);
/// <summary>
/// Public methods and properties should not contain the word "Int" that is not followed by 16, 32, or 64,
/// "Long", "Short", or "Float". These should be converted to their .NET names "Int32", "Int64", "Int16", and "Short".
/// Note we need to ignore common words such as "point", "intern", and "intersect".
/// </summary>
private static readonly Regex ContainsNonNetNumeric = new Regex("(?<![Pp]o|[Pp]r|[Jj]o)[Ii]nt(?!16|32|64|er|eg|ro)|[Ll]ong(?!est|er)|[Ss]hort(?!est|er)|[Ff]loat", RegexOptions.Compiled);
/// <summary>
/// Constants should not contain the word INT that is not followed by 16, 32, or 64, LONG, SHORT, or FLOAT
/// </summary>
private static readonly Regex ConstContainsNonNetNumeric = new Regex("(?<!PO|PR|JO)INT(?!16|32|64|ER|EG|RO)|LONG(?!EST|ER)|SHORT(?!EST|ER)|FLOAT", RegexOptions.Compiled);
/// <summary>
/// Matches IL code pattern for a method body with only a return statement for a local variable.
/// In this case, the array is writable by the consumer.
/// </summary>
private static readonly Regex MethodBodyReturnValueOnly = new Regex("\\0\\u0002\\{(?:.|\\\\u\\d\\d\\d\\d|\\0|\\[a-z]){3}\\u0004\\n\\+\\0\\u0006\\*", RegexOptions.Compiled);
//[Test, LuceneNetSpecific]
public virtual void TestProtectedFieldNames(Type typeFromTargetAssembly)
{
var names = GetInvalidProtectedFields(typeFromTargetAssembly.Assembly);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " invalid protected field names detected. " +
"Protected fields must be camelCase and prefixed with 'm_' to prevent naming conflicts with properties.");
}
//[Test, LuceneNetSpecific]
public virtual void TestPrivateFieldNames(Type typeFromTargetAssembly)
{
TestPrivateFieldNames(typeFromTargetAssembly, null);
}
//[Test, LuceneNetSpecific]
public virtual void TestPrivateFieldNames(Type typeFromTargetAssembly, string exceptionRegex)
{
var names = GetInvalidPrivateFields(typeFromTargetAssembly.Assembly, exceptionRegex);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " invalid private field names detected. " +
"Private field names should be camelCase.");
}
public virtual void TestPublicFields(Type typeFromTargetAssembly)
{
TestPublicFields(typeFromTargetAssembly, null);
}
//[Test, LuceneNetSpecific]
public virtual void TestPublicFields(Type typeFromTargetAssembly, string exceptionRegex)
{
var names = GetInvalidPublicFields(typeFromTargetAssembly.Assembly, exceptionRegex);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " public fields detected. Consider using public properties instead." +
"Public properties that return arrays should be decorated with the WritableArray attribute.");
}
//[Test, LuceneNetSpecific]
public virtual void TestMethodParameterNames(Type typeFromTargetAssembly)
{
var names = GetInvalidMethodParameterNames(typeFromTargetAssembly.Assembly);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " invalid method parameter names detected. " +
"Parameter names must be camelCase and may not start or end with '_'.");
}
//[Test, LuceneNetSpecific]
public virtual void TestInterfaceNames(Type typeFromTargetAssembly)
{
var names = GetInvalidInterfaceNames(typeFromTargetAssembly.Assembly);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " invalid interface names detected. " +
"Interface names must begin with a capital 'I' followed by another capital letter.");
}
//[Test, LuceneNetSpecific]
public virtual void TestClassNames(Type typeFromTargetAssembly)
{
var names = GetInvalidClassNames(typeFromTargetAssembly.Assembly);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " invalid class names detected. " +
"Class names must be Pascal case, but may not follow the interface naming " +
"convention of captial 'I' followed by another capital letter.");
}
//[Test, LuceneNetSpecific]
public virtual void TestForPropertiesWithNoGetter(Type typeFromTargetAssembly)
{
var names = GetPropertiesWithNoGetter(typeFromTargetAssembly.Assembly);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " properties with a setter but no getter detected. " +
"Getters are required for properties. If the main functionality is to set a value, " +
"consider using a method instead (prefixed with Set).");
}
//[Test, LuceneNetSpecific]
public virtual void TestForPropertiesThatReturnArray(Type typeFromTargetAssembly)
{
var names = GetPropertiesThatReturnArray(typeFromTargetAssembly.Assembly);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " properties that return Array detected. " +
"Properties should generally not return Array. Change to a method (prefixed with Get) " +
"or if returning an array that can be written to was intended, decorate with the WritableArray attribute. " +
"Note that returning an array field from either a property or method means the array can be written to by " +
"the consumer if the array is not cloned using arr.ToArray().");
}
#if !NETSTANDARD1_6
//[Test, LuceneNetSpecific]
public virtual void TestForMethodsThatReturnWritableArray(Type typeFromTargetAssembly)
{
var names = GetMethodsThatReturnWritableArray(typeFromTargetAssembly.Assembly);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " methods that return a writable Array detected. " +
"An array should be cloned before returning using arr.ToArray() or if it is intended to be writable, " +
"decorate with the WritableArray attribute and consider making it a property for clarity.");
}
#endif
//[Test, LuceneNetSpecific]
public virtual void TestForPublicMembersContainingComparer(Type typeFromTargetAssembly)
{
var names = new List<string>();
names.AddRange(GetProtectedFieldsContainingComparer(typeFromTargetAssembly.Assembly));
names.AddRange(GetMembersContainingComparer(typeFromTargetAssembly.Assembly));
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " member names containing the word 'comparer' detected. " +
"In .NET, we need to change the word 'comparer' to 'comparer'.");
}
//[Test, LuceneNetSpecific]
public virtual void TestForPublicMembersNamedSize(Type typeFromTargetAssembly)
{
var names = GetMembersNamedSize(typeFromTargetAssembly.Assembly);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " member names named 'Size'. " +
"In .NET, we need to change the name 'Size' to either 'Count' or 'Length', " +
"and it should generally be made a property.");
}
//[Test, LuceneNetSpecific]
public virtual void TestForPublicMembersContainingNonNetNumeric(Type typeFromTargetAssembly)
{
var names = GetMembersContainingNonNetNumeric(typeFromTargetAssembly.Assembly);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " member names containing the word 'Int' not followed " +
"by 16, 32, or 64, 'Long', 'Short', or 'Float' detected. " +
"In .NET, we need to change to 'Short' to 'Int16', 'Int' to 'Int32', 'Long' to 'Int64', and 'Float' to 'Single'.");
}
//[Test, LuceneNetSpecific]
public virtual void TestForTypesContainingNonNetNumeric(Type typeFromTargetAssembly)
{
var names = GetTypesContainingNonNetNumeric(typeFromTargetAssembly.Assembly);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " member names containing the word 'Int' not followed " +
"by 16, 32, or 64, 'Long', 'Short', or 'Float' detected. " +
"In .NET, we need to change to 'Short' to 'Int16', 'Int' to 'Int32', 'Long' to 'Int64', and 'Float' to 'Single'." +
"\n\nIMPORTANT: Before making changes, make sure to rename any types with ambiguous use of the word `Single` (meaning 'singular' rather than `System.Single`) to avoid confusion.");
}
//[Test, LuceneNetSpecific]
public virtual void TestForPublicMembersWithNullableEnum(Type typeFromTargetAssembly)
{
var names = GetPublicNullableEnumMembers(typeFromTargetAssembly.Assembly);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " members that are type nullable enum detected. " +
"Nullable enum parameters, fields, methods, and properties should be eliminated (where possible), either by " +
"eliminating the logic that depends on 'null'. Sometimes, it makes sense to keep a nullable enum parameter. " +
"In those cases, mark the member with the [ExceptionToNullableEnumConvention] attribute.");
}
//[Test, LuceneNetSpecific]
public virtual void TestForMembersAcceptingOrReturningIEnumerable(Type typeFromTargetAssembly)
{
TestForMembersAcceptingOrReturningIEnumerable(typeFromTargetAssembly, null);
}
//[Test, LuceneNetSpecific]
public virtual void TestForMembersAcceptingOrReturningIEnumerable(Type typeFromTargetAssembly, string exceptionRegex)
{
var names = GetMembersAcceptingOrReturningType(typeof(IEnumerable<>), typeFromTargetAssembly.Assembly, false, exceptionRegex);
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " members that accept or return IEnumerable<T> detected.");
}
//[Test, LuceneNetSpecific]
public virtual void TestForMembersAcceptingOrReturningListOrDictionary(Type typeFromTargetAssembly)
{
TestForMembersAcceptingOrReturningListOrDictionary(typeFromTargetAssembly, null);
}
//[Test, LuceneNetSpecific]
public virtual void TestForMembersAcceptingOrReturningListOrDictionary(Type typeFromTargetAssembly, string exceptionRegex)
{
var names = new List<string>();
names.AddRange(GetMembersAcceptingOrReturningType(typeof(List<>), typeFromTargetAssembly.Assembly, true, exceptionRegex));
names.AddRange(GetMembersAcceptingOrReturningType(typeof(Dictionary<,>), typeFromTargetAssembly.Assembly, true, exceptionRegex));
//if (VERBOSE)
//{
foreach (var name in names)
{
Console.WriteLine(name);
}
//}
Assert.IsFalse(names.Any(), names.Count() + " members that accept or return List<T> or Dictionary<K, V> detected. " +
"These should be changed to IList<T> and IDictionary<K, V>, respectively.");
}
private static IEnumerable<string> GetInvalidPrivateFields(Assembly assembly, string exceptionRegex)
{
var result = new List<string>();
var classes = assembly.GetTypes().Where(t => t.IsClass);
foreach (var c in classes)
{
var fields = c.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static);
foreach (var field in fields)
{
if (field.Name.StartsWith("<", StringComparison.Ordinal)) // Ignore auto-implemented properties
{
continue;
}
if (field.DeclaringType.GetEvent(field.Name) != null) // Ignore events
{
continue;
}
if ((field.IsPrivate || field.IsAssembly) && !PrivateFieldName.IsMatch(field.Name) && field.DeclaringType.Equals(c.UnderlyingSystemType))
{
var name = string.Concat(c.FullName, ".", field.Name);
if (!IsException(name, exceptionRegex))
{
result.Add(name);
}
}
}
}
return result.ToArray();
}
private static IEnumerable<string> GetInvalidProtectedFields(Assembly assembly)
{
var result = new List<string>();
var classes = assembly.GetTypes().Where(t => t.IsClass);
foreach (var c in classes)
{
if (!string.IsNullOrEmpty(c.Namespace) && c.Namespace.StartsWith("Lucene.Net.Support", StringComparison.Ordinal))
{
continue;
}
if (!string.IsNullOrEmpty(c.Name) && c.Name.Equals("AssemblyKeys", StringComparison.Ordinal))
{
continue;
}
var fields = c.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach (var field in fields)
{
if (field.Name.StartsWith("<", StringComparison.Ordinal)) // Ignore auto-implemented properties
{
continue;
}
if (field.DeclaringType.GetEvent(field.Name) != null) // Ignore events
{
continue;
}
if ((field.IsFamily || field.IsFamilyOrAssembly) && !ProtectedFieldName.IsMatch(field.Name) && field.DeclaringType.Equals(c.UnderlyingSystemType))
{
result.Add(string.Concat(c.FullName, ".", field.Name));
}
}
}
return result.ToArray();
}
/// <summary>
/// All public fields are invalid
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
private static IEnumerable<string> GetInvalidPublicFields(Assembly assembly, string exceptionRegex)
{
var result = new List<string>();
var classes = assembly.GetTypes().Where(t => t.IsClass);
foreach (var c in classes)
{
if (c.Name.StartsWith("<", StringComparison.Ordinal)) // Ignore classes produced by anonymous methods
{
continue;
}
if (!string.IsNullOrEmpty(c.Namespace) && c.Namespace.StartsWith("Lucene.Net.Support", StringComparison.Ordinal))
{
continue;
}
var fields = c.GetFields(BindingFlags.Public | BindingFlags.Instance);
foreach (var field in fields)
{
if (field.Name.StartsWith("<", StringComparison.Ordinal)) // Ignore auto-implemented properties
{
continue;
}
if (field.DeclaringType.GetEvent(field.Name) != null) // Ignore events
{
continue;
}
if (field.IsPublic && field.DeclaringType.Equals(c.UnderlyingSystemType))
{
var name = string.Concat(c.FullName, ".", field.Name);
if (!IsException(name, exceptionRegex))
{
result.Add(name);
}
}
}
}
return result.ToArray();
}
private static IEnumerable<string> GetInvalidMethodParameterNames(Assembly assembly)
{
var result = new List<string>();
var classes = assembly.GetTypes().Where(t => t.IsClass);
foreach (var c in classes)
{
var methods = c.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (var method in methods)
{
if (method.Name.StartsWith("<", StringComparison.Ordinal)) // Ignore auto-generated methods
{
continue;
}
var parameters = method.GetParameters();
foreach (var parameter in parameters)
{
if (!MethodParameterName.IsMatch(parameter.Name) && method.DeclaringType.Equals(c.UnderlyingSystemType))
{
result.Add(string.Concat(c.FullName, ".", method.Name, " -parameter- ", parameter.Name));
}
}
}
}
return result.ToArray();
}
private static IEnumerable<string> GetInvalidInterfaceNames(Assembly assembly)
{
var result = new List<string>();
var interfaces = assembly.GetTypes().Where(t => t.IsInterface);
foreach (var i in interfaces)
{
if (!InterfaceName.IsMatch(i.Name))
{
result.Add(i.FullName);
}
}
return result.ToArray();
}
private static IEnumerable<string> GetInvalidClassNames(Assembly assembly)
{
var result = new List<string>();
var classes = assembly.GetTypes().Where(t => t.IsClass);
foreach (var c in classes)
{
if (c.Name.StartsWith("<", StringComparison.Ordinal)) // Ignore classes produced by anonymous methods
{
continue;
}
if (c.IsDefined(typeof(ExceptionToClassNameConventionAttribute)))
{
continue;
}
if (!ClassName.IsMatch(c.Name) || InterfaceName.IsMatch(c.Name))
{
result.Add(c.FullName);
}
}
return result.ToArray();
}
private static IEnumerable<string> GetPropertiesWithNoGetter(Assembly assembly)
{
var result = new List<string>();
var classes = assembly.GetTypes().Where(t => t.IsClass);
foreach (var c in classes)
{
var properties = c.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var property in properties)
{
if (property.GetSetMethod(true) != null && property.GetGetMethod(true) == null && property.DeclaringType.Equals(c.UnderlyingSystemType))
{
result.Add(string.Concat(c.FullName, ".", property.Name));
}
}
}
return result.ToArray();
}
private static IEnumerable<string> GetPropertiesThatReturnArray(Assembly assembly)
{
var result = new List<string>();
var classes = assembly.GetTypes().Where(t => t.IsClass);
foreach (var c in classes)
{
var properties = c.GetProperties(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var property in properties)
{
// Skip attributes with WritableArrayAttribute defined. These are
// properties that were intended to expose arrays, as per MSDN this
// is not a .NET best practice. However, Lucene's design requires that
// this be done.
if (property.IsDefined(typeof(WritableArrayAttribute)))
{
continue;
}
var getMethod = property.GetGetMethod();
if (getMethod != null && getMethod.ReturnParameter != null && getMethod.ReturnParameter.ParameterType.IsArray && property.DeclaringType.Equals(c.UnderlyingSystemType))
{
result.Add(string.Concat(c.FullName, ".", property.Name));
}
}
}
return result.ToArray();
}
private static IEnumerable<string> GetProtectedFieldsContainingComparer(Assembly assembly)
{
var result = new List<string>();
var classes = assembly.GetTypes().Where(t => t.IsClass);
foreach (var c in classes)
{
var fields = c.GetFields(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach (var field in fields)
{
if (field.Name.StartsWith("<", StringComparison.Ordinal)) // Ignore auto-implemented properties
{
continue;
}
if (field.DeclaringType.GetEvent(field.Name) != null) // Ignore events
{
continue;
}
if ((field.IsFamily || field.IsFamilyOrAssembly) && ContainsComparer.IsMatch(field.Name) && field.DeclaringType.Equals(c.UnderlyingSystemType))
{
result.Add(string.Concat(c.FullName, ".", field.Name));
}
}
}
return result.ToArray();
}
private static IEnumerable<string> GetMembersContainingComparer(Assembly assembly)
{
var result = new List<string>();
var types = assembly.GetTypes();
foreach (var t in types)
{
if (ContainsComparer.IsMatch(t.Name) && t.IsVisible)
{
result.Add(t.FullName);
}
var members = t.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (var member in members)
{
if (ContainsComparer.IsMatch(member.Name) && member.DeclaringType.Equals(t.UnderlyingSystemType))
{
if (member.MemberType == MemberTypes.Method && !(member.Name.StartsWith("get_", StringComparison.Ordinal) || member.Name.StartsWith("set_", StringComparison.Ordinal)))
{
result.Add(string.Concat(t.FullName, ".", member.Name, "()"));
}
else if (member.MemberType == MemberTypes.Property)
{
result.Add(string.Concat(t.FullName, ".", member.Name));
}
else if (member.MemberType == MemberTypes.Event)
{
result.Add(string.Concat(t.FullName, ".", member.Name, " (event)"));
}
}
}
}
return result.ToArray();
}
private static IEnumerable<string> GetMembersNamedSize(Assembly assembly)
{
var result = new List<string>();
var types = assembly.GetTypes();
foreach (var t in types)
{
var members = t.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (var member in members)
{
if ("Size".Equals(member.Name, StringComparison.OrdinalIgnoreCase) && member.DeclaringType.Equals(t.UnderlyingSystemType))
{
if (member.MemberType == MemberTypes.Method && !(member.Name.StartsWith("get_", StringComparison.Ordinal) || member.Name.StartsWith("set_", StringComparison.Ordinal)))
{
var method = (MethodInfo)member;
// Ignore methods with parameters
if (!method.GetParameters().Any())
{
result.Add(string.Concat(t.FullName, ".", member.Name, "()"));
}
}
else if (member.MemberType == MemberTypes.Property)
{
result.Add(string.Concat(t.FullName, ".", member.Name));
}
}
}
}
return result.ToArray();
}
private static IEnumerable<string> GetMembersContainingNonNetNumeric(Assembly assembly)
{
var result = new List<string>();
var types = assembly.GetTypes();
foreach (var t in types)
{
//if (ContainsComparer.IsMatch(t.Name) && t.IsVisible)
//{
// result.Add(t.FullName);
//}
var members = t.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (var member in members)
{
// Ignore properties, methods, and events with IgnoreNetNumericConventionAttribute
if (member.IsDefined(typeof(ExceptionToNetNumericConventionAttribute)))
{
continue;
}
if (ContainsNonNetNumeric.IsMatch(member.Name) && member.DeclaringType.Equals(t.UnderlyingSystemType))
{
if (member.MemberType == MemberTypes.Method && !(member.Name.StartsWith("get_", StringComparison.Ordinal) || member.Name.StartsWith("set_", StringComparison.Ordinal)))
{
result.Add(string.Concat(t.FullName, ".", member.Name, "()"));
}
else if (member.MemberType == MemberTypes.Property)
{
result.Add(string.Concat(t.FullName, ".", member.Name));
}
else if (member.MemberType == MemberTypes.Event)
{
result.Add(string.Concat(t.FullName, ".", member.Name, " (event)"));
}
}
}
}
return result.ToArray();
}
private static IEnumerable<string> GetTypesContainingNonNetNumeric(Assembly assembly)
{
var result = new List<string>();
var types = assembly.GetTypes();
foreach (var t in types)
{
if (t.IsDefined(typeof(ExceptionToNetNumericConventionAttribute)))
{
continue;
}
if (ContainsNonNetNumeric.IsMatch(t.Name))
{
result.Add(t.FullName);
}
}
return result.ToArray();
}
#if !NETSTANDARD1_6
private static IEnumerable<string> GetMethodsThatReturnWritableArray(Assembly assembly)
{
var result = new List<string>();
var classes = assembly.GetTypes().Where(t => t.IsClass);
foreach (var c in classes)
{
if (c.Name.StartsWith("<", StringComparison.Ordinal)) // Ignore classes produced by anonymous methods
{
continue;
}
var methods = c.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance);
foreach (var method in methods)
{
// Skip attributes with WritableArrayAttribute defined. These are
// properties that were intended to expose arrays, as per MSDN this
// is not a .NET best practice. However, Lucene's design requires that
// this be done.
if (method.IsDefined(typeof(WritableArrayAttribute)))
{
continue;
}
// Ignore property method definitions
if (method.Name.StartsWith("get_", StringComparison.Ordinal) || method.Name.StartsWith("set_", StringComparison.Ordinal))
{
continue;
}
if (method != null && method.ReturnParameter != null
&& method.ReturnParameter.ParameterType.IsArray
&& method.DeclaringType.Equals(c.UnderlyingSystemType))
{
var methodBody = method.GetMethodBody();
if (methodBody != null)
{
var il = Encoding.UTF8.GetString(methodBody.GetILAsByteArray());
if (MethodBodyReturnValueOnly.IsMatch(il))
{
result.Add(string.Concat(c.FullName, ".", method.Name));
}
}
}
}
}
return result.ToArray();
}
#endif
private static IEnumerable<string> GetPublicNullableEnumMembers(Assembly assembly)
{
var result = new List<string>();
var types = assembly.GetTypes();
foreach (var t in types)
{
var members = t.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (var member in members)
{
if (member.Name.StartsWith("<", StringComparison.Ordinal)) // Ignore auto-generated methods
{
continue;
}
// Ignore properties, methods, and events with IgnoreNetNumericConventionAttribute
if (member.IsDefined(typeof(ExceptionToNullableEnumConventionAttribute)))
{
continue;
}
if (member.DeclaringType.Equals(t.UnderlyingSystemType))
{
if (member.MemberType == MemberTypes.Method && !(member.Name.StartsWith("get_", StringComparison.Ordinal) || member.Name.StartsWith("set_", StringComparison.Ordinal)))
{
var method = (MethodInfo)member;
if (!method.IsPrivate)
{
if (method.ReturnParameter != null
&& Nullable.GetUnderlyingType(method.ReturnParameter.ParameterType) != null
&& method.ReturnParameter.ParameterType.GetGenericArguments()[0].IsEnum)
{
result.Add(string.Concat(t.FullName, ".", member.Name, "()"));
}
var parameters = method.GetParameters();
foreach (var parameter in parameters)
{
if (Nullable.GetUnderlyingType(parameter.ParameterType) != null
&& parameter.ParameterType.GetGenericArguments()[0].IsEnum
&& member.DeclaringType.Equals(t.UnderlyingSystemType))
{
result.Add(string.Concat(t.FullName, ".", member.Name, "()", " -parameter- ", parameter.Name));
}
}
}
}
else if (member.MemberType == MemberTypes.Constructor)
{
var constructor = (ConstructorInfo)member;
if (!constructor.IsPrivate)
{
var parameters = constructor.GetParameters();
foreach (var parameter in parameters)
{
if (Nullable.GetUnderlyingType(parameter.ParameterType) != null
&& parameter.ParameterType.GetGenericArguments()[0].IsEnum
&& member.DeclaringType.Equals(t.UnderlyingSystemType))
{
result.Add(string.Concat(t.FullName, ".", member.Name, "()", " -parameter- ", parameter.Name));
}
}
}
}
else if (member.MemberType == MemberTypes.Property
&& Nullable.GetUnderlyingType(((PropertyInfo)member).PropertyType) != null
&& ((PropertyInfo)member).PropertyType.GetGenericArguments()[0].IsEnum
&& IsNonPrivateProperty((PropertyInfo)member))
{
result.Add(string.Concat(t.FullName, ".", member.Name));
}
else if (member.MemberType == MemberTypes.Field
&& Nullable.GetUnderlyingType(((FieldInfo)member).FieldType) != null
&& ((FieldInfo)member).FieldType.GetGenericArguments()[0].IsEnum
&& (((FieldInfo)member).IsFamily || ((FieldInfo)member).IsFamilyOrAssembly))
{
result.Add(string.Concat(t.FullName, ".", member.Name, " (field)"));
}
}
}
}
return result.ToArray();
}
private static bool IsNonPrivateProperty(PropertyInfo property)
{
var getMethod = property.GetGetMethod();
var setMethod = property.GetSetMethod();
return ((getMethod != null && !getMethod.IsPrivate) ||
(setMethod != null && !setMethod.IsPrivate));
}
private static bool IsException(string name, string exceptionRegex)
{
bool hasExceptions = !string.IsNullOrWhiteSpace(exceptionRegex);
return (hasExceptions && Regex.IsMatch(name, exceptionRegex));
}
/// <summary>
/// Some parameters were incorrectly changed from List to IEnumerable during the port. This is
/// to track down constructor and method parameters and property and method return types
/// containing IEnumerable
/// </summary>
/// <param name="assembly"></param>
/// <returns></returns>
private static IEnumerable<string> GetMembersAcceptingOrReturningType(Type lookFor, Assembly assembly, bool publiclyVisibleOnly, string exceptionRegex)
{
var result = new List<string>();
var types = assembly.GetTypes();
foreach (var t in types)
{
var members = t.GetMembers(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.Static);
foreach (var member in members)
{
if (member.Name.StartsWith("<", StringComparison.Ordinal)) // Ignore auto-generated methods
{
continue;
}
if (member.DeclaringType.Equals(t.UnderlyingSystemType))
{
if (member.MemberType == MemberTypes.Method && !(member.Name.StartsWith("get_", StringComparison.Ordinal) || member.Name.StartsWith("set_", StringComparison.Ordinal)))
{
var method = (MethodInfo)member;
if (!publiclyVisibleOnly || !method.IsPrivate)
{
if (method.ReturnParameter != null
&& method.ReturnParameter.ParameterType.IsGenericType
&& method.ReturnParameter.ParameterType.GetGenericTypeDefinition().IsAssignableFrom(lookFor))
{
var name = string.Concat(t.FullName, ".", member.Name, "()");
if (!IsException(name, exceptionRegex))
{
result.Add(name);
}
}
var parameters = method.GetParameters();
foreach (var parameter in parameters)
{
if (parameter.ParameterType.IsGenericType
&& parameter.ParameterType.GetGenericTypeDefinition().IsAssignableFrom(lookFor)
&& member.DeclaringType.Equals(t.UnderlyingSystemType))
{
var name = string.Concat(t.FullName, ".", member.Name, "()", " -parameter- ", parameter.Name);
if (!IsException(name, exceptionRegex))
{
result.Add(name);
}
}
}
}
}
else if (member.MemberType == MemberTypes.Constructor)
{
var constructor = (ConstructorInfo)member;
if (!publiclyVisibleOnly || !constructor.IsPrivate)
{
var parameters = constructor.GetParameters();
foreach (var parameter in parameters)
{
if (parameter.ParameterType.IsGenericType
&& parameter.ParameterType.GetGenericTypeDefinition().IsAssignableFrom(lookFor)
&& member.DeclaringType.Equals(t.UnderlyingSystemType))
{
var name = string.Concat(t.FullName, ".", member.Name, "()", " -parameter- ", parameter.Name);
if (!IsException(name, exceptionRegex))
{
result.Add(name);
}
}
}
}
}
else if (member.MemberType == MemberTypes.Property
&& ((PropertyInfo)member).PropertyType.IsGenericType
&& ((PropertyInfo)member).PropertyType.GetGenericTypeDefinition().IsAssignableFrom(lookFor)
&& (!publiclyVisibleOnly || IsNonPrivateProperty((PropertyInfo)member)))
{
var name = string.Concat(string.Concat(t.FullName, ".", member.Name));
if (!IsException(name, exceptionRegex))
{
result.Add(name);
}
}
//else if (member.MemberType == MemberTypes.Field
// && ((FieldInfo)member).IndexableFieldType.IsGenericType
// && ((FieldInfo)member).IndexableFieldType.GetGenericTypeDefinition().IsAssignableFrom(lookFor)
// && (!publiclyVisibleOnly || (((FieldInfo)member).IsFamily || ((FieldInfo)member).IsFamilyOrAssembly)))
//{
// result.Add(string.Concat(t.FullName, ".", member.Name, " (field)"));
//}
}
}
}
return result.ToArray();
}
}
}