Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
354 changes: 353 additions & 1 deletion src/CppAst.Tests/TestFunctions.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
using NUnit.Framework;
using System;
using System.Collections.Generic;
using System.IO;

namespace CppAst.Tests
{
Expand Down Expand Up @@ -224,7 +227,7 @@ public void TestFunctionExport()
Assert.True(cppFunction.IsPublicExport());
}
},
new CppParserOptions() { }
new CppParserOptions() { }
);

ParseAssert(text,
Expand Down Expand Up @@ -353,7 +356,356 @@ public void TestFunctionPointersByParam()
);
}

[Test]
public void TestFunctionBody()
{
var options = new CppParserOptions();
options.ParseFunctionBodies = true;
var headerFilename = "test_function_body.h";

var text = @"
void function0();
int function1(int a, float b) {
return a + (int)b;
}
float function2(int x);
";

var currentDirectory = Environment.CurrentDirectory;
var headerFile = Path.Combine(currentDirectory, headerFilename);
File.WriteAllText(headerFile, text);

var compilation = CppParser.ParseFile(headerFile, options);

Assert.False(compilation.HasErrors);
Assert.AreEqual(3, compilation.Functions.Count);

{
var cppFunction = compilation.Functions[0];
Assert.AreEqual("function0", cppFunction.Name);
Assert.IsNull(cppFunction.BodySpan);
}

{
var cppFunction = compilation.Functions[1];
Assert.AreEqual("function1", cppFunction.Name);
Assert.IsNotNull(cppFunction.BodySpan);
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}

{
var cppFunction = compilation.Functions[2];
Assert.AreEqual("function2", cppFunction.Name);
Assert.IsNull(cppFunction.BodySpan);
}
}

[Test]
public void TestInlineMethodBody()
{
var options = new CppParserOptions();
options.ParseFunctionBodies = true;
var headerFilename = "test_inline_method_body.h";

var text = @"
typedef unsigned int ImWchar;

class ImFont {
public:
bool IsGlyphInFont(ImWchar c)
{
return false;
}

bool AnotherMethod(ImWchar c) {
if (c == 0) return true;
return false;
}
};
";

var currentDirectory = Environment.CurrentDirectory;
var headerFile = Path.Combine(currentDirectory, headerFilename);
File.WriteAllText(headerFile, text);

var compilation = CppParser.ParseFile(headerFile, options);

Assert.False(compilation.HasErrors);
Assert.AreEqual(1, compilation.Classes.Count);

var cls = compilation.Classes[0];
Assert.AreEqual("ImFont", cls.Name);
Assert.AreEqual(2, cls.Functions.Count);

{
var cppFunction = cls.Functions[0];
Assert.AreEqual("IsGlyphInFont", cppFunction.Name);
Assert.IsNotNull(cppFunction.BodySpan, "IsGlyphInFont should have BodySpan - this is the bug reported");
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}

{
var cppFunction = cls.Functions[1];
Assert.AreEqual("AnotherMethod", cppFunction.Name);
Assert.IsNotNull(cppFunction.BodySpan, "AnotherMethod should have BodySpan");
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}
}

[Test]
public void TestFunctionOverloadBodySpan()
{
var options = new CppParserOptions();
options.ParseFunctionBodies = true;
var headerFilename = "test_function_overload_body.h";

var text = @"
int process(int x)
{
return x * 2;
}

int process(float y)
{
return (int)(y * 3.0f);
}

int process(int x, int y)
{
return x + y;
}
";

var currentDirectory = Environment.CurrentDirectory;
var headerFile = Path.Combine(currentDirectory, headerFilename);
File.WriteAllText(headerFile, text);

var compilation = CppParser.ParseFile(headerFile, options);

Assert.False(compilation.HasErrors);
Assert.AreEqual(3, compilation.Functions.Count);

{
var cppFunction = compilation.Functions[0];
Assert.AreEqual("process", cppFunction.Name);
Assert.AreEqual(1, cppFunction.Parameters.Count);
Assert.IsNotNull(cppFunction.BodySpan, "process(int) should have BodySpan");
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}

{
var cppFunction = compilation.Functions[1];
Assert.AreEqual("process", cppFunction.Name);
Assert.AreEqual(1, cppFunction.Parameters.Count);
Assert.IsNotNull(cppFunction.BodySpan, "process(float) should have BodySpan");
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}

{
var cppFunction = compilation.Functions[2];
Assert.AreEqual("process", cppFunction.Name);
Assert.AreEqual(2, cppFunction.Parameters.Count);
Assert.IsNotNull(cppFunction.BodySpan, "process(int, int) should have BodySpan");
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}
HashSet<int> startLines = new HashSet<int>();
foreach (var function in compilation.Functions)
{
Assert.True(startLines.Add(function.BodySpan.Value.Start.Line));
}
}

[Test]
public void TestMethodOverloadBodySpan()
{
var options = new CppParserOptions();
options.ParseFunctionBodies = true;
var headerFilename = "test_method_overload_body.h";

var text = @"
class Calculator {
public:
int calculate(int x) {
return x * 2;
}

int calculate(float y) {
return (int)(y * 3.0f);
}

int calculate(int x, int y) {
return x + y;
}
};
";

var currentDirectory = Environment.CurrentDirectory;
var headerFile = Path.Combine(currentDirectory, headerFilename);
File.WriteAllText(headerFile, text);

var compilation = CppParser.ParseFile(headerFile, options);

Assert.False(compilation.HasErrors);
Assert.AreEqual(1, compilation.Classes.Count);

var cls = compilation.Classes[0];
Assert.AreEqual("Calculator", cls.Name);
Assert.AreEqual(3, cls.Functions.Count);

{
var cppFunction = cls.Functions[0];
Assert.AreEqual("calculate", cppFunction.Name);
Assert.AreEqual(1, cppFunction.Parameters.Count);
Assert.IsNotNull(cppFunction.BodySpan, "calculate(int) should have BodySpan");
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}

{
var cppFunction = cls.Functions[1];
Assert.AreEqual("calculate", cppFunction.Name);
Assert.AreEqual(1, cppFunction.Parameters.Count);
Assert.IsNotNull(cppFunction.BodySpan, "calculate(float) should have BodySpan");
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}

{
var cppFunction = cls.Functions[2];
Assert.AreEqual("calculate", cppFunction.Name);
Assert.AreEqual(2, cppFunction.Parameters.Count);
Assert.IsNotNull(cppFunction.BodySpan, "calculate(int, int) should have BodySpan");
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}

HashSet<int> startLines = new HashSet<int>();
foreach (var function in cls.Functions)
{
Assert.True(startLines.Add(function.BodySpan.Value.Start.Line));
}
}

[Test]
public void TestMethodOverloadDefinitionOutsideClassBodySpan()
{
var options = new CppParserOptions();
options.ParseFunctionBodies = true;
var headerFilename = "test_method_overload_outside_class_body.h";

var text = @"
class Calculator {
public:
int calculate(int x);
int calculate(float y);
int calculate(int x, int y);
};

int Calculator::calculate(int x)
{
return x * 2;
}

int Calculator::calculate(float y)
{
return (int)(y * 3.0f);
}

int Calculator::calculate(int x, int y)
{
return x + y;
}

class Calculator2 {
public:
int calculate(int x);
int calculate(float y);
int calculate(int x, int y);
};

int Calculator2::calculate(int x)
{
return x * 2;
}

int Calculator2::calculate(float y)
{
return (int)(y * 3.0f);
}

int Calculator2::calculate(int x, int y)
{
return x + y;
}
";

var currentDirectory = Environment.CurrentDirectory;
var headerFile = Path.Combine(currentDirectory, headerFilename);
File.WriteAllText(headerFile, text);

var compilation = CppParser.ParseFile(headerFile, options);

Assert.False(compilation.HasErrors);
Assert.AreEqual(2, compilation.Classes.Count);

var cls = compilation.Classes[0];
Assert.AreEqual("Calculator", cls.Name);
Assert.AreEqual(3, cls.Functions.Count);

{
var cppFunction = cls.Functions[0];
Assert.AreEqual("calculate", cppFunction.Name);
Assert.AreEqual(1, cppFunction.Parameters.Count);
Assert.IsNotNull(cppFunction.BodySpan, "calculate(int) should have BodySpan");
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}

{
var cppFunction = cls.Functions[1];
Assert.AreEqual("calculate", cppFunction.Name);
Assert.AreEqual(1, cppFunction.Parameters.Count);
Assert.IsNotNull(cppFunction.BodySpan, "calculate(float) should have BodySpan");
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}

{
var cppFunction = cls.Functions[2];
Assert.AreEqual("calculate", cppFunction.Name);
Assert.AreEqual(2, cppFunction.Parameters.Count);
Assert.IsNotNull(cppFunction.BodySpan, "calculate(int, int) should have BodySpan");
Assert.Greater(cppFunction.BodySpan.Value.Start.Line, 0);
Assert.Greater(cppFunction.BodySpan.Value.End.Line, 0);
Assert.GreaterOrEqual(cppFunction.BodySpan.Value.End.Offset, cppFunction.BodySpan.Value.Start.Offset);
}

HashSet<int> startLines = new HashSet<int>();
foreach (var cls2 in compilation.Classes)
{
foreach (var function in cls2.Functions)
{
Assert.True(startLines.Add(function.BodySpan.Value.Start.Line));
}
}
}

}
}
5 changes: 5 additions & 0 deletions src/CppAst/CppFunction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,11 @@ public int DefaultParamCount
}
}

/// <summary>
/// Gets or sets the source span of the function body implementation.
/// </summary>
public CppSourceSpan? BodySpan { get; set; }

/// <summary>
/// Gets or sets the flags of this function.
/// </summary>
Expand Down
Loading
Loading