diff --git a/src/AST/Namespace.cs b/src/AST/Namespace.cs index 733be99f..79b45f62 100644 --- a/src/AST/Namespace.cs +++ b/src/AST/Namespace.cs @@ -178,6 +178,11 @@ namespace CppSharp.AST return null; return @namespace.FindEnum(enumName, createDecl); + } + + public Enumeration FindEnum(IntPtr ptr) + { + return Enums.FirstOrDefault(f => f.OriginalPtr == ptr); } public Function FindFunction(string name, bool createDecl = false) @@ -348,7 +353,12 @@ namespace CppSharp.AST public Enumeration FindEnumWithItem(string name) { - return Enums.Find(e => e.ItemsByName.ContainsKey(name)); + var result = Enums.Find(e => e.ItemsByName.ContainsKey(name)); + if (result == null) + result = Namespaces.Select(ns => ns.FindEnumWithItem(name)).FirstOrDefault(); + if (result == null) + result = Classes.Select(c => c.FindEnumWithItem(name)).FirstOrDefault(); + return result; } public virtual IEnumerable FindOperator(CXXOperatorKind kind) diff --git a/src/Core/Parser/ASTConverter.cs b/src/Core/Parser/ASTConverter.cs index 8803e982..2dbb728d 100644 --- a/src/Core/Parser/ASTConverter.cs +++ b/src/Core/Parser/ASTConverter.cs @@ -1023,7 +1023,7 @@ namespace CppSharp { var item = decl.getItems(i); var _item = Visit(item) as AST.Enumeration.Item; - _enum.Items.Add(_item); + _enum.AddItem(_item); } return _enum; diff --git a/src/CppParser/AST.cpp b/src/CppParser/AST.cpp index 35567fdc..02a99767 100644 --- a/src/CppParser/AST.cpp +++ b/src/CppParser/AST.cpp @@ -270,6 +270,17 @@ Class* DeclarationContext::FindClass(const std::string& Name, bool IsComplete, return newClass; } +Enumeration* DeclarationContext::FindEnum(void* OriginalPtr) +{ + auto foundEnum = std::find_if(Enums.begin(), Enums.end(), + [&](Enumeration* enumeration) { return enumeration->OriginalPtr == OriginalPtr; }); + + if (foundEnum != Enums.end()) + return *foundEnum; + + return nullptr; +} + Enumeration* DeclarationContext::FindEnum(const std::string& Name, bool Create) { auto entries = split(Name, "::"); @@ -304,6 +315,27 @@ Enumeration* DeclarationContext::FindEnum(const std::string& Name, bool Create) return _namespace->FindEnum(enumName, Create); } +Enumeration* DeclarationContext::FindEnumWithItem(const std::string& Name) +{ + auto foundEnumIt = std::find_if(Enums.begin(), Enums.end(), + [&](Enumeration* _enum) { return _enum->FindItemByName(Name) != nullptr; }); + if (foundEnumIt != Enums.end()) + return *foundEnumIt; + for (auto it = Namespaces.begin(); it != Namespaces.end(); ++it) + { + auto foundEnum = (*it)->FindEnumWithItem(Name); + if (foundEnum != nullptr) + return foundEnum; + } + for (auto it = Classes.begin(); it != Classes.end(); ++it) + { + auto foundEnum = (*it)->FindEnumWithItem(Name); + if (foundEnum != nullptr) + return foundEnum; + } + return nullptr; +} + Function* DeclarationContext::FindFunction(const std::string& Name, bool Create) { auto foundFunction = std::find_if(Functions.begin(), Functions.end(), @@ -384,6 +416,8 @@ DEF_VECTOR(Function, Parameter*, Parameters) Method::Method() : IsDefaultConstructor(false), IsCopyConstructor(false), IsMoveConstructor(false) { Kind = DeclarationKind::Method; } +// Enumeration + Enumeration::Enumeration() : Declaration(DeclarationKind::Enumeration), Modifiers((EnumModifiers)0), Type(0), BuiltinType(0) {} @@ -396,6 +430,15 @@ Enumeration::Item::Item(const Item& rhs) : Declaration(rhs), DEF_STRING(Enumeration::Item, Expression) +Enumeration::Item* Enumeration::FindItemByName(const std::string& Name) +{ + auto foundEnumItem = std::find_if(Items.begin(), Items.end(), + [&](Item _item) { return _item.Name == Name; }); + if (foundEnumItem != Items.end()) + return &*foundEnumItem; + return nullptr; +} + Variable::Variable() : Declaration(DeclarationKind::Variable) {} DEF_STRING(Variable, Mangled) diff --git a/src/CppParser/AST.h b/src/CppParser/AST.h index ad124955..d0eb17da 100644 --- a/src/CppParser/AST.h +++ b/src/CppParser/AST.h @@ -402,7 +402,9 @@ struct CS_API DeclarationContext : public Declaration CS_IGNORE FunctionTemplate* FindFunctionTemplate(const std::string& Name, const std::vector& Params); + CS_IGNORE Enumeration* FindEnum(void* OriginalPtr); CS_IGNORE Enumeration* FindEnum(const std::string& Name, bool Create = false); + CS_IGNORE Enumeration* FindEnumWithItem(const std::string& Name); CS_IGNORE Function* FindFunction(const std::string& Name, bool Create = false); @@ -560,6 +562,8 @@ struct CS_API Enumeration : public Declaration CppSharp::CppParser::AST::Type* Type; CppSharp::CppParser::AST::BuiltinType* BuiltinType; VECTOR(Item, Items) + + Item* FindItemByName(const std::string& Name); }; struct CS_API Variable : public Declaration diff --git a/src/CppParser/Parser.cpp b/src/CppParser/Parser.cpp index 96c311c7..bcc2ed67 100644 --- a/src/CppParser/Parser.cpp +++ b/src/CppParser/Parser.cpp @@ -1705,18 +1705,48 @@ Type* Parser::WalkType(clang::QualType QualType, clang::TypeLoc* TL, Enumeration* Parser::WalkEnum(clang::EnumDecl* ED) { + using namespace clang; + auto NS = GetNamespace(ED); assert(NS && "Expected a valid namespace"); - auto Name = GetTagDeclName(ED); - auto E = NS->FindEnum(Name, /*Create=*/false); + auto E = NS->FindEnum(ED->getCanonicalDecl()); + if (E && !E->IsIncomplete) + return E; + + if (!E) + { + auto Name = GetTagDeclName(ED); + if (!Name.empty()) + E = NS->FindEnum(Name, /*Create=*/false); + else + { + // Enum with no identifier - try to find existing enum through enum items + for (auto it = ED->enumerator_begin(); it != ED->enumerator_end(); ++it) + { + EnumConstantDecl* ECD = (*it); + auto EnumItemName = ECD->getNameAsString(); + E = NS->FindEnumWithItem(EnumItemName); + break; + } + } + } if (E && !E->IsIncomplete) return E; if (!E) { - E = NS->FindEnum(Name, /*Create=*/true); + auto Name = GetTagDeclName(ED); + if (!Name.empty()) + E = NS->FindEnum(Name, /*Create=*/true); + else + { + E = new Enumeration(); + E->Name = Name; + E->_Namespace = NS; + NS->Enums.push_back(E); + } HandleDeclaration(ED, E); } diff --git a/src/Generator.Tests/AST/TestAST.cs b/src/Generator.Tests/AST/TestAST.cs index cf4b2d24..3292e1df 100644 --- a/src/Generator.Tests/AST/TestAST.cs +++ b/src/Generator.Tests/AST/TestAST.cs @@ -160,5 +160,13 @@ namespace CppSharp.Generator.Tests.AST .First(); Assert.IsTrue(plusOperator.Visit(testVisitor)); } + + [Test] + public void TestASTEnumItemByName() + { + var @enum = AstContext.FindEnum("TestASTEnumItemByName").Single(); + Assert.NotNull(@enum); + Assert.IsTrue(@enum.ItemsByName.ContainsKey("TestItemByName")); + } } } diff --git a/src/Generator.Tests/Passes/TestPasses.cs b/src/Generator.Tests/Passes/TestPasses.cs index 58d8edec..121e311e 100644 --- a/src/Generator.Tests/Passes/TestPasses.cs +++ b/src/Generator.Tests/Passes/TestPasses.cs @@ -1,4 +1,5 @@ using System.Linq; +using CppSharp; using CppSharp.Passes; using NUnit.Framework; @@ -95,6 +96,40 @@ namespace CppSharp.Generator.Tests.Passes Assert.That(@enum.Items[0].Name, Is.EqualTo("_0")); } + [Test] + public void TestUnnamedEnumSupport() + { + passBuilder.AddPass(new CleanInvalidDeclNamesPass()); + passBuilder.RunPasses(pass => pass.VisitLibrary(AstContext)); + + var unnamedEnum1 = AstContext.FindEnum("Unnamed_Enum_1").Single(); + var unnamedEnum2 = AstContext.FindEnum("Unnamed_Enum_2").Single(); + Assert.IsNotNull(unnamedEnum1); + Assert.IsNotNull(unnamedEnum2); + + Assert.AreEqual(2, unnamedEnum1.Items.Count); + Assert.AreEqual(2, unnamedEnum2.Items.Count); + + Assert.AreEqual(1, unnamedEnum1.Items[0].Value); + Assert.AreEqual(2, unnamedEnum1.Items[1].Value); + Assert.AreEqual(3, unnamedEnum2.Items[0].Value); + Assert.AreEqual(4, unnamedEnum2.Items[1].Value); + } + + [Test] + public void TestUniqueNamesAcrossTranslationUnits() + { + passBuilder.AddPass(new CleanInvalidDeclNamesPass()); + passBuilder.RunPasses(pass => pass.VisitLibrary(AstContext)); + + var unnamedEnum1 = AstContext.GetEnumWithMatchingItem("UnnamedEnumA1"); + var unnamedEnum2 = AstContext.GetEnumWithMatchingItem("UnnamedEnumB1"); + Assert.IsNotNull(unnamedEnum1); + Assert.IsNotNull(unnamedEnum2); + + Assert.AreNotEqual(unnamedEnum1.Name, unnamedEnum2.Name); + } + [Test] public void TestStructInheritance() { diff --git a/src/Generator/Passes/CleanInvalidDeclNamesPass.cs b/src/Generator/Passes/CleanInvalidDeclNamesPass.cs index 8e1be9e0..113b871f 100644 --- a/src/Generator/Passes/CleanInvalidDeclNamesPass.cs +++ b/src/Generator/Passes/CleanInvalidDeclNamesPass.cs @@ -68,23 +68,30 @@ namespace CppSharp.Passes } } - return base.VisitClassDecl(@class); + var currentUniqueName = this.uniqueName; + this.uniqueName = 0; + var ret = base.VisitClassDecl(@class); + this.uniqueName = currentUniqueName; + + return ret; } public override bool VisitFunctionDecl(Function function) { - uniqueName = 0; + var currentUniqueName = this.uniqueName; + this.uniqueName = 0; var ret = base.VisitFunctionDecl(function); - uniqueName = 0; + this.uniqueName = currentUniqueName; return ret; } public override bool VisitEvent(Event @event) { - uniqueName = 0; + var currentUniqueName = this.uniqueName; + this.uniqueName = 0; var ret = base.VisitEvent(@event); - uniqueName = 0; + this.uniqueName = currentUniqueName; return ret; } @@ -92,9 +99,10 @@ namespace CppSharp.Passes public override bool VisitFunctionType(FunctionType type, TypeQualifiers quals) { - uniqueName = 0; + var currentUniqueName = this.uniqueName; + this.uniqueName = 0; var ret = base.VisitFunctionType(type, quals); - uniqueName = 0; + this.uniqueName = currentUniqueName; return ret; } diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp index f81b1482..67c5632e 100644 --- a/src/Parser/Parser.cpp +++ b/src/Parser/Parser.cpp @@ -1719,15 +1719,43 @@ CppSharp::AST::Enumeration^ Parser::WalkEnum(clang::EnumDecl* ED) auto NS = GetNamespace(ED); assert(NS && "Expected a valid namespace"); - auto Name = marshalString(GetTagDeclName(ED)); - auto E = NS->FindEnum(Name, /*Create=*/false); + auto E = NS->FindEnum(System::IntPtr(ED->getCanonicalDecl())); + if (E && !E->IsIncomplete) + return E; + + if (!E) + { + auto Name = marshalString(GetTagDeclName(ED)); + if (!System::String::IsNullOrEmpty(Name)) + E = NS->FindEnum(Name, /*Create=*/false); + else + { + // Enum with no identifier - try to find existing enum through enum items + for (auto it = ED->enumerator_begin(); it != ED->enumerator_end(); ++it) + { + EnumConstantDecl* ECD = (*it); + auto EnumItemName = marshalString(ECD->getNameAsString()); + E = NS->FindEnumWithItem(EnumItemName); + break; + } + } + } if (E && !E->IsIncomplete) return E; if (!E) { - E = NS->FindEnum(Name, /*Create=*/true); + auto Name = marshalString(GetTagDeclName(ED)); + if (!System::String::IsNullOrEmpty(Name)) + E = NS->FindEnum(Name, /*Create=*/true); + else + { + E = gcnew CppSharp::AST::Enumeration(); + E->Name = Name; + E->Namespace = NS; + NS->Enums->Add(E); + } HandleDeclaration(ED, E); } diff --git a/tests/Native/AST.h b/tests/Native/AST.h index 6db63b44..0a7091de 100644 --- a/tests/Native/AST.h +++ b/tests/Native/AST.h @@ -19,4 +19,7 @@ namespace Math Complex Complex::operator+(Complex &other) { return Complex(re + other.re, im + other.im); } -} \ No newline at end of file +} + +// Tests Enum.ItemByName +enum TestASTEnumItemByName { TestItemByName }; \ No newline at end of file diff --git a/tests/Native/Enums.h b/tests/Native/Enums.h new file mode 100644 index 00000000..1043d58a --- /dev/null +++ b/tests/Native/Enums.h @@ -0,0 +1,9 @@ +enum +{ + UnnamedEnumA1, + EnumUnnamedA2 +}; + +// This line will make sure that a visitor won't enumerate all enums across +// different translation units at once. +struct TestUniqueNames {}; \ No newline at end of file diff --git a/tests/Native/Passes.h b/tests/Native/Passes.h index 09cd1e5a..4d288b89 100644 --- a/tests/Native/Passes.h +++ b/tests/Native/Passes.h @@ -39,4 +39,16 @@ struct TestReadOnlyProperties // TestStructInheritance struct S1 { int F1, F2; }; -struct S2 : S1 { int F3; }; \ No newline at end of file +struct S2 : S1 { int F3; }; + +// Tests unnamed enums +enum { Unnamed_Enum_1_A = 1, Unnamed_Enum_1_B = 2 }; +enum { Unnamed_Enum_2_A = 3, Unnamed_Enum_2_B = 4 }; + +// Tests unique name for unnamed enums across translation units +#include "Enums.h" +enum +{ + UnnamedEnumB1, + EnumUnnamedB2 +};