tools: enforce restriction on YAML

Some consumers have reported difficulty parsing an uncommon YAML
construction [1] [2] [3]. Extend the linter to help ensure that
construction is not used in future contributions.

[1] https://github.com/tc39/test262/issues/1997
[2] https://github.com/tc39/test262/pull/2505
[3] https://github.com/tc39/test262/issues/3171
This commit is contained in:
Mike Pennisi 2021-09-03 14:16:21 -04:00 committed by Rick Waldron
parent 2b01f137ef
commit c4e54648c0
4 changed files with 45 additions and 2 deletions

View File

@ -94,7 +94,9 @@ The code must be a BSD or BSD-style compatible with the license of this project.
### Frontmatter ### Frontmatter
The Test262 frontmatter is a string of [YAML](https://en.wikipedia.org/wiki/YAML) enclosed by the comment start tag `/*---` and end tag `---*/`. There must be exactly one Frontmatter per test. The Test262 frontmatter is a string of [YAML](https://en.wikipedia.org/wiki/YAML) enclosed by the comment start tag `/*---` and end tag `---*/`. In order to simplify parsing, scalar values spanning multiple lines may not be expressed using "flow" notation.
There must be exactly one Frontmatter per test.
Test262 supports the following keys: Test262 supports the following keys:

View File

@ -1,3 +1,5 @@
import yaml
from ..check import Check from ..check import Check
_REQUIRED_FIELDS = set(['description']) _REQUIRED_FIELDS = set(['description'])
@ -20,6 +22,14 @@ class CheckFrontmatter(Check):
if meta is None: if meta is None:
return 'No valid YAML-formatted frontmatter' return 'No valid YAML-formatted frontmatter'
for parsing_event in meta.parsing_events:
if not isinstance(parsing_event, yaml.ScalarEvent):
continue
if parsing_event.style is not None:
continue
if parsing_event.start_mark.line != parsing_event.end_mark.line:
return 'YAML multiline scalar values in flow notation are disallowed (use "|" or ">")'
fields = set(meta.keys()) fields = set(meta.keys())
missing = _REQUIRED_FIELDS - fields missing = _REQUIRED_FIELDS - fields

View File

@ -1,6 +1,23 @@
import re import re
import yaml import yaml
class Result(dict):
def __init__(self, meta, events):
self.parsing_events = events
super(Result, self).__init__(**meta)
class MyLoader(yaml.SafeLoader):
events = None
def __init__(self, *args, **kwargs):
MyLoader.events = []
super(MyLoader, self).__init__(*args, **kwargs)
def get_event(self):
event = super(MyLoader, self).get_event()
MyLoader.events.append(event)
return event
def parse(src): def parse(src):
'''Parse the YAML-formatted metadata found in a given string of source '''Parse the YAML-formatted metadata found in a given string of source
code. Tolerate missing or invalid metadata; those conditions are handled by code. Tolerate missing or invalid metadata; those conditions are handled by
@ -11,6 +28,6 @@ def parse(src):
return None return None
try: try:
return yaml.safe_load(match.group(1)) return Result(yaml.load(match.group(1), MyLoader), MyLoader.events)
except (yaml.scanner.ScannerError, yaml.parser.ParserError): except (yaml.scanner.ScannerError, yaml.parser.ParserError):
return None return None

View File

@ -0,0 +1,14 @@
FRONTMATTER
^ expected errors | v input
// Copyright (C) 2021 the V8 project authors. All rights reserved.
// This code is governed by the BSD license found in the LICENSE file.
/*---
description: YAML supports mutli-line scalar values in flow notation, but
some consumers of Test262 do not, so they should be disallowed.
esid: sec-assignment-operators-static-semantics-early-errors
info: This is some information
---*/
function* g() {
yield 23;
}