Skip to content

Commit d1704cf

Browse files
authored
Merge pull request #17444 from hvitved/rust/final-classes
Rust/Swift: Make all public AST classes `final`
2 parents add0332 + 964e97c commit d1704cf

File tree

1,672 files changed

+25597
-6129
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

1,672 files changed

+25597
-6129
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ repos:
1515
- id: clang-format
1616

1717
- repo: https://github.com/pre-commit/mirrors-autopep8
18-
rev: v1.6.0
18+
rev: v2.0.4
1919
hooks:
2020
- id: autopep8
2121
files: ^misc/codegen/.*\.py

misc/codegen/generators/qlgen.py

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,18 +2,19 @@
22
QL code generation
33
44
`generate(opts, renderer)` will generate in the library directory:
5-
* generated/Raw.qll with thin class wrappers around DB types
6-
* generated/Synth.qll with the base algebraic datatypes for AST entities
7-
* generated/<group>/<Class>.qll with generated properties for each class
8-
* if not already modified, a elements/<group>/<Class>.qll stub to customize the above classes
9-
* elements.qll importing all the above stubs
10-
* if not already modified, a elements/<group>/<Class>Constructor.qll stub to customize the algebraic datatype
5+
* `generated/Raw.qll` with thin class wrappers around DB types
6+
* `generated/Synth.qll` with the base algebraic datatypes for AST entities
7+
* `generated/<group>/<Class>.qll` with generated properties for each class
8+
* if not already modified, an `elements/<group>/<Class>Impl.qll` stub to customize the above classes
9+
* `elements/<group>/<Class>.qll` that wraps the internal `<Class>Impl.qll` file in a public `final` class.
10+
* `elements.qll` importing all the above public classes
11+
* if not already modified, an `elements/<group>/<Class>Constructor.qll` stub to customize the algebraic datatype
1112
characteristic predicate
12-
* generated/SynthConstructors.qll importing all the above constructor stubs
13-
* generated/PureSynthConstructors.qll importing constructor stubs for pure synthesized types (that is, not
13+
* `generated/SynthConstructors.qll` importing all the above constructor stubs
14+
* `generated/PureSynthConstructors.qll` importing constructor stubs for pure synthesized types (that is, not
1415
corresponding to raw types)
1516
Moreover in the test directory for each <Class> in <group> it will generate beneath the
16-
extractor-tests/generated/<group>/<Class> directory either
17+
`extractor-tests/generated/<group>/<Class>` directory either
1718
* a `MISSING_SOURCE.txt` explanation file if no source is present, or
1819
* one `<Class>.ql` test query for all single properties and on `<Class>_<property>.ql` test query for each optional or
1920
repeated property
@@ -164,6 +165,7 @@ def get_ql_class(cls: schema.Class, lookup: typing.Dict[str, schema.Class]) -> q
164165
return ql.Class(
165166
name=cls.name,
166167
bases=cls.bases,
168+
bases_impl=[base + "Impl::" + base for base in cls.bases],
167169
final=not cls.derived,
168170
properties=properties,
169171
dir=pathlib.Path(cls.group or ""),
@@ -210,15 +212,17 @@ def get_import(file: pathlib.Path, root_dir: pathlib.Path):
210212
return str(stem).replace("/", ".")
211213

212214

213-
def get_types_used_by(cls: ql.Class) -> typing.Iterable[str]:
215+
def get_types_used_by(cls: ql.Class, is_impl: bool) -> typing.Iterable[str]:
214216
for b in cls.bases:
215-
yield b.base
217+
yield b.base + "Impl" if is_impl else b.base
216218
for p in cls.properties:
217219
yield p.type
220+
if cls.root:
221+
yield cls.name # used in `getResolveStep` and `resolve`
218222

219223

220-
def get_classes_used_by(cls: ql.Class) -> typing.List[str]:
221-
return sorted(set(t for t in get_types_used_by(cls) if t[0].isupper() and t != cls.name))
224+
def get_classes_used_by(cls: ql.Class, is_impl: bool) -> typing.List[str]:
225+
return sorted(set(t for t in get_types_used_by(cls, is_impl) if t[0].isupper() and (is_impl or t != cls.name)))
222226

223227

224228
def format(codeql, files):
@@ -239,6 +243,10 @@ def _get_path(cls: schema.Class) -> pathlib.Path:
239243
return pathlib.Path(cls.group or "", cls.name).with_suffix(".qll")
240244

241245

246+
def _get_path_impl(cls: schema.Class) -> pathlib.Path:
247+
return pathlib.Path(cls.group or "", cls.name+"Impl").with_suffix(".qll")
248+
249+
242250
def _get_all_properties(cls: schema.Class, lookup: typing.Dict[str, schema.Class],
243251
already_seen: typing.Optional[typing.Set[int]] = None) -> \
244252
typing.Iterable[typing.Tuple[schema.Class, schema.Property]]:
@@ -315,11 +323,14 @@ def _get_stub(cls: schema.Class, base_import: str, generated_import_prefix: str)
315323
else:
316324
accessors = []
317325
return ql.Stub(name=cls.name, base_import=base_import, import_prefix=generated_import_prefix,
318-
doc=cls.doc, synth_accessors=accessors,
319-
internal="ql_internal" in cls.pragmas)
326+
doc=cls.doc, synth_accessors=accessors)
327+
320328

329+
def _get_class_public(cls: schema.Class) -> ql.ClassPublic:
330+
return ql.ClassPublic(name=cls.name, doc=cls.doc, internal="ql_internal" in cls.pragmas)
321331

322-
_stub_qldoc_header = "// the following QLdoc is generated: if you need to edit it, do it in the schema file\n"
332+
333+
_stub_qldoc_header = "// the following QLdoc is generated: if you need to edit it, do it in the schema file\n "
323334

324335
_class_qldoc_re = re.compile(
325336
rf"(?P<qldoc>(?:{re.escape(_stub_qldoc_header)})?/\*\*.*?\*/\s*|^\s*)(?:class\s+(?P<class>\w+))?",
@@ -330,13 +341,13 @@ def _patch_class_qldoc(cls: str, qldoc: str, stub_file: pathlib.Path):
330341
""" Replace or insert `qldoc` as the QLdoc of class `cls` in `stub_file` """
331342
if not qldoc or not stub_file.exists():
332343
return
333-
qldoc = "\n".join(l.rstrip() for l in qldoc.splitlines())
344+
qldoc = "\n ".join(l.rstrip() for l in qldoc.splitlines())
334345
with open(stub_file) as input:
335346
contents = input.read()
336347
for match in _class_qldoc_re.finditer(contents):
337348
if match["class"] == cls:
338349
qldoc_start, qldoc_end = match.span("qldoc")
339-
contents = f"{contents[:qldoc_start]}{_stub_qldoc_header}{qldoc}\n{contents[qldoc_end:]}"
350+
contents = f"{contents[:qldoc_start]}{_stub_qldoc_header}{qldoc}\n {contents[qldoc_end:]}"
340351
tmp = stub_file.with_suffix(f"{stub_file.suffix}.bkp")
341352
with open(tmp, "w") as out:
342353
out.write(contents)
@@ -370,6 +381,8 @@ def generate(opts, renderer):
370381
raise RootElementHasChildren(root)
371382

372383
imports = {}
384+
imports_impl = {}
385+
classes_used_by = {}
373386
generated_import_prefix = get_import(out, opts.root_dir)
374387
registry = opts.generated_registry or pathlib.Path(
375388
os.path.commonpath((out, stub_out, test_out)), ".generated.list")
@@ -382,24 +395,34 @@ def generate(opts, renderer):
382395

383396
classes_by_dir_and_name = sorted(classes.values(), key=lambda cls: (cls.dir, cls.name))
384397
for c in classes_by_dir_and_name:
385-
imports[c.name] = get_import(stub_out / c.path, opts.root_dir)
398+
path = get_import(stub_out / c.path, opts.root_dir)
399+
imports[c.name] = path
400+
imports_impl[c.name + "Impl"] = path + "Impl"
386401

387402
for c in classes.values():
388403
qll = out / c.path.with_suffix(".qll")
389-
c.imports = [imports[t] for t in get_classes_used_by(c)]
404+
c.imports = [imports[t] if t in imports else imports_impl[t] +
405+
"::Impl as " + t for t in get_classes_used_by(c, is_impl=True)]
406+
classes_used_by[c.name] = get_classes_used_by(c, is_impl=False)
390407
c.import_prefix = generated_import_prefix
391408
renderer.render(c, qll)
392409

393410
for c in data.classes.values():
394411
path = _get_path(c)
395-
stub_file = stub_out / path
412+
path_impl = _get_path_impl(c)
413+
stub_file = stub_out / path_impl
396414
base_import = get_import(out / path, opts.root_dir)
397415
stub = _get_stub(c, base_import, generated_import_prefix)
416+
398417
if not renderer.is_customized_stub(stub_file):
399418
renderer.render(stub, stub_file)
400419
else:
401420
qldoc = renderer.render_str(stub, template='ql_stub_class_qldoc')
402421
_patch_class_qldoc(c.name, qldoc, stub_file)
422+
class_public = _get_class_public(c)
423+
class_public_file = stub_out / path
424+
class_public.imports = [imports[t] for t in classes_used_by[c.name]]
425+
renderer.render(class_public, class_public_file)
403426

404427
# for example path/to/elements -> path/to/elements.qll
405428
renderer.render(ql.ImportList([i for name, i in imports.items() if not classes[name].internal]),

misc/codegen/lib/ql.py

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class Class:
101101

102102
name: str
103103
bases: List[Base] = field(default_factory=list)
104+
bases_impl: List[Base] = field(default_factory=list)
104105
final: bool = False
105106
properties: List[Property] = field(default_factory=list)
106107
dir: pathlib.Path = pathlib.Path()
@@ -114,7 +115,9 @@ class Class:
114115
hideable: bool = False
115116

116117
def __post_init__(self):
117-
self.bases = [Base(str(b), str(prev)) for b, prev in zip(self.bases, itertools.chain([""], self.bases))]
118+
def get_bases(bases): return [Base(str(b), str(prev)) for b, prev in zip(bases, itertools.chain([""], bases))]
119+
self.bases = get_bases(self.bases)
120+
self.bases_impl = get_bases(self.bases_impl)
118121
if self.properties:
119122
self.properties[0].first = True
120123

@@ -159,13 +162,26 @@ class Stub:
159162
base_import: str
160163
import_prefix: str
161164
synth_accessors: List[SynthUnderlyingAccessor] = field(default_factory=list)
162-
internal: bool = False
163165
doc: List[str] = field(default_factory=list)
164166

165167
@property
166168
def has_synth_accessors(self) -> bool:
167169
return bool(self.synth_accessors)
168170

171+
@property
172+
def has_qldoc(self) -> bool:
173+
return bool(self.doc)
174+
175+
176+
@dataclass
177+
class ClassPublic:
178+
template: ClassVar = 'ql_class_public'
179+
180+
name: str
181+
imports: List[str] = field(default_factory=list)
182+
internal: bool = False
183+
doc: List[str] = field(default_factory=list)
184+
169185
@property
170186
def has_qldoc(self) -> bool:
171187
return bool(self.doc) or self.internal

misc/codegen/templates/dbscheme.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// generated by {{generator}}
1+
// generated by {{generator}}, do not edit
22
{{#includes}}
33

44
// from {{src}}

misc/codegen/templates/ql_class.mustache

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// generated by {{generator}}
1+
// generated by {{generator}}, do not edit
22
/**
33
* This module provides the generated definition of `{{name}}`.
44
* INTERNAL: Do not import directly.
@@ -9,7 +9,9 @@ private import {{import_prefix}}.Raw
99
{{#imports}}
1010
import {{.}}
1111
{{/imports}}
12-
12+
{{#root}}
13+
private class {{name}}Alias = {{name}};
14+
{{/root}}
1315
/**
1416
* INTERNAL: This module contains the fully generated definition of `{{name}}` and should not
1517
* be referenced directly.
@@ -22,7 +24,7 @@ module Generated {
2224
* INTERNAL: Do not reference the `Generated::{{name}}` class directly.
2325
* Use the subclass `{{name}}`, where the following predicates are available.
2426
*/
25-
class {{name}} extends Synth::T{{name}}{{#bases}}, {{.}}{{/bases}} {
27+
class {{name}} extends Synth::T{{name}}{{#bases_impl}}, {{.}}{{/bases_impl}} {
2628
{{#root}}
2729
/**
2830
* Gets the string representation of this element.
@@ -49,13 +51,13 @@ module Generated {
4951
* Classes can override this to indicate this node should be in the "hidden" AST, mostly reserved
5052
* for conversions and syntactic sugar nodes like parentheses.
5153
*/
52-
{{name}} getResolveStep() { none() } // overridden by subclasses
54+
{{name}}Alias getResolveStep() { none() } // overridden by subclasses
5355

5456
/**
5557
* Gets the element that should substitute this element in the explicit AST, applying `getResolveStep`
5658
* transitively.
5759
*/
58-
final {{name}} resolve() {
60+
final {{name}}Alias resolve() {
5961
not exists(this.getResolveStep()) and result = this
6062
or
6163
result = this.getResolveStep().resolve()
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// generated by {{generator}}, do not edit
2+
/**
3+
* This module provides the public class `{{name}}`.
4+
*/
5+
6+
private import {{name}}Impl
7+
{{#imports}}
8+
import {{.}}
9+
{{/imports}}
10+
11+
{{>ql_stub_class_qldoc}}
12+
final class {{name}} = Impl::{{name}};

misc/codegen/templates/ql_imports.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// generated by {{generator}}
1+
// generated by {{generator}}, do not edit
22
/**
33
* This module exports all modules providing `Element` subclasses.
44
*/

misc/codegen/templates/ql_parent.mustache

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
// generated by {{generator}}
1+
// generated by {{generator}}, do not edit
22
/**
33
* This module provides the generated parent/child relationship.
44
*/

misc/codegen/templates/ql_stub.mustache

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,16 @@ private import {{import_prefix}}.Raw
66
private import {{import_prefix}}.Synth
77
{{/has_synth_accessors}}
88

9-
{{>ql_stub_class_qldoc}}
10-
class {{name}} extends Generated::{{name}} {
9+
/**
10+
* INTERNAL: This module contains the customizable definition of `{{name}}` and should not
11+
* be referenced directly.
12+
*/
13+
module Impl {
14+
{{>ql_stub_class_qldoc}}
15+
class {{name}} extends Generated::{{name}} {
1116
{{#synth_accessors}}
1217
private
1318
cached {{type}} getUnderlying{{argument}}() { this = Synth::T{{name}}({{#constructorparams}}{{^first}},{{/first}}{{param}}{{/constructorparams}})}
1419
{{/synth_accessors}}
15-
}
20+
}
21+
}
Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
/**
22
* This module provides a hand-modifiable wrapper around the generated class `{{name}}`.
3-
{{#internal}}
3+
*
44
* INTERNAL: Do not use.
5-
{{/internal}}
65
*/

0 commit comments

Comments
 (0)