mirror of
https://github.com/github/codeql.git
synced 2026-02-19 08:23:45 +01:00
Implement basic printAst query
This commit is contained in:
@@ -123,7 +123,7 @@ fn convert_nodes(nodes: &Vec<node_types::Entry>) -> Vec<dbscheme::Entry> {
|
||||
create_containerparent_table(),
|
||||
create_source_location_prefix_table(),
|
||||
];
|
||||
let mut top_members: Vec<String> = Vec::new();
|
||||
let mut ast_node_members: Vec<String> = Vec::new();
|
||||
|
||||
for node in nodes {
|
||||
match &node {
|
||||
@@ -162,7 +162,7 @@ fn convert_nodes(nodes: &Vec<node_types::Entry>) -> Vec<dbscheme::Entry> {
|
||||
}],
|
||||
keysets: None,
|
||||
};
|
||||
top_members.push(node_types::escape_name(&name));
|
||||
ast_node_members.push(node_types::escape_name(&name));
|
||||
|
||||
// If the type also has fields or children, then we create either
|
||||
// auxiliary tables or columns in the defining table for them.
|
||||
@@ -198,8 +198,8 @@ fn convert_nodes(nodes: &Vec<node_types::Entry>) -> Vec<dbscheme::Entry> {
|
||||
|
||||
// Create a union of all database types.
|
||||
entries.push(dbscheme::Entry::Union(dbscheme::Union {
|
||||
name: "top".to_string(),
|
||||
members: top_members,
|
||||
name: "ast_node".to_string(),
|
||||
members: ast_node_members,
|
||||
}));
|
||||
|
||||
entries
|
||||
|
||||
@@ -76,7 +76,7 @@ fn create_supertype_map(nodes: &[node_types::Entry]) -> SupertypeMap {
|
||||
}
|
||||
|
||||
fn get_base_classes(name: &str, supertype_map: &SupertypeMap) -> Vec<ql::Type> {
|
||||
let mut base_classes: Vec<ql::Type> = vec![ql::Type::Normal("Top".to_owned())];
|
||||
let mut base_classes: Vec<ql::Type> = vec![ql::Type::Normal("AstNode".to_owned())];
|
||||
|
||||
if let Some(supertypes) = supertype_map.get(name) {
|
||||
base_classes.extend(
|
||||
@@ -89,10 +89,24 @@ fn get_base_classes(name: &str, supertype_map: &SupertypeMap) -> Vec<ql::Type> {
|
||||
base_classes
|
||||
}
|
||||
|
||||
/// Creates the hard-coded `Top` class that acts as a supertype of all classes we
|
||||
/// generate.
|
||||
fn create_top_class() -> ql::Class {
|
||||
let to_string = create_none_predicate("toString", false, Some(ql::Type::String), vec![]);
|
||||
/// Creates the hard-coded `AstNode` class that acts as a supertype of all
|
||||
/// classes we generate.
|
||||
fn create_ast_node_class() -> ql::Class {
|
||||
// Default implementation of `toString` calls `this.describeQlClass()`
|
||||
let to_string = ql::Predicate {
|
||||
name: "toString".to_owned(),
|
||||
overridden: false,
|
||||
return_type: Some(ql::Type::String),
|
||||
formal_parameters: vec![],
|
||||
body: ql::Expression::Equals(
|
||||
Box::new(ql::Expression::Var("result".to_owned())),
|
||||
Box::new(ql::Expression::Dot(
|
||||
Box::new(ql::Expression::Var("this".to_owned())),
|
||||
"describeQlClass".to_owned(),
|
||||
vec![],
|
||||
)),
|
||||
),
|
||||
};
|
||||
let get_location = create_none_predicate(
|
||||
"getLocation",
|
||||
false,
|
||||
@@ -102,15 +116,30 @@ fn create_top_class() -> ql::Class {
|
||||
let get_a_field_or_child = create_none_predicate(
|
||||
"getAFieldOrChild",
|
||||
false,
|
||||
Some(ql::Type::Normal("Top".to_owned())),
|
||||
Some(ql::Type::Normal("AstNode".to_owned())),
|
||||
vec![],
|
||||
);
|
||||
let describe_ql_class = ql::Predicate {
|
||||
name: "describeQlClass".to_owned(),
|
||||
overridden: false,
|
||||
return_type: Some(ql::Type::String),
|
||||
formal_parameters: vec![],
|
||||
body: ql::Expression::Equals(
|
||||
Box::new(ql::Expression::Var("result".to_owned())),
|
||||
Box::new(ql::Expression::String("???".to_owned())),
|
||||
),
|
||||
};
|
||||
ql::Class {
|
||||
name: "Top".to_owned(),
|
||||
name: "AstNode".to_owned(),
|
||||
is_abstract: false,
|
||||
supertypes: vec![ql::Type::AtType("top".to_owned())],
|
||||
supertypes: vec![ql::Type::AtType("ast_node".to_owned())],
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![to_string, get_location, get_a_field_or_child],
|
||||
predicates: vec![
|
||||
to_string,
|
||||
get_location,
|
||||
get_a_field_or_child,
|
||||
describe_ql_class,
|
||||
],
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +188,7 @@ fn create_field_class(
|
||||
]
|
||||
.concat(),
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![],
|
||||
predicates: vec![create_describe_ql_class(&class_name)],
|
||||
}));
|
||||
field_union_name
|
||||
}
|
||||
@@ -188,16 +217,17 @@ fn dbscheme_name_to_class_name(dbscheme_name: &str) -> String {
|
||||
.join("")
|
||||
}
|
||||
|
||||
/// Creates a `toString` predicate that returns the given text.
|
||||
fn create_to_string_predicate(text: &str) -> ql::Predicate {
|
||||
/// Creates an overridden `describeQlClass` predicate that returns the given
|
||||
/// name.
|
||||
fn create_describe_ql_class(class_name: &str) -> ql::Predicate {
|
||||
ql::Predicate {
|
||||
name: "toString".to_owned(),
|
||||
name: "describeQlClass".to_owned(),
|
||||
overridden: true,
|
||||
return_type: Some(ql::Type::String),
|
||||
formal_parameters: vec![],
|
||||
body: ql::Expression::Equals(
|
||||
Box::new(ql::Expression::Var("result".to_owned())),
|
||||
Box::new(ql::Expression::String(text.to_owned())),
|
||||
Box::new(ql::Expression::String(class_name.to_owned())),
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -385,7 +415,7 @@ pub fn convert_nodes(nodes: &Vec<node_types::Entry>) -> Vec<ql::TopLevel> {
|
||||
let mut classes: Vec<ql::TopLevel> = vec![
|
||||
ql::TopLevel::Import("codeql.files.FileSystem".to_owned()),
|
||||
ql::TopLevel::Import("codeql.Locations".to_owned()),
|
||||
ql::TopLevel::Class(create_top_class()),
|
||||
ql::TopLevel::Class(create_ast_node_class()),
|
||||
];
|
||||
|
||||
for node in nodes {
|
||||
@@ -410,7 +440,7 @@ pub fn convert_nodes(nodes: &Vec<node_types::Entry>) -> Vec<ql::TopLevel> {
|
||||
]
|
||||
.concat(),
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![],
|
||||
predicates: vec![create_describe_ql_class(&class_name)],
|
||||
}));
|
||||
}
|
||||
node_types::Entry::Table { type_name, fields } => {
|
||||
@@ -444,7 +474,7 @@ pub fn convert_nodes(nodes: &Vec<node_types::Entry>) -> Vec<ql::TopLevel> {
|
||||
.concat(),
|
||||
characteristic_predicate: None,
|
||||
predicates: vec![
|
||||
create_to_string_predicate(&main_class_name),
|
||||
create_describe_ql_class(&main_class_name),
|
||||
create_get_location_predicate(&main_table_name, main_table_arity),
|
||||
],
|
||||
};
|
||||
@@ -479,7 +509,7 @@ pub fn convert_nodes(nodes: &Vec<node_types::Entry>) -> Vec<ql::TopLevel> {
|
||||
main_class.predicates.push(ql::Predicate {
|
||||
name: "getAFieldOrChild".to_owned(),
|
||||
overridden: true,
|
||||
return_type: Some(ql::Type::Normal("Top".to_owned())),
|
||||
return_type: Some(ql::Type::Normal("AstNode".to_owned())),
|
||||
formal_parameters: vec![],
|
||||
body: ql::Expression::Or(get_child_exprs),
|
||||
});
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
75
ql/src/codeql_ruby/printAst.qll
Normal file
75
ql/src/codeql_ruby/printAst.qll
Normal file
@@ -0,0 +1,75 @@
|
||||
/**
|
||||
* Provides queries to pretty-print a Ruby abstract syntax tree as a graph.
|
||||
*
|
||||
* By default, this will print the AST for all nodes in the database. To change
|
||||
* this behavior, extend `PrintASTConfiguration` and override `shouldPrintNode`
|
||||
* to hold for only the AST nodes you wish to view.
|
||||
*/
|
||||
|
||||
import codeql_ruby.ast
|
||||
|
||||
/**
|
||||
* The query can extend this class to control which nodes are printed.
|
||||
*/
|
||||
class PrintAstConfiguration extends string {
|
||||
PrintAstConfiguration() { this = "PrintAstConfiguration" }
|
||||
|
||||
/**
|
||||
* Holds if the given node should be printed.
|
||||
*/
|
||||
predicate shouldPrintNode(AstNode n) { any() }
|
||||
}
|
||||
|
||||
/**
|
||||
* A node in the output tree.
|
||||
*/
|
||||
class PrintAstNode extends AstNode {
|
||||
string getProperty(string key) {
|
||||
key = "semmle.label" and
|
||||
result = this.toString()
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a textual representation of this node in the PrintAST output tree.
|
||||
*/
|
||||
override string toString() { result = "[" + this.describeQlClass() + "] " + super.toString() }
|
||||
|
||||
/**
|
||||
* Holds if this node should be printed in the output. By default, all nodes
|
||||
* are printed, but the query can override
|
||||
* `PrintAstConfiguration.shouldPrintNode` to filter the output.
|
||||
*/
|
||||
predicate shouldPrint() { shouldPrintNode(this) }
|
||||
}
|
||||
|
||||
private predicate shouldPrintNode(AstNode n) {
|
||||
exists(PrintAstConfiguration config | config.shouldPrintNode(n))
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `node` belongs to the output tree, and its property `key` has the
|
||||
* given `value`.
|
||||
*/
|
||||
query predicate nodes(PrintAstNode node, string key, string value) {
|
||||
node.shouldPrint() and
|
||||
value = node.getProperty(key)
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if `target` is a child of `source` in the AST, and property `key` of
|
||||
* the edge has the given `value`.
|
||||
*/
|
||||
query predicate edges(PrintAstNode source, PrintAstNode target, string key, string value) {
|
||||
source.shouldPrint() and
|
||||
target.shouldPrint() and
|
||||
target = source.getAFieldOrChild() and
|
||||
key = "semmle.label" and
|
||||
value = "edge"
|
||||
}
|
||||
|
||||
/**
|
||||
* Holds if property `key` of the graph has the given `value`.
|
||||
*/
|
||||
query predicate graphProperties(string key, string value) {
|
||||
key = "semmle.graphKind" and value = "tree"
|
||||
}
|
||||
32
ql/src/printAst.ql
Normal file
32
ql/src/printAst.ql
Normal file
@@ -0,0 +1,32 @@
|
||||
/**
|
||||
* @name Print AST
|
||||
* @description Produces a representation of a file's Abstract Syntax Tree.
|
||||
* This query is used by the VS Code extension.
|
||||
* @id ruby/print-ast
|
||||
* @kind graph
|
||||
* @tags ide-contextual-queries/print-ast
|
||||
*/
|
||||
|
||||
import codeql_ruby.printAst
|
||||
|
||||
/**
|
||||
* The source file to generate an AST from.
|
||||
*/
|
||||
external string selectedSourceFile();
|
||||
|
||||
/**
|
||||
* Returns an appropriately encoded version of a filename `name`
|
||||
* passed by the VS Code extension in order to coincide with the
|
||||
* output of `.getFile()` on locatable entities.
|
||||
*/
|
||||
cached
|
||||
File getEncodedFile(string name) { result.getAbsolutePath().replaceAll(":", "_") = name }
|
||||
|
||||
/**
|
||||
* Overrides the configuration to print only nodes in the selected source file.
|
||||
*/
|
||||
class Cfg extends PrintAstConfiguration {
|
||||
override predicate shouldPrintNode(AstNode n) {
|
||||
n.getLocation().getFile() = getEncodedFile(selectedSourceFile())
|
||||
}
|
||||
}
|
||||
@@ -1856,5 +1856,5 @@ tilde_unnamed_def(
|
||||
int loc: @location ref
|
||||
);
|
||||
|
||||
@top = @alias | @argument_list | @array | @assignment | @bare_string | @bare_symbol | @begin | @begin_block | @binary | @block | @block_argument | @block_parameter | @block_parameters | @break | @call | @case__ | @chained_string | @class | @conditional | @destructured_left_assignment | @destructured_parameter | @do | @do_block | @element_reference | @else | @elsif | @empty_statement | @end_block | @ensure | @exception_variable | @exceptions | @for | @hash | @hash_splat_argument | @hash_splat_parameter | @heredoc_body | @if | @if_modifier | @in | @interpolation | @keyword_parameter | @lambda | @lambda_parameters | @left_assignment_list | @method | @method_call | @method_parameters | @module | @next | @operator | @operator_assignment | @optional_parameter | @pair | @parenthesized_statements | @pattern | @program | @range | @rational | @redo | @regex | @rescue | @rescue_modifier | @rest_assignment | @retry | @return | @right_assignment_list | @scope_resolution | @setter | @singleton_class | @singleton_method | @splat_argument | @splat_parameter | @string__ | @string_array | @subshell | @superclass | @symbol | @symbol_array | @then | @unary | @undef | @unless | @unless_modifier | @until | @until_modifier | @when | @while | @while_modifier | @yield | @bang_unnamed | @bangequal_unnamed | @bangtilde_unnamed | @dquote_unnamed | @hashlbrace_unnamed | @percent_unnamed | @percentequal_unnamed | @percentilparen_unnamed | @percentwlparen_unnamed | @ampersand_unnamed | @ampersandampersand_unnamed | @ampersandampersandequal_unnamed | @ampersanddot_unnamed | @ampersandequal_unnamed | @lparen_unnamed | @rparen_unnamed | @star_unnamed | @starstar_unnamed | @starstarequal_unnamed | @starequal_unnamed | @plus_unnamed | @plusequal_unnamed | @plusat_unnamed | @comma_unnamed | @minus_unnamed | @minusequal_unnamed | @minusrangle_unnamed | @minusat_unnamed | @dot_unnamed | @dotdot_unnamed | @dotdotdot_unnamed | @slash_unnamed | @slashequal_unnamed | @colon_unnamed | @colondquote_unnamed | @coloncolon_unnamed | @semicolon_unnamed | @langle_unnamed | @langlelangle_unnamed | @langlelangleequal_unnamed | @langleequal_unnamed | @langleequalrangle_unnamed | @equal_unnamed | @equalequal_unnamed | @equalequalequal_unnamed | @equalrangle_unnamed | @equaltilde_unnamed | @rangle_unnamed | @rangleequal_unnamed | @ranglerangle_unnamed | @ranglerangleequal_unnamed | @question_unnamed | @b_e_g_i_n__unnamed | @e_n_d__unnamed | @lbracket_unnamed | @lbracketrbracket_unnamed | @lbracketrbracketequal_unnamed | @rbracket_unnamed | @caret_unnamed | @caretequal_unnamed | @underscore__e_n_d____unnamed | @backtick_unnamed | @alias_unnamed | @and_unnamed | @begin_unnamed | @break_unnamed | @case_unnamed | @character | @class_unnamed | @class_variable | @comment | @complex | @constant | @def_unnamed | @definedquestion_unnamed | @do_unnamed | @else_unnamed | @elsif_unnamed | @end_unnamed | @ensure_unnamed | @escape_sequence | @false | @float__ | @for_unnamed | @global_variable | @heredoc_beginning | @heredoc_end | @identifier | @if_unnamed | @in_unnamed | @instance_variable | @integer | @module_unnamed | @next_unnamed | @nil | @not_unnamed | @or_unnamed | @r_unnamed | @redo_unnamed | @rescue_unnamed | @retry_unnamed | @return_unnamed | @self | @super | @then_unnamed | @true | @undef_unnamed | @uninterpreted | @unless_unnamed | @until_unnamed | @when_unnamed | @while_unnamed | @yield_unnamed | @lbrace_unnamed | @pipe_unnamed | @pipeequal_unnamed | @pipepipe_unnamed | @pipepipeequal_unnamed | @rbrace_unnamed | @tilde_unnamed
|
||||
@ast_node = @alias | @argument_list | @array | @assignment | @bare_string | @bare_symbol | @begin | @begin_block | @binary | @block | @block_argument | @block_parameter | @block_parameters | @break | @call | @case__ | @chained_string | @class | @conditional | @destructured_left_assignment | @destructured_parameter | @do | @do_block | @element_reference | @else | @elsif | @empty_statement | @end_block | @ensure | @exception_variable | @exceptions | @for | @hash | @hash_splat_argument | @hash_splat_parameter | @heredoc_body | @if | @if_modifier | @in | @interpolation | @keyword_parameter | @lambda | @lambda_parameters | @left_assignment_list | @method | @method_call | @method_parameters | @module | @next | @operator | @operator_assignment | @optional_parameter | @pair | @parenthesized_statements | @pattern | @program | @range | @rational | @redo | @regex | @rescue | @rescue_modifier | @rest_assignment | @retry | @return | @right_assignment_list | @scope_resolution | @setter | @singleton_class | @singleton_method | @splat_argument | @splat_parameter | @string__ | @string_array | @subshell | @superclass | @symbol | @symbol_array | @then | @unary | @undef | @unless | @unless_modifier | @until | @until_modifier | @when | @while | @while_modifier | @yield | @bang_unnamed | @bangequal_unnamed | @bangtilde_unnamed | @dquote_unnamed | @hashlbrace_unnamed | @percent_unnamed | @percentequal_unnamed | @percentilparen_unnamed | @percentwlparen_unnamed | @ampersand_unnamed | @ampersandampersand_unnamed | @ampersandampersandequal_unnamed | @ampersanddot_unnamed | @ampersandequal_unnamed | @lparen_unnamed | @rparen_unnamed | @star_unnamed | @starstar_unnamed | @starstarequal_unnamed | @starequal_unnamed | @plus_unnamed | @plusequal_unnamed | @plusat_unnamed | @comma_unnamed | @minus_unnamed | @minusequal_unnamed | @minusrangle_unnamed | @minusat_unnamed | @dot_unnamed | @dotdot_unnamed | @dotdotdot_unnamed | @slash_unnamed | @slashequal_unnamed | @colon_unnamed | @colondquote_unnamed | @coloncolon_unnamed | @semicolon_unnamed | @langle_unnamed | @langlelangle_unnamed | @langlelangleequal_unnamed | @langleequal_unnamed | @langleequalrangle_unnamed | @equal_unnamed | @equalequal_unnamed | @equalequalequal_unnamed | @equalrangle_unnamed | @equaltilde_unnamed | @rangle_unnamed | @rangleequal_unnamed | @ranglerangle_unnamed | @ranglerangleequal_unnamed | @question_unnamed | @b_e_g_i_n__unnamed | @e_n_d__unnamed | @lbracket_unnamed | @lbracketrbracket_unnamed | @lbracketrbracketequal_unnamed | @rbracket_unnamed | @caret_unnamed | @caretequal_unnamed | @underscore__e_n_d____unnamed | @backtick_unnamed | @alias_unnamed | @and_unnamed | @begin_unnamed | @break_unnamed | @case_unnamed | @character | @class_unnamed | @class_variable | @comment | @complex | @constant | @def_unnamed | @definedquestion_unnamed | @do_unnamed | @else_unnamed | @elsif_unnamed | @end_unnamed | @ensure_unnamed | @escape_sequence | @false | @float__ | @for_unnamed | @global_variable | @heredoc_beginning | @heredoc_end | @identifier | @if_unnamed | @in_unnamed | @instance_variable | @integer | @module_unnamed | @next_unnamed | @nil | @not_unnamed | @or_unnamed | @r_unnamed | @redo_unnamed | @rescue_unnamed | @retry_unnamed | @return_unnamed | @self | @super | @then_unnamed | @true | @undef_unnamed | @uninterpreted | @unless_unnamed | @until_unnamed | @when_unnamed | @while_unnamed | @yield_unnamed | @lbrace_unnamed | @pipe_unnamed | @pipeequal_unnamed | @pipepipe_unnamed | @pipepipeequal_unnamed | @rbrace_unnamed | @tilde_unnamed
|
||||
|
||||
|
||||
Reference in New Issue
Block a user