From 521845be81233243bae4b521a5f28d4e6e1405b0 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Thu, 17 Apr 2014 21:16:20 +0200 Subject: [PATCH 1/8] Fixed parser to include enumerations which don't have an identifier set. --- src/Parser/Parser.cpp | 24 ++++++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp index 87a5b605..581108a1 100644 --- a/src/Parser/Parser.cpp +++ b/src/Parser/Parser.cpp @@ -1714,14 +1714,34 @@ CppSharp::AST::Enumeration^ Parser::WalkEnum(clang::EnumDecl* ED) assert(NS && "Expected a valid namespace"); auto Name = marshalString(GetTagDeclName(ED)); - auto E = NS->FindEnum(Name, /*Create=*/false); + CppSharp::AST::Enumeration^ E = nullptr; + 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); + if (!System::String::IsNullOrEmpty(Name)) + E = NS->FindEnum(Name, /*Create=*/true); + else + { + E = gcnew CppSharp::AST::Enumeration(); + E->Namespace = NS; + NS->Enums->Add(E); + } HandleDeclaration(ED, E); } From c2bfdcc1a64525c16f91f6a8b543a00c63f14f09 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Thu, 17 Apr 2014 23:21:18 +0200 Subject: [PATCH 2/8] Helper method FindEnumWithItem will look into namespaces and classes too now. --- src/AST/Namespace.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/AST/Namespace.cs b/src/AST/Namespace.cs index 733be99f..1e312221 100644 --- a/src/AST/Namespace.cs +++ b/src/AST/Namespace.cs @@ -348,7 +348,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) From 23a0fc233913f514a532898880661bed911cb1d7 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Wed, 30 Apr 2014 15:03:43 +0200 Subject: [PATCH 3/8] Fixed a null pointer exception when parsing files with unnamed enums. --- src/AST/Namespace.cs | 5 +++++ src/Parser/Parser.cpp | 30 +++++++++++++++++++----------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/src/AST/Namespace.cs b/src/AST/Namespace.cs index 1e312221..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) diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp index 581108a1..25e144d4 100644 --- a/src/Parser/Parser.cpp +++ b/src/Parser/Parser.cpp @@ -1713,19 +1713,25 @@ CppSharp::AST::Enumeration^ Parser::WalkEnum(clang::EnumDecl* ED) auto NS = GetNamespace(ED); assert(NS && "Expected a valid namespace"); - auto Name = marshalString(GetTagDeclName(ED)); - CppSharp::AST::Enumeration^ E = nullptr; - if (!System::String::IsNullOrEmpty(Name)) - E = NS->FindEnum(Name, /*Create=*/false); - else + auto E = NS->FindEnum(System::IntPtr(ED->getCanonicalDecl())); + if (E && !E->IsIncomplete) + return E; + + if (!E) { - // Enum with no identifier - try to find existing enum through enum items - for (auto it = ED->enumerator_begin(); it != ED->enumerator_end(); ++it) + auto Name = marshalString(GetTagDeclName(ED)); + if (!System::String::IsNullOrEmpty(Name)) + E = NS->FindEnum(Name, /*Create=*/false); + else { - EnumConstantDecl* ECD = (*it); - auto EnumItemName = marshalString(ECD->getNameAsString()); - E = NS->FindEnumWithItem(EnumItemName); - break; + // 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; + } } } @@ -1734,11 +1740,13 @@ CppSharp::AST::Enumeration^ Parser::WalkEnum(clang::EnumDecl* ED) if (!E) { + 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); } From 502b9e1637a5d4955f1d03cbe055f102527f7cb2 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Mon, 5 May 2014 06:11:13 +0200 Subject: [PATCH 4/8] Added test case for unnamed enums - should pass with old parser, but fail with new one. --- tests/Basic/Basic.Tests.cs | 9 +++++++++ tests/Basic/Basic.h | 4 ++++ 2 files changed, 13 insertions(+) diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index e9fd6e1d..172e4597 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -302,5 +302,14 @@ public class BasicTests : GeneratorTestFixture ClassC classC = (ClassC)classB; Assert.AreEqual(classB.Value, classC.Value); } + + [Test] + public unsafe void TestUnnamedEnums() + { + Assert.AreEqual(1, (int)Unnamed_Enum_1.Unnamed_Enum_1_A); + Assert.AreEqual(2, (int)Unnamed_Enum_1.Unnamed_Enum_1_B); + Assert.AreEqual(3, (int)Unnamed_Enum_2.Unnamed_Enum_2_A); + Assert.AreEqual(4, (int)Unnamed_Enum_2.Unnamed_Enum_2_B); + } } \ No newline at end of file diff --git a/tests/Basic/Basic.h b/tests/Basic/Basic.h index 3a60dcfd..c5edae1d 100644 --- a/tests/Basic/Basic.h +++ b/tests/Basic/Basic.h @@ -86,6 +86,10 @@ enum Enum F = -9 }; +// 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 }; + class DLL_API Hello { union NestedPrivate { From 24cd3840b56ec853f2a661bef91be4adef456e43 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Mon, 5 May 2014 18:11:21 +0200 Subject: [PATCH 5/8] Added support for unnamed enums to new parser. --- src/CppParser/AST.cpp | 43 ++++++++++++++++++++++++++++++++++++++++ src/CppParser/AST.h | 4 ++++ src/CppParser/Parser.cpp | 36 ++++++++++++++++++++++++++++++--- 3 files changed, 80 insertions(+), 3 deletions(-) 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 07f9ad9e..2a813be1 100644 --- a/src/CppParser/Parser.cpp +++ b/src/CppParser/Parser.cpp @@ -1695,18 +1695,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); } From 6fa7077dbd2a6bbda55703420eba666ae215550c Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Wed, 7 May 2014 19:03:43 +0200 Subject: [PATCH 6/8] Moved unnamed enums test to generator tests. --- src/Generator.Tests/Passes/TestPasses.cs | 20 ++++++++++++++++++++ tests/Basic/Basic.Tests.cs | 9 --------- tests/Basic/Basic.h | 4 ---- tests/Native/Passes.h | 6 +++++- 4 files changed, 25 insertions(+), 14 deletions(-) diff --git a/src/Generator.Tests/Passes/TestPasses.cs b/src/Generator.Tests/Passes/TestPasses.cs index c2656f73..f03e25dc 100644 --- a/src/Generator.Tests/Passes/TestPasses.cs +++ b/src/Generator.Tests/Passes/TestPasses.cs @@ -95,6 +95,26 @@ 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 TestStructInheritance() { diff --git a/tests/Basic/Basic.Tests.cs b/tests/Basic/Basic.Tests.cs index 172e4597..e9fd6e1d 100644 --- a/tests/Basic/Basic.Tests.cs +++ b/tests/Basic/Basic.Tests.cs @@ -302,14 +302,5 @@ public class BasicTests : GeneratorTestFixture ClassC classC = (ClassC)classB; Assert.AreEqual(classB.Value, classC.Value); } - - [Test] - public unsafe void TestUnnamedEnums() - { - Assert.AreEqual(1, (int)Unnamed_Enum_1.Unnamed_Enum_1_A); - Assert.AreEqual(2, (int)Unnamed_Enum_1.Unnamed_Enum_1_B); - Assert.AreEqual(3, (int)Unnamed_Enum_2.Unnamed_Enum_2_A); - Assert.AreEqual(4, (int)Unnamed_Enum_2.Unnamed_Enum_2_B); - } } \ No newline at end of file diff --git a/tests/Basic/Basic.h b/tests/Basic/Basic.h index c5edae1d..3a60dcfd 100644 --- a/tests/Basic/Basic.h +++ b/tests/Basic/Basic.h @@ -86,10 +86,6 @@ enum Enum F = -9 }; -// 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 }; - class DLL_API Hello { union NestedPrivate { diff --git a/tests/Native/Passes.h b/tests/Native/Passes.h index 2435b21d..f2a44b04 100644 --- a/tests/Native/Passes.h +++ b/tests/Native/Passes.h @@ -32,4 +32,8 @@ struct TestRename // 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 }; From d1a3141f5b17b871f407f96a84fdf61553854005 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Wed, 7 May 2014 19:48:00 +0200 Subject: [PATCH 7/8] The unique name set by the CleanInvalidDeclNames pass will now truly be unique across translation units. --- src/Generator.Tests/Passes/TestPasses.cs | 15 +++++++++++++ .../Passes/CleanInvalidDeclNamesPass.cs | 22 +++++++++++++------ tests/Native/Enums.h | 9 ++++++++ tests/Native/Passes.h | 8 +++++++ 4 files changed, 47 insertions(+), 7 deletions(-) create mode 100644 tests/Native/Enums.h diff --git a/src/Generator.Tests/Passes/TestPasses.cs b/src/Generator.Tests/Passes/TestPasses.cs index f03e25dc..a1c83436 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; @@ -115,6 +116,20 @@ namespace CppSharp.Generator.Tests.Passes 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/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 f2a44b04..8268c8b9 100644 --- a/tests/Native/Passes.h +++ b/tests/Native/Passes.h @@ -37,3 +37,11 @@ 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 +}; \ No newline at end of file From bd0ec409e945c8346e2e3073e4af19f73a19d742 Mon Sep 17 00:00:00 2001 From: Elias Holzer Date: Wed, 7 May 2014 20:15:39 +0200 Subject: [PATCH 8/8] Fixed adding of enum items in ASTConverter. --- src/Core/Parser/ASTConverter.cs | 2 +- src/Generator.Tests/AST/TestAST.cs | 8 ++++++++ tests/Native/AST.h | 5 ++++- 3 files changed, 13 insertions(+), 2 deletions(-) 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/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/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