Skip to content

Commit

Permalink
[FEATURE] Clang read qt signal/slot
Browse files Browse the repository at this point in the history
- ADDED ::
  - Support for `__attribute__((annotate("qt_signal")))` annotation
    detection for the libclang-based frontend.
  - Get parent translation unit from any cursor
- REMOVED ::
  - `.unit` field fron `WrapConf` value - when needed it can be retrieved
    using `getParentTranslationUnit()`
- RELATED ::
  - #25
  • Loading branch information
haxscramper committed Nov 12, 2021
1 parent 03c27dc commit 88ad79d
Show file tree
Hide file tree
Showing 6 changed files with 115 additions and 89 deletions.
2 changes: 0 additions & 2 deletions src/hcparse/hc_parsefront.nim
Original file line number Diff line number Diff line change
Expand Up @@ -195,8 +195,6 @@ proc parseFile*(
result.index, file, flags, {
tufSkipFunctionBodies, tufDetailedPreprocessingRecord})

conf.unit = result.unit

result.api = result.unit.splitDeclarations(conf, cache)
result.isExplicitlyAdded = true

Expand Down
8 changes: 4 additions & 4 deletions src/hcparse/hc_typeconv.nim
Original file line number Diff line number Diff line change
Expand Up @@ -483,7 +483,7 @@ proc getTypeName*(cxtype: CXType, conf: WrapConf): string =

else:
conf.err $curs
conf.err "Type name for ", curs.treeRepr(conf.unit)
conf.err "Type name for ", curs.treeRepr()
raiseAssert(
&"Cannot convert cursor of kind {curs.cxKind} to type")

Expand Down Expand Up @@ -703,7 +703,7 @@ proc toInitCall*(

else:
conf.err cursor[0].cxKind()
conf.debug "\n" & cursor.treeRepr(conf.unit)
conf.debug "\n" & cursor.treeRepr()
conf.debug cursor.getSpellingLocation()

if isNil(result):
Expand Down Expand Up @@ -755,7 +755,7 @@ proc toInitCall*(

of ckIntegerLiteral, ckCharacterLiteral, ckFloatingLiteral,
ckStringLiteral:
let tokens = cursor.tokenStrings(conf.unit)
let tokens = cursor.tokenStrings()

case cursor.cxKind():
of ckIntegerLiteral:
Expand Down Expand Up @@ -801,6 +801,6 @@ proc toInitCall*(
else:
conf.err "Implement for kind", cursor.cxKind()
conf.debug cursor.getSpellingLocation()
conf.debug cursor.tokenStrings(conf.unit)
conf.debug cursor.tokenStrings()

return aux(cursor, false, cache)
177 changes: 96 additions & 81 deletions src/hcparse/read_libclang/cxtypes.nim
Original file line number Diff line number Diff line change
Expand Up @@ -156,11 +156,86 @@ proc templateParams*(cxType: CxType): seq[CxType] =


#*************************************************************************#
#******************************* CXToken *******************************#
#*************** Translation unit construction wrappers ****************#
#*************************************************************************#

#============================= Diagnostics =============================#
proc getDiagnostics*(unit: CXTranslationUnit): seq[CXDiagnostic] =
for i in 0 ..< getNumDiagnostics(unit):
result.add getDiagnostic(unit, i)

proc `$`*(diag: CXDiagnostic): string =
$diag.formatDiagnostic(1)



#================================ Index ================================#

proc createIndex*(
excludeDeclarations: bool = false,
showDiagnostics: bool = false): CXIndex =

createIndex(excludeDeclarations.cint, showDiagnostics.cint)

#===================== Compile commands & database =====================#

proc createDatabase*(directory: string): CXCompilationDatabase =
## Create compilation database from database found in directory
## `directory`. On failure `IOError` is raised.
var err: CXCompilationDatabase_Error # NOTE might use to provide
# better diagnostics/exceptions

result = compilationDatabase_fromDirectory(directory.cstring, addr err)
if err == cdeCanNotLoadDatabase:
raise newException(
IOError,
&"Failed to find compilation database in directory {directory}")

proc getArgs*(command: CXCompileCommand): seq[string] =
## Get arguments for compile command `command`
for i in 0 ..< command.getNumArgs():
result.add $command.getArg(i)

iterator items*(commands: CXCompileCommands): CXCompileCommand =
for i in 0 ..< commands.getSize():
yield commands.getCommand(i)

proc `[]`*(commands: CXCompileCommands, idx: int): CXCompileCommand =
assert cuint(idx) < commands.getSize()
return commands.getCommand(cuint idx)

proc absFile*(command: CXCompileCommand): AbsFile =
AbsFile $command.getFilename()

#========================== Translation unit ===========================#
proc isNil*(tu: CXTranslationUnit): bool =
cast[ptr[CXTranslationUnitImpl]](tu) == nil

proc getFile*(tu: CXTranslationUnit): AbsFile =
AbsFile($tu.getTranslationUnitSpelling())

proc getTuFile*(cx: CXCursor): AbsFile =
assert cx.kind == ckTranslationUnit
AbsFile($getCursorSpelling(cx))

proc getParentTranslationUnit*(cursor: CxCursor): CxTranslationUnit =
var parent = cursor
while not(parent of {ckTranslationUnit}):
parent = parent.getCursorSemanticParent()

if parent of ckTranslationUnit:
result = parent.getTranslationUnit()
assert notNil(result)

else:
raise newGetterError(
"Could not get translation unit from cursor")


#*************************************************************************#
#******************************* CXToken *******************************#
#*************************************************************************#


proc tokens*(cursor: CXCursor, tu: CXTranslationUnit): seq[CXToken] =
## Get sequence of tokens that make up cursor.
Expand All @@ -173,7 +248,8 @@ proc tokens*(cursor: CXCursor, tu: CXTranslationUnit): seq[CXToken] =
for i in 0 ..< nTokens:
result.add (cast[ptr UncheckedArray[CXToken]](tokens))[i]

proc tokenStrings*(cursor: CXCursor, tu: CXTranslationUnit): seq[string] =
proc tokenStrings*(cursor: CXCursor): seq[string] =
let tu = getParentTranslationUnit(cursor)
for tok in cursor.tokens(tu):
result.add $getTokenSpelling(tu, tok)

Expand Down Expand Up @@ -218,90 +294,11 @@ proc fromTokens*(toks: seq[CXToken], unit: CXTranslationUnit): string =



#*************************************************************************#
#*************** Translation unit construction wrappers ****************#
#*************************************************************************#

#============================= Diagnostics =============================#
proc getDiagnostics*(unit: CXTranslationUnit): seq[CXDiagnostic] =
for i in 0 ..< getNumDiagnostics(unit):
result.add getDiagnostic(unit, i)

proc `$`*(diag: CXDiagnostic): string =
$diag.formatDiagnostic(1)



#================================ Index ================================#

proc createIndex*(
excludeDeclarations: bool = false,
showDiagnostics: bool = false): CXIndex =

createIndex(excludeDeclarations.cint, showDiagnostics.cint)

#===================== Compile commands & database =====================#

proc createDatabase*(directory: string): CXCompilationDatabase =
## Create compilation database from database found in directory
## `directory`. On failure `IOError` is raised.
var err: CXCompilationDatabase_Error # NOTE might use to provide
# better diagnostics/exceptions

result = compilationDatabase_fromDirectory(directory.cstring, addr err)
if err == cdeCanNotLoadDatabase:
raise newException(
IOError,
&"Failed to find compilation database in directory {directory}")

proc getArgs*(command: CXCompileCommand): seq[string] =
## Get arguments for compile command `command`
for i in 0 ..< command.getNumArgs():
result.add $command.getArg(i)

iterator items*(commands: CXCompileCommands): CXCompileCommand =
for i in 0 ..< commands.getSize():
yield commands.getCommand(i)

proc `[]`*(commands: CXCompileCommands, idx: int): CXCompileCommand =
assert cuint(idx) < commands.getSize()
return commands.getCommand(cuint idx)

proc absFile*(command: CXCompileCommand): AbsFile =
AbsFile $command.getFilename()

#========================== Translation unit ===========================#

proc getFile*(tu: CXTranslationUnit): AbsFile =
AbsFile($tu.getTranslationUnitSpelling())

proc getTuFile*(cx: CXCursor): AbsFile =
assert cx.kind == ckTranslationUnit
AbsFile($getCursorSpelling(cx))

#*************************************************************************#
#****************************** CXCursor *******************************#
#*************************************************************************#
proc `$`*(cxkind: CXCursorKind): string =
case cxkind:
of ckBaseSpecifier: "ckBaseSpecifier"
of ckAlignedAttr: "ckAlignedAttr"
of ckMacroDefinition: "ckMacroDefinition"
of ckMacroExpansion: "ckMacroExpansion"
of ckFinalAttr: "ckFinalAttr"
of ckAccessSpecifier: "ckAccessSpecifier"
of ckNullPtrLiteralExpr: "ckNullPtrLiteralExpr"
of ckWarnUnusedAttr: "ckWarnUnusedAttr"
of ckWarnUnusedResultAttr: "ckWarnUnusedResultAttr"
of ckConversionFunction: "ckConversionFunction"
of ckMethod: "ckMethod"
of ckConstructor: "ckContructor"
of ckDestructor: "ckDestructor"
of ckClassTemplate: "ckClassTemplate"
of ckOverloadCandidate: "ckOverloadCandidate"

else:
$getCursorKindSpelling(cxkind)

proc `$`*(cursor: CXCursor): string =
case cursor.kind:
Expand Down Expand Up @@ -760,6 +757,15 @@ proc treeRepr*(

add hshow(cursor.kind)

if isAttribute(cursor.kind) != 0: add " attr" + fgBlue
elif isExpression(cursor.kind) != 0: add " expr" + fgBlue
elif isDeclaration(cursor.kind) != 0: discard # add " decl" + fgBlue
elif isReference(cursor.kind) != 0: add " tref" + fgBlue
elif isStatement(cursor.kind) != 0: add " stmt" + fgBlue
elif isPreprocessing(cursor.kind) != 0: add " prep" + fgBlue
elif isUnexposed(cursor.kind) != 0: add " unex" + fgBlue


if tu.isNone():
add " "
add $cursor + fgYellow
Expand All @@ -771,6 +777,7 @@ proc treeRepr*(
add " "
add hshow(raises)


if not(cursor.cxType() of tkInvalid):
add " "
add hshow(cursor.cxType)
Expand Down Expand Up @@ -854,3 +861,11 @@ proc getClassBaseCursors*(inCursor: CXCursor): seq[CXCursor] =
let base = node[0].getCursorReferenced()
baseQue.addLast base
result.add base

proc isAnnotatedWith*(
cursor: CxCursor, tu: CxTranslationUnit, attr: string): bool =

for subnode in items(cursor):
if subnode of ckAnnotateAttr:
if $subnode == attr:
return true
8 changes: 8 additions & 0 deletions src/hcparse/read_libclang/hc_clangreader.nim
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,14 @@ proc wrapProcedure*(
if pr.cursor.isStatic():
result.flags.incl cpfStatic

let tu = pr.cursor.getParentTranslationUnit()

if pr.cursor.isAnnotatedWith(tu, "qt_signal"):
result.flags.incl cpfSignal

if pr.cursor.isAnnotatedWith(tu, "qt_slot"):
result.flags.incl cpfSlot

case pr.cursor.kind:
of ckConstructor:
result.constructorOf = some cxxPair(parentDecl.get().ident)
Expand Down
2 changes: 1 addition & 1 deletion src/hcparse/read_libclang/hc_types.nim
Original file line number Diff line number Diff line change
Expand Up @@ -355,7 +355,6 @@ type
logger*: HLogger
header*: AbsFile ## Current main translation file (header)

unit*: CXTranslationUnit
refidMap*: RefidMap

# REFACTOR return CxxHeader spec directly
Expand Down Expand Up @@ -452,6 +451,7 @@ proc getName*(cn: CName): string =
getName(cn.cursor)



proc getSemanticNamespaces*(
conf: WrapConf, parent: CXCursor, filterInline: bool = true, withType: bool = true
): seq[CXCursor] =
Expand Down
7 changes: 6 additions & 1 deletion tests/tLibclangBasic.nim
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,12 @@ struct Struct {
int field;
int get() const;
void set(int arg);
public __attribute__((annotate("qt_signal"))):
void set1(int arg);
void set2(int arg);
};
template <typename T, class Z = int>
Expand Down

0 comments on commit 88ad79d

Please sign in to comment.