cseq 0.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- cseq/__init__.py +9 -0
- cseq/acceptance.py +162 -0
- cseq/api.py +328 -0
- cseq/binary.py +122 -0
- cseq/cache.py +420 -0
- cseq/cli.py +895 -0
- cseq/compile_db.py +98 -0
- cseq/config.py +138 -0
- cseq/cpu_rules.py +48 -0
- cseq/dependencies.py +202 -0
- cseq/docs.py +21 -0
- cseq/dwarf.py +115 -0
- cseq/event_store.py +378 -0
- cseq/explain.py +113 -0
- cseq/fast_parser.py +286 -0
- cseq/html.py +485 -0
- cseq/html_bundle.py +464 -0
- cseq/hybrid_parser.py +120 -0
- cseq/linker.py +117 -0
- cseq/marker.py +294 -0
- cseq/model.py +367 -0
- cseq/parser.py +797 -0
- cseq/parser_contract_cases.json +69 -0
- cseq/parser_dependency_lock.json +59 -0
- cseq/parser_environment.py +186 -0
- cseq/parser_migration.py +126 -0
- cseq/plugin.py +216 -0
- cseq/project.py +305 -0
- cseq/query.py +62 -0
- cseq/resources/README.md +171 -0
- cseq/resources/design.md +6197 -0
- cseq/resources/verification_report.html +26 -0
- cseq/runtime.py +177 -0
- cseq/runtime_address.py +79 -0
- cseq/runtime_cpu.py +87 -0
- cseq/scanner.py +54 -0
- cseq/sequence.py +281 -0
- cseq/server.py +104 -0
- cseq/source_index.py +227 -0
- cseq/static_store.py +280 -0
- cseq/trace_analysis.py +336 -0
- cseq/trace_diff.py +56 -0
- cseq/tree_sitter_parser.py +468 -0
- cseq/valueflow.py +179 -0
- cseq-0.0.1.dist-info/METADATA +181 -0
- cseq-0.0.1.dist-info/RECORD +49 -0
- cseq-0.0.1.dist-info/WHEEL +5 -0
- cseq-0.0.1.dist-info/entry_points.txt +2 -0
- cseq-0.0.1.dist-info/top_level.txt +1 -0
cseq/model.py
ADDED
|
@@ -0,0 +1,367 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from dataclasses import dataclass, field
|
|
4
|
+
from array import array
|
|
5
|
+
from enum import Enum
|
|
6
|
+
from pathlib import Path
|
|
7
|
+
from typing import Iterable, Any
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class ParseStatus(str, Enum):
|
|
11
|
+
OK = "OK"
|
|
12
|
+
PARTIAL = "PARTIAL"
|
|
13
|
+
ERROR_RECOVERED = "ERROR_RECOVERED"
|
|
14
|
+
OPAQUE_REGION = "OPAQUE_REGION"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, slots=True)
|
|
18
|
+
class SourceFile:
|
|
19
|
+
path: Path
|
|
20
|
+
project_relative_path: str
|
|
21
|
+
suffix: str
|
|
22
|
+
size_bytes: int
|
|
23
|
+
content_hash: str
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
@dataclass(frozen=True, slots=True)
|
|
27
|
+
class TranslationUnit:
|
|
28
|
+
source: SourceFile
|
|
29
|
+
arguments: tuple[str, ...] = ()
|
|
30
|
+
working_directory: Path | None = None
|
|
31
|
+
configuration_name: str | None = None
|
|
32
|
+
# Ephemeral per-TU source text. Project may populate this so dependency
|
|
33
|
+
# fingerprinting and the fast parser share one file read. It is never part
|
|
34
|
+
# of persistent IR/cache identity and is released with the parse artifact.
|
|
35
|
+
source_text: str | None = None
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@dataclass(frozen=True, slots=True)
|
|
39
|
+
class SourceRange:
|
|
40
|
+
start_line: int | None = None
|
|
41
|
+
start_column: int | None = None
|
|
42
|
+
end_line: int | None = None
|
|
43
|
+
end_column: int | None = None
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@dataclass(frozen=True, slots=True)
|
|
47
|
+
class FunctionRecord:
|
|
48
|
+
name: str
|
|
49
|
+
qualified_id: str
|
|
50
|
+
source_path: str
|
|
51
|
+
source_range: SourceRange
|
|
52
|
+
storage_class: str | None = None
|
|
53
|
+
type_qualifier: str | None = None
|
|
54
|
+
parameters: tuple[str, ...] = ()
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dataclass(frozen=True, slots=True)
|
|
60
|
+
class CompactFunctionRecord:
|
|
61
|
+
"""Memory-lean FunctionRecord-compatible view for conservative fast parsing.
|
|
62
|
+
|
|
63
|
+
The stable qualified id and SourceRange are derived lazily instead of being
|
|
64
|
+
stored for every function. This materially reduces peak memory on
|
|
65
|
+
million-function projects while keeping the same public attribute contract.
|
|
66
|
+
"""
|
|
67
|
+
name: str
|
|
68
|
+
source_path: str
|
|
69
|
+
start_line: int
|
|
70
|
+
start_column: int
|
|
71
|
+
end_line: int
|
|
72
|
+
end_column: int
|
|
73
|
+
storage_class: str | None = None
|
|
74
|
+
type_qualifier: str | None = None
|
|
75
|
+
parameters: tuple[str, ...] = ()
|
|
76
|
+
|
|
77
|
+
@property
|
|
78
|
+
def source_range(self) -> SourceRange:
|
|
79
|
+
return SourceRange(self.start_line, self.start_column, self.end_line, self.end_column)
|
|
80
|
+
|
|
81
|
+
@property
|
|
82
|
+
def qualified_id(self) -> str:
|
|
83
|
+
return (
|
|
84
|
+
f"{self.source_path}:{self.start_line}:{self.start_column}:"
|
|
85
|
+
f"{self.storage_class or 'external'}:{self.name}:{self.type_qualifier or '?'}"
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class CompactFunctionTable:
|
|
90
|
+
"""Columnar storage for fast-parser function metadata.
|
|
91
|
+
|
|
92
|
+
Records are materialized only when a caller iterates/addresses them. The
|
|
93
|
+
table avoids retaining one Python object and one SourceRange per function.
|
|
94
|
+
"""
|
|
95
|
+
__slots__ = (
|
|
96
|
+
"source_path", "names", "start_lines", "start_columns",
|
|
97
|
+
"end_lines", "end_columns", "meta_ids", "meta", "_meta_index"
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def __init__(self, source_path: str) -> None:
|
|
101
|
+
self.source_path = source_path
|
|
102
|
+
self.names: list[str] = []
|
|
103
|
+
self.start_lines = array("I")
|
|
104
|
+
self.start_columns = array("I")
|
|
105
|
+
self.end_lines = array("I")
|
|
106
|
+
self.end_columns = array("I")
|
|
107
|
+
self.meta_ids = array("I")
|
|
108
|
+
self.meta: list[tuple[str | None, str | None, tuple[str, ...]]] = []
|
|
109
|
+
self._meta_index: dict[tuple[str | None, str | None, tuple[str, ...]], int] = {}
|
|
110
|
+
|
|
111
|
+
def append_fields(
|
|
112
|
+
self, name: str, start_line: int, start_column: int, end_line: int, end_column: int,
|
|
113
|
+
storage_class: str | None, type_qualifier: str | None, parameters: tuple[str, ...],
|
|
114
|
+
) -> None:
|
|
115
|
+
key = (storage_class, type_qualifier, parameters)
|
|
116
|
+
meta_id = self._meta_index.get(key)
|
|
117
|
+
if meta_id is None:
|
|
118
|
+
meta_id = len(self.meta)
|
|
119
|
+
self.meta.append(key)
|
|
120
|
+
self._meta_index[key] = meta_id
|
|
121
|
+
self.names.append(name)
|
|
122
|
+
self.start_lines.append(start_line or 0)
|
|
123
|
+
self.start_columns.append(start_column or 0)
|
|
124
|
+
self.end_lines.append(end_line or 0)
|
|
125
|
+
self.end_columns.append(end_column or 0)
|
|
126
|
+
self.meta_ids.append(meta_id)
|
|
127
|
+
|
|
128
|
+
def __len__(self) -> int:
|
|
129
|
+
return len(self.names)
|
|
130
|
+
|
|
131
|
+
def __getitem__(self, index: int) -> CompactFunctionRecord:
|
|
132
|
+
if index < 0:
|
|
133
|
+
index += len(self.names)
|
|
134
|
+
storage, type_qualifier, parameters = self.meta[self.meta_ids[index]]
|
|
135
|
+
return CompactFunctionRecord(
|
|
136
|
+
self.names[index], self.source_path,
|
|
137
|
+
self.start_lines[index], self.start_columns[index],
|
|
138
|
+
self.end_lines[index], self.end_columns[index],
|
|
139
|
+
storage, type_qualifier, parameters,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
def __iter__(self):
|
|
143
|
+
for i in range(len(self.names)):
|
|
144
|
+
yield self[i]
|
|
145
|
+
|
|
146
|
+
def iter_named(self, names: set[str]):
|
|
147
|
+
if not names:
|
|
148
|
+
return
|
|
149
|
+
for i, name in enumerate(self.names):
|
|
150
|
+
if name in names:
|
|
151
|
+
yield self[i]
|
|
152
|
+
|
|
153
|
+
def index_of_qid(self, qid: str) -> int:
|
|
154
|
+
# Artifacts are intentionally small enough (normally hundreds of
|
|
155
|
+
# functions/TU) that a local scan is cheaper than retaining a second
|
|
156
|
+
# project-wide qualified-id dictionary.
|
|
157
|
+
for i in range(len(self.names)):
|
|
158
|
+
if self[i].qualified_id == qid:
|
|
159
|
+
return i
|
|
160
|
+
return -1
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@dataclass(frozen=True, slots=True)
|
|
164
|
+
class Diagnostic:
|
|
165
|
+
severity: str
|
|
166
|
+
message: str
|
|
167
|
+
raw: str | None = None
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
class ValueSlotKind(str, Enum):
|
|
171
|
+
FUNCTION = "FUNCTION"
|
|
172
|
+
LOCAL = "LOCAL"
|
|
173
|
+
PARAMETER = "PARAMETER"
|
|
174
|
+
STRUCT_FIELD = "STRUCT_FIELD"
|
|
175
|
+
GLOBAL = "GLOBAL"
|
|
176
|
+
GLOBAL_ARRAY_ELEMENT = "GLOBAL_ARRAY_ELEMENT"
|
|
177
|
+
GLOBAL_ARRAY_SUMMARY = "GLOBAL_ARRAY_SUMMARY"
|
|
178
|
+
HEAP_ALLOCATION = "HEAP_ALLOCATION"
|
|
179
|
+
UNION_SUMMARY = "UNION_SUMMARY"
|
|
180
|
+
POINTER_MEMORY = "POINTER_MEMORY"
|
|
181
|
+
UNKNOWN = "UNKNOWN"
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
@dataclass(frozen=True, slots=True)
|
|
185
|
+
class ValueSlot:
|
|
186
|
+
key: str
|
|
187
|
+
kind: ValueSlotKind
|
|
188
|
+
name: str
|
|
189
|
+
function_id: str | None = None
|
|
190
|
+
field_name: str | None = None
|
|
191
|
+
index: int | None = None
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class FlowEdgeKind(str, Enum):
|
|
195
|
+
ASSIGN = "ASSIGN"
|
|
196
|
+
INIT = "INIT"
|
|
197
|
+
ARG_TO_PARAM = "ARG_TO_PARAM"
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
@dataclass(frozen=True, slots=True)
|
|
201
|
+
class FlowEdge:
|
|
202
|
+
source_key: str
|
|
203
|
+
target_key: str
|
|
204
|
+
kind: FlowEdgeKind
|
|
205
|
+
source_path: str | None = None
|
|
206
|
+
source_range: SourceRange = SourceRange()
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
class CallTargetKind(str, Enum):
|
|
210
|
+
INTERNAL_EXACT = "INTERNAL_EXACT"
|
|
211
|
+
INTERNAL_POSSIBLE = "INTERNAL_POSSIBLE"
|
|
212
|
+
INDIRECT_EXACT = "INDIRECT_EXACT"
|
|
213
|
+
INDIRECT_POSSIBLE = "INDIRECT_POSSIBLE"
|
|
214
|
+
EXTERNAL_DECLARED = "EXTERNAL_DECLARED"
|
|
215
|
+
UNKNOWN = "UNKNOWN"
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
@dataclass(frozen=True, slots=True)
|
|
219
|
+
class CallSiteRecord:
|
|
220
|
+
caller_id: str
|
|
221
|
+
caller_name: str
|
|
222
|
+
callee_name: str | None
|
|
223
|
+
raw_text: str
|
|
224
|
+
source_path: str
|
|
225
|
+
source_range: SourceRange
|
|
226
|
+
target_kind: CallTargetKind = CallTargetKind.UNKNOWN
|
|
227
|
+
target_function_id: str | None = None
|
|
228
|
+
target_function_ids: tuple[str, ...] = ()
|
|
229
|
+
unknown_possible: bool = False
|
|
230
|
+
callee_slot_key: str | None = None
|
|
231
|
+
argument_slot_keys: tuple[str | None, ...] = ()
|
|
232
|
+
control_context: tuple[str, ...] = ()
|
|
233
|
+
macro_expanded: bool = False
|
|
234
|
+
spelling_range: SourceRange | None = None
|
|
235
|
+
expansion_range: SourceRange | None = None
|
|
236
|
+
guard_expression: str | None = None
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
class CpuEvidenceLayer(str, Enum):
|
|
240
|
+
STATIC_AFFINITY = "STATIC_AFFINITY"
|
|
241
|
+
BUILD_DOMAIN = "BUILD_DOMAIN"
|
|
242
|
+
OBSERVED_RUNTIME_DOMAIN = "OBSERVED_RUNTIME_DOMAIN"
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
class CpuValueKind(str, Enum):
|
|
246
|
+
CPU = "CPU"
|
|
247
|
+
CPU_GROUP = "CPU_GROUP"
|
|
248
|
+
UNKNOWN = "UNKNOWN"
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
@dataclass(frozen=True, slots=True)
|
|
252
|
+
class CpuEvidenceRecord:
|
|
253
|
+
target_function_id: str
|
|
254
|
+
layer: CpuEvidenceLayer
|
|
255
|
+
value_kind: CpuValueKind
|
|
256
|
+
value: str
|
|
257
|
+
provenance: str
|
|
258
|
+
explicit_override: bool = False
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
@dataclass(frozen=True, slots=True)
|
|
262
|
+
class LinkerEvidenceRecord:
|
|
263
|
+
symbol_name: str
|
|
264
|
+
address: int
|
|
265
|
+
object_file: str | None
|
|
266
|
+
section: str | None
|
|
267
|
+
matched_function_ids: tuple[str, ...] = ()
|
|
268
|
+
provenance: str = "linker-map"
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
@dataclass(frozen=True, slots=True)
|
|
272
|
+
class BinaryEvidenceRecord:
|
|
273
|
+
symbol_name: str
|
|
274
|
+
address: int
|
|
275
|
+
size: int
|
|
276
|
+
symbol_kind: str
|
|
277
|
+
matched_function_ids: tuple[str, ...] = ()
|
|
278
|
+
binary_hash: str = ""
|
|
279
|
+
provenance: str = "binary"
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
@dataclass(frozen=True, slots=True)
|
|
283
|
+
class DwarfEvidenceRecord:
|
|
284
|
+
function_id: str
|
|
285
|
+
source_path: str
|
|
286
|
+
line: int
|
|
287
|
+
address: int
|
|
288
|
+
dwarf_file: str
|
|
289
|
+
provenance: str = "dwarf"
|
|
290
|
+
|
|
291
|
+
|
|
292
|
+
@dataclass(frozen=True, slots=True)
|
|
293
|
+
class GuardedSourceRegion:
|
|
294
|
+
source_path: str
|
|
295
|
+
source_range: SourceRange
|
|
296
|
+
guard_expression: str
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
@dataclass(slots=True)
|
|
300
|
+
class FileParseArtifact:
|
|
301
|
+
translation_unit: TranslationUnit
|
|
302
|
+
status: ParseStatus
|
|
303
|
+
functions: list[FunctionRecord | CompactFunctionRecord] | CompactFunctionTable = field(default_factory=list)
|
|
304
|
+
callsites: list[CallSiteRecord] = field(default_factory=list)
|
|
305
|
+
value_slots: list[ValueSlot] = field(default_factory=list)
|
|
306
|
+
flow_edges: list[FlowEdge] = field(default_factory=list)
|
|
307
|
+
diagnostics: list[Diagnostic] = field(default_factory=list)
|
|
308
|
+
opaque_regions: list[SourceRange] = field(default_factory=list)
|
|
309
|
+
guarded_regions: list[GuardedSourceRegion] = field(default_factory=list)
|
|
310
|
+
parser_backend: str = "unknown"
|
|
311
|
+
|
|
312
|
+
@property
|
|
313
|
+
def has_errors(self) -> bool:
|
|
314
|
+
return any(d.severity in {"error", "fatal"} for d in self.diagnostics)
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
@dataclass(slots=True)
|
|
318
|
+
class ProjectIndex:
|
|
319
|
+
root: Path
|
|
320
|
+
source_files: list[SourceFile] = field(default_factory=list)
|
|
321
|
+
parse_artifacts: list[FileParseArtifact] = field(default_factory=list)
|
|
322
|
+
callsites: list[CallSiteRecord] = field(default_factory=list)
|
|
323
|
+
value_slots: list[ValueSlot] = field(default_factory=list)
|
|
324
|
+
flow_edges: list[FlowEdge] = field(default_factory=list)
|
|
325
|
+
cpu_evidence: list[CpuEvidenceRecord] = field(default_factory=list)
|
|
326
|
+
linker_evidence: list[LinkerEvidenceRecord] = field(default_factory=list)
|
|
327
|
+
binary_evidence: list[BinaryEvidenceRecord] = field(default_factory=list)
|
|
328
|
+
dwarf_evidence: list[DwarfEvidenceRecord] = field(default_factory=list)
|
|
329
|
+
guarded_regions: list[GuardedSourceRegion] = field(default_factory=list)
|
|
330
|
+
static_symbol_store: Any | None = None
|
|
331
|
+
|
|
332
|
+
def functions(self) -> Iterable[FunctionRecord | CompactFunctionRecord]:
|
|
333
|
+
if self.static_symbol_store is not None:
|
|
334
|
+
yield from self.static_symbol_store.iter_functions()
|
|
335
|
+
return
|
|
336
|
+
for artifact in self.parse_artifacts:
|
|
337
|
+
yield from artifact.functions
|
|
338
|
+
|
|
339
|
+
def functions_named(self, names: set[str]) -> Iterable[FunctionRecord | CompactFunctionRecord]:
|
|
340
|
+
if not names:
|
|
341
|
+
return
|
|
342
|
+
if self.static_symbol_store is not None:
|
|
343
|
+
yield from self.static_symbol_store.iter_named(names)
|
|
344
|
+
return
|
|
345
|
+
for artifact in self.parse_artifacts:
|
|
346
|
+
funcs = artifact.functions
|
|
347
|
+
if isinstance(funcs, CompactFunctionTable):
|
|
348
|
+
yield from funcs.iter_named(names)
|
|
349
|
+
else:
|
|
350
|
+
for fn in funcs:
|
|
351
|
+
if fn.name in names:
|
|
352
|
+
yield fn
|
|
353
|
+
|
|
354
|
+
def functions_by_ids(self, qids: set[str]) -> Iterable[FunctionRecord | CompactFunctionRecord]:
|
|
355
|
+
if not qids:
|
|
356
|
+
return
|
|
357
|
+
if self.static_symbol_store is not None:
|
|
358
|
+
yield from self.static_symbol_store.iter_ids(qids)
|
|
359
|
+
return
|
|
360
|
+
for fn in self.functions():
|
|
361
|
+
if fn.qualified_id in qids:
|
|
362
|
+
yield fn
|
|
363
|
+
|
|
364
|
+
def function_count(self) -> int:
|
|
365
|
+
if self.static_symbol_store is not None:
|
|
366
|
+
return self.static_symbol_store.count()
|
|
367
|
+
return sum(len(a.functions) for a in self.parse_artifacts)
|