Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 29 additions & 0 deletions QuantConnectStubsGenerator.Tests/GeneratorTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -504,6 +504,35 @@ internal event EventHandler InternalPropertyEvent
Assert.IsNull(internalFieldEvent);
}

[TestCase("implicit", true)]
[TestCase("explicit", false)]
public void StringConversionOperatorDeterminesStrInheritance(string keyword, bool shouldInheritFromStr)
{
var testGenerator = new TestGenerator
{
Files = new()
{
{ "TestClass.cs", $@"
namespace QuantConnect
{{
public class TestClass
{{
public static {keyword} operator string(TestClass obj)
{{
return obj.ToString();
}}
}}
}}" }
}
};

var result = testGenerator.GenerateModelsPublic();

var testClass = result.GetNamespaceByName("QuantConnect").GetClasses().Single(c => c.Type.Name == "TestClass");

Assert.AreEqual(shouldInheritFromStr, testClass.InheritsFrom.Any(t => t.Name == "str" && t.Namespace == null));
}

internal class TestGenerator : Generator
{
public Dictionary<string, string> Files { get; set; }
Expand Down
2 changes: 1 addition & 1 deletion QuantConnectStubsGenerator/Generator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -691,7 +691,7 @@ private void HandleSymbolGenericClass(Class cls, ParseContext context)
}

var symbolGenericBaseClasses = cls.InheritsFrom
.Where(x => x.Namespace.StartsWith("QuantConnect") && x.TypeParameters.Count > 0 && x.TypeParameters.Any(y => y.Equals(PythonType.SymbolType)))
.Where(x => x.Namespace != null && x.Namespace.StartsWith("QuantConnect") && x.TypeParameters.Count > 0 && x.TypeParameters.Any(y => y.Equals(PythonType.SymbolType)))
.ToList();

if (symbolGenericBaseClasses.Count == 0)
Expand Down
25 changes: 24 additions & 1 deletion QuantConnectStubsGenerator/Parser/ClassParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,17 +60,40 @@

private Class ParseClass(BaseTypeDeclarationSyntax node)
{
var inheritsFrom = ParseInheritedTypes(node).ToList();

// If this class implicitly converts to string in C#, it means you can use it
// wherever a string is expected. We reflect that in Python by inheriting from str
if (HasImplicitConversionToString(node) && !inheritsFrom.Any(t => t.Name == "str" && t.Namespace == null))
{
inheritsFrom.Insert(0, new PythonType("str"));
}

return new Class(_typeConverter.GetType(node, true, true, false))
{
Static = HasModifier(node, "static"),
Documentation = GetXmlDocumentation(node, CodeEntityType.Class),
Interface = node is InterfaceDeclarationSyntax,
InheritsFrom = ParseInheritedTypes(node).ToList(),
InheritsFrom = inheritsFrom,
MetaClass = ParseMetaClass(node),
AvoidImplicitTypes = HasAttribute(node.AttributeLists, "StubsAvoidImplicits")
};
}

private static bool HasImplicitConversionToString(BaseTypeDeclarationSyntax node)
{
if (node is not TypeDeclarationSyntax typeDecl)
{
return false;
}

return typeDecl.Members
.OfType<ConversionOperatorDeclarationSyntax>()
.Any(op => op.ImplicitOrExplicitKeyword.IsKind(SyntaxKind.ImplicitKeyword)
&& op.Type is PredefinedTypeSyntax predefined
&& predefined.Keyword.IsKind(SyntaxKind.StringKeyword));
}

private IEnumerable<PythonType> ParseInheritedTypes(BaseTypeDeclarationSyntax node)
{
var types = new List<PythonType>();
Expand Down Expand Up @@ -98,7 +121,7 @@
// "Cannot create consistent method ordering" errors appear when a Python class
// extends from classes A and B where B extends from A
// In this case we remove the direct inheritance on A
var interfacesToRemove = new HashSet<INamedTypeSymbol>();

Check warning on line 124 in QuantConnectStubsGenerator/Parser/ClassParser.cs

View workflow job for this annotation

GitHub Actions / build

Use 'SymbolEqualityComparer' when comparing symbols

Check warning on line 124 in QuantConnectStubsGenerator/Parser/ClassParser.cs

View workflow job for this annotation

GitHub Actions / build

Use 'SymbolEqualityComparer' when comparing symbols
foreach (var typeA in symbol.Interfaces)
{
foreach (var typeB in symbol.Interfaces)
Expand Down
Loading