-
Notifications
You must be signed in to change notification settings - Fork 5
Adding function information to the /render page #134
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
base: main
Are you sure you want to change the base?
Changes from all commits
f7b084e
3151ea6
ca084e1
7a7b13f
2ae4d60
d5bbffb
a2ba7d5
7618ad8
d675eac
6e6d675
e944b01
a71484b
34e3ae6
328d86d
dcea710
69bf1a4
0d3f03e
c279feb
fac3bef
ce19dd5
4d7f20f
542004b
5dc7ce2
dc4457f
b73437c
7253af7
f69336f
26c9883
1768167
c17b84c
52c72f7
8c74c99
89a9f51
051ccc6
8adbcfd
9880873
d145111
3ab9247
009f53a
e6c8172
607b7fe
2469d6d
41ac697
fd7aa24
421878f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -166,3 +166,17 @@ function processSwitchlike(switchSyntax: SyntaxNode, ctx: Context): BasicBlock { | |
|
||
return blockHandler.update({ entry: headNode, exit: mergeNode }); | ||
} | ||
|
||
const nodeType = { | ||
functionDefinition: "function_definition", | ||
|
||
// identifier lookups | ||
identifier: "identifier", | ||
}; | ||
|
||
export function extractCFunctionName(func: SyntaxNode): string | undefined { | ||
if (func.type === nodeType.functionDefinition) { | ||
return func.descendantsOfType(nodeType.identifier)[0]?.text; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there a case where this really is undefined, or is it due to the weird typing in the current web-tree-sitter library? If it's the weird types, we have |
||
} | ||
return undefined; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ import { | |
processReturnStatement, | ||
processThrowStatement, | ||
} from "./common-patterns.ts"; | ||
import { extractNameByNodeType } from "./function-utils.ts"; | ||
import { | ||
type Context, | ||
GenericCFGBuilder, | ||
|
@@ -148,3 +149,72 @@ function processTryStatement(trySyntax: SyntaxNode, ctx: Context): BasicBlock { | |
}); | ||
}); | ||
} | ||
|
||
const nodeType = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What value do we get from this object? |
||
functionDefinition: "function_definition", | ||
lambdaExpression: "lambda_expression", | ||
|
||
// inline/function‑assignment cases | ||
variableDeclaration: "variable_declaration", | ||
initDeclarator: "init_declarator", | ||
|
||
// identifier lookups | ||
identifier: "identifier", | ||
|
||
//Unnamed functions | ||
anonymous: "<anonymous>", | ||
|
||
//Special cases , operator-name and destructor-name | ||
operatorName: "operator_name", | ||
destructorName: "destructor_name", | ||
}; | ||
|
||
function findCppNameInParentHierarchy( | ||
func: SyntaxNode, | ||
parentType: string, | ||
childType: string, | ||
): string | undefined { | ||
let parent = func.parent; | ||
while (parent) { | ||
if (parent.type === parentType) { | ||
return extractNameByNodeType(parent, childType); | ||
} | ||
parent = parent.parent; | ||
} | ||
return undefined; | ||
} | ||
|
||
export function extractCppFunctionName(func: SyntaxNode): string | undefined { | ||
if (func.type === nodeType.functionDefinition) { | ||
// Look for an operator overload (This should happen before the identifier/destructor). | ||
const operatorNode = func.descendantsOfType(nodeType.operatorName)[0]; | ||
if (operatorNode) return operatorNode.text; | ||
|
||
// else, look for the destructor name, which is a special case because identifier returns without the ~ | ||
const destructorNode = func.descendantsOfType(nodeType.destructorName)[0]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The order of checks here does not work when we have nested classes. Consider the following: int square(int num) {
class X {
~X() {};
};
return num * num;
} We'll get I think it's important to add tests with nested functions and classes to ensure we don't have more of these. I'm guessing that iterating through the children instead of all descendants will yield better results. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh..good catch. That's because I gave destructors higher priority when extracting the function name. I'll change it, maybe it's time to try queries for the C and C++ cases. |
||
if (destructorNode) { | ||
return destructorNode.text; | ||
} | ||
//if neither of those, look for the identifier | ||
const idNode = func.descendantsOfType(nodeType.identifier)[0]; | ||
if (idNode) return idNode.text; | ||
} | ||
if (func.type === nodeType.lambdaExpression) { | ||
// if the lambda is assigned to a variable, return the variable name | ||
// otherwise return "<Anonymous>" | ||
return ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. If an anonymous function is declared inside a named function, we'll get the name of the outer function here. Consider: void f() {
auto my_func = [](){
[](){}();
};
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Just wrote a comment about this, same here.
we get
we get
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But functions do not stand alone. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For the render page, I believe we only get the function code and not the whole file (correct me if I'm wrong).
If that were the case, the node would stand alone so querying the nested function should work exactly as I already did. |
||
findCppNameInParentHierarchy( | ||
func, | ||
nodeType.variableDeclaration, | ||
nodeType.identifier, | ||
) || | ||
findCppNameInParentHierarchy( | ||
func, | ||
nodeType.initDeclarator, | ||
nodeType.identifier, | ||
) || | ||
nodeType.anonymous | ||
); | ||
} | ||
return undefined; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -12,6 +12,10 @@ import { | |
processReturnStatement, | ||
processStatementSequence, | ||
} from "./common-patterns.ts"; | ||
import { | ||
extractNameByNodeType, | ||
extractTaggedValueFromTreeSitterQuery, | ||
} from "./function-utils.ts"; | ||
import { | ||
type Context, | ||
GenericCFGBuilder, | ||
|
@@ -419,3 +423,75 @@ function processSwitchlike( | |
|
||
return blockHandler.update({ entry: headNode, exit: mergeNode }); | ||
} | ||
|
||
const nodeType = { | ||
// Function-related node types | ||
functionDeclaration: "function_declaration", | ||
methodDeclaration: "method_declaration", | ||
funcLiteral: "func_literal", | ||
|
||
// identifier lookups | ||
identifier: "identifier", | ||
fieldIdentifier: "field_identifier", | ||
|
||
// Unnamed functions | ||
anonymous: "<anonymous>", | ||
}; | ||
|
||
const shortVarQueryAndTag = { | ||
query: ` | ||
(short_var_declaration | ||
left: (expression_list | ||
(identifier) @var.name)) | ||
`, | ||
tag: "var.name", | ||
}; | ||
|
||
const varDeclarationQueryAndTag = { | ||
query: ` | ||
(var_declaration | ||
(var_spec | ||
(identifier) @var.name)) | ||
`, | ||
tag: "var.name", | ||
}; | ||
|
||
const assignmentQueryAndTag = { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. All three queries can be unified with an alternation in the query. |
||
query: ` | ||
(assignment_statement | ||
left: (expression_list | ||
(identifier) @var.name)) | ||
`, | ||
tag: "var.name", | ||
}; | ||
|
||
export function extractGoFunctionName(func: SyntaxNode): string | undefined { | ||
switch (func.type) { | ||
case nodeType.functionDeclaration: | ||
return extractNameByNodeType(func, nodeType.identifier); | ||
case nodeType.methodDeclaration: | ||
return extractNameByNodeType(func, nodeType.fieldIdentifier); | ||
case nodeType.funcLiteral: | ||
// Check if the func_literal is assigned to a variable or is a standalone function | ||
return ( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It is important to limit the depth of a query, so that they properly ignore nesting. func main() {
var x = func() {
y := func() {}
y()
}
x()
} See There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You're right. I'll check it out and make the changes. |
||
extractTaggedValueFromTreeSitterQuery( | ||
func, | ||
shortVarQueryAndTag.query, | ||
shortVarQueryAndTag.tag, | ||
) || | ||
extractTaggedValueFromTreeSitterQuery( | ||
func, | ||
varDeclarationQueryAndTag.query, | ||
varDeclarationQueryAndTag.tag, | ||
) || | ||
extractTaggedValueFromTreeSitterQuery( | ||
func, | ||
assignmentQueryAndTag.query, | ||
assignmentQueryAndTag.tag, | ||
) || | ||
nodeType.anonymous | ||
); | ||
default: | ||
return undefined; | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -17,6 +17,10 @@ import { | |
processStatementSequence, | ||
processThrowStatement, | ||
} from "./common-patterns.ts"; | ||
import { | ||
extractNameByNodeType, | ||
extractTaggedValueFromTreeSitterQuery, | ||
} from "./function-utils.ts"; | ||
import { | ||
type Context, | ||
GenericCFGBuilder, | ||
|
@@ -319,3 +323,82 @@ function processTryStatement(trySyntax: SyntaxNode, ctx: Context): BasicBlock { | |
}); | ||
}); | ||
} | ||
|
||
const nodeType = { | ||
// function‑declaration cases | ||
functionDeclaration: "function_declaration", | ||
generatorFunctionDeclaration: "generator_function_declaration", | ||
|
||
// inline/function‑expression cases | ||
generatorFunction: "generator_function", | ||
arrowFunction: "arrow_function", | ||
functionExpression: "function_expression", | ||
|
||
// methods | ||
methodDefinition: "method_definition", | ||
|
||
// identifier lookups | ||
identifier: "identifier", | ||
propertyIdentifier: "property_identifier", | ||
|
||
// Unnamed functions | ||
anonymous: "<anonymous>", | ||
}; | ||
|
||
const variableDeclaratorQueryAndTag = { | ||
query: ` | ||
(lexical_declaration | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What of |
||
(variable_declarator | ||
name: (identifier) @var.name)) | ||
`, | ||
tag: "var.name", | ||
}; | ||
|
||
/** | ||
* Extracts the name of a TypeScript function based on its syntax node type. | ||
* | ||
* @param {SyntaxNode} func - The syntax node representing the TypeScript function. | ||
* @returns {string | undefined} The function name, or `undefined` if not found. | ||
*/ | ||
export function extractTypeScriptFunctionName( | ||
func: SyntaxNode, | ||
): string | undefined { | ||
switch (func.type) { | ||
case nodeType.functionDeclaration: | ||
case nodeType.generatorFunctionDeclaration: | ||
return extractNameByNodeType(func, nodeType.identifier); | ||
|
||
case nodeType.generatorFunction: | ||
case nodeType.arrowFunction: | ||
return ( | ||
extractTaggedValueFromTreeSitterQuery( | ||
func, | ||
variableDeclaratorQueryAndTag.query, | ||
variableDeclaratorQueryAndTag.tag, | ||
) || nodeType.anonymous | ||
); | ||
|
||
case nodeType.methodDefinition: | ||
return extractNameByNodeType(func, nodeType.propertyIdentifier); | ||
|
||
case nodeType.functionExpression: { | ||
// first check for a direct name | ||
const optionalIdentifier = extractNameByNodeType( | ||
func, | ||
nodeType.identifier, | ||
); | ||
if (optionalIdentifier) return optionalIdentifier; | ||
|
||
// otherwise fall back to a variable‑assignment name | ||
return ( | ||
extractTaggedValueFromTreeSitterQuery( | ||
func, | ||
variableDeclaratorQueryAndTag.query, | ||
variableDeclaratorQueryAndTag.tag, | ||
) || nodeType.anonymous | ||
); | ||
} | ||
default: | ||
return undefined; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this constant for? Why not use the names directlu?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll modify it