Skip to content

Commit 128d102

Browse files
authored
Merge pull request github#12871 from aibaars/py-yaml
Python: add YAML support
2 parents 20f555c + 5b6d3af commit 128d102

File tree

20 files changed

+6402
-1
lines changed

20 files changed

+6402
-1
lines changed

python/downgrades/0355ecf0ac589e66467a378e0e9d60f41ee4a757/old.dbscheme

Lines changed: 1147 additions & 0 deletions
Large diffs are not rendered by default.

python/downgrades/0355ecf0ac589e66467a378e0e9d60f41ee4a757/semmlecode.python.dbscheme

Lines changed: 1106 additions & 0 deletions
Large diffs are not rendered by default.
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
description: Add YAML tables
2+
compatibility: backwards
3+
4+
yaml.rel: delete
5+
yaml_anchors.rel: delete
6+
yaml_aliases.rel: delete
7+
yaml_scalars.rel: delete
8+
yaml_errors.rel: delete
9+
yaml_locations.rel: delete
10+
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
category: minorAnalysis
3+
---
4+
* Added support for querying the contents of YAML files.

python/ql/lib/qlpack.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,5 +9,6 @@ dependencies:
99
codeql/regex: ${workspace}
1010
codeql/tutorial: ${workspace}
1111
codeql/util: ${workspace}
12+
codeql/yaml: ${workspace}
1213
dataExtensions:
1314
- semmle/python/frameworks/**/model.yml

python/ql/lib/semmle/python/PrintAst.qll

Lines changed: 78 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88

99
import python
1010
import semmle.python.RegexTreeView
11+
import semmle.python.Yaml
1112

1213
private newtype TPrintAstConfiguration = MkPrintAstConfiguration()
1314

@@ -53,7 +54,9 @@ private newtype TPrintAstNode =
5354
shouldPrint(list.getAnItem(), _) and
5455
not list = any(Module mod).getBody() and
5556
not forall(AstNode child | child = list.getAnItem() | isNotNeeded(child))
56-
}
57+
} or
58+
TYamlNode(YamlNode node) or
59+
TYamlMappingNode(YamlMapping mapping, int i) { exists(mapping.getKeyNode(i)) }
5760

5861
/**
5962
* A node in the output tree.
@@ -633,6 +636,80 @@ private module PrettyPrinting {
633636
}
634637
}
635638

639+
/**
640+
* Classes for printing YAML AST.
641+
*/
642+
module PrintYaml {
643+
/**
644+
* A print node representing a YAML value in a .yml file.
645+
*/
646+
class YamlNodeNode extends PrintAstNode, TYamlNode {
647+
YamlNode node;
648+
649+
YamlNodeNode() { this = TYamlNode(node) }
650+
651+
override string toString() {
652+
result = "[" + concat(node.getAPrimaryQlClass(), ",") + "] " + node.toString()
653+
}
654+
655+
override Location getLocation() { result = node.getLocation() }
656+
657+
/**
658+
* Gets the `YAMLNode` represented by this node.
659+
*/
660+
final YamlNode getValue() { result = node }
661+
662+
override PrintAstNode getChild(int childIndex) {
663+
exists(YamlNode child | result.(YamlNodeNode).getValue() = child |
664+
child = node.getChildNode(childIndex)
665+
)
666+
}
667+
}
668+
669+
/**
670+
* A print node representing a `YAMLMapping`.
671+
*
672+
* Each child of this node aggregates the key and value of a mapping.
673+
*/
674+
class YamlMappingNode extends YamlNodeNode {
675+
override YamlMapping node;
676+
677+
override PrintAstNode getChild(int childIndex) {
678+
exists(YamlMappingMapNode map | map = result | map.maps(node, childIndex))
679+
}
680+
}
681+
682+
/**
683+
* A print node representing the `i`th mapping in `mapping`.
684+
*/
685+
class YamlMappingMapNode extends PrintAstNode, TYamlMappingNode {
686+
YamlMapping mapping;
687+
int i;
688+
689+
YamlMappingMapNode() { this = TYamlMappingNode(mapping, i) }
690+
691+
override string toString() {
692+
result = "(Mapping " + i + ")" and not exists(mapping.getKeyNode(i).(YamlScalar).getValue())
693+
or
694+
result = "(Mapping " + i + ") " + mapping.getKeyNode(i).(YamlScalar).getValue() + ":"
695+
}
696+
697+
/**
698+
* Holds if this print node represents the `index`th mapping of `m`.
699+
*/
700+
predicate maps(YamlMapping m, int index) {
701+
m = mapping and
702+
index = i
703+
}
704+
705+
override PrintAstNode getChild(int childIndex) {
706+
childIndex = 0 and result.(YamlNodeNode).getValue() = mapping.getKeyNode(i)
707+
or
708+
childIndex = 1 and result.(YamlNodeNode).getValue() = mapping.getValueNode(i)
709+
}
710+
}
711+
}
712+
636713
/** Holds if `node` belongs to the output tree, and its property `key` has the given `value`. */
637714
query predicate nodes(PrintAstNode node, string key, string value) { value = node.getProperty(key) }
638715

python/ql/lib/semmle/python/Yaml.qll

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Provides classes for working with YAML data.
3+
*
4+
* YAML documents are represented as abstract syntax trees whose nodes
5+
* are either YAML values or alias nodes referring to another YAML value.
6+
*/
7+
8+
private import codeql.yaml.Yaml as LibYaml
9+
10+
private module YamlSig implements LibYaml::InputSig {
11+
import semmle.python.Files
12+
13+
class LocatableBase extends @yaml_locatable {
14+
Location getLocation() { yaml_locations(this, result) }
15+
16+
string toString() { none() }
17+
}
18+
19+
class NodeBase extends LocatableBase, @yaml_node {
20+
NodeBase getChildNode(int i) { yaml(result, _, this, i, _, _) }
21+
22+
string getTag() { yaml(this, _, _, _, result, _) }
23+
24+
string getAnchor() { yaml_anchors(this, result) }
25+
26+
override string toString() { yaml(this, _, _, _, _, result) }
27+
}
28+
29+
class ScalarNodeBase extends NodeBase, @yaml_scalar_node {
30+
int getStyle() { yaml_scalars(this, result, _) }
31+
32+
string getValue() { yaml_scalars(this, _, result) }
33+
}
34+
35+
class CollectionNodeBase extends NodeBase, @yaml_collection_node { }
36+
37+
class MappingNodeBase extends CollectionNodeBase, @yaml_mapping_node { }
38+
39+
class SequenceNodeBase extends CollectionNodeBase, @yaml_sequence_node { }
40+
41+
class AliasNodeBase extends NodeBase, @yaml_alias_node {
42+
string getTarget() { yaml_aliases(this, result) }
43+
}
44+
45+
class ParseErrorBase extends LocatableBase, @yaml_error {
46+
string getMessage() { yaml_errors(this, result) }
47+
}
48+
}
49+
50+
import LibYaml::Make<YamlSig>

python/ql/lib/semmlecode.python.dbscheme

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1104,3 +1104,44 @@ xmllocations(int xmlElement: @xmllocatable ref,
11041104
int location: @location_default ref);
11051105

11061106
@xmllocatable = @xmlcharacters | @xmlelement | @xmlcomment | @xmlattribute | @xmldtd | @file | @xmlnamespace;
1107+
1108+
/**
1109+
* YAML
1110+
*/
1111+
1112+
#keyset[parent, idx]
1113+
yaml (unique int id: @yaml_node,
1114+
int kind: int ref,
1115+
int parent: @yaml_node_parent ref,
1116+
int idx: int ref,
1117+
varchar(900) tag: string ref,
1118+
varchar(900) tostring: string ref);
1119+
1120+
case @yaml_node.kind of
1121+
0 = @yaml_scalar_node
1122+
| 1 = @yaml_mapping_node
1123+
| 2 = @yaml_sequence_node
1124+
| 3 = @yaml_alias_node
1125+
;
1126+
1127+
@yaml_collection_node = @yaml_mapping_node | @yaml_sequence_node;
1128+
1129+
@yaml_node_parent = @yaml_collection_node | @file;
1130+
1131+
yaml_anchors (unique int node: @yaml_node ref,
1132+
varchar(900) anchor: string ref);
1133+
1134+
yaml_aliases (unique int alias: @yaml_alias_node ref,
1135+
varchar(900) target: string ref);
1136+
1137+
yaml_scalars (unique int scalar: @yaml_scalar_node ref,
1138+
int style: int ref,
1139+
varchar(900) value: string ref);
1140+
1141+
yaml_errors (unique int id: @yaml_error,
1142+
varchar(900) message: string ref);
1143+
1144+
yaml_locations(unique int locatable: @yaml_locatable ref,
1145+
int location: @location_default ref);
1146+
1147+
@yaml_locatable = @yaml_node | @yaml_error;

0 commit comments

Comments
 (0)