Skip to content

Commit 998a75e

Browse files
authored
Merge pull request #9690 from github/redsun82/swift-stop-stub-reverts
Swift: prevent accidental revert of modified stub
2 parents a6ae6cf + ad38cf2 commit 998a75e

File tree

2 files changed

+43
-9
lines changed

2 files changed

+43
-9
lines changed

swift/codegen/generators/qlgen.py

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,16 @@
1212
log = logging.getLogger(__name__)
1313

1414

15-
class FormatError(Exception):
15+
class Error(Exception):
16+
def __str__(self):
17+
return self.args[0]
18+
19+
20+
class FormatError(Error):
21+
pass
22+
23+
24+
class ModifiedStubMarkedAsGeneratedError(Error):
1625
pass
1726

1827

@@ -84,11 +93,22 @@ def get_classes_used_by(cls: ql.Class):
8493
return sorted(set(t for t in get_types_used_by(cls) if t[0].isupper()))
8594

8695

87-
def is_generated(file):
96+
def _is_generated_stub(file, check_modification=False):
8897
with open(file) as contents:
8998
for line in contents:
90-
return line.startswith("// generated")
91-
return False
99+
if not line.startswith("// generated"):
100+
return False
101+
break
102+
else:
103+
# no lines
104+
return False
105+
if check_modification:
106+
# one line already read, if we can read 4 other we are past the normal stub generation
107+
line_threshold = 4
108+
if sum(1 for _ in zip(range(line_threshold), contents)) == line_threshold:
109+
raise ModifiedStubMarkedAsGeneratedError(
110+
f"{file.name} stub was modified but is still marked as generated")
111+
return True
92112

93113

94114
def format(codeql, files):
@@ -138,7 +158,7 @@ def generate(opts, renderer):
138158
test_out = opts.ql_test_output
139159
missing_test_source_filename = "MISSING_SOURCE.txt"
140160
existing = {q for q in out.rglob("*.qll")}
141-
existing |= {q for q in stub_out.rglob("*.qll") if is_generated(q)}
161+
existing |= {q for q in stub_out.rglob("*.qll") if _is_generated_stub(q, check_modification=True)}
142162
existing |= {q for q in test_out.rglob("*.ql")}
143163
existing |= {q for q in test_out.rglob(missing_test_source_filename)}
144164

@@ -157,7 +177,7 @@ def generate(opts, renderer):
157177
c.imports = [imports[t] for t in get_classes_used_by(c)]
158178
renderer.render(c, qll)
159179
stub_file = stub_out / c.path.with_suffix(".qll")
160-
if not stub_file.is_file() or is_generated(stub_file):
180+
if not stub_file.is_file() or _is_generated_stub(stub_file):
161181
stub = ql.Stub(
162182
name=c.name, base_import=get_import(qll, opts.swift_dir))
163183
renderer.render(stub, stub_file)

swift/codegen/test/test_qlgen.py

Lines changed: 17 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import subprocess
33
import sys
44

5+
import pytest
6+
57
from swift.codegen.generators import qlgen
68
from swift.codegen.lib import ql
79
from swift.codegen.test.utils import *
@@ -36,17 +38,22 @@ def children_file(): return ql_output_path() / "GetImmediateParent.qll"
3638

3739

3840
@pytest.fixture
39-
def generate(input, opts, renderer):
41+
def qlgen_opts(opts):
4042
opts.ql_stub_output = stub_path()
4143
opts.ql_output = ql_output_path()
4244
opts.ql_test_output = ql_test_output_path()
4345
opts.ql_format = True
4446
opts.swift_dir = paths.swift_dir
47+
return opts
48+
49+
50+
@pytest.fixture
51+
def generate(input, qlgen_opts, renderer):
4552
renderer.written = []
4653

4754
def func(classes):
4855
input.classes = classes
49-
return run_generation(qlgen.generate, opts, renderer)
56+
return run_generation(qlgen.generate, qlgen_opts, renderer)
5057

5158
return func
5259

@@ -349,7 +356,7 @@ def test_empty_cleanup(generate, renderer):
349356
assert renderer.mock_calls[-1] == mock.call.cleanup(set())
350357

351358

352-
def test_non_empty_cleanup(opts, generate, renderer, tmp_path):
359+
def test_non_empty_cleanup(opts, generate, renderer):
353360
ql_a = opts.ql_output / "A.qll"
354361
ql_b = opts.ql_output / "B.qll"
355362
stub_a = opts.ql_stub_output / "A.qll"
@@ -369,6 +376,13 @@ def test_non_empty_cleanup(opts, generate, renderer, tmp_path):
369376
{ql_a, ql_b, stub_a, test_a, test_b})
370377

371378

379+
def test_modified_stub_still_generated(qlgen_opts, renderer):
380+
stub = qlgen_opts.ql_stub_output / "A.qll"
381+
write(stub, "// generated\n\n\n\nsomething\n")
382+
with pytest.raises(qlgen.ModifiedStubMarkedAsGeneratedError):
383+
run_generation(qlgen.generate, qlgen_opts, renderer)
384+
385+
372386
def test_test_missing_source(generate_tests):
373387
generate_tests([
374388
schema.Class("A"),

0 commit comments

Comments
 (0)