diff --git a/src/Generator.Tests/CppSharp.Generator.Tests.csproj b/src/Generator.Tests/CppSharp.Generator.Tests.csproj index b931b04c..daad660e 100644 --- a/src/Generator.Tests/CppSharp.Generator.Tests.csproj +++ b/src/Generator.Tests/CppSharp.Generator.Tests.csproj @@ -1,8 +1,8 @@  - - - + + + diff --git a/src/Generator.Tests/Passes/TestPasses.cs b/src/Generator.Tests/Passes/TestPasses.cs index bc3e145e..45387cdd 100644 --- a/src/Generator.Tests/Passes/TestPasses.cs +++ b/src/Generator.Tests/Passes/TestPasses.cs @@ -39,19 +39,31 @@ namespace CppSharp.Generator.Tests.Passes } [Test] - public void TestCheckFlagEnumsPass() + public void TestCheckEnumsPass() { var @enum = AstContext.Enum("FlagEnum"); + var enum2 = AstContext.Enum("FlagEnum2"); + var boolClassEnum = AstContext.Enum("BoolEnum"); + var ucharClassEnum = AstContext.Enum("UCharEnum"); + Assert.IsFalse(@enum.IsFlags); + Assert.IsFalse(enum2.IsFlags); + Assert.IsFalse(boolClassEnum.IsFlags); + Assert.IsFalse(ucharClassEnum.IsFlags); - var @enum2 = AstContext.Enum("FlagEnum2"); - Assert.IsFalse(@enum2.IsFlags); + Assert.IsTrue(boolClassEnum.BuiltinType.Type == PrimitiveType.Bool); + Assert.IsTrue(ucharClassEnum.BuiltinType.Type == PrimitiveType.UChar); - passBuilder.AddPass(new CheckFlagEnumsPass()); + passBuilder.AddPass(new CheckEnumsPass()); passBuilder.RunPasses(pass => pass.VisitASTContext(AstContext)); Assert.IsTrue(@enum.IsFlags); - Assert.IsFalse(@enum2.IsFlags); + Assert.IsFalse(enum2.IsFlags); + Assert.IsFalse(boolClassEnum.IsFlags); + Assert.IsTrue(ucharClassEnum.IsFlags); + + Assert.IsTrue(boolClassEnum.BuiltinType.Type != PrimitiveType.Bool, "C# does not support Bool enums"); + Assert.IsTrue(ucharClassEnum.BuiltinType.Type == PrimitiveType.UChar); } [Test] diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 161d051c..0edbcf1c 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -244,7 +244,7 @@ namespace CppSharp passes.AddPass(new FastDelegateToDelegatesPass()); passes.AddPass(new FieldToPropertyPass()); passes.AddPass(new CheckIgnoredDeclsPass()); - passes.AddPass(new CheckFlagEnumsPass()); + passes.AddPass(new CheckEnumsPass()); passes.AddPass(new MakeProtectedNestedTypesPublicPass()); if (Options.IsCSharpGenerator) diff --git a/src/Generator/Passes/CheckEnumsPass.cs b/src/Generator/Passes/CheckEnumsPass.cs new file mode 100644 index 00000000..5cbd8611 --- /dev/null +++ b/src/Generator/Passes/CheckEnumsPass.cs @@ -0,0 +1,76 @@ +using CppSharp.AST; +using CppSharp.Extensions; + +namespace CppSharp.Passes +{ + /// + /// Validates enumerations and checks if any should be treated as a collection + /// of flags (and annotate them with the .NET [Flags] when generated). + /// + public class CheckEnumsPass : TranslationUnitPass + { + public CheckEnumsPass() + => VisitOptions.ResetFlags(VisitFlags.NamespaceEnums); + + private static bool IsFlagEnum(Enumeration @enum) + { + // If the enumeration only has power of two values, assume it's + // a flags enum. + + var isFlags = true; + var hasBigRange = false; + + foreach (var item in @enum.Items) + { + var value = item.Value; + + if (value >= 4) + hasBigRange = true; + + if (value <= 1 || value.IsPowerOfTwo()) + continue; + + isFlags = false; + } + + // Only apply this heuristic if there are enough values to have a + // reasonable chance that it really is a bitfield. + + return isFlags && hasBigRange; + } + + private bool IsValidEnumBaseType(Enumeration @enum) + { + if (Options.IsCSharpGenerator) + return @enum.BuiltinType.Type.IsIntegerType(); + + return @enum.BuiltinType.Type.IsIntegerType() || @enum.BuiltinType.Type == PrimitiveType.Bool; + } + + public override bool VisitEnumDecl(Enumeration @enum) + { + if (!base.VisitEnumDecl(@enum)) + return false; + + if (!IsValidEnumBaseType(@enum)) + { + if (@enum.BuiltinType.Type == PrimitiveType.Bool) + { + @enum.BuiltinType = new BuiltinType(PrimitiveType.UChar); + } + else + { + Diagnostics.Warning( + "The enum `{0}` has a base type of `{1}`, which is currently not supported. The base type will be ignored.", + @enum, @enum.BuiltinType); + @enum.BuiltinType = new BuiltinType(PrimitiveType.Int); + } + } + + if (IsFlagEnum(@enum)) + @enum.Modifiers |= Enumeration.EnumModifiers.Flags; + + return true; + } + } +} diff --git a/src/Generator/Passes/CheckFlagEnumsPass.cs b/src/Generator/Passes/CheckFlagEnumsPass.cs deleted file mode 100644 index 46c888da..00000000 --- a/src/Generator/Passes/CheckFlagEnumsPass.cs +++ /dev/null @@ -1,52 +0,0 @@ -using CppSharp.AST; - -namespace CppSharp.Passes -{ - /// - /// Checks for enumerations that should be treated as a collection - /// of flags (and annotated with the .NET [Flags] when generated). - /// - public class CheckFlagEnumsPass : TranslationUnitPass - { - public CheckFlagEnumsPass() - => VisitOptions.ResetFlags(VisitFlags.NamespaceEnums); - - private static bool IsFlagEnum(Enumeration @enum) - { - // If the enumeration only has power of two values, assume it's - // a flags enum. - - var isFlags = true; - var hasBigRange = false; - - foreach (var item in @enum.Items) - { - var value = item.Value; - - if (value >= 4) - hasBigRange = true; - - if (value <= 1 || value.IsPowerOfTwo()) - continue; - - isFlags = false; - } - - // Only apply this heuristic if there are enough values to have a - // reasonable chance that it really is a bitfield. - - return isFlags && hasBigRange; - } - - public override bool VisitEnumDecl(Enumeration @enum) - { - if (IsFlagEnum(@enum)) - { - @enum.Modifiers |= Enumeration.EnumModifiers.Flags; - return true; - } - - return base.VisitEnumDecl(@enum); - } - } -} diff --git a/tests/dotnet/Common/Common.Tests.cs b/tests/dotnet/Common/Common.Tests.cs index eb088354..81552884 100644 --- a/tests/dotnet/Common/Common.Tests.cs +++ b/tests/dotnet/Common/Common.Tests.cs @@ -172,6 +172,7 @@ public class CommonTests } } + [Test] public void TestPrimitiveInOutParameters() { using (var hello = new Hello()) diff --git a/tests/dotnet/Native/ASTExtensions.h b/tests/dotnet/Native/ASTExtensions.h index f616d3ff..c52eb1c0 100644 --- a/tests/dotnet/Native/ASTExtensions.h +++ b/tests/dotnet/Native/ASTExtensions.h @@ -1,7 +1,7 @@ #include "AST.h" #include -// Tests class templates accross translation units +// Tests class templates across translation units // Explicit instantiation template class TestTemplateClass; // Implicit instantiations diff --git a/tests/dotnet/Native/Passes.h b/tests/dotnet/Native/Passes.h index be83d523..e3b2b9b8 100644 --- a/tests/dotnet/Native/Passes.h +++ b/tests/dotnet/Native/Passes.h @@ -14,6 +14,20 @@ enum FlagEnum2 D1 = 1 << 4, }; +enum class BoolEnum : bool +{ + True = true, + False = false, +}; + +enum class UCharEnum : unsigned char +{ + A = 1 << 0, + B = 1 << 1, + C = 1 << 2, + D = 1 << 3, +}; + class Foo { void toIgnore() { }