Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Module support #425

Draft
wants to merge 2 commits into
base: master
Choose a base branch
from
Draft
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
9 changes: 9 additions & 0 deletions src/Clang.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,23 @@ end

include("string.jl")

include("file.jl")
export CLFile, name, unique_id, get_filename

include("index.jl")
export Index

include("trans_unit.jl")
export TranslationUnit, spelling, parse_header, parse_headers

include("module.jl")
export get_module, ast_file, name
export parent_module, full_name, is_system
export toplevel_headers

include("cursor.jl")
export kind, name, spelling, value
export file, file_line_column
export get_filename, get_file_line_column, get_function_args
export is_typedef_anon, is_forward_declaration, is_inclusion_directive
export search, children
Expand Down
60 changes: 27 additions & 33 deletions src/cursor.jl
Original file line number Diff line number Diff line change
Expand Up @@ -423,57 +423,51 @@ function spelling(k::CXCursorKind)
return s
end


"""
get_filename(x::CXFile) -> String
Return the complete file and path name of the given file
file_line_column(CLCursor) -> (CLFile, Int, Int)
Return file, line and column number.
"""
function get_filename(x::CXFile)
cxstr = clang_getFileName(x)
ptr = clang_getCString(cxstr)
ptr == C_NULL && return ""
s = unsafe_string(ptr)
clang_disposeString(cxstr)
return s
function file_line_column(c::CLCursor)
file = Ref{CXFile}(C_NULL)
line = Ref{Cuint}(0)
column = Ref{Cuint}(0)
offset = Ref{Cuint}(0)
location = clang_getCursorLocation(c)
clang_getExpansionLocation(location, file, line, column, offset)
return CLFile(file[]), Int(line[]), Int(column[])
end

"""
file(c::CLCursor) -> CLFile
Return the file referenced by the input cursor.
"""
function file(c::CLCursor)
f, _, _ = file_line_column(c)
return f
end

"""
get_filename(c::Union{CXCursor,CLCursor}) -> String
Return the complete file and path name of the given file referenced by the input cursor.
"""
function get_filename(c::Union{CXCursor,CLCursor})
file = Ref{CXFile}(C_NULL)
location = clang_getCursorLocation(c)
clang_getExpansionLocation(location, file, Ref{Cuint}(0), Ref{Cuint}(0), Ref{Cuint}(0))
if file[] != C_NULL
str = GC.@preserve file get_filename(file[])
return str
else
return ""
end
f, _, _ = get_file_line_column(c)
return f
end

"""
get_file_line_column(c::Union{CXCursor,CLCursor}) -> (String, Int, Int)
Return file name, line and column number.
"""
function get_file_line_column(c::Union{CXCursor,CLCursor})
file = Ref{CXFile}(C_NULL)
line = Ref{Cuint}(0)
column = Ref{Cuint}(0)
offset = Ref{Cuint}(0)
location = clang_getCursorLocation(c)
clang_getExpansionLocation(location, file, line, column, offset)
if file[] != C_NULL
cxstr = clang_getFileName(file[])
ptr = clang_getCString(cxstr)
s = unsafe_string(ptr)
clang_disposeString(cxstr)
return s, Int(line[]), Int(column[])
else
return "", 0, 0
end
f, l, c = file_line_column(c)
return name(f), l, c
end




"""
search(cursors::Vector{CLCursor}, ismatch::Function) -> Vector{CLCursor}
Return vector of CLCursors that match predicate. `ismatch` is a function that accepts a CLCursor argument.
Expand Down
40 changes: 40 additions & 0 deletions src/file.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
## Wrap CXFile
struct CLFile
file::CXFile
end

Base.convert(::Type{CLFile}, x::CXFile) = CLFile(x)
Base.cconvert(::Type{CXFile}, x::CLFile) = x
Base.unsafe_convert(::Type{CXFile}, x::CLFile) = x.file

Base.show(io::IO, x::CLFile) = print(io, """CLFile ("$(name(x))")""")


"""
get_filename(x::CXFile) -> String
Return the complete file and path name of the given file
"""
function get_filename(file::CXFile)
name(CLFile(file))
end


"""
name(x::CLFile) -> String
Return the complete file and path name of the given file
"""
function name(file::CLFile)
file |> clang_getFileName |> _cxstring_to_string
end

function unique_id(file::CLFile)
id = Ref{CXFileUniqueID}()
ret = clang_getFileUniqueID(file, id)
@assert ret==0 "Error getting unique id for $(file)"
id[]
end





43 changes: 43 additions & 0 deletions src/module.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
struct CLModule
mod::CXModule
end

Base.convert(::Type{CLModule}, x::CXModule) = CLModule(x)
Base.cconvert(::Type{CXModule}, x::CLModule) = x
Base.unsafe_convert(::Type{CXModule}, x::CLModule) = x.mod

Base.show(io::IO, x::CLModule) = print(io, "CLModule ($(full_name(x)))")


function get_module(tu::TranslationUnit, file::CLFile)::CLModule
return Clang.clang_getModuleForFile(tu, file)
end

function ast_file(mod::CLModule)::CLFile
return Clang.clang_Module_getASTFile(mod)
end

function parent_module(mod::CLModule)::CLModule
return Clang.clang_Module_getParent(mod)
end

function name(mod::CLModule)
return Clang.clang_Module_getName(mod) |> _cxstring_to_string
end

function full_name(mod::CLModule)
return Clang.clang_Module_getFullName(mod) |> _cxstring_to_string
end

function is_system(mod::CLModule)
return Bool(Clang.clang_Module_isSystem(mod))
end

function toplevel_headers(tu::TranslationUnit, mod::CLModule)
num = Clang.clang_Module_getNumTopLevelHeaders(tu, mod)
headers = Vector{CLFile}(undef, num)
for i=1:num
headers[i] = Clang.clang_Module_getTopLevelHeader(tu, mod, i-1)
end
return headers
end
49 changes: 49 additions & 0 deletions test/file.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
using Clang
using Test

@testset "File" begin

@testset "Null File" begin
@test name(CLFile(C_NULL)) == ""
end

@testset "Cursor filename" begin
mktempdir() do dir
header = joinpath(dir,"test.h")
open(header, "w") do io
write(io, "void foo();")
end
index = Index()
tu = parse_header(index, header)
@test tu |> Clang.getTranslationUnitCursor |> children |> only |> file |> name == header
end
end

@testset "Unique ID" begin
@test_throws AssertionError unique_id(CLFile(C_NULL))
mktempdir() do dir
header = joinpath(dir,"test.h")
open(header, "w") do io
println(io, "void foo();")
end
a = joinpath(dir,"a.h")
open(a, "w") do io
println(io, "#include \"test.h\"")
end

b = joinpath(dir,"b.h")
open(b, "w") do io
println(io, "#include \"test.h\"")
end

index = Index()
tu_a = parse_header(index, a)
tu_b = parse_header(index, b)
cu_a = tu_a |> Clang.getTranslationUnitCursor |> children |> only
cu_b = tu_b |> Clang.getTranslationUnitCursor |> children |> only
@test (cu_a |> file |> unique_id) === (cu_b |> file |> unique_id)
end

end

end
53 changes: 53 additions & 0 deletions test/module.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
using Clang
# using Test

@testset "Module" begin
modulemap = """
module foo {
module bar [system] {
header "test.h"
export *
}
}

"""
header = """
#pragma once
typedef struct {int a;int b;} Bar;
"""

source = """
@import foo.bar;
"""

dir = mktempdir()
args = [
"-x",
"objective-c",
"-fmodules",
"-I$dir",
]

create(f,s) = begin
fp = joinpath(dir,f);
open(fp,"w") do io
print(io,s)
end
fp
end

create("module.modulemap", modulemap)
h = create("test.h", header)
f = create("source.h", source)

index = Index()
tu = parse_header(index, f, args)
f = tu |> Clang.getTranslationUnitCursor |> children |> first |> file
mod = get_module(tu, f)
@test name(mod) == "bar"
@test full_name(mod) == "foo.bar"
@test full_name(parent_module(mod)) == "foo"
@test toplevel_headers(tu,mod) |> only |> name == h
@test is_system(mod) == true
@test ast_file(mod) |> name != ""
end
3 changes: 3 additions & 0 deletions test/runtests.jl
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using Clang
using Test

include("file.jl")
include("module.jl")

include("generators.jl")

include("test_mpi.jl")
Expand Down