From 6f276debf6843e47db7f55e50483d261f4a352c8 Mon Sep 17 00:00:00 2001 From: Dimitar Dobrev Date: Fri, 20 Nov 2020 03:48:02 +0200 Subject: [PATCH] Don't check the unsupported operator= for symbols Signed-off-by: Dimitar Dobrev --- src/Generator.Tests/Passes/TestPasses.cs | 16 +- src/Generator/Driver.cs | 11 +- .../Passes/CheckDuplicatedNamesPass.cs | 8 +- .../Passes/CheckOperatorsOverloads.cs | 144 ++-------------- src/Generator/Passes/ValidateOperatorsPass.cs | 156 ++++++++++++++++++ 5 files changed, 184 insertions(+), 151 deletions(-) create mode 100644 src/Generator/Passes/ValidateOperatorsPass.cs diff --git a/src/Generator.Tests/Passes/TestPasses.cs b/src/Generator.Tests/Passes/TestPasses.cs index 856767b5..5e46c204 100644 --- a/src/Generator.Tests/Passes/TestPasses.cs +++ b/src/Generator.Tests/Passes/TestPasses.cs @@ -186,21 +186,17 @@ namespace CppSharp.Generator.Tests.Passes Assert.IsNotNull(@class); var overloads = @class.Methods.Where(m => m.Name == "Method"); var constMethod = overloads - .Where(m => m.IsConst && m.Parameters.Count == 0) - .FirstOrDefault(); + .FirstOrDefault(m => m.IsConst && m.Parameters.Count == 0); var nonConstMethod = overloads - .Where(m => !m.IsConst && m.Parameters.Count == 0) - .FirstOrDefault(); + .FirstOrDefault(m => !m.IsConst && m.Parameters.Count == 0); Assert.IsNotNull(constMethod); Assert.IsNotNull(nonConstMethod); Assert.IsTrue(constMethod.GenerationKind == GenerationKind.None); Assert.IsTrue(nonConstMethod.GenerationKind == GenerationKind.Generate); var constMethodWithParam = overloads - .Where(m => m.IsConst && m.Parameters.Count == 1) - .FirstOrDefault(); + .FirstOrDefault(m => m.IsConst && m.Parameters.Count == 1); var nonConstMethodWithParam = overloads - .Where(m => !m.IsConst && m.Parameters.Count == 1) - .FirstOrDefault(); + .FirstOrDefault(m => !m.IsConst && m.Parameters.Count == 1); Assert.IsNotNull(constMethodWithParam); Assert.IsNotNull(nonConstMethodWithParam); Assert.IsTrue(constMethodWithParam.GenerationKind == GenerationKind.None); @@ -226,7 +222,7 @@ namespace CppSharp.Generator.Tests.Passes [Test] public void TestAbstractOperator() { - passBuilder.AddPass(new CheckOperatorsOverloadsPass()); + passBuilder.AddPass(new ValidateOperatorsPass()); passBuilder.RunPasses(pass => pass.VisitASTContext(AstContext)); var @class = AstContext.FindDecl("ClassWithAbstractOperator").First(); @@ -246,7 +242,7 @@ namespace CppSharp.Generator.Tests.Passes Assert.IsNotNull(@public); Assert.AreEqual(AccessSpecifier.Public, @public.Access); */ - var @protected = @class.Fields.Where(f => f.Name == "Protected").FirstOrDefault(); + var @protected = @class.Fields.Find(f => f.Name == "Protected"); Assert.IsNotNull(@protected); Assert.AreEqual(AccessSpecifier.Protected, @protected.Access); } diff --git a/src/Generator/Driver.cs b/src/Generator/Driver.cs index 73d07595..2279481b 100644 --- a/src/Generator/Driver.cs +++ b/src/Generator/Driver.cs @@ -223,17 +223,18 @@ namespace CppSharp TranslationUnitPasses.AddPass(new CheckIgnoredDeclsPass()); } + if (Options.IsCLIGenerator || Options.IsCSharpGenerator) + { + TranslationUnitPasses.AddPass(new MoveFunctionToClassPass()); + TranslationUnitPasses.AddPass(new ValidateOperatorsPass()); + } + library.SetupPasses(this); TranslationUnitPasses.AddPass(new FindSymbolsPass()); TranslationUnitPasses.AddPass(new CheckMacroPass()); TranslationUnitPasses.AddPass(new CheckStaticClass()); - if (Options.IsCLIGenerator || Options.IsCSharpGenerator) - { - TranslationUnitPasses.AddPass(new MoveFunctionToClassPass()); - } - TranslationUnitPasses.AddPass(new CheckAmbiguousFunctions()); TranslationUnitPasses.AddPass(new ConstructorToConversionOperatorPass()); TranslationUnitPasses.AddPass(new MarshalPrimitivePointersAsRefTypePass()); diff --git a/src/Generator/Passes/CheckDuplicatedNamesPass.cs b/src/Generator/Passes/CheckDuplicatedNamesPass.cs index a2832527..8d9f526f 100644 --- a/src/Generator/Passes/CheckDuplicatedNamesPass.cs +++ b/src/Generator/Passes/CheckDuplicatedNamesPass.cs @@ -189,13 +189,7 @@ namespace CppSharp.Passes public class CheckDuplicatedNamesPass : TranslationUnitPass { - private readonly IDictionary names; - - public CheckDuplicatedNamesPass() - { - ClearVisitedDeclarations = false; - names = new Dictionary(); - } + private readonly IDictionary names = new Dictionary(); public override bool VisitASTContext(ASTContext context) { diff --git a/src/Generator/Passes/CheckOperatorsOverloads.cs b/src/Generator/Passes/CheckOperatorsOverloads.cs index d78901ca..617a1272 100644 --- a/src/Generator/Passes/CheckOperatorsOverloads.cs +++ b/src/Generator/Passes/CheckOperatorsOverloads.cs @@ -10,17 +10,24 @@ namespace CppSharp.Passes /// public class CheckOperatorsOverloadsPass : TranslationUnitPass { - public CheckOperatorsOverloadsPass() - { - ClearVisitedDeclarations = false; - } + public CheckOperatorsOverloadsPass() => + VisitOptions.VisitClassBases = + VisitOptions.VisitClassFields = + VisitOptions.VisitEventParameters = + VisitOptions.VisitFunctionParameters = + VisitOptions.VisitFunctionReturnType = + VisitOptions.VisitClassMethods = + VisitOptions.VisitNamespaceEnums = + VisitOptions.VisitNamespaceEvents = + VisitOptions.VisitNamespaceTemplates = + VisitOptions.VisitNamespaceTypedefs = + VisitOptions.VisitNamespaceVariables = + VisitOptions.VisitPropertyAccessors = + VisitOptions.VisitTemplateArguments = false; public override bool VisitClassDecl(Class @class) { - if (@class.CompleteDeclaration != null) - return VisitClassDecl(@class.CompleteDeclaration as Class); - - if (!VisitDeclarationContext(@class)) + if (!base.VisitClassDecl(@class)) return false; // Check for C++ operators that cannot be represented in .NET. @@ -49,14 +56,6 @@ namespace CppSharp.Passes { foreach (var @operator in @class.Operators.Where(o => o.IsGenerated)) { - if (!IsValidOperatorOverload(@operator) || @operator.IsPure) - { - Diagnostics.Debug("Invalid operator overload {0}::{1}", - @class.OriginalName, @operator.OperatorKind); - @operator.ExplicitlyIgnore(); - continue; - } - if (@operator.IsNonMemberOperator) continue; @@ -184,118 +183,5 @@ namespace CppSharp.Passes index = 0; return CXXOperatorKind.None; } - - private bool IsValidOperatorOverload(Method @operator) - { - // These follow the order described in MSDN (Overloadable Operators). - - switch (@operator.OperatorKind) - { - // These unary operators can be overloaded - case CXXOperatorKind.Plus: - case CXXOperatorKind.Minus: - case CXXOperatorKind.Exclaim: - case CXXOperatorKind.Tilde: - - // These binary operators can be overloaded - case CXXOperatorKind.Slash: - case CXXOperatorKind.Percent: - case CXXOperatorKind.Amp: - case CXXOperatorKind.Pipe: - case CXXOperatorKind.Caret: - - // The array indexing operator can be overloaded - case CXXOperatorKind.Subscript: - - // The conversion operators can be overloaded - case CXXOperatorKind.Conversion: - case CXXOperatorKind.ExplicitConversion: - return true; - - // The comparison operators can be overloaded if their return type is bool - case CXXOperatorKind.EqualEqual: - case CXXOperatorKind.ExclaimEqual: - case CXXOperatorKind.Less: - case CXXOperatorKind.Greater: - case CXXOperatorKind.LessEqual: - case CXXOperatorKind.GreaterEqual: - return @operator.ReturnType.Type.IsPrimitiveType(PrimitiveType.Bool); - - // Only prefix operators can be overloaded - case CXXOperatorKind.PlusPlus: - case CXXOperatorKind.MinusMinus: - Class @class; - var returnType = @operator.OriginalReturnType.Type.Desugar(); - returnType = (returnType.GetFinalPointee() ?? returnType).Desugar(); - return returnType.TryGetClass(out @class) && - @class.GetNonIgnoredRootBase() == - ((Class) @operator.Namespace).GetNonIgnoredRootBase() && - @operator.Parameters.Count == 0; - - // Bitwise shift operators can only be overloaded if the second parameter is int - case CXXOperatorKind.LessLess: - case CXXOperatorKind.GreaterGreater: - { - Parameter parameter = @operator.Parameters.Last(); - Type type = parameter.Type.Desugar(); - switch (Options.GeneratorKind) - { - case GeneratorKind.CLI: - return type.IsPrimitiveType(PrimitiveType.Int); - case GeneratorKind.CSharp: - Types.TypeMap typeMap; - if (Context.TypeMaps.FindTypeMap(type, out typeMap)) - { - var mappedTo = typeMap.CSharpSignatureType( - new TypePrinterContext - { - Parameter = parameter, - Type = type - }); - var cilType = mappedTo as CILType; - if (cilType?.Type == typeof(int)) - return true; - } - break; - } - return false; - } - - // No parameters means the dereference operator - cannot be overloaded - case CXXOperatorKind.Star: - return @operator.Parameters.Count > 0; - - // Assignment operators cannot be overloaded - case CXXOperatorKind.PlusEqual: - case CXXOperatorKind.MinusEqual: - case CXXOperatorKind.StarEqual: - case CXXOperatorKind.SlashEqual: - case CXXOperatorKind.PercentEqual: - case CXXOperatorKind.AmpEqual: - case CXXOperatorKind.PipeEqual: - case CXXOperatorKind.CaretEqual: - case CXXOperatorKind.LessLessEqual: - case CXXOperatorKind.GreaterGreaterEqual: - - // The conditional logical operators cannot be overloaded - case CXXOperatorKind.AmpAmp: - case CXXOperatorKind.PipePipe: - - // These operators cannot be overloaded. - case CXXOperatorKind.Equal: - case CXXOperatorKind.Comma: - case CXXOperatorKind.ArrowStar: - case CXXOperatorKind.Arrow: - case CXXOperatorKind.Call: - case CXXOperatorKind.Conditional: - case CXXOperatorKind.Coawait: - case CXXOperatorKind.New: - case CXXOperatorKind.Delete: - case CXXOperatorKind.Array_New: - case CXXOperatorKind.Array_Delete: - default: - return false; - } - } } } diff --git a/src/Generator/Passes/ValidateOperatorsPass.cs b/src/Generator/Passes/ValidateOperatorsPass.cs new file mode 100644 index 00000000..87fb912c --- /dev/null +++ b/src/Generator/Passes/ValidateOperatorsPass.cs @@ -0,0 +1,156 @@ +using System.Linq; +using CppSharp.AST; +using CppSharp.AST.Extensions; +using CppSharp.Generators; + +namespace CppSharp.Passes +{ + /// + /// Validates operators for C# (if they can be overloaded). + /// + public class ValidateOperatorsPass : TranslationUnitPass + { + public ValidateOperatorsPass() => + VisitOptions.VisitClassBases = + VisitOptions.VisitClassFields = + VisitOptions.VisitEventParameters = + VisitOptions.VisitFunctionParameters = + VisitOptions.VisitFunctionReturnType = + VisitOptions.VisitNamespaceEnums = + VisitOptions.VisitNamespaceEvents = + VisitOptions.VisitNamespaceTemplates = + VisitOptions.VisitNamespaceTypedefs = + VisitOptions.VisitNamespaceVariables = + VisitOptions.VisitPropertyAccessors = + VisitOptions.VisitTemplateArguments = false; + + public override bool VisitMethodDecl(Method method) + { + if (!base.VisitMethodDecl(method) || + !method.IsOperator || !method.IsGenerated) + return false; + + if (!IsValidOperatorOverload(method) || method.IsPure) + { + Diagnostics.Debug("Invalid operator overload {0}::{1}", + method.Namespace.Name, method.OperatorKind); + method.ExplicitlyIgnore(); + } + + return true; + } + + private bool IsValidOperatorOverload(Method @operator) + { + // These follow the order described in MSDN (Overloadable Operators). + + switch (@operator.OperatorKind) + { + // These unary operators can be overloaded + case CXXOperatorKind.Plus: + case CXXOperatorKind.Minus: + case CXXOperatorKind.Exclaim: + case CXXOperatorKind.Tilde: + + // These binary operators can be overloaded + case CXXOperatorKind.Slash: + case CXXOperatorKind.Percent: + case CXXOperatorKind.Amp: + case CXXOperatorKind.Pipe: + case CXXOperatorKind.Caret: + + // The array indexing operator can be overloaded + case CXXOperatorKind.Subscript: + + // The conversion operators can be overloaded + case CXXOperatorKind.Conversion: + case CXXOperatorKind.ExplicitConversion: + return true; + + // The comparison operators can be overloaded if their return type is bool + case CXXOperatorKind.EqualEqual: + case CXXOperatorKind.ExclaimEqual: + case CXXOperatorKind.Less: + case CXXOperatorKind.Greater: + case CXXOperatorKind.LessEqual: + case CXXOperatorKind.GreaterEqual: + return @operator.ReturnType.Type.IsPrimitiveType(PrimitiveType.Bool); + + // Only prefix operators can be overloaded + case CXXOperatorKind.PlusPlus: + case CXXOperatorKind.MinusMinus: + Class @class; + var returnType = @operator.OriginalReturnType.Type.Desugar(); + returnType = (returnType.GetFinalPointee() ?? returnType).Desugar(); + return returnType.TryGetClass(out @class) && + @class.GetNonIgnoredRootBase() == + ((Class) @operator.Namespace).GetNonIgnoredRootBase() && + @operator.Parameters.Count == 0; + + // Bitwise shift operators can only be overloaded if the second parameter is int + case CXXOperatorKind.LessLess: + case CXXOperatorKind.GreaterGreater: + { + Parameter parameter = @operator.Parameters.Last(); + Type type = parameter.Type.Desugar(); + switch (Options.GeneratorKind) + { + case GeneratorKind.CLI: + return type.IsPrimitiveType(PrimitiveType.Int); + case GeneratorKind.CSharp: + Types.TypeMap typeMap; + if (Context.TypeMaps.FindTypeMap(type, out typeMap)) + { + var mappedTo = typeMap.CSharpSignatureType( + new TypePrinterContext + { + Parameter = parameter, + Type = type + }); + var cilType = mappedTo as CILType; + if (cilType?.Type == typeof(int)) + return true; + } + break; + } + return false; + } + + // No parameters means the dereference operator - cannot be overloaded + case CXXOperatorKind.Star: + return @operator.Parameters.Count > 0; + + // Assignment operators cannot be overloaded + case CXXOperatorKind.PlusEqual: + case CXXOperatorKind.MinusEqual: + case CXXOperatorKind.StarEqual: + case CXXOperatorKind.SlashEqual: + case CXXOperatorKind.PercentEqual: + case CXXOperatorKind.AmpEqual: + case CXXOperatorKind.PipeEqual: + case CXXOperatorKind.CaretEqual: + case CXXOperatorKind.LessLessEqual: + case CXXOperatorKind.GreaterGreaterEqual: + + // The conditional logical operators cannot be overloaded + case CXXOperatorKind.AmpAmp: + case CXXOperatorKind.PipePipe: + + // These operators cannot be overloaded. + case CXXOperatorKind.Equal: + case CXXOperatorKind.Comma: + case CXXOperatorKind.ArrowStar: + case CXXOperatorKind.Arrow: + case CXXOperatorKind.Call: + case CXXOperatorKind.Conditional: + case CXXOperatorKind.Coawait: + case CXXOperatorKind.New: + case CXXOperatorKind.Delete: + case CXXOperatorKind.Array_New: + case CXXOperatorKind.Array_Delete: + default: + return false; + } + } + } +}