mirror of
https://github.com/github/codeql.git
synced 2026-04-28 02:05:14 +02:00
Codegen: allow parametrized pragmas
This commit is contained in:
@@ -32,11 +32,15 @@ class Property:
|
||||
name: Optional[str] = None
|
||||
type: Optional[str] = None
|
||||
is_child: bool = False
|
||||
pragmas: List[str] = field(default_factory=list)
|
||||
pragmas: List[str] | Dict[str, object] = field(default_factory=dict)
|
||||
doc: Optional[str] = None
|
||||
description: List[str] = field(default_factory=list)
|
||||
synth: bool = False
|
||||
|
||||
def __post_init__(self):
|
||||
if not isinstance(self.pragmas, dict):
|
||||
self.pragmas = dict.fromkeys(self.pragmas, None)
|
||||
|
||||
@property
|
||||
def is_single(self) -> bool:
|
||||
return self.kind == self.Kind.SINGLE
|
||||
@@ -88,7 +92,7 @@ class Class:
|
||||
derived: Set[str] = field(default_factory=set)
|
||||
properties: List[Property] = field(default_factory=list)
|
||||
group: str = ""
|
||||
pragmas: List[str] = field(default_factory=list)
|
||||
pragmas: List[str] | Dict[str, object] = field(default_factory=dict)
|
||||
synth: Optional[Union[SynthInfo, bool]] = None
|
||||
"""^^^ filled with `True` for non-final classes with only synthesized final descendants """
|
||||
doc: List[str] = field(default_factory=list)
|
||||
@@ -97,6 +101,10 @@ class Class:
|
||||
test_with: Optional[str] = None
|
||||
rust_doc_test_function: Optional["FunctionInfo"] = "() -> ()" # TODO: parametrized pragmas
|
||||
|
||||
def __post_init__(self):
|
||||
if not isinstance(self.pragmas, dict):
|
||||
self.pragmas = dict.fromkeys(self.pragmas, None)
|
||||
|
||||
@property
|
||||
def final(self):
|
||||
return not self.derived
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
from typing import Callable as _Callable, List as _List
|
||||
from typing import Callable as _Callable, Dict as _Dict
|
||||
from misc.codegen.lib import schema as _schema
|
||||
import inspect as _inspect
|
||||
from dataclasses import dataclass as _dataclass
|
||||
@@ -94,6 +94,7 @@ class _Pragma(_schema.PropertyModifier):
|
||||
For schema classes it acts as a python decorator with `@`.
|
||||
"""
|
||||
pragma: str
|
||||
value: object = None
|
||||
remove: bool = False
|
||||
|
||||
def __post_init__(self):
|
||||
@@ -108,20 +109,32 @@ class _Pragma(_schema.PropertyModifier):
|
||||
|
||||
def __call__(self, cls: type) -> type:
|
||||
""" use this pragma as a decorator on classes """
|
||||
if "_pragmas" in cls.__dict__: # not using hasattr as we don't want to land on inherited pragmas
|
||||
self._apply(cls._pragmas)
|
||||
elif not self.remove:
|
||||
cls._pragmas = [self.pragma]
|
||||
# not using hasattr as we don't want to land on inherited pragmas
|
||||
if "_pragmas" not in cls.__dict__:
|
||||
cls._pragmas = {}
|
||||
self._apply(cls._pragmas)
|
||||
return cls
|
||||
|
||||
def _apply(self, pragmas: _List[str]) -> None:
|
||||
def _apply(self, pragmas: _Dict[str, object]) -> None:
|
||||
if self.remove:
|
||||
try:
|
||||
pragmas.remove(self.pragma)
|
||||
except ValueError:
|
||||
pass
|
||||
pragmas.pop(self.pragma, None)
|
||||
else:
|
||||
pragmas.append(self.pragma)
|
||||
pragmas[self.pragma] = self.value
|
||||
|
||||
|
||||
@_dataclass
|
||||
class _ParametrizedPragma:
|
||||
""" A class or property parametrized pragma.
|
||||
Needs to be applied to a parameter to give a pragma.
|
||||
"""
|
||||
pragma: str
|
||||
function: _Callable[[...], object] = None
|
||||
|
||||
def __call__(self, *args, **kwargs) -> _Pragma:
|
||||
return _Pragma(self.pragma, value=self.function(*args, **kwargs))
|
||||
|
||||
def __invert__(self) -> _Pragma:
|
||||
return _Pragma(self.pragma, remove=True)
|
||||
|
||||
|
||||
class _Optionalizer(_schema.PropertyModifier):
|
||||
@@ -251,9 +264,9 @@ def annotate(annotated_cls: type) -> _Callable[[type], _PropertyAnnotation]:
|
||||
if cls.__doc__ is not None:
|
||||
annotated_cls.__doc__ = cls.__doc__
|
||||
old_pragmas = getattr(annotated_cls, "_pragmas", None)
|
||||
new_pragmas = getattr(cls, "_pragmas", [])
|
||||
new_pragmas = getattr(cls, "_pragmas", {})
|
||||
if old_pragmas:
|
||||
old_pragmas.extend(new_pragmas)
|
||||
old_pragmas.update(new_pragmas)
|
||||
else:
|
||||
annotated_cls._pragmas = new_pragmas
|
||||
for a, v in cls.__dict__.items():
|
||||
|
||||
@@ -49,7 +49,7 @@ def _get_class(cls: type) -> schema.Class:
|
||||
hideable=getattr(cls, "_hideable", False),
|
||||
test_with=_get_name(getattr(cls, "_test_with", None)),
|
||||
# in the following we don't use `getattr` to avoid inheriting
|
||||
pragmas=cls.__dict__.get("_pragmas", []),
|
||||
pragmas=cls.__dict__.get("_pragmas", {}),
|
||||
synth=cls.__dict__.get("_synth", None),
|
||||
properties=[
|
||||
a | _PropertyNamer(n)
|
||||
|
||||
@@ -798,8 +798,7 @@ def test_annotate_decorations():
|
||||
pass
|
||||
|
||||
assert data.classes == {
|
||||
"Root": schema.Class("Root", hideable=True,
|
||||
pragmas=["qltest_skip", "cpp_skip", "qltest_collapse_hierarchy"]),
|
||||
"Root": schema.Class("Root", hideable=True, pragmas=["qltest_skip", "cpp_skip", "qltest_collapse_hierarchy"]),
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user