Fixes for RoslynDeclarationProvider and Conversion.TryFindConversion (#271)

* RoslynDeclarationProvider uses all available source codes
to build the requested type declaration.

* Conversion.TryFindConversion looks for casts defined on the toType as
well.

Co-authored-by: Dmitry Kats <ratkillerx@hotmail.com>
This commit is contained in:
msdmkats 2020-08-13 18:47:59 +03:00 коммит произвёл GitHub
Родитель 421507bb6e
Коммит 980f1e2513
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
6 изменённых файлов: 121 добавлений и 28 удалений

Просмотреть файл

@ -474,9 +474,9 @@ namespace Microsoft.ML.Probabilistic.Compiler.Reflection
}
}
// check for custom conversions
MemberInfo[] implicits = fromType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName,
MemberInfo[] implicitsOnFromType = fromType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName,
"op_Implicit");
foreach (MemberInfo member in implicits)
foreach (MemberInfo member in implicitsOnFromType)
{
MethodInfo method = (MethodInfo) member;
if (method.ReturnType == toType)
@ -489,9 +489,25 @@ namespace Microsoft.ML.Probabilistic.Compiler.Reflection
return true;
}
}
MemberInfo[] explicits = fromType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName,
MemberInfo[] implicitsOnToType = toType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName,
"op_Implicit");
foreach (MemberInfo member in implicitsOnToType)
{
MethodInfo method = (MethodInfo)member;
ParameterInfo[] parameters = method.GetParameters();
if (parameters.Length == 1 && parameters[0].ParameterType == fromType)
{
info.SubclassCount = 1000;
info.Converter = delegate (object value)
{
return Util.Invoke(method, null, value);
};
return true;
}
}
MemberInfo[] explicitsOnFromType = fromType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName,
"op_Explicit");
foreach (MemberInfo member in explicits)
foreach (MemberInfo member in explicitsOnFromType)
{
MethodInfo method = (MethodInfo) member;
if (method.ReturnType == toType)
@ -504,6 +520,23 @@ namespace Microsoft.ML.Probabilistic.Compiler.Reflection
return true;
}
}
MemberInfo[] explicitsOnToType = toType.FindMembers(MemberTypes.Method, BindingFlags.Public | BindingFlags.Static | BindingFlags.InvokeMethod, Type.FilterName,
"op_Explicit");
foreach (MemberInfo member in explicitsOnToType)
{
MethodInfo method = (MethodInfo)member;
ParameterInfo[] parameters = method.GetParameters();
if (parameters.Length == 1 && parameters[0].ParameterType == fromType)
{
info.IsExplicit = true;
info.Converter = delegate (object value)
{
return Util.Invoke(method, null, value);
};
return true;
}
}
// lastly try the IConvertible interface
if (toType.IsPrimitive)
{

Просмотреть файл

@ -4,10 +4,9 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax;
@ -17,17 +16,9 @@ namespace Microsoft.ML.Probabilistic.Compiler
{
public SourceCode TryGetSource(Type t)
{
var asm = t.Assembly;
return asm.GetManifestResourceNames().Where(s => s.EndsWith(".cs")).Select(s =>
bool IsPrimaryFile(SourceFile sf)
{
var stream = asm.GetManifestResourceStream(s);
using (var reader = new StreamReader(stream))
{
return new SourceCode(s, reader.ReadToEnd());
}
}).FirstOrDefault(code =>
{
var tree = CSharpSyntaxTree.ParseText(code.SourceText, null, code.FilePath);
var tree = CSharpSyntaxTree.ParseText(sf.SourceText, null, sf.FilePath);
var root = tree.GetRoot();
var typeDecl = root.DescendantNodes()
.OfType<NamespaceDeclarationSyntax>()
@ -38,7 +29,24 @@ namespace Microsoft.ML.Probabilistic.Compiler
.Where(md => md.Identifier.ValueText.Equals(t.Name))
.FirstOrDefault();
return typeDecl != null;
});
}
var asm = t.Assembly;
SourceFile primaryFile = null;
List<SourceFile> additionalFiles = new List<SourceFile>();
foreach (var s in asm.GetManifestResourceNames().Where(s => s.EndsWith(".cs")))
{
SourceFile sf;
var stream = asm.GetManifestResourceStream(s);
using (var reader = new StreamReader(stream))
{
sf = new SourceFile(s, reader.ReadToEnd());
}
if (primaryFile == null && IsPrimaryFile(sf))
primaryFile = sf;
else
additionalFiles.Add(sf);
}
return primaryFile == null ? null : new SourceCode(primaryFile, additionalFiles.ToImmutableArray());
}
}
}

Просмотреть файл

@ -3,25 +3,41 @@
// See the LICENSE file in the project root for more information.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Collections.Immutable;
namespace Microsoft.ML.Probabilistic.Compiler
{
public class SourceCode
public class SourceFile
{
public string FilePath { get; }
public string SourceText { get; }
public SourceCode(string filePath, string sourceText)
public SourceFile(string filePath, string sourceText)
{
FilePath = filePath;
SourceText = sourceText;
}
}
public class SourceCode
{
/// <summary>
/// Source file that contains the primary definition.
/// </summary>
public SourceFile PrimaryFile { get; }
/// <summary>
/// Additional files that contain the definitions of some of the types referenced in the primary file.
/// Should be used to build a more complete semantic model.
/// </summary>
public ImmutableArray<SourceFile> AddtionalFiles { get; }
public SourceCode(SourceFile primaryFile, ImmutableArray<SourceFile> additionalFiles)
{
PrimaryFile = primaryFile;
AddtionalFiles = additionalFiles;
}
}
public interface ISourceProvider
{
SourceCode TryGetSource(Type t);

Просмотреть файл

@ -71,10 +71,12 @@ namespace Microsoft.ML.Probabilistic.Compiler
var source = SourceProvider.TryGetSource(t);
if (source == null)
throw new InvalidOperationException($"Cannot find source code for the type {t.Name}");
var tree = CSharpSyntaxTree.ParseText(source.SourceText, null, source.FilePath, Encoding.UTF8);
var primaryTree = CSharpSyntaxTree.ParseText(source.PrimaryFile.SourceText, null, source.PrimaryFile.FilePath, Encoding.UTF8);
var allTrees = source.AddtionalFiles.Select(f => CSharpSyntaxTree.ParseText(f.SourceText, null, f.FilePath, Encoding.UTF8)).ToList();
allTrees.Add(primaryTree);
var options = new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary);
var targetAssemblyName = Path.GetFileNameWithoutExtension(tree.FilePath);
var targetAssemblyName = Path.GetFileNameWithoutExtension(primaryTree.FilePath);
var asmName = t.Assembly.GetName();
var pk = asmName.GetPublicKey();
if (pk != null && pk.Length > 0)
@ -86,8 +88,8 @@ namespace Microsoft.ML.Probabilistic.Compiler
.WithPublicSign(true)
.WithCryptoPublicKey(System.Collections.Immutable.ImmutableArray.Create(pk));
}
var compilation = CSharpCompilation.Create(targetAssemblyName, new[] { tree }, references, options);
var model = compilation.GetSemanticModel(tree);
var compilation = CSharpCompilation.Create(targetAssemblyName, allTrees, references, options);
var model = compilation.GetSemanticModel(primaryTree);
var errors = compilation.GetDiagnostics().ToList();

Просмотреть файл

@ -143,6 +143,14 @@ namespace Microsoft.ML.Probabilistic.Tests
Assert.True(Conversion.TryGetConversion(typeof (Tester), typeof (int[]), out conv));
o = conv.Converter(t);
Console.WriteLine("{0}: {1}", o.GetType(), o);
Assert.True(Conversion.TryGetConversion(typeof(int), typeof(Tester), out conv));
Assert.True(conv.IsExplicit);
Assert.True(Conversion.TryGetConversion(typeof(int[]), typeof(Tester), out conv));
Assert.True(conv.IsExplicit);
Assert.True(Conversion.TryGetConversion(typeof(ImplicitlyConvertibleToTesterDefinesCast), typeof(Tester), out conv));
Assert.False(conv.IsExplicit);
Assert.True(Conversion.TryGetConversion(typeof(ImplicitlyConvertibleToTesterCastDefinedOnTester), typeof(Tester), out conv));
Assert.False(conv.IsExplicit);
// conversion from null
Assert.False(Conversion.TryGetConversion(typeof (Nullable), typeof (int), out conv));
@ -842,8 +850,35 @@ namespace Microsoft.ML.Probabilistic.Tests
{
return t.arrayField;
}
public static explicit operator Tester(int x)
{
var tester = new Tester
{
intField = x
};
return tester;
}
public static explicit operator Tester(int[] x)
{
var tester = new Tester
{
arrayField = x
};
return tester;
}
public static implicit operator Tester(ImplicitlyConvertibleToTesterCastDefinedOnTester _) => new Tester();
}
public class ImplicitlyConvertibleToTesterDefinesCast
{
public static implicit operator Tester(ImplicitlyConvertibleToTesterDefinesCast _) => new Tester();
}
public class ImplicitlyConvertibleToTesterCastDefinedOnTester { }
/// <summary>
/// Help class of dynamically invoking methods.
/// </summary>

Просмотреть файл

@ -70,7 +70,6 @@ namespace Microsoft.ML.Probabilistic.Tests
[Fact]
[Trait("Category", "CsoftModel")]
[Trait("Category", "OpenBug")]
public void MethodInAnotherFileTest()
{
Assert.Throws<CompilationFailedException>(() =>