mirror of
https://github.com/github/codeql.git
synced 2026-04-28 18:25:24 +02:00
Swift: add docstring parsing
This commit is contained in:
@@ -2,6 +2,7 @@ from typing import Callable as _Callable, Union as _Union
|
||||
from functools import singledispatch as _singledispatch
|
||||
from swift.codegen.lib import schema as _schema
|
||||
import inspect as _inspect
|
||||
from dataclasses import dataclass as _dataclass
|
||||
|
||||
|
||||
class _ChildModifier(_schema.PropertyModifier):
|
||||
@@ -11,6 +12,14 @@ class _ChildModifier(_schema.PropertyModifier):
|
||||
prop.is_child = True
|
||||
|
||||
|
||||
@_dataclass
|
||||
class _DescModifier(_schema.PropertyModifier):
|
||||
description: str
|
||||
|
||||
def modify(self, prop: _schema.Property):
|
||||
prop.doc = _schema.split_doc(self.description)
|
||||
|
||||
|
||||
def include(source: str):
|
||||
# add to `includes` variable in calling context
|
||||
_inspect.currentframe().f_back.f_locals.setdefault(
|
||||
@@ -97,6 +106,7 @@ optional = _TypeModifier(_Optionalizer())
|
||||
list = _TypeModifier(_Listifier())
|
||||
|
||||
child = _ChildModifier()
|
||||
desc = _DescModifier
|
||||
|
||||
qltest = _Namespace(
|
||||
skip=_Pragma("qltest_skip"),
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
""" schema.yml format representation """
|
||||
import pathlib
|
||||
import re
|
||||
import types
|
||||
import typing
|
||||
from dataclasses import dataclass, field
|
||||
@@ -35,6 +36,8 @@ class Property:
|
||||
type: Optional[str] = None
|
||||
is_child: bool = False
|
||||
pragmas: List[str] = field(default_factory=list)
|
||||
doc_name: Optional[str] = None
|
||||
doc: List[str] = field(default_factory=list)
|
||||
|
||||
@property
|
||||
def is_single(self) -> bool:
|
||||
@@ -76,6 +79,7 @@ class Class:
|
||||
group: str = ""
|
||||
pragmas: List[str] = field(default_factory=list)
|
||||
ipa: Optional[IpaInfo] = None
|
||||
doc: List[str] = field(default_factory=list)
|
||||
|
||||
@property
|
||||
def final(self):
|
||||
@@ -154,6 +158,27 @@ class PropertyModifier:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
def split_doc(doc):
|
||||
# implementation inspired from https://peps.python.org/pep-0257/
|
||||
if not doc:
|
||||
return []
|
||||
lines = doc.splitlines()
|
||||
# Determine minimum indentation (first line doesn't count):
|
||||
strippedlines = (line.lstrip() for line in lines[1:])
|
||||
indents = [len(line) - len(stripped) for line, stripped in zip(lines[1:], strippedlines) if stripped]
|
||||
# Remove indentation (first line is special):
|
||||
trimmed = [lines[0].strip()]
|
||||
if indents:
|
||||
indent = min(indents)
|
||||
trimmed.extend(line[indent:].rstrip() for line in lines[1:])
|
||||
# Strip off trailing and leading blank lines:
|
||||
while trimmed and not trimmed[-1]:
|
||||
trimmed.pop()
|
||||
while trimmed and not trimmed[0]:
|
||||
trimmed.pop(0)
|
||||
return trimmed
|
||||
|
||||
|
||||
@dataclass
|
||||
class _PropertyNamer(PropertyModifier):
|
||||
name: str
|
||||
@@ -181,6 +206,7 @@ def _get_class(cls: type) -> Class:
|
||||
a | _PropertyNamer(n)
|
||||
for n, a in cls.__dict__.get("__annotations__", {}).items()
|
||||
],
|
||||
doc=split_doc(cls.__doc__),
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -141,6 +141,7 @@ def test_no_mixed_groups_in_bases():
|
||||
class D(B, C):
|
||||
pass
|
||||
|
||||
|
||||
#
|
||||
|
||||
|
||||
@@ -409,5 +410,137 @@ def test_ipa_class_on_dangling():
|
||||
pass
|
||||
|
||||
|
||||
def test_class_docstring():
|
||||
@schema.load
|
||||
class data:
|
||||
class A:
|
||||
"""Very important class."""
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', doc=["Very important class."])
|
||||
}
|
||||
|
||||
|
||||
def test_property_docstring():
|
||||
@schema.load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.desc("very important property.")
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[schema.SingleProperty('x', 'int', doc=["very important property."])])
|
||||
}
|
||||
|
||||
|
||||
def test_class_docstring_newline():
|
||||
@schema.load
|
||||
class data:
|
||||
class A:
|
||||
"""Very important
|
||||
class."""
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', doc=["Very important", "class."])
|
||||
}
|
||||
|
||||
|
||||
def test_property_docstring_newline():
|
||||
@schema.load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.desc("""very important
|
||||
property.""")
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[schema.SingleProperty('x', 'int', doc=["very important", "property."])])
|
||||
}
|
||||
|
||||
|
||||
def test_class_docstring_stripped():
|
||||
@schema.load
|
||||
class data:
|
||||
class A:
|
||||
"""
|
||||
|
||||
Very important class.
|
||||
|
||||
"""
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', doc=["Very important class."])
|
||||
}
|
||||
|
||||
|
||||
def test_property_docstring_stripped():
|
||||
@schema.load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.desc("""
|
||||
|
||||
very important property.
|
||||
|
||||
""")
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[schema.SingleProperty('x', 'int', doc=["very important property."])])
|
||||
}
|
||||
|
||||
|
||||
def test_class_docstring_split():
|
||||
@schema.load
|
||||
class data:
|
||||
class A:
|
||||
"""Very important class.
|
||||
|
||||
As said, very important."""
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', doc=["Very important class.", "", "As said, very important."])
|
||||
}
|
||||
|
||||
|
||||
def test_property_docstring_split():
|
||||
@schema.load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.desc("""very important property.
|
||||
|
||||
Very very important.""")
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[
|
||||
schema.SingleProperty('x', 'int', doc=["very important property.", "", "Very very important."])])
|
||||
}
|
||||
|
||||
|
||||
def test_class_docstring_indent():
|
||||
@schema.load
|
||||
class data:
|
||||
class A:
|
||||
"""
|
||||
Very important class.
|
||||
As said, very important.
|
||||
"""
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', doc=["Very important class.", " As said, very important."])
|
||||
}
|
||||
|
||||
|
||||
def test_property_docstring_indent():
|
||||
@schema.load
|
||||
class data:
|
||||
class A:
|
||||
x: int | defs.desc("""
|
||||
very important property.
|
||||
Very very important.
|
||||
""")
|
||||
|
||||
assert data.classes == {
|
||||
'A': schema.Class('A', properties=[
|
||||
schema.SingleProperty('x', 'int', doc=["very important property.", " Very very important."])])
|
||||
}
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
sys.exit(pytest.main([__file__] + sys.argv[1:]))
|
||||
|
||||
Reference in New Issue
Block a user