Tools and libraries to glue C/C++ APIs to high-level languages
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 

583 lines
25 KiB

using System;
using System.Linq;
using CppSharp.AST;
using CppSharp.AST.Extensions;
using CppSharp.Generators.CSharp;
using CppSharp.Passes;
using NUnit.Framework;
namespace CppSharp.Generator.Tests.AST
{
[TestFixture]
public class TestAST : ASTTestFixture
{
[OneTimeSetUp]
public void Init()
{
CppSharp.AST.Type.TypePrinterDelegate = type =>
{
PrimitiveType primitiveType;
return type.IsPrimitiveType(out primitiveType) ? primitiveType.ToString() : string.Empty;
};
ParseLibrary("AST.h", "ASTExtensions.h");
}
[OneTimeTearDown]
public void CleanUp()
{
ParserOptions.Dispose();
}
[Test]
public void TestASTParameter()
{
var func = AstContext.FindFunction("TestParameterProperties").FirstOrDefault();
Assert.IsNotNull(func);
var paramNames = new[] { "a", "b", "c" };
var paramTypes = new[]
{
new QualifiedType(new BuiltinType(PrimitiveType.Bool)),
new QualifiedType(
new PointerType()
{
Modifier = PointerType.TypeModifier.LVReference,
QualifiedPointee = new QualifiedType(
new BuiltinType(PrimitiveType.Short),
new TypeQualifiers { IsConst = true })
}),
new QualifiedType(
new PointerType
{
Modifier = PointerType.TypeModifier.Pointer,
QualifiedPointee = new QualifiedType(new BuiltinType(PrimitiveType.Int))
})
};
for (int i = 0; i < func.Parameters.Count; i++)
{
var param = func.Parameters[i];
Assert.AreEqual(paramNames[i], param.Name, "Parameter.Name");
Assert.AreEqual(paramTypes[i], param.QualifiedType, "Parameter.QualifiedType");
Assert.AreEqual(i, param.Index, "Parameter.Index");
}
Assert.IsTrue(func.Parameters[2].HasDefaultValue, "Parameter.HasDefaultValue");
}
[Test]
public void TestASTHelperMethods()
{
var @namespace = AstContext.FindDecl<Namespace>("Math").FirstOrDefault();
Assert.IsNotNull(@namespace, "Couldn't find Math namespace.");
var @class = @namespace.FindClass("Complex");
Assert.IsNotNull(@class, "Couldn't find Math::Complex class.");
var plusOperator = @class.FindOperator(CXXOperatorKind.Plus).FirstOrDefault();
Assert.IsNotNull(plusOperator, "Couldn't find operator+ in Math::Complex class.");
var typedef = @namespace.FindTypedef("Single");
Assert.IsNotNull(typedef);
}
#region TestVisitor
class TestVisitor : IDeclVisitor<bool>
{
public bool VisitDeclaration(Declaration decl)
{
throw new System.NotImplementedException();
}
public bool VisitTranslationUnit(TranslationUnit unit)
{
throw new System.NotImplementedException();
}
public bool VisitClassDecl(Class @class)
{
throw new System.NotImplementedException();
}
public bool VisitClassTemplateSpecializationDecl(ClassTemplateSpecialization specialization)
{
throw new NotImplementedException();
}
public bool VisitFieldDecl(Field field)
{
throw new System.NotImplementedException();
}
public bool VisitFunctionDecl(Function function)
{
throw new System.NotImplementedException();
}
public bool VisitMethodDecl(Method method)
{
return true;
}
public bool VisitParameterDecl(Parameter parameter)
{
throw new System.NotImplementedException();
}
public bool VisitTypedefDecl(TypedefDecl typedef)
{
throw new System.NotImplementedException();
}
public bool VisitTypeAliasDecl(TypeAlias typeAlias)
{
throw new NotImplementedException();
}
public bool VisitEnumDecl(Enumeration @enum)
{
throw new System.NotImplementedException();
}
public bool VisitEnumItemDecl(Enumeration.Item item)
{
throw new NotImplementedException();
}
public bool VisitVariableDecl(Variable variable)
{
throw new System.NotImplementedException();
}
public bool VisitClassTemplateDecl(ClassTemplate template)
{
throw new System.NotImplementedException();
}
public bool VisitFunctionTemplateDecl(FunctionTemplate template)
{
throw new System.NotImplementedException();
}
public bool VisitMacroDefinition(MacroDefinition macro)
{
throw new System.NotImplementedException();
}
public bool VisitNamespace(Namespace @namespace)
{
throw new System.NotImplementedException();
}
public bool VisitEvent(Event @event)
{
throw new System.NotImplementedException();
}
public bool VisitProperty(Property property)
{
throw new System.NotImplementedException();
}
public bool VisitFriend(Friend friend)
{
throw new System.NotImplementedException();
}
public bool VisitTemplateParameterDecl(TypeTemplateParameter templateParameter)
{
throw new NotImplementedException();
}
public bool VisitNonTypeTemplateParameterDecl(NonTypeTemplateParameter nonTypeTemplateParameter)
{
throw new NotImplementedException();
}
public bool VisitTemplateTemplateParameterDecl(TemplateTemplateParameter templateTemplateParameter)
{
throw new NotImplementedException();
}
public bool VisitTypeAliasTemplateDecl(TypeAliasTemplate typeAliasTemplate)
{
throw new NotImplementedException();
}
public bool VisitFunctionTemplateSpecializationDecl(FunctionTemplateSpecialization specialization)
{
throw new NotImplementedException();
}
public bool VisitVarTemplateDecl(VarTemplate template)
{
throw new NotImplementedException();
}
public bool VisitVarTemplateSpecializationDecl(VarTemplateSpecialization template)
{
throw new NotImplementedException();
}
public bool VisitTypedefNameDecl(TypedefNameDecl typedef)
{
throw new NotImplementedException();
}
}
#endregion
[Test]
public void TestASTVisitor()
{
var testVisitor = new TestVisitor();
var plusOperator = AstContext.TranslationUnits
.SelectMany(u => u.Namespaces.Where(n => n.Name == "Math"))
.SelectMany(n => n.Classes.Where(c => c.Name == "Complex"))
.SelectMany(c => c.Methods.Where(m => m.OperatorKind == CXXOperatorKind.Plus))
.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"));
}
[Test]
public void TestASTFunctionTemplates()
{
var @class = AstContext.FindClass("TestTemplateFunctions").FirstOrDefault();
Assert.IsNotNull(@class, "Couldn't find TestTemplateFunctions class.");
Assert.AreEqual(6, @class.Templates.Count());
var twoParamMethodTemplate = @class.Templates.OfType<FunctionTemplate>()
.FirstOrDefault(t => t.Name == "MethodTemplateWithTwoTypeParameter");
Assert.IsNotNull(twoParamMethodTemplate);
Assert.AreEqual(2, twoParamMethodTemplate.Parameters.Count);
Assert.AreEqual("T", twoParamMethodTemplate.Parameters[0].Name);
Assert.AreEqual("S", twoParamMethodTemplate.Parameters[1].Name);
var twoParamMethod = twoParamMethodTemplate.TemplatedFunction as Method;
Assert.IsNotNull(twoParamMethod);
Assert.IsInstanceOf<TemplateParameterType>(twoParamMethod.Parameters[0].Type);
Assert.IsInstanceOf<TemplateParameterType>(twoParamMethod.Parameters[1].Type);
Assert.AreEqual(twoParamMethodTemplate.Parameters[0],
((TemplateParameterType) twoParamMethod.Parameters[0].Type).Parameter);
Assert.AreEqual(twoParamMethodTemplate.Parameters[1],
((TemplateParameterType) twoParamMethod.Parameters[1].Type).Parameter);
Assert.AreEqual(0, ((TemplateParameterType) twoParamMethod.Parameters[0].Type).Index);
Assert.AreEqual(1, ((TemplateParameterType) twoParamMethod.Parameters[1].Type).Index);
}
[Test]
public void TestASTClassTemplates()
{
var template = AstContext.TranslationUnits
.SelectMany(u => u.Templates.OfType<ClassTemplate>())
.FirstOrDefault(t => t.Name == "TestTemplateClass");
Assert.IsNotNull(template, "Couldn't find TestTemplateClass class.");
Assert.AreEqual(1, template.Parameters.Count);
var templateTypeParameter = template.Parameters[0];
Assert.AreEqual("T", templateTypeParameter.Name);
var ctor = template.TemplatedClass.Constructors
.FirstOrDefault(c => c.Parameters.Count == 1 && c.Parameters[0].Name == "v");
Assert.IsNotNull(ctor);
var paramType = ctor.Parameters[0].Type as TemplateParameterType;
Assert.IsNotNull(paramType);
Assert.AreEqual(templateTypeParameter, paramType.Parameter);
Assert.AreEqual(5, template.Specializations.Count);
Assert.AreEqual(TemplateSpecializationKind.ExplicitInstantiationDefinition, template.Specializations[0].SpecializationKind);
Assert.AreEqual(TemplateSpecializationKind.ExplicitInstantiationDefinition, template.Specializations[3].SpecializationKind);
Assert.AreEqual(TemplateSpecializationKind.Undeclared, template.Specializations[4].SpecializationKind);
var typeDef = AstContext.FindTypedef("TestTemplateClassInt").FirstOrDefault();
Assert.IsNotNull(typeDef, "Couldn't find TestTemplateClassInt typedef.");
var integerInst = typeDef.Type as TemplateSpecializationType;
Assert.AreEqual(1, integerInst.Arguments.Count);
var intArgument = integerInst.Arguments[0];
Assert.AreEqual(new BuiltinType(PrimitiveType.Int), intArgument.Type.Type);
ClassTemplateSpecialization classTemplateSpecialization;
Assert.IsTrue(typeDef.Type.TryGetDeclaration(out classTemplateSpecialization));
Assert.AreSame(classTemplateSpecialization.TemplatedDecl.TemplatedClass, template.TemplatedClass);
}
[Test]
public void TestFindClassInNamespace()
{
Assert.IsNotNull(AstContext.FindClass("HiddenInNamespace").FirstOrDefault());
}
[Test]
public void TestLineNumber()
{
Assert.AreEqual(70, AstContext.FindClass("HiddenInNamespace").First().LineNumberStart);
}
[Test]
public void TestLineNumberOfFriend()
{
Assert.AreEqual(93, AstContext.FindFunction("operator+").First().LineNumberStart);
}
static string StripWindowsNewLines(string text)
{
return text.ReplaceLineBreaks(string.Empty);
}
[Test]
public void TestSignature()
{
Assert.AreEqual("void testSignature()", AstContext.FindFunction("testSignature").Single().Signature);
Assert.AreEqual("void testImpl(){}",
StripWindowsNewLines(AstContext.FindFunction("testImpl").Single().Signature));
Assert.AreEqual("void testConstSignature() const",
AstContext.FindClass("HasConstFunction").Single().FindMethod("testConstSignature").Signature);
Assert.AreEqual("void testConstSignatureWithTrailingMacro() const",
AstContext.FindClass("HasConstFunction").Single().FindMethod("testConstSignatureWithTrailingMacro").Signature);
// TODO: restore when the const of a return type is fixed properly
//Assert.AreEqual("const int& testConstRefSignature()", AstContext.FindClass("HasConstFunction").Single().FindMethod("testConstRefSignature").Signature);
//Assert.AreEqual("const int& testStaticConstRefSignature()", AstContext.FindClass("HasConstFunction").Single().FindMethod("testStaticConstRefSignature").Signature);
}
[Test]
public void TestAmbiguity()
{
new CheckAmbiguousFunctions { Context = Driver.Context }.VisitASTContext(AstContext);
Assert.IsTrue(AstContext.FindClass("HasAmbiguousFunctions").Single().FindMethod("ambiguous").IsAmbiguous);
}
[Test]
public void TestAtomics()
{
var type = AstContext.FindClass("Atomics").Single().Fields
.Find(f => f.Name == "AtomicInt").Type as BuiltinType;
Assert.IsTrue(type != null && type.IsPrimitiveType(PrimitiveType.Int));
}
[Test]
public void TestMacroLineNumber()
{
Assert.AreEqual(103, AstContext.FindClass("HasAmbiguousFunctions").First().Specifiers.Last().LineNumberStart);
}
[Test]
public void TestImplicitDeclaration()
{
Assert.IsTrue(AstContext.FindClass("ImplicitCtor").First().Constructors.First(
c => c.Parameters.Count == 0).IsImplicit);
}
[Test]
public void TestSpecializationArguments()
{
var classTemplate = AstContext.FindDecl<ClassTemplate>("TestSpecializationArguments").FirstOrDefault();
Assert.IsTrue(classTemplate.Specializations[0].Arguments[0].Type.Type.IsPrimitiveType(PrimitiveType.Int));
}
[Test]
public void TestFunctionInstantiatedFrom()
{
var classTemplate = AstContext.FindDecl<ClassTemplate>("TestSpecializationArguments").FirstOrDefault();
Assert.AreEqual(classTemplate.Specializations[0].Constructors.First(
c => !c.IsCopyConstructor && !c.IsMoveConstructor).InstantiatedFrom,
classTemplate.TemplatedClass.Constructors.First(c => !c.IsCopyConstructor && !c.IsMoveConstructor));
}
[Test]
public void TestComments()
{
var @class = AstContext.FindCompleteClass("TestComments");
var commentClass = @class.Comment.FullComment.CommentToString(CommentKind.BCPLSlash);
Assert.AreEqual(@"/// <summary>
/// <para>Hash set/map base class.</para>
/// <para>Note that to prevent extra memory use due to vtable pointer, %HashBase intentionally does not declare a virtual destructor</para>
/// <para>and therefore %HashBase pointers should never be used.</para>
/// </summary>".Replace("\r", string.Empty), commentClass.Replace("\r", string.Empty));
var method = @class.Methods.First(m => m.Name == "GetIOHandlerControlSequence");
var commentMethod = method.Comment.FullComment.CommentToString(CommentKind.BCPL);
Assert.AreEqual(@"// <summary>
// <para>Get the string that needs to be written to the debugger stdin file</para>
// <para>handle when a control character is typed.</para>
// </summary>
// <param name=""ch"">The character that was typed along with the control key</param>
// <returns>
// <para>The string that should be written into the file handle that is</para>
// <para>feeding the input stream for the debugger, or NULL if there is</para>
// <para>no string for this control key.</para>
// </returns>
// <remarks>
// <para>Some GUI programs will intercept &quot;control + char&quot; sequences and want</para>
// <para>to have them do what normally would happen when using a real</para>
// <para>terminal, so this function allows GUI programs to emulate this</para>
// <para>functionality.</para>
// </remarks>".Replace("\r", string.Empty), commentMethod.Replace("\r", string.Empty));
var methodTestDoxygen = @class.Methods.First(m => m.Name == "SBAttachInfo");
var commentMethodDoxygen = methodTestDoxygen.Comment.FullComment.CommentToString(CommentKind.BCPLSlash);
Assert.AreEqual(@"/// <summary>Attach to a process by name.</summary>
/// <param name=""path"">A full or partial name for the process to attach to.</param>
/// <param name=""wait_for"">
/// <para>If <c>false,</c> attach to an existing process whose name matches.</para>
/// <para>If <c>true,</c> then wait for the next process whose name matches.</para>
/// </param>
/// <remarks>
/// <para>This function implies that a future call to SBTarget::Attach(...)</para>
/// <para>will be synchronous.</para>
/// </remarks>".Replace("\r", string.Empty), commentMethodDoxygen.Replace("\r", string.Empty));
var methodDoxygenCustomTags = @class.Methods.First(m => m.Name == "glfwDestroyWindow");
new CleanCommentsPass { }.VisitFull(methodDoxygenCustomTags.Comment.FullComment);
var commentMethodDoxygenCustomTag = methodDoxygenCustomTags.Comment.FullComment.CommentToString(CommentKind.BCPLSlash);
Assert.AreEqual(@"/// <summary>Destroys the specified window and its context.</summary>
/// <param name=""window"">The window to destroy.</param>
/// <remarks>
/// <para>This function destroys the specified window and its context. On calling</para>
/// <para>this function, no further callbacks will be called for that window.</para>
/// <para>If the context of the specified window is current on the main thread, it is</para>
/// <para>detached before being destroyed.</para>
/// <para>The context of the specified window must not be current on any other</para>
/// <para>thread when this function is called.</para>
/// <para>This function must not be called from a callback.</para>
/// <para>This function must only be called from the main thread.</para>
/// <para>Added in version 3.0. Replaces `glfwCloseWindow`.</para>
/// </remarks>".Replace("\r", string.Empty), commentMethodDoxygenCustomTag.Replace("\r", string.Empty));
}
[Test]
public void TestCompletionOfClassTemplates()
{
var templates = AstContext.FindDecl<ClassTemplate>("ForwardedTemplate").ToList();
var template = templates.Single(t => t.DebugText.Replace("\r", string.Empty) ==
"template <typename T>\r\nclass ForwardedTemplate\r\n{\r\n}".Replace("\r", string.Empty));
Assert.IsFalse(template.IsIncomplete);
}
[Test]
public void TestOriginalNamesOfSpecializations()
{
var template = AstContext.FindDecl<ClassTemplate>("TestSpecializationArguments").First();
Assert.That(template.Specializations[0].Constructors.First().QualifiedName,
Is.Not.EqualTo(template.Specializations[1].Constructors.First().QualifiedName));
}
[Test]
public void TestPrintingConstPointerWithConstType()
{
var cppTypePrinter = new CppTypePrinter { PrintScopeKind = TypePrintScopeKind.Qualified };
var builtin = new BuiltinType(PrimitiveType.Char);
var pointee = new QualifiedType(builtin, new TypeQualifiers { IsConst = true });
var pointer = new QualifiedType(new PointerType(pointee), new TypeQualifiers { IsConst = true });
var type = pointer.Visit(cppTypePrinter);
Assert.That(type, Is.EqualTo("const char* const"));
}
[Test]
public void TestPrintingSpecializationWithConstValue()
{
var template = AstContext.FindDecl<ClassTemplate>("TestSpecializationArguments").First();
var cppTypePrinter = new CppTypePrinter { PrintScopeKind = TypePrintScopeKind.Qualified };
Assert.That(template.Specializations.Last().Visit(cppTypePrinter),
Is.EqualTo("TestSpecializationArguments<const TestASTEnumItemByName>"));
}
[Test]
public void TestLayoutBase()
{
var @class = AstContext.FindCompleteClass("TestComments");
Assert.That(@class.Layout.Bases.Count, Is.EqualTo(0));
}
[Test]
public void TestFunctionSpecifications()
{
var constExprNoExcept = AstContext.FindFunction("constExprNoExcept").First();
Assert.IsTrue(constExprNoExcept.IsConstExpr);
var functionType = (FunctionType) constExprNoExcept.FunctionType.Type;
Assert.That(functionType.ExceptionSpecType,
Is.EqualTo(ExceptionSpecType.BasicNoexcept));
var regular = AstContext.FindFunction("testSignature").First();
Assert.IsFalse(regular.IsConstExpr);
var regularFunctionType = (FunctionType) regular.FunctionType.Type;
Assert.That(regularFunctionType.ExceptionSpecType,
Is.EqualTo(ExceptionSpecType.None));
}
[Test]
public void TestFunctionSpecializationInfo()
{
var functionWithSpecInfo = AstContext.TranslationUnits.Find(
t => t.IsValid && t.FileName == "AST.h").Functions.First(
f => f.Name == "functionWithSpecInfo" && !f.IsDependent);
var @float = new QualifiedType(new BuiltinType(PrimitiveType.Float));
Assert.That(functionWithSpecInfo.SpecializationInfo.Arguments.Count, Is.EqualTo(2));
foreach (var arg in functionWithSpecInfo.SpecializationInfo.Arguments)
Assert.That(arg.Type, Is.EqualTo(@float));
}
[Test]
public void TestVolatile()
{
var cppTypePrinter = new CppTypePrinter { PrintScopeKind = TypePrintScopeKind.Qualified };
var builtin = new BuiltinType(PrimitiveType.Char);
var pointee = new QualifiedType(builtin, new TypeQualifiers { IsConst = true, IsVolatile = true });
var type = pointee.Visit(cppTypePrinter);
Assert.That(type, Is.EqualTo("const volatile char"));
}
[Test]
public void TestFindFunctionInNamespace()
{
var function = AstContext.FindFunction("Math::function").FirstOrDefault();
Assert.That(function, Is.Not.Null);
}
[Test]
public void TestPrintNestedInSpecialization()
{
var template = AstContext.FindDecl<ClassTemplate>("TestTemplateClass").First();
var cppTypePrinter = new CppTypePrinter { PrintScopeKind = TypePrintScopeKind.Qualified };
Assert.That(template.Specializations[3].Classes.First().Visit(cppTypePrinter),
Is.EqualTo("TestTemplateClass<Math::Complex>::NestedInTemplate"));
}
[Test]
public void TestPrintQualifiedSpecialization()
{
var functionWithSpecializationArg = AstContext.FindFunction("functionWithSpecializationArg").First();
var cppTypePrinter = new CppTypePrinter { PrintScopeKind = TypePrintScopeKind.Qualified };
Assert.That(functionWithSpecializationArg.Parameters[0].Visit(cppTypePrinter),
Is.EqualTo("const TestTemplateClass<int>"));
}
[Test]
public void TestDependentNameType()
{
var template = AstContext.FindDecl<ClassTemplate>(
"TestSpecializationArguments").First();
var type = template.TemplatedClass.Fields[0].Type.Visit(
new CSharpTypePrinter(Driver.Context));
Assert.That($"{type}",
Is.EqualTo("global::Test.TestTemplateClass<T>.NestedInTemplate"));
}
[Test]
public void TestTemplateConstructorName()
{
new CleanInvalidDeclNamesPass { Context = Driver.Context }.VisitASTContext(AstContext);
var template = AstContext.FindClass("TestTemplateClass").First();
foreach (var constructor in template.Constructors)
Assert.That(constructor.Name, Is.EqualTo("TestTemplateClass<T>"));
}
[Test]
public void TestClassContext()
{
var @classA = AstContext.FindClass("ClassA").First();
Assert.That(@classA.Classes.Count, Is.EqualTo(0));
Assert.That(@classA.Redeclarations.Count, Is.EqualTo(0));
var @classB = AstContext.FindClass("ClassB").First();
Assert.That(@classB.Redeclarations.Count, Is.EqualTo(1));
var @classC = AstContext.FindClass("ClassC").First();
Assert.That(@classC.Redeclarations.Count, Is.EqualTo(2));
}
}
}