diff --git a/src/CppAst.Tests/TestFunctions.cs b/src/CppAst.Tests/TestFunctions.cs
index 55a3599..89ee0ea 100644
--- a/src/CppAst.Tests/TestFunctions.cs
+++ b/src/CppAst.Tests/TestFunctions.cs
@@ -1,4 +1,6 @@
using NUnit.Framework;
+using System;
+using System.IO;
namespace CppAst.Tests
{
@@ -353,6 +355,154 @@ 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 TestMethodDefinitionOutsideClass()
+ {
+ var options = new CppParserOptions();
+ options.ParseFunctionBodies = true;
+ var headerFilename = "test_method_outside_class.h";
+
+ var text = @"
+typedef unsigned int ImWchar;
+
+class ImFont {
+public:
+ bool IsGlyphInFont(ImWchar c);
+};
+
+bool ImFont::IsGlyphInFont(ImWchar c)
+{
+ 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(1, 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 (method defined outside class)");
+ 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);
+ }
+ }
+
}
diff --git a/src/CppAst/CppFunction.cs b/src/CppAst/CppFunction.cs
index 6cee273..7667643 100644
--- a/src/CppAst/CppFunction.cs
+++ b/src/CppAst/CppFunction.cs
@@ -93,6 +93,11 @@ public int DefaultParamCount
}
}
+ ///
+ /// Gets or sets the source span of the function body implementation.
+ ///
+ public CppSourceSpan? BodySpan { get; set; }
+
///
/// Gets or sets the flags of this function.
///
diff --git a/src/CppAst/CppModelBuilder.cs b/src/CppAst/CppModelBuilder.cs
index 517e6ad..0cae389 100644
--- a/src/CppAst/CppModelBuilder.cs
+++ b/src/CppAst/CppModelBuilder.cs
@@ -1549,11 +1549,69 @@ private CppFunction VisitFunctionDecl(CXCursor cursor, CXCursor parent, void* da
//We need ignore the function define out in the class definition here(Otherwise it will has two same functions here~)!
var semKind = cursor.SemanticParent.Kind;
- if ((semKind == CXCursorKind.CXCursor_StructDecl ||
+ bool isMethodDefinedOutsideClass = (semKind == CXCursorKind.CXCursor_StructDecl ||
semKind == CXCursorKind.CXCursor_ClassDecl ||
semKind == CXCursorKind.CXCursor_ClassTemplate)
- && cursor.LexicalParent != cursor.SemanticParent)
+ && cursor.LexicalParent != cursor.SemanticParent;
+
+ if (isMethodDefinedOutsideClass)
{
+ // Find the previously created function in the class and update its BodySpan
+ if (cppClass != null)
+ {
+ CppFunction existingFunction = null;
+
+ // Search in Functions, Constructors, and Destructors
+ foreach (var func in cppClass.Functions)
+ {
+ if (func.Name == functionName)
+ {
+ existingFunction = func;
+ break;
+ }
+ }
+
+ if (existingFunction == null)
+ {
+ foreach (var ctor in cppClass.Constructors)
+ {
+ if (ctor.Name == functionName)
+ {
+ existingFunction = ctor;
+ break;
+ }
+ }
+ }
+
+ if (existingFunction == null)
+ {
+ foreach (var dtor in cppClass.Destructors)
+ {
+ if (dtor.Name == functionName)
+ {
+ existingFunction = dtor;
+ break;
+ }
+ }
+ }
+
+ // If we found the existing function, update its BodySpan
+ if (existingFunction != null && cursor.IsDefinition)
+ {
+ cursor.VisitChildren((childCursor, functionCursor, clientData) =>
+ {
+ if (childCursor.Kind == CXCursorKind.CXCursor_CompoundStmt)
+ {
+ var bodyStart = GetSourceLocation(childCursor.Extent.Start);
+ var bodyEnd = GetSourceLocation(childCursor.Extent.End);
+ existingFunction.BodySpan = new CppSourceSpan(bodyStart, bodyEnd);
+ return CXChildVisitResult.CXChildVisit_Break;
+ }
+ return CXChildVisitResult.CXChildVisit_Continue;
+ }, new CXClientData((IntPtr)data));
+ }
+ }
+
return null;
}
@@ -1649,6 +1707,21 @@ private CppFunction VisitFunctionDecl(CXCursor cursor, CXCursor parent, void* da
ParseAttributes(cursor, cppFunction, true);
cppFunction.CallingConvention = GetCallingConvention(cursor.Type);
+ if (cursor.IsDefinition)
+ {
+ cursor.VisitChildren((childCursor, functionCursor, clientData) =>
+ {
+ if (childCursor.Kind == CXCursorKind.CXCursor_CompoundStmt)
+ {
+ var bodyStart = GetSourceLocation(childCursor.Extent.Start);
+ var bodyEnd = GetSourceLocation(childCursor.Extent.End);
+ cppFunction.BodySpan = new CppSourceSpan(bodyStart, bodyEnd);
+ return CXChildVisitResult.CXChildVisit_Break;
+ }
+ return CXChildVisitResult.CXChildVisit_Continue;
+ }, new CXClientData((IntPtr)data));
+ }
+
int i = 0;
cursor.VisitChildren((argCursor, functionCursor, clientData) =>
{
diff --git a/src/CppAst/CppParser.cs b/src/CppAst/CppParser.cs
index f1fdd86..2dd717c 100644
--- a/src/CppAst/CppParser.cs
+++ b/src/CppAst/CppParser.cs
@@ -122,7 +122,10 @@ private static unsafe CppCompilation ParseInternal(List cppFile
}
var translationFlags = CXTranslationUnit_Flags.CXTranslationUnit_None;
- translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_SkipFunctionBodies; // Don't traverse function bodies
+ if (!options.ParseFunctionBodies)
+ {
+ translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_SkipFunctionBodies; // Don't traverse function bodies
+ }
translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_IncludeAttributedTypes; // Include attributed types in CXType
translationFlags |= CXTranslationUnit_Flags.CXTranslationUnit_VisitImplicitAttributes; // Implicit attributes should be visited
diff --git a/src/CppAst/CppParserOptions.cs b/src/CppAst/CppParserOptions.cs
index 1de9bb0..4f23954 100644
--- a/src/CppAst/CppParserOptions.cs
+++ b/src/CppAst/CppParserOptions.cs
@@ -37,6 +37,7 @@ public CppParserOptions()
ParseSystemIncludes = true;
ParseTokenAttributes = false;
ParseCommentAttribute = false;
+ ParseFunctionBodies = false;
// Default triple targets
TargetCpu = IntPtr.Size == 8 ? CppTargetCpu.X86_64 : CppTargetCpu.X86;
@@ -101,6 +102,11 @@ public CppParserOptions()
///
public bool ParseCommentAttribute { get; set; }
+ ///
+ /// Gets or sets a boolean indicating whether to parse function bodies. Default is false
+ ///
+ public bool ParseFunctionBodies { get; set; }
+
///
/// Sets to true and return this instance.
///