Skip to content

Commit c8f3cbe

Browse files
dguidoclaude
andauthored
feat: parse NatSpec custom fields (#644)
* feat: parse NatSpec custom fields Add support for parsing custom NatSpec tags (custom:*) at both the contract level (DevDoc) and method level (DevMethod). Custom fields are stored in a _custom dict and included in export() output. Fixes #295 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * test: add unit tests for NatSpec custom field parsing Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 9ea880c commit c8f3cbe

File tree

2 files changed

+356
-4
lines changed

2 files changed

+356
-4
lines changed

crytic_compile/utils/natspec.py

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,12 +47,14 @@ def __init__(self, method: dict) -> None:
4747
"""Init the object
4848
4949
Args:
50-
method (Dict): Method infos (author, details, params, return)
50+
method (Dict): Method infos (author, details, params, return, custom:*)
5151
"""
5252
self._author: str | None = method.get("author", None)
5353
self._details: str | None = method.get("details", None)
5454
self._params: dict[str, str] = method.get("params", {})
5555
self._return: str | None = method.get("return", None)
56+
# Extract custom fields (keys starting with "custom:")
57+
self._custom: dict[str, str] = {k: v for k, v in method.items() if k.startswith("custom:")}
5658

5759
@property
5860
def author(self) -> str | None:
@@ -90,18 +92,30 @@ def params(self) -> dict[str, str]:
9092
"""
9193
return self._params
9294

95+
@property
96+
def custom(self) -> dict[str, str]:
97+
"""Return the method custom fields
98+
99+
Returns:
100+
Dict[str, str]: custom field name => value (e.g. "custom:security" => "value")
101+
"""
102+
return self._custom
103+
93104
def export(self) -> dict:
94105
"""Export to a python dict
95106
96107
Returns:
97108
Dict: Exported dev method
98109
"""
99-
return {
110+
result = {
100111
"author": self.author,
101112
"details": self.details,
102113
"params": self.params,
103114
"return": self.method_return,
104115
}
116+
# Include custom fields if present
117+
result.update(self.custom)
118+
return result
105119

106120

107121
class UserDoc:
@@ -159,14 +173,16 @@ def __init__(self, devdoc: dict):
159173
"""Init the object
160174
161175
Args:
162-
devdoc (Dict): dev doc (author, details, methods, title)
176+
devdoc (Dict): dev doc (author, details, methods, title, custom:*)
163177
"""
164178
self._author: str | None = devdoc.get("author", None)
165179
self._details: str | None = devdoc.get("details", None)
166180
self._methods: dict[str, DevMethod] = {
167181
k: DevMethod(item) for k, item in devdoc.get("methods", {}).items()
168182
}
169183
self._title: str | None = devdoc.get("title", None)
184+
# Extract contract-level custom fields (keys starting with "custom:")
185+
self._custom: dict[str, str] = {k: v for k, v in devdoc.items() if k.startswith("custom:")}
170186

171187
@property
172188
def author(self) -> str | None:
@@ -204,18 +220,30 @@ def title(self) -> str | None:
204220
"""
205221
return self._title
206222

223+
@property
224+
def custom(self) -> dict[str, str]:
225+
"""Return the contract-level custom fields
226+
227+
Returns:
228+
Dict[str, str]: custom field name => value (e.g. "custom:security" => "value")
229+
"""
230+
return self._custom
231+
207232
def export(self) -> dict:
208233
"""Export to a python dict
209234
210235
Returns:
211236
Dict: Exported dev doc
212237
"""
213-
return {
238+
result = {
214239
"methods": {k: items.export() for k, items in self.methods.items()},
215240
"author": self.author,
216241
"details": self.details,
217242
"title": self.title,
218243
}
244+
# Include custom fields if present
245+
result.update(self.custom)
246+
return result
219247

220248

221249
class Natspec:

0 commit comments

Comments
 (0)