Sphinx 7.2.5__py3-none-any.whl → 7.3.0__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.

Potentially problematic release.


This version of Sphinx might be problematic. Click here for more details.

Files changed (388) hide show
  1. sphinx/__init__.py +8 -9
  2. sphinx/addnodes.py +31 -28
  3. sphinx/application.py +9 -15
  4. sphinx/builders/__init__.py +5 -6
  5. sphinx/builders/_epub_base.py +17 -9
  6. sphinx/builders/changes.py +10 -5
  7. sphinx/builders/dirhtml.py +4 -2
  8. sphinx/builders/dummy.py +3 -2
  9. sphinx/builders/epub3.py +5 -3
  10. sphinx/builders/gettext.py +24 -7
  11. sphinx/builders/html/__init__.py +88 -96
  12. sphinx/builders/html/_assets.py +16 -16
  13. sphinx/builders/html/transforms.py +4 -2
  14. sphinx/builders/latex/__init__.py +40 -33
  15. sphinx/builders/latex/nodes.py +6 -2
  16. sphinx/builders/latex/transforms.py +17 -8
  17. sphinx/builders/latex/util.py +1 -1
  18. sphinx/builders/linkcheck.py +86 -27
  19. sphinx/builders/manpage.py +8 -6
  20. sphinx/builders/singlehtml.py +5 -4
  21. sphinx/builders/texinfo.py +18 -14
  22. sphinx/builders/text.py +3 -2
  23. sphinx/builders/xml.py +5 -2
  24. sphinx/cmd/build.py +119 -76
  25. sphinx/cmd/make_mode.py +21 -20
  26. sphinx/cmd/quickstart.py +13 -16
  27. sphinx/config.py +432 -250
  28. sphinx/deprecation.py +23 -13
  29. sphinx/directives/__init__.py +8 -8
  30. sphinx/directives/code.py +7 -7
  31. sphinx/directives/other.py +23 -13
  32. sphinx/directives/patches.py +7 -6
  33. sphinx/domains/__init__.py +2 -2
  34. sphinx/domains/c/__init__.py +796 -0
  35. sphinx/domains/c/_ast.py +1421 -0
  36. sphinx/domains/c/_ids.py +65 -0
  37. sphinx/domains/c/_parser.py +1048 -0
  38. sphinx/domains/c/_symbol.py +700 -0
  39. sphinx/domains/changeset.py +11 -7
  40. sphinx/domains/citation.py +5 -2
  41. sphinx/domains/cpp/__init__.py +1089 -0
  42. sphinx/domains/cpp/_ast.py +3635 -0
  43. sphinx/domains/cpp/_ids.py +537 -0
  44. sphinx/domains/cpp/_parser.py +2117 -0
  45. sphinx/domains/cpp/_symbol.py +1092 -0
  46. sphinx/domains/index.py +6 -4
  47. sphinx/domains/javascript.py +16 -13
  48. sphinx/domains/math.py +9 -4
  49. sphinx/domains/python/__init__.py +890 -0
  50. sphinx/domains/python/_annotations.py +507 -0
  51. sphinx/domains/python/_object.py +426 -0
  52. sphinx/domains/rst.py +12 -7
  53. sphinx/domains/{std.py → std/__init__.py} +19 -16
  54. sphinx/environment/__init__.py +21 -19
  55. sphinx/environment/adapters/indexentries.py +2 -2
  56. sphinx/environment/adapters/toctree.py +10 -9
  57. sphinx/environment/collectors/__init__.py +6 -3
  58. sphinx/environment/collectors/asset.py +4 -3
  59. sphinx/environment/collectors/dependencies.py +3 -2
  60. sphinx/environment/collectors/metadata.py +6 -5
  61. sphinx/environment/collectors/title.py +3 -2
  62. sphinx/environment/collectors/toctree.py +5 -4
  63. sphinx/errors.py +13 -2
  64. sphinx/events.py +14 -9
  65. sphinx/ext/apidoc.py +9 -11
  66. sphinx/ext/autodoc/__init__.py +105 -71
  67. sphinx/ext/autodoc/directive.py +7 -6
  68. sphinx/ext/autodoc/importer.py +132 -52
  69. sphinx/ext/autodoc/mock.py +7 -5
  70. sphinx/ext/autodoc/preserve_defaults.py +4 -3
  71. sphinx/ext/autodoc/type_comment.py +2 -1
  72. sphinx/ext/autodoc/typehints.py +5 -4
  73. sphinx/ext/autosectionlabel.py +3 -2
  74. sphinx/ext/autosummary/__init__.py +21 -17
  75. sphinx/ext/autosummary/generate.py +9 -9
  76. sphinx/ext/coverage.py +26 -20
  77. sphinx/ext/doctest.py +38 -33
  78. sphinx/ext/duration.py +1 -0
  79. sphinx/ext/extlinks.py +4 -3
  80. sphinx/ext/githubpages.py +3 -2
  81. sphinx/ext/graphviz.py +10 -7
  82. sphinx/ext/ifconfig.py +5 -5
  83. sphinx/ext/imgconverter.py +6 -5
  84. sphinx/ext/imgmath.py +9 -8
  85. sphinx/ext/inheritance_diagram.py +31 -31
  86. sphinx/ext/intersphinx.py +140 -23
  87. sphinx/ext/linkcode.py +3 -2
  88. sphinx/ext/mathjax.py +2 -1
  89. sphinx/ext/napoleon/__init__.py +12 -7
  90. sphinx/ext/napoleon/docstring.py +34 -32
  91. sphinx/ext/todo.py +10 -7
  92. sphinx/ext/viewcode.py +12 -11
  93. sphinx/extension.py +18 -8
  94. sphinx/highlighting.py +39 -20
  95. sphinx/io.py +17 -8
  96. sphinx/jinja2glue.py +16 -15
  97. sphinx/locale/__init__.py +30 -23
  98. sphinx/locale/ar/LC_MESSAGES/sphinx.mo +0 -0
  99. sphinx/locale/ar/LC_MESSAGES/sphinx.po +818 -761
  100. sphinx/locale/bg/LC_MESSAGES/sphinx.mo +0 -0
  101. sphinx/locale/bg/LC_MESSAGES/sphinx.po +811 -754
  102. sphinx/locale/bn/LC_MESSAGES/sphinx.mo +0 -0
  103. sphinx/locale/bn/LC_MESSAGES/sphinx.po +835 -778
  104. sphinx/locale/ca/LC_MESSAGES/sphinx.mo +0 -0
  105. sphinx/locale/ca/LC_MESSAGES/sphinx.po +864 -807
  106. sphinx/locale/cak/LC_MESSAGES/sphinx.mo +0 -0
  107. sphinx/locale/cak/LC_MESSAGES/sphinx.po +816 -759
  108. sphinx/locale/cs/LC_MESSAGES/sphinx.mo +0 -0
  109. sphinx/locale/cs/LC_MESSAGES/sphinx.po +837 -780
  110. sphinx/locale/cy/LC_MESSAGES/sphinx.mo +0 -0
  111. sphinx/locale/cy/LC_MESSAGES/sphinx.po +819 -762
  112. sphinx/locale/da/LC_MESSAGES/sphinx.mo +0 -0
  113. sphinx/locale/da/LC_MESSAGES/sphinx.po +838 -781
  114. sphinx/locale/de/LC_MESSAGES/sphinx.mo +0 -0
  115. sphinx/locale/de/LC_MESSAGES/sphinx.po +838 -781
  116. sphinx/locale/de_DE/LC_MESSAGES/sphinx.mo +0 -0
  117. sphinx/locale/de_DE/LC_MESSAGES/sphinx.po +811 -754
  118. sphinx/locale/el/LC_MESSAGES/sphinx.mo +0 -0
  119. sphinx/locale/el/LC_MESSAGES/sphinx.po +853 -796
  120. sphinx/locale/en_DE/LC_MESSAGES/sphinx.mo +0 -0
  121. sphinx/locale/en_DE/LC_MESSAGES/sphinx.po +811 -754
  122. sphinx/locale/en_FR/LC_MESSAGES/sphinx.mo +0 -0
  123. sphinx/locale/en_FR/LC_MESSAGES/sphinx.po +811 -754
  124. sphinx/locale/en_GB/LC_MESSAGES/sphinx.mo +0 -0
  125. sphinx/locale/en_GB/LC_MESSAGES/sphinx.po +856 -799
  126. sphinx/locale/en_HK/LC_MESSAGES/sphinx.mo +0 -0
  127. sphinx/locale/en_HK/LC_MESSAGES/sphinx.po +811 -754
  128. sphinx/locale/eo/LC_MESSAGES/sphinx.mo +0 -0
  129. sphinx/locale/eo/LC_MESSAGES/sphinx.po +820 -763
  130. sphinx/locale/es/LC_MESSAGES/sphinx.mo +0 -0
  131. sphinx/locale/es/LC_MESSAGES/sphinx.po +856 -799
  132. sphinx/locale/es_CO/LC_MESSAGES/sphinx.mo +0 -0
  133. sphinx/locale/es_CO/LC_MESSAGES/sphinx.po +811 -754
  134. sphinx/locale/et/LC_MESSAGES/sphinx.mo +0 -0
  135. sphinx/locale/et/LC_MESSAGES/sphinx.po +845 -788
  136. sphinx/locale/eu/LC_MESSAGES/sphinx.mo +0 -0
  137. sphinx/locale/eu/LC_MESSAGES/sphinx.po +837 -780
  138. sphinx/locale/fa/LC_MESSAGES/sphinx.mo +0 -0
  139. sphinx/locale/fa/LC_MESSAGES/sphinx.po +854 -797
  140. sphinx/locale/fi/LC_MESSAGES/sphinx.mo +0 -0
  141. sphinx/locale/fi/LC_MESSAGES/sphinx.po +816 -759
  142. sphinx/locale/fr/LC_MESSAGES/sphinx.js +1 -1
  143. sphinx/locale/fr/LC_MESSAGES/sphinx.mo +0 -0
  144. sphinx/locale/fr/LC_MESSAGES/sphinx.po +904 -847
  145. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.mo +0 -0
  146. sphinx/locale/fr_FR/LC_MESSAGES/sphinx.po +811 -754
  147. sphinx/locale/gl/LC_MESSAGES/sphinx.js +54 -54
  148. sphinx/locale/gl/LC_MESSAGES/sphinx.mo +0 -0
  149. sphinx/locale/gl/LC_MESSAGES/sphinx.po +1506 -1449
  150. sphinx/locale/he/LC_MESSAGES/sphinx.js +1 -1
  151. sphinx/locale/he/LC_MESSAGES/sphinx.mo +0 -0
  152. sphinx/locale/he/LC_MESSAGES/sphinx.po +823 -766
  153. sphinx/locale/hi/LC_MESSAGES/sphinx.mo +0 -0
  154. sphinx/locale/hi/LC_MESSAGES/sphinx.po +853 -796
  155. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.mo +0 -0
  156. sphinx/locale/hi_IN/LC_MESSAGES/sphinx.po +811 -754
  157. sphinx/locale/hr/LC_MESSAGES/sphinx.mo +0 -0
  158. sphinx/locale/hr/LC_MESSAGES/sphinx.po +844 -787
  159. sphinx/locale/hu/LC_MESSAGES/sphinx.mo +0 -0
  160. sphinx/locale/hu/LC_MESSAGES/sphinx.po +837 -780
  161. sphinx/locale/id/LC_MESSAGES/sphinx.mo +0 -0
  162. sphinx/locale/id/LC_MESSAGES/sphinx.po +854 -797
  163. sphinx/locale/is/LC_MESSAGES/sphinx.mo +0 -0
  164. sphinx/locale/is/LC_MESSAGES/sphinx.po +811 -754
  165. sphinx/locale/it/LC_MESSAGES/sphinx.mo +0 -0
  166. sphinx/locale/it/LC_MESSAGES/sphinx.po +837 -780
  167. sphinx/locale/ja/LC_MESSAGES/sphinx.mo +0 -0
  168. sphinx/locale/ja/LC_MESSAGES/sphinx.po +853 -796
  169. sphinx/locale/ka/LC_MESSAGES/sphinx.mo +0 -0
  170. sphinx/locale/ka/LC_MESSAGES/sphinx.po +848 -791
  171. sphinx/locale/ko/LC_MESSAGES/sphinx.mo +0 -0
  172. sphinx/locale/ko/LC_MESSAGES/sphinx.po +855 -798
  173. sphinx/locale/lt/LC_MESSAGES/sphinx.mo +0 -0
  174. sphinx/locale/lt/LC_MESSAGES/sphinx.po +837 -780
  175. sphinx/locale/lv/LC_MESSAGES/sphinx.mo +0 -0
  176. sphinx/locale/lv/LC_MESSAGES/sphinx.po +837 -780
  177. sphinx/locale/mk/LC_MESSAGES/sphinx.mo +0 -0
  178. sphinx/locale/mk/LC_MESSAGES/sphinx.po +825 -768
  179. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.js +27 -27
  180. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.mo +0 -0
  181. sphinx/locale/nb_NO/LC_MESSAGES/sphinx.po +876 -818
  182. sphinx/locale/ne/LC_MESSAGES/sphinx.mo +0 -0
  183. sphinx/locale/ne/LC_MESSAGES/sphinx.po +837 -780
  184. sphinx/locale/nl/LC_MESSAGES/sphinx.mo +0 -0
  185. sphinx/locale/nl/LC_MESSAGES/sphinx.po +844 -787
  186. sphinx/locale/pl/LC_MESSAGES/sphinx.mo +0 -0
  187. sphinx/locale/pl/LC_MESSAGES/sphinx.po +845 -788
  188. sphinx/locale/pt/LC_MESSAGES/sphinx.mo +0 -0
  189. sphinx/locale/pt/LC_MESSAGES/sphinx.po +811 -754
  190. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.mo +0 -0
  191. sphinx/locale/pt_BR/LC_MESSAGES/sphinx.po +908 -851
  192. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.mo +0 -0
  193. sphinx/locale/pt_PT/LC_MESSAGES/sphinx.po +837 -780
  194. sphinx/locale/ro/LC_MESSAGES/sphinx.mo +0 -0
  195. sphinx/locale/ro/LC_MESSAGES/sphinx.po +837 -780
  196. sphinx/locale/ru/LC_MESSAGES/sphinx.mo +0 -0
  197. sphinx/locale/ru/LC_MESSAGES/sphinx.po +838 -781
  198. sphinx/locale/si/LC_MESSAGES/sphinx.mo +0 -0
  199. sphinx/locale/si/LC_MESSAGES/sphinx.po +823 -766
  200. sphinx/locale/sk/LC_MESSAGES/sphinx.mo +0 -0
  201. sphinx/locale/sk/LC_MESSAGES/sphinx.po +854 -797
  202. sphinx/locale/sl/LC_MESSAGES/sphinx.mo +0 -0
  203. sphinx/locale/sl/LC_MESSAGES/sphinx.po +832 -775
  204. sphinx/locale/sphinx.pot +813 -755
  205. sphinx/locale/sq/LC_MESSAGES/sphinx.js +1 -1
  206. sphinx/locale/sq/LC_MESSAGES/sphinx.mo +0 -0
  207. sphinx/locale/sq/LC_MESSAGES/sphinx.po +865 -808
  208. sphinx/locale/sr/LC_MESSAGES/sphinx.mo +0 -0
  209. sphinx/locale/sr/LC_MESSAGES/sphinx.po +835 -778
  210. sphinx/locale/sr@latin/LC_MESSAGES/sphinx.mo +0 -0
  211. sphinx/locale/sr_RS/LC_MESSAGES/sphinx.mo +0 -0
  212. sphinx/locale/sv/LC_MESSAGES/sphinx.mo +0 -0
  213. sphinx/locale/sv/LC_MESSAGES/sphinx.po +837 -780
  214. sphinx/locale/ta/LC_MESSAGES/sphinx.js +54 -54
  215. sphinx/locale/ta/LC_MESSAGES/sphinx.mo +0 -0
  216. sphinx/locale/ta/LC_MESSAGES/sphinx.po +1530 -1473
  217. sphinx/locale/te/LC_MESSAGES/sphinx.mo +0 -0
  218. sphinx/locale/te/LC_MESSAGES/sphinx.po +811 -754
  219. sphinx/locale/tr/LC_MESSAGES/sphinx.mo +0 -0
  220. sphinx/locale/tr/LC_MESSAGES/sphinx.po +853 -796
  221. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.mo +0 -0
  222. sphinx/locale/uk_UA/LC_MESSAGES/sphinx.po +833 -776
  223. sphinx/locale/ur/LC_MESSAGES/sphinx.mo +0 -0
  224. sphinx/locale/ur/LC_MESSAGES/sphinx.po +811 -754
  225. sphinx/locale/vi/LC_MESSAGES/sphinx.mo +0 -0
  226. sphinx/locale/vi/LC_MESSAGES/sphinx.po +837 -780
  227. sphinx/locale/yue/LC_MESSAGES/sphinx.mo +0 -0
  228. sphinx/locale/yue/LC_MESSAGES/sphinx.po +811 -754
  229. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.mo +0 -0
  230. sphinx/locale/zh_CN/LC_MESSAGES/sphinx.po +855 -798
  231. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.mo +0 -0
  232. sphinx/locale/zh_HK/LC_MESSAGES/sphinx.po +811 -754
  233. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.js +1 -1
  234. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.mo +0 -0
  235. sphinx/locale/zh_TW/LC_MESSAGES/sphinx.po +879 -822
  236. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.mo +0 -0
  237. sphinx/locale/zh_TW.Big5/LC_MESSAGES/sphinx.po +811 -754
  238. sphinx/parsers.py +7 -5
  239. sphinx/project.py +18 -11
  240. sphinx/pycode/__init__.py +6 -5
  241. sphinx/pycode/ast.py +23 -8
  242. sphinx/pycode/parser.py +6 -5
  243. sphinx/registry.py +12 -6
  244. sphinx/roles.py +103 -57
  245. sphinx/search/__init__.py +17 -18
  246. sphinx/search/da.py +2 -2
  247. sphinx/search/de.py +2 -2
  248. sphinx/search/en.py +1 -1
  249. sphinx/search/es.py +2 -2
  250. sphinx/search/fi.py +2 -2
  251. sphinx/search/fr.py +2 -2
  252. sphinx/search/hu.py +2 -2
  253. sphinx/search/it.py +2 -2
  254. sphinx/search/ja.py +13 -22
  255. sphinx/search/nl.py +2 -2
  256. sphinx/search/no.py +2 -2
  257. sphinx/search/pt.py +2 -2
  258. sphinx/search/ro.py +1 -1
  259. sphinx/search/ru.py +2 -2
  260. sphinx/search/sv.py +2 -2
  261. sphinx/search/tr.py +1 -1
  262. sphinx/search/zh.py +2 -3
  263. sphinx/templates/graphviz/graphviz.css +1 -1
  264. sphinx/testing/fixtures.py +41 -24
  265. sphinx/testing/path.py +1 -1
  266. sphinx/testing/util.py +142 -53
  267. sphinx/texinputs/sphinx.xdy +1 -1
  268. sphinx/texinputs/sphinxlatextables.sty +1 -1
  269. sphinx/texinputs/sphinxpackagesubstitutefont.sty +21 -0
  270. sphinx/themes/agogo/layout.html +4 -4
  271. sphinx/themes/agogo/static/agogo.css_t +1 -1
  272. sphinx/themes/agogo/theme.toml +22 -0
  273. sphinx/themes/basic/defindex.html +1 -1
  274. sphinx/themes/basic/domainindex.html +1 -1
  275. sphinx/themes/basic/genindex-single.html +1 -1
  276. sphinx/themes/basic/genindex-split.html +1 -1
  277. sphinx/themes/basic/genindex.html +1 -1
  278. sphinx/themes/basic/globaltoc.html +1 -1
  279. sphinx/themes/basic/layout.html +1 -1
  280. sphinx/themes/basic/localtoc.html +1 -1
  281. sphinx/themes/basic/page.html +1 -1
  282. sphinx/themes/basic/relations.html +1 -1
  283. sphinx/themes/basic/search.html +5 -20
  284. sphinx/themes/basic/searchbox.html +3 -3
  285. sphinx/themes/basic/searchfield.html +3 -3
  286. sphinx/themes/basic/sourcelink.html +1 -1
  287. sphinx/themes/basic/static/basic.css_t +1 -1
  288. sphinx/themes/basic/static/doctools.js +1 -1
  289. sphinx/themes/basic/static/language_data.js_t +2 -2
  290. sphinx/themes/basic/static/searchtools.js +105 -60
  291. sphinx/themes/basic/theme.toml +23 -0
  292. sphinx/themes/bizstyle/layout.html +1 -6
  293. sphinx/themes/bizstyle/static/bizstyle.css_t +1 -1
  294. sphinx/themes/bizstyle/static/bizstyle.js_t +1 -1
  295. sphinx/themes/bizstyle/static/css3-mediaqueries_src.js +3 -3
  296. sphinx/themes/bizstyle/theme.toml +12 -0
  297. sphinx/themes/classic/layout.html +1 -1
  298. sphinx/themes/classic/static/classic.css_t +1 -1
  299. sphinx/themes/classic/static/sidebar.js_t +1 -1
  300. sphinx/themes/classic/theme.toml +34 -0
  301. sphinx/themes/default/theme.toml +2 -0
  302. sphinx/themes/epub/epub-cover.html +1 -1
  303. sphinx/themes/epub/layout.html +1 -1
  304. sphinx/themes/epub/static/epub.css_t +1 -1
  305. sphinx/themes/epub/theme.toml +10 -0
  306. sphinx/themes/haiku/layout.html +3 -3
  307. sphinx/themes/haiku/static/haiku.css_t +2 -2
  308. sphinx/themes/haiku/theme.toml +16 -0
  309. sphinx/themes/nature/static/nature.css_t +1 -1
  310. sphinx/themes/nature/theme.toml +6 -0
  311. sphinx/themes/nonav/layout.html +1 -1
  312. sphinx/themes/nonav/static/nonav.css_t +1 -1
  313. sphinx/themes/nonav/theme.toml +10 -0
  314. sphinx/themes/pyramid/static/epub.css_t +1 -1
  315. sphinx/themes/pyramid/static/pyramid.css_t +1 -1
  316. sphinx/themes/pyramid/theme.toml +6 -0
  317. sphinx/themes/scrolls/artwork/logo.svg +1 -1
  318. sphinx/themes/scrolls/layout.html +2 -2
  319. sphinx/themes/scrolls/static/scrolls.css_t +1 -1
  320. sphinx/themes/scrolls/theme.toml +15 -0
  321. sphinx/themes/sphinxdoc/static/sphinxdoc.css_t +1 -1
  322. sphinx/themes/sphinxdoc/theme.toml +6 -0
  323. sphinx/themes/traditional/static/traditional.css_t +1 -1
  324. sphinx/themes/traditional/theme.toml +9 -0
  325. sphinx/theming.py +427 -131
  326. sphinx/transforms/__init__.py +21 -24
  327. sphinx/transforms/compact_bullet_list.py +5 -5
  328. sphinx/transforms/i18n.py +30 -28
  329. sphinx/transforms/post_transforms/__init__.py +9 -7
  330. sphinx/transforms/post_transforms/code.py +4 -1
  331. sphinx/transforms/post_transforms/images.py +17 -13
  332. sphinx/transforms/references.py +3 -1
  333. sphinx/util/__init__.py +15 -11
  334. sphinx/util/_io.py +34 -0
  335. sphinx/util/_pathlib.py +23 -18
  336. sphinx/util/build_phase.py +1 -0
  337. sphinx/util/cfamily.py +19 -11
  338. sphinx/util/console.py +101 -21
  339. sphinx/util/display.py +3 -2
  340. sphinx/util/docfields.py +12 -8
  341. sphinx/util/docutils.py +21 -35
  342. sphinx/util/exceptions.py +3 -2
  343. sphinx/util/fileutil.py +5 -5
  344. sphinx/util/http_date.py +9 -2
  345. sphinx/util/i18n.py +40 -9
  346. sphinx/util/inspect.py +317 -245
  347. sphinx/util/inventory.py +22 -5
  348. sphinx/util/logging.py +81 -7
  349. sphinx/util/matching.py +2 -1
  350. sphinx/util/math.py +1 -2
  351. sphinx/util/nodes.py +39 -29
  352. sphinx/util/osutil.py +25 -6
  353. sphinx/util/parallel.py +6 -1
  354. sphinx/util/requests.py +8 -5
  355. sphinx/util/rst.py +8 -6
  356. sphinx/util/tags.py +3 -3
  357. sphinx/util/template.py +8 -3
  358. sphinx/util/typing.py +76 -42
  359. sphinx/versioning.py +6 -2
  360. sphinx/writers/html.py +1 -1
  361. sphinx/writers/html5.py +17 -13
  362. sphinx/writers/latex.py +12 -12
  363. sphinx/writers/manpage.py +13 -7
  364. sphinx/writers/texinfo.py +13 -10
  365. sphinx/writers/text.py +13 -23
  366. sphinx/writers/xml.py +1 -1
  367. sphinx-7.2.5.dist-info/LICENSE → sphinx-7.3.0.dist-info/LICENSE.rst +1 -1
  368. {sphinx-7.2.5.dist-info → sphinx-7.3.0.dist-info}/METADATA +13 -12
  369. sphinx-7.3.0.dist-info/RECORD +581 -0
  370. sphinx/domains/c.py +0 -3906
  371. sphinx/domains/cpp.py +0 -8233
  372. sphinx/domains/python.py +0 -1769
  373. sphinx/themes/agogo/theme.conf +0 -20
  374. sphinx/themes/basic/theme.conf +0 -16
  375. sphinx/themes/bizstyle/theme.conf +0 -10
  376. sphinx/themes/classic/theme.conf +0 -32
  377. sphinx/themes/default/theme.conf +0 -2
  378. sphinx/themes/epub/theme.conf +0 -8
  379. sphinx/themes/haiku/theme.conf +0 -14
  380. sphinx/themes/nature/theme.conf +0 -4
  381. sphinx/themes/nonav/theme.conf +0 -8
  382. sphinx/themes/pyramid/theme.conf +0 -4
  383. sphinx/themes/scrolls/theme.conf +0 -13
  384. sphinx/themes/sphinxdoc/theme.conf +0 -4
  385. sphinx/themes/traditional/theme.conf +0 -7
  386. sphinx-7.2.5.dist-info/RECORD +0 -569
  387. {sphinx-7.2.5.dist-info → sphinx-7.3.0.dist-info}/WHEEL +0 -0
  388. {sphinx-7.2.5.dist-info → sphinx-7.3.0.dist-info}/entry_points.txt +0 -0
sphinx/domains/c.py DELETED
@@ -1,3906 +0,0 @@
1
- """The C language domain."""
2
-
3
- from __future__ import annotations
4
-
5
- import re
6
- from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union, cast
7
-
8
- from docutils import nodes
9
- from docutils.parsers.rst import directives
10
-
11
- from sphinx import addnodes
12
- from sphinx.directives import ObjectDescription
13
- from sphinx.domains import Domain, ObjType
14
- from sphinx.locale import _, __
15
- from sphinx.roles import SphinxRole, XRefRole
16
- from sphinx.transforms import SphinxTransform
17
- from sphinx.transforms.post_transforms import ReferencesResolver
18
- from sphinx.util import logging
19
- from sphinx.util.cfamily import (
20
- ASTAttributeList,
21
- ASTBaseBase,
22
- ASTBaseParenExprList,
23
- BaseParser,
24
- DefinitionError,
25
- NoOldIdError,
26
- StringifyTransform,
27
- UnsupportedMultiCharacterCharLiteral,
28
- anon_identifier_re,
29
- binary_literal_re,
30
- char_literal_re,
31
- float_literal_re,
32
- float_literal_suffix_re,
33
- hex_literal_re,
34
- identifier_re,
35
- integer_literal_re,
36
- integers_literal_suffix_re,
37
- octal_literal_re,
38
- verify_description_mode,
39
- )
40
- from sphinx.util.docfields import Field, GroupedField, TypedField
41
- from sphinx.util.docutils import SphinxDirective
42
- from sphinx.util.nodes import make_refnode
43
-
44
- if TYPE_CHECKING:
45
- from collections.abc import Generator, Iterator
46
-
47
- from docutils.nodes import Element, Node, TextElement, system_message
48
-
49
- from sphinx.addnodes import pending_xref
50
- from sphinx.application import Sphinx
51
- from sphinx.builders import Builder
52
- from sphinx.environment import BuildEnvironment
53
- from sphinx.util.typing import OptionSpec
54
-
55
- logger = logging.getLogger(__name__)
56
- T = TypeVar('T')
57
-
58
- DeclarationType = Union[
59
- "ASTStruct", "ASTUnion", "ASTEnum", "ASTEnumerator",
60
- "ASTType", "ASTTypeWithInit", "ASTMacro",
61
- ]
62
-
63
- # https://en.cppreference.com/w/c/keyword
64
- _keywords = [
65
- 'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double',
66
- 'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long',
67
- 'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct',
68
- 'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while',
69
- '_Alignas', '_Alignof', '_Atomic', '_Bool', '_Complex',
70
- '_Decimal32', '_Decimal64', '_Decimal128',
71
- '_Generic', '_Imaginary', '_Noreturn', '_Static_assert', '_Thread_local',
72
- ]
73
- # These are only keyword'y when the corresponding headers are included.
74
- # They are used as default value for c_extra_keywords.
75
- _macroKeywords = [
76
- 'alignas', 'alignof', 'bool', 'complex', 'imaginary', 'noreturn', 'static_assert',
77
- 'thread_local',
78
- ]
79
-
80
- # these are ordered by precedence
81
- _expression_bin_ops = [
82
- ['||', 'or'],
83
- ['&&', 'and'],
84
- ['|', 'bitor'],
85
- ['^', 'xor'],
86
- ['&', 'bitand'],
87
- ['==', '!=', 'not_eq'],
88
- ['<=', '>=', '<', '>'],
89
- ['<<', '>>'],
90
- ['+', '-'],
91
- ['*', '/', '%'],
92
- ['.*', '->*'],
93
- ]
94
- _expression_unary_ops = ["++", "--", "*", "&", "+", "-", "!", "not", "~", "compl"]
95
- _expression_assignment_ops = ["=", "*=", "/=", "%=", "+=", "-=",
96
- ">>=", "<<=", "&=", "and_eq", "^=", "xor_eq", "|=", "or_eq"]
97
-
98
- _max_id = 1
99
- _id_prefix = [None, 'c.', 'Cv2.']
100
- # Ids are used in lookup keys which are used across pickled files,
101
- # so when _max_id changes, make sure to update the ENV_VERSION.
102
-
103
- _string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
104
- r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.S)
105
-
106
- # bool, complex, and imaginary are macro "keywords", so they are handled separately
107
- _simple_type_specifiers_re = re.compile(r"""
108
- \b(
109
- void|_Bool
110
- |signed|unsigned
111
- |short|long
112
- |char
113
- |int
114
- |__uint128|__int128
115
- |__int(8|16|32|64|128) # extension
116
- |float|double
117
- |_Decimal(32|64|128)
118
- |_Complex|_Imaginary
119
- |__float80|_Float64x|__float128|_Float128|__ibm128 # extension
120
- |__fp16 # extension
121
- |_Sat|_Fract|fract|_Accum|accum # extension
122
- )\b
123
- """, re.VERBOSE)
124
-
125
-
126
- class _DuplicateSymbolError(Exception):
127
- def __init__(self, symbol: Symbol, declaration: ASTDeclaration) -> None:
128
- assert symbol
129
- assert declaration
130
- self.symbol = symbol
131
- self.declaration = declaration
132
-
133
- def __str__(self) -> str:
134
- return "Internal C duplicate symbol error:\n%s" % self.symbol.dump(0)
135
-
136
-
137
- class ASTBase(ASTBaseBase):
138
- def describe_signature(self, signode: TextElement, mode: str,
139
- env: BuildEnvironment, symbol: Symbol) -> None:
140
- raise NotImplementedError(repr(self))
141
-
142
-
143
- # Names
144
- ################################################################################
145
-
146
- class ASTIdentifier(ASTBaseBase):
147
- def __init__(self, identifier: str) -> None:
148
- assert identifier is not None
149
- assert len(identifier) != 0
150
- self.identifier = identifier
151
-
152
- def __eq__(self, other: Any) -> bool:
153
- return type(other) is ASTIdentifier and self.identifier == other.identifier
154
-
155
- def is_anon(self) -> bool:
156
- return self.identifier[0] == '@'
157
-
158
- # and this is where we finally make a difference between __str__ and the display string
159
-
160
- def __str__(self) -> str:
161
- return self.identifier
162
-
163
- def get_display_string(self) -> str:
164
- return "[anonymous]" if self.is_anon() else self.identifier
165
-
166
- def describe_signature(self, signode: TextElement, mode: str, env: BuildEnvironment,
167
- prefix: str, symbol: Symbol) -> None:
168
- # note: slightly different signature of describe_signature due to the prefix
169
- verify_description_mode(mode)
170
- if self.is_anon():
171
- node = addnodes.desc_sig_name(text="[anonymous]")
172
- else:
173
- node = addnodes.desc_sig_name(self.identifier, self.identifier)
174
- if mode == 'markType':
175
- targetText = prefix + self.identifier
176
- pnode = addnodes.pending_xref('', refdomain='c',
177
- reftype='identifier',
178
- reftarget=targetText, modname=None,
179
- classname=None)
180
- pnode['c:parent_key'] = symbol.get_lookup_key()
181
- pnode += node
182
- signode += pnode
183
- elif mode == 'lastIsName':
184
- nameNode = addnodes.desc_name()
185
- nameNode += node
186
- signode += nameNode
187
- elif mode == 'noneIsName':
188
- signode += node
189
- else:
190
- raise Exception('Unknown description mode: %s' % mode)
191
-
192
-
193
- class ASTNestedName(ASTBase):
194
- def __init__(self, names: list[ASTIdentifier], rooted: bool) -> None:
195
- assert len(names) > 0
196
- self.names = names
197
- self.rooted = rooted
198
-
199
- @property
200
- def name(self) -> ASTNestedName:
201
- return self
202
-
203
- def get_id(self, version: int) -> str:
204
- return '.'.join(str(n) for n in self.names)
205
-
206
- def _stringify(self, transform: StringifyTransform) -> str:
207
- res = '.'.join(transform(n) for n in self.names)
208
- if self.rooted:
209
- return '.' + res
210
- else:
211
- return res
212
-
213
- def describe_signature(self, signode: TextElement, mode: str,
214
- env: BuildEnvironment, symbol: Symbol) -> None:
215
- verify_description_mode(mode)
216
- # just print the name part, with template args, not template params
217
- if mode == 'noneIsName':
218
- if self.rooted:
219
- unreachable = "Can this happen?"
220
- raise AssertionError(unreachable) # TODO
221
- signode += nodes.Text('.')
222
- for i in range(len(self.names)):
223
- if i != 0:
224
- unreachable = "Can this happen?"
225
- raise AssertionError(unreachable) # TODO
226
- signode += nodes.Text('.')
227
- n = self.names[i]
228
- n.describe_signature(signode, mode, env, '', symbol)
229
- elif mode == 'param':
230
- assert not self.rooted, str(self)
231
- assert len(self.names) == 1
232
- self.names[0].describe_signature(signode, 'noneIsName', env, '', symbol)
233
- elif mode in ('markType', 'lastIsName', 'markName'):
234
- # Each element should be a pending xref targeting the complete
235
- # prefix.
236
- prefix = ''
237
- first = True
238
- names = self.names[:-1] if mode == 'lastIsName' else self.names
239
- # If lastIsName, then wrap all of the prefix in a desc_addname,
240
- # else append directly to signode.
241
- # TODO: also for C?
242
- # NOTE: Breathe previously relied on the prefix being in the desc_addname node,
243
- # so it can remove it in inner declarations.
244
- dest = signode
245
- if mode == 'lastIsName':
246
- dest = addnodes.desc_addname()
247
- if self.rooted:
248
- prefix += '.'
249
- if mode == 'lastIsName' and len(names) == 0:
250
- signode += addnodes.desc_sig_punctuation('.', '.')
251
- else:
252
- dest += addnodes.desc_sig_punctuation('.', '.')
253
- for i in range(len(names)):
254
- ident = names[i]
255
- if not first:
256
- dest += addnodes.desc_sig_punctuation('.', '.')
257
- prefix += '.'
258
- first = False
259
- txt_ident = str(ident)
260
- if txt_ident != '':
261
- ident.describe_signature(dest, 'markType', env, prefix, symbol)
262
- prefix += txt_ident
263
- if mode == 'lastIsName':
264
- if len(self.names) > 1:
265
- dest += addnodes.desc_sig_punctuation('.', '.')
266
- signode += dest
267
- self.names[-1].describe_signature(signode, mode, env, '', symbol)
268
- else:
269
- raise Exception('Unknown description mode: %s' % mode)
270
-
271
-
272
- ################################################################################
273
- # Expressions
274
- ################################################################################
275
-
276
- class ASTExpression(ASTBase):
277
- pass
278
-
279
-
280
- # Primary expressions
281
- ################################################################################
282
-
283
- class ASTLiteral(ASTExpression):
284
- pass
285
-
286
-
287
- class ASTBooleanLiteral(ASTLiteral):
288
- def __init__(self, value: bool) -> None:
289
- self.value = value
290
-
291
- def _stringify(self, transform: StringifyTransform) -> str:
292
- if self.value:
293
- return 'true'
294
- else:
295
- return 'false'
296
-
297
- def describe_signature(self, signode: TextElement, mode: str,
298
- env: BuildEnvironment, symbol: Symbol) -> None:
299
- txt = str(self)
300
- signode += addnodes.desc_sig_keyword(txt, txt)
301
-
302
-
303
- class ASTNumberLiteral(ASTLiteral):
304
- def __init__(self, data: str) -> None:
305
- self.data = data
306
-
307
- def _stringify(self, transform: StringifyTransform) -> str:
308
- return self.data
309
-
310
- def describe_signature(self, signode: TextElement, mode: str,
311
- env: BuildEnvironment, symbol: Symbol) -> None:
312
- txt = str(self)
313
- signode += addnodes.desc_sig_literal_number(txt, txt)
314
-
315
-
316
- class ASTCharLiteral(ASTLiteral):
317
- def __init__(self, prefix: str, data: str) -> None:
318
- self.prefix = prefix # may be None when no prefix
319
- self.data = data
320
- decoded = data.encode().decode('unicode-escape')
321
- if len(decoded) == 1:
322
- self.value = ord(decoded)
323
- else:
324
- raise UnsupportedMultiCharacterCharLiteral(decoded)
325
-
326
- def _stringify(self, transform: StringifyTransform) -> str:
327
- if self.prefix is None:
328
- return "'" + self.data + "'"
329
- else:
330
- return self.prefix + "'" + self.data + "'"
331
-
332
- def describe_signature(self, signode: TextElement, mode: str,
333
- env: BuildEnvironment, symbol: Symbol) -> None:
334
- txt = str(self)
335
- signode += addnodes.desc_sig_literal_char(txt, txt)
336
-
337
-
338
- class ASTStringLiteral(ASTLiteral):
339
- def __init__(self, data: str) -> None:
340
- self.data = data
341
-
342
- def _stringify(self, transform: StringifyTransform) -> str:
343
- return self.data
344
-
345
- def describe_signature(self, signode: TextElement, mode: str,
346
- env: BuildEnvironment, symbol: Symbol) -> None:
347
- txt = str(self)
348
- signode += addnodes.desc_sig_literal_string(txt, txt)
349
-
350
-
351
- class ASTIdExpression(ASTExpression):
352
- def __init__(self, name: ASTNestedName):
353
- # note: this class is basically to cast a nested name as an expression
354
- self.name = name
355
-
356
- def _stringify(self, transform: StringifyTransform) -> str:
357
- return transform(self.name)
358
-
359
- def get_id(self, version: int) -> str:
360
- return self.name.get_id(version)
361
-
362
- def describe_signature(self, signode: TextElement, mode: str,
363
- env: BuildEnvironment, symbol: Symbol) -> None:
364
- self.name.describe_signature(signode, mode, env, symbol)
365
-
366
-
367
- class ASTParenExpr(ASTExpression):
368
- def __init__(self, expr):
369
- self.expr = expr
370
-
371
- def _stringify(self, transform: StringifyTransform) -> str:
372
- return '(' + transform(self.expr) + ')'
373
-
374
- def get_id(self, version: int) -> str:
375
- return self.expr.get_id(version)
376
-
377
- def describe_signature(self, signode: TextElement, mode: str,
378
- env: BuildEnvironment, symbol: Symbol) -> None:
379
- signode += addnodes.desc_sig_punctuation('(', '(')
380
- self.expr.describe_signature(signode, mode, env, symbol)
381
- signode += addnodes.desc_sig_punctuation(')', ')')
382
-
383
-
384
- # Postfix expressions
385
- ################################################################################
386
-
387
- class ASTPostfixOp(ASTBase):
388
- pass
389
-
390
-
391
- class ASTPostfixCallExpr(ASTPostfixOp):
392
- def __init__(self, lst: ASTParenExprList | ASTBracedInitList) -> None:
393
- self.lst = lst
394
-
395
- def _stringify(self, transform: StringifyTransform) -> str:
396
- return transform(self.lst)
397
-
398
- def describe_signature(self, signode: TextElement, mode: str,
399
- env: BuildEnvironment, symbol: Symbol) -> None:
400
- self.lst.describe_signature(signode, mode, env, symbol)
401
-
402
-
403
- class ASTPostfixArray(ASTPostfixOp):
404
- def __init__(self, expr: ASTExpression) -> None:
405
- self.expr = expr
406
-
407
- def _stringify(self, transform: StringifyTransform) -> str:
408
- return '[' + transform(self.expr) + ']'
409
-
410
- def describe_signature(self, signode: TextElement, mode: str,
411
- env: BuildEnvironment, symbol: Symbol) -> None:
412
- signode += addnodes.desc_sig_punctuation('[', '[')
413
- self.expr.describe_signature(signode, mode, env, symbol)
414
- signode += addnodes.desc_sig_punctuation(']', ']')
415
-
416
-
417
- class ASTPostfixInc(ASTPostfixOp):
418
- def _stringify(self, transform: StringifyTransform) -> str:
419
- return '++'
420
-
421
- def describe_signature(self, signode: TextElement, mode: str,
422
- env: BuildEnvironment, symbol: Symbol) -> None:
423
- signode += addnodes.desc_sig_operator('++', '++')
424
-
425
-
426
- class ASTPostfixDec(ASTPostfixOp):
427
- def _stringify(self, transform: StringifyTransform) -> str:
428
- return '--'
429
-
430
- def describe_signature(self, signode: TextElement, mode: str,
431
- env: BuildEnvironment, symbol: Symbol) -> None:
432
- signode += addnodes.desc_sig_operator('--', '--')
433
-
434
-
435
- class ASTPostfixMemberOfPointer(ASTPostfixOp):
436
- def __init__(self, name):
437
- self.name = name
438
-
439
- def _stringify(self, transform: StringifyTransform) -> str:
440
- return '->' + transform(self.name)
441
-
442
- def describe_signature(self, signode: TextElement, mode: str,
443
- env: BuildEnvironment, symbol: Symbol) -> None:
444
- signode += addnodes.desc_sig_operator('->', '->')
445
- self.name.describe_signature(signode, 'noneIsName', env, symbol)
446
-
447
-
448
- class ASTPostfixExpr(ASTExpression):
449
- def __init__(self, prefix: ASTExpression, postFixes: list[ASTPostfixOp]):
450
- self.prefix = prefix
451
- self.postFixes = postFixes
452
-
453
- def _stringify(self, transform: StringifyTransform) -> str:
454
- res = [transform(self.prefix)]
455
- for p in self.postFixes:
456
- res.append(transform(p))
457
- return ''.join(res)
458
-
459
- def describe_signature(self, signode: TextElement, mode: str,
460
- env: BuildEnvironment, symbol: Symbol) -> None:
461
- self.prefix.describe_signature(signode, mode, env, symbol)
462
- for p in self.postFixes:
463
- p.describe_signature(signode, mode, env, symbol)
464
-
465
-
466
- # Unary expressions
467
- ################################################################################
468
-
469
- class ASTUnaryOpExpr(ASTExpression):
470
- def __init__(self, op: str, expr: ASTExpression):
471
- self.op = op
472
- self.expr = expr
473
-
474
- def _stringify(self, transform: StringifyTransform) -> str:
475
- if self.op[0] in 'cn':
476
- return self.op + " " + transform(self.expr)
477
- else:
478
- return self.op + transform(self.expr)
479
-
480
- def describe_signature(self, signode: TextElement, mode: str,
481
- env: BuildEnvironment, symbol: Symbol) -> None:
482
- if self.op[0] in 'cn':
483
- signode += addnodes.desc_sig_keyword(self.op, self.op)
484
- signode += addnodes.desc_sig_space()
485
- else:
486
- signode += addnodes.desc_sig_operator(self.op, self.op)
487
- self.expr.describe_signature(signode, mode, env, symbol)
488
-
489
-
490
- class ASTSizeofType(ASTExpression):
491
- def __init__(self, typ):
492
- self.typ = typ
493
-
494
- def _stringify(self, transform: StringifyTransform) -> str:
495
- return "sizeof(" + transform(self.typ) + ")"
496
-
497
- def describe_signature(self, signode: TextElement, mode: str,
498
- env: BuildEnvironment, symbol: Symbol) -> None:
499
- signode += addnodes.desc_sig_keyword('sizeof', 'sizeof')
500
- signode += addnodes.desc_sig_punctuation('(', '(')
501
- self.typ.describe_signature(signode, mode, env, symbol)
502
- signode += addnodes.desc_sig_punctuation(')', ')')
503
-
504
-
505
- class ASTSizeofExpr(ASTExpression):
506
- def __init__(self, expr: ASTExpression):
507
- self.expr = expr
508
-
509
- def _stringify(self, transform: StringifyTransform) -> str:
510
- return "sizeof " + transform(self.expr)
511
-
512
- def describe_signature(self, signode: TextElement, mode: str,
513
- env: BuildEnvironment, symbol: Symbol) -> None:
514
- signode += addnodes.desc_sig_keyword('sizeof', 'sizeof')
515
- signode += addnodes.desc_sig_space()
516
- self.expr.describe_signature(signode, mode, env, symbol)
517
-
518
-
519
- class ASTAlignofExpr(ASTExpression):
520
- def __init__(self, typ: ASTType):
521
- self.typ = typ
522
-
523
- def _stringify(self, transform: StringifyTransform) -> str:
524
- return "alignof(" + transform(self.typ) + ")"
525
-
526
- def describe_signature(self, signode: TextElement, mode: str,
527
- env: BuildEnvironment, symbol: Symbol) -> None:
528
- signode += addnodes.desc_sig_keyword('alignof', 'alignof')
529
- signode += addnodes.desc_sig_punctuation('(', '(')
530
- self.typ.describe_signature(signode, mode, env, symbol)
531
- signode += addnodes.desc_sig_punctuation(')', ')')
532
-
533
-
534
- # Other expressions
535
- ################################################################################
536
-
537
- class ASTCastExpr(ASTExpression):
538
- def __init__(self, typ: ASTType, expr: ASTExpression):
539
- self.typ = typ
540
- self.expr = expr
541
-
542
- def _stringify(self, transform: StringifyTransform) -> str:
543
- res = ['(']
544
- res.append(transform(self.typ))
545
- res.append(')')
546
- res.append(transform(self.expr))
547
- return ''.join(res)
548
-
549
- def describe_signature(self, signode: TextElement, mode: str,
550
- env: BuildEnvironment, symbol: Symbol) -> None:
551
- signode += addnodes.desc_sig_punctuation('(', '(')
552
- self.typ.describe_signature(signode, mode, env, symbol)
553
- signode += addnodes.desc_sig_punctuation(')', ')')
554
- self.expr.describe_signature(signode, mode, env, symbol)
555
-
556
-
557
- class ASTBinOpExpr(ASTBase):
558
- def __init__(self, exprs: list[ASTExpression], ops: list[str]):
559
- assert len(exprs) > 0
560
- assert len(exprs) == len(ops) + 1
561
- self.exprs = exprs
562
- self.ops = ops
563
-
564
- def _stringify(self, transform: StringifyTransform) -> str:
565
- res = []
566
- res.append(transform(self.exprs[0]))
567
- for i in range(1, len(self.exprs)):
568
- res.append(' ')
569
- res.append(self.ops[i - 1])
570
- res.append(' ')
571
- res.append(transform(self.exprs[i]))
572
- return ''.join(res)
573
-
574
- def describe_signature(self, signode: TextElement, mode: str,
575
- env: BuildEnvironment, symbol: Symbol) -> None:
576
- self.exprs[0].describe_signature(signode, mode, env, symbol)
577
- for i in range(1, len(self.exprs)):
578
- signode += addnodes.desc_sig_space()
579
- op = self.ops[i - 1]
580
- if ord(op[0]) >= ord('a') and ord(op[0]) <= ord('z'):
581
- signode += addnodes.desc_sig_keyword(op, op)
582
- else:
583
- signode += addnodes.desc_sig_operator(op, op)
584
- signode += addnodes.desc_sig_space()
585
- self.exprs[i].describe_signature(signode, mode, env, symbol)
586
-
587
-
588
- class ASTAssignmentExpr(ASTExpression):
589
- def __init__(self, exprs: list[ASTExpression], ops: list[str]):
590
- assert len(exprs) > 0
591
- assert len(exprs) == len(ops) + 1
592
- self.exprs = exprs
593
- self.ops = ops
594
-
595
- def _stringify(self, transform: StringifyTransform) -> str:
596
- res = []
597
- res.append(transform(self.exprs[0]))
598
- for i in range(1, len(self.exprs)):
599
- res.append(' ')
600
- res.append(self.ops[i - 1])
601
- res.append(' ')
602
- res.append(transform(self.exprs[i]))
603
- return ''.join(res)
604
-
605
- def describe_signature(self, signode: TextElement, mode: str,
606
- env: BuildEnvironment, symbol: Symbol) -> None:
607
- self.exprs[0].describe_signature(signode, mode, env, symbol)
608
- for i in range(1, len(self.exprs)):
609
- signode += addnodes.desc_sig_space()
610
- op = self.ops[i - 1]
611
- if ord(op[0]) >= ord('a') and ord(op[0]) <= ord('z'):
612
- signode += addnodes.desc_sig_keyword(op, op)
613
- else:
614
- signode += addnodes.desc_sig_operator(op, op)
615
- signode += addnodes.desc_sig_space()
616
- self.exprs[i].describe_signature(signode, mode, env, symbol)
617
-
618
-
619
- class ASTFallbackExpr(ASTExpression):
620
- def __init__(self, expr: str):
621
- self.expr = expr
622
-
623
- def _stringify(self, transform: StringifyTransform) -> str:
624
- return self.expr
625
-
626
- def get_id(self, version: int) -> str:
627
- return str(self.expr)
628
-
629
- def describe_signature(self, signode: TextElement, mode: str,
630
- env: BuildEnvironment, symbol: Symbol) -> None:
631
- signode += nodes.literal(self.expr, self.expr)
632
-
633
-
634
- ################################################################################
635
- # Types
636
- ################################################################################
637
-
638
- class ASTTrailingTypeSpec(ASTBase):
639
- pass
640
-
641
-
642
- class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
643
- def __init__(self, names: list[str]) -> None:
644
- assert len(names) != 0
645
- self.names = names
646
-
647
- def _stringify(self, transform: StringifyTransform) -> str:
648
- return ' '.join(self.names)
649
-
650
- def describe_signature(self, signode: TextElement, mode: str,
651
- env: BuildEnvironment, symbol: Symbol) -> None:
652
- first = True
653
- for n in self.names:
654
- if not first:
655
- signode += addnodes.desc_sig_space()
656
- else:
657
- first = False
658
- signode += addnodes.desc_sig_keyword_type(n, n)
659
-
660
-
661
- class ASTTrailingTypeSpecName(ASTTrailingTypeSpec):
662
- def __init__(self, prefix: str, nestedName: ASTNestedName) -> None:
663
- self.prefix = prefix
664
- self.nestedName = nestedName
665
-
666
- @property
667
- def name(self) -> ASTNestedName:
668
- return self.nestedName
669
-
670
- def _stringify(self, transform: StringifyTransform) -> str:
671
- res = []
672
- if self.prefix:
673
- res.append(self.prefix)
674
- res.append(' ')
675
- res.append(transform(self.nestedName))
676
- return ''.join(res)
677
-
678
- def describe_signature(self, signode: TextElement, mode: str,
679
- env: BuildEnvironment, symbol: Symbol) -> None:
680
- if self.prefix:
681
- signode += addnodes.desc_sig_keyword(self.prefix, self.prefix)
682
- signode += addnodes.desc_sig_space()
683
- self.nestedName.describe_signature(signode, mode, env, symbol=symbol)
684
-
685
-
686
- class ASTFunctionParameter(ASTBase):
687
- def __init__(self, arg: ASTTypeWithInit | None, ellipsis: bool = False) -> None:
688
- self.arg = arg
689
- self.ellipsis = ellipsis
690
-
691
- def get_id(self, version: int, objectType: str, symbol: Symbol) -> str:
692
- # the anchor will be our parent
693
- return symbol.parent.declaration.get_id(version, prefixed=False)
694
-
695
- def _stringify(self, transform: StringifyTransform) -> str:
696
- if self.ellipsis:
697
- return '...'
698
- else:
699
- return transform(self.arg)
700
-
701
- def describe_signature(self, signode: Any, mode: str,
702
- env: BuildEnvironment, symbol: Symbol) -> None:
703
- verify_description_mode(mode)
704
- if self.ellipsis:
705
- signode += addnodes.desc_sig_punctuation('...', '...')
706
- else:
707
- self.arg.describe_signature(signode, mode, env, symbol=symbol)
708
-
709
-
710
- class ASTParameters(ASTBase):
711
- def __init__(self, args: list[ASTFunctionParameter], attrs: ASTAttributeList) -> None:
712
- self.args = args
713
- self.attrs = attrs
714
-
715
- @property
716
- def function_params(self) -> list[ASTFunctionParameter]:
717
- return self.args
718
-
719
- def _stringify(self, transform: StringifyTransform) -> str:
720
- res = []
721
- res.append('(')
722
- first = True
723
- for a in self.args:
724
- if not first:
725
- res.append(', ')
726
- first = False
727
- res.append(str(a))
728
- res.append(')')
729
- if len(self.attrs) != 0:
730
- res.append(' ')
731
- res.append(transform(self.attrs))
732
- return ''.join(res)
733
-
734
- def describe_signature(self, signode: TextElement, mode: str,
735
- env: BuildEnvironment, symbol: Symbol) -> None:
736
- verify_description_mode(mode)
737
- multi_line_parameter_list = False
738
- test_node: Element = signode
739
- while test_node.parent:
740
- if not isinstance(test_node, addnodes.desc_signature):
741
- test_node = test_node.parent
742
- continue
743
- multi_line_parameter_list = test_node.get('multi_line_parameter_list', False)
744
- break
745
-
746
- # only use the desc_parameterlist for the outer list, not for inner lists
747
- if mode == 'lastIsName':
748
- paramlist = addnodes.desc_parameterlist()
749
- paramlist['multi_line_parameter_list'] = multi_line_parameter_list
750
- for arg in self.args:
751
- param = addnodes.desc_parameter('', '', noemph=True)
752
- arg.describe_signature(param, 'param', env, symbol=symbol)
753
- paramlist += param
754
- signode += paramlist
755
- else:
756
- signode += addnodes.desc_sig_punctuation('(', '(')
757
- first = True
758
- for arg in self.args:
759
- if not first:
760
- signode += addnodes.desc_sig_punctuation(',', ',')
761
- signode += addnodes.desc_sig_space()
762
- first = False
763
- arg.describe_signature(signode, 'markType', env, symbol=symbol)
764
- signode += addnodes.desc_sig_punctuation(')', ')')
765
-
766
- if len(self.attrs) != 0:
767
- signode += addnodes.desc_sig_space()
768
- self.attrs.describe_signature(signode)
769
-
770
-
771
- class ASTDeclSpecsSimple(ASTBaseBase):
772
- def __init__(self, storage: str, threadLocal: str, inline: bool,
773
- restrict: bool, volatile: bool, const: bool, attrs: ASTAttributeList) -> None:
774
- self.storage = storage
775
- self.threadLocal = threadLocal
776
- self.inline = inline
777
- self.restrict = restrict
778
- self.volatile = volatile
779
- self.const = const
780
- self.attrs = attrs
781
-
782
- def mergeWith(self, other: ASTDeclSpecsSimple) -> ASTDeclSpecsSimple:
783
- if not other:
784
- return self
785
- return ASTDeclSpecsSimple(self.storage or other.storage,
786
- self.threadLocal or other.threadLocal,
787
- self.inline or other.inline,
788
- self.volatile or other.volatile,
789
- self.const or other.const,
790
- self.restrict or other.restrict,
791
- self.attrs + other.attrs)
792
-
793
- def _stringify(self, transform: StringifyTransform) -> str:
794
- res: list[str] = []
795
- if len(self.attrs) != 0:
796
- res.append(transform(self.attrs))
797
- if self.storage:
798
- res.append(self.storage)
799
- if self.threadLocal:
800
- res.append(self.threadLocal)
801
- if self.inline:
802
- res.append('inline')
803
- if self.restrict:
804
- res.append('restrict')
805
- if self.volatile:
806
- res.append('volatile')
807
- if self.const:
808
- res.append('const')
809
- return ' '.join(res)
810
-
811
- def describe_signature(self, modifiers: list[Node]) -> None:
812
- def _add(modifiers: list[Node], text: str) -> None:
813
- if len(modifiers) != 0:
814
- modifiers.append(addnodes.desc_sig_space())
815
- modifiers.append(addnodes.desc_sig_keyword(text, text))
816
-
817
- if len(modifiers) != 0 and len(self.attrs) != 0:
818
- modifiers.append(addnodes.desc_sig_space())
819
- tempNode = nodes.TextElement()
820
- self.attrs.describe_signature(tempNode)
821
- modifiers.extend(tempNode.children)
822
- if self.storage:
823
- _add(modifiers, self.storage)
824
- if self.threadLocal:
825
- _add(modifiers, self.threadLocal)
826
- if self.inline:
827
- _add(modifiers, 'inline')
828
- if self.restrict:
829
- _add(modifiers, 'restrict')
830
- if self.volatile:
831
- _add(modifiers, 'volatile')
832
- if self.const:
833
- _add(modifiers, 'const')
834
-
835
-
836
- class ASTDeclSpecs(ASTBase):
837
- def __init__(self, outer: str,
838
- leftSpecs: ASTDeclSpecsSimple,
839
- rightSpecs: ASTDeclSpecsSimple,
840
- trailing: ASTTrailingTypeSpec) -> None:
841
- # leftSpecs and rightSpecs are used for output
842
- # allSpecs are used for id generation TODO: remove?
843
- self.outer = outer
844
- self.leftSpecs = leftSpecs
845
- self.rightSpecs = rightSpecs
846
- self.allSpecs = self.leftSpecs.mergeWith(self.rightSpecs)
847
- self.trailingTypeSpec = trailing
848
-
849
- def _stringify(self, transform: StringifyTransform) -> str:
850
- res: list[str] = []
851
- l = transform(self.leftSpecs)
852
- if len(l) > 0:
853
- res.append(l)
854
- if self.trailingTypeSpec:
855
- if len(res) > 0:
856
- res.append(" ")
857
- res.append(transform(self.trailingTypeSpec))
858
- r = str(self.rightSpecs)
859
- if len(r) > 0:
860
- if len(res) > 0:
861
- res.append(" ")
862
- res.append(r)
863
- return "".join(res)
864
-
865
- def describe_signature(self, signode: TextElement, mode: str,
866
- env: BuildEnvironment, symbol: Symbol) -> None:
867
- verify_description_mode(mode)
868
- modifiers: list[Node] = []
869
-
870
- self.leftSpecs.describe_signature(modifiers)
871
-
872
- for m in modifiers:
873
- signode += m
874
- if self.trailingTypeSpec:
875
- if len(modifiers) > 0:
876
- signode += addnodes.desc_sig_space()
877
- self.trailingTypeSpec.describe_signature(signode, mode, env,
878
- symbol=symbol)
879
- modifiers = []
880
- self.rightSpecs.describe_signature(modifiers)
881
- if len(modifiers) > 0:
882
- signode += addnodes.desc_sig_space()
883
- for m in modifiers:
884
- signode += m
885
-
886
-
887
- # Declarator
888
- ################################################################################
889
-
890
- class ASTArray(ASTBase):
891
- def __init__(self, static: bool, const: bool, volatile: bool, restrict: bool,
892
- vla: bool, size: ASTExpression):
893
- self.static = static
894
- self.const = const
895
- self.volatile = volatile
896
- self.restrict = restrict
897
- self.vla = vla
898
- self.size = size
899
- if vla:
900
- assert size is None
901
- if size is not None:
902
- assert not vla
903
-
904
- def _stringify(self, transform: StringifyTransform) -> str:
905
- el = []
906
- if self.static:
907
- el.append('static')
908
- if self.restrict:
909
- el.append('restrict')
910
- if self.volatile:
911
- el.append('volatile')
912
- if self.const:
913
- el.append('const')
914
- if self.vla:
915
- return '[' + ' '.join(el) + '*]'
916
- elif self.size:
917
- el.append(transform(self.size))
918
- return '[' + ' '.join(el) + ']'
919
-
920
- def describe_signature(self, signode: TextElement, mode: str,
921
- env: BuildEnvironment, symbol: Symbol) -> None:
922
- verify_description_mode(mode)
923
- signode += addnodes.desc_sig_punctuation('[', '[')
924
- addSpace = False
925
-
926
- def _add(signode: TextElement, text: str) -> bool:
927
- if addSpace:
928
- signode += addnodes.desc_sig_space()
929
- signode += addnodes.desc_sig_keyword(text, text)
930
- return True
931
-
932
- if self.static:
933
- addSpace = _add(signode, 'static')
934
- if self.restrict:
935
- addSpace = _add(signode, 'restrict')
936
- if self.volatile:
937
- addSpace = _add(signode, 'volatile')
938
- if self.const:
939
- addSpace = _add(signode, 'const')
940
- if self.vla:
941
- signode += addnodes.desc_sig_punctuation('*', '*')
942
- elif self.size:
943
- if addSpace:
944
- signode += addnodes.desc_sig_space()
945
- self.size.describe_signature(signode, 'markType', env, symbol)
946
- signode += addnodes.desc_sig_punctuation(']', ']')
947
-
948
-
949
- class ASTDeclarator(ASTBase):
950
- @property
951
- def name(self) -> ASTNestedName:
952
- raise NotImplementedError(repr(self))
953
-
954
- @property
955
- def function_params(self) -> list[ASTFunctionParameter]:
956
- raise NotImplementedError(repr(self))
957
-
958
- def require_space_after_declSpecs(self) -> bool:
959
- raise NotImplementedError(repr(self))
960
-
961
-
962
- class ASTDeclaratorNameParam(ASTDeclarator):
963
- def __init__(self, declId: ASTNestedName,
964
- arrayOps: list[ASTArray], param: ASTParameters) -> None:
965
- self.declId = declId
966
- self.arrayOps = arrayOps
967
- self.param = param
968
-
969
- @property
970
- def name(self) -> ASTNestedName:
971
- return self.declId
972
-
973
- @property
974
- def function_params(self) -> list[ASTFunctionParameter]:
975
- return self.param.function_params
976
-
977
- # ------------------------------------------------------------------------
978
-
979
- def require_space_after_declSpecs(self) -> bool:
980
- return self.declId is not None
981
-
982
- def _stringify(self, transform: StringifyTransform) -> str:
983
- res = []
984
- if self.declId:
985
- res.append(transform(self.declId))
986
- for op in self.arrayOps:
987
- res.append(transform(op))
988
- if self.param:
989
- res.append(transform(self.param))
990
- return ''.join(res)
991
-
992
- def describe_signature(self, signode: TextElement, mode: str,
993
- env: BuildEnvironment, symbol: Symbol) -> None:
994
- verify_description_mode(mode)
995
- if self.declId:
996
- self.declId.describe_signature(signode, mode, env, symbol)
997
- for op in self.arrayOps:
998
- op.describe_signature(signode, mode, env, symbol)
999
- if self.param:
1000
- self.param.describe_signature(signode, mode, env, symbol)
1001
-
1002
-
1003
- class ASTDeclaratorNameBitField(ASTDeclarator):
1004
- def __init__(self, declId: ASTNestedName, size: ASTExpression):
1005
- self.declId = declId
1006
- self.size = size
1007
-
1008
- @property
1009
- def name(self) -> ASTNestedName:
1010
- return self.declId
1011
-
1012
- # ------------------------------------------------------------------------
1013
-
1014
- def require_space_after_declSpecs(self) -> bool:
1015
- return self.declId is not None
1016
-
1017
- def _stringify(self, transform: StringifyTransform) -> str:
1018
- res = []
1019
- if self.declId:
1020
- res.append(transform(self.declId))
1021
- res.append(" : ")
1022
- res.append(transform(self.size))
1023
- return ''.join(res)
1024
-
1025
- def describe_signature(self, signode: TextElement, mode: str,
1026
- env: BuildEnvironment, symbol: Symbol) -> None:
1027
- verify_description_mode(mode)
1028
- if self.declId:
1029
- self.declId.describe_signature(signode, mode, env, symbol)
1030
- signode += addnodes.desc_sig_space()
1031
- signode += addnodes.desc_sig_punctuation(':', ':')
1032
- signode += addnodes.desc_sig_space()
1033
- self.size.describe_signature(signode, mode, env, symbol)
1034
-
1035
-
1036
- class ASTDeclaratorPtr(ASTDeclarator):
1037
- def __init__(self, next: ASTDeclarator, restrict: bool, volatile: bool, const: bool,
1038
- attrs: ASTAttributeList) -> None:
1039
- assert next
1040
- self.next = next
1041
- self.restrict = restrict
1042
- self.volatile = volatile
1043
- self.const = const
1044
- self.attrs = attrs
1045
-
1046
- @property
1047
- def name(self) -> ASTNestedName:
1048
- return self.next.name
1049
-
1050
- @property
1051
- def function_params(self) -> list[ASTFunctionParameter]:
1052
- return self.next.function_params
1053
-
1054
- def require_space_after_declSpecs(self) -> bool:
1055
- return self.const or self.volatile or self.restrict or \
1056
- len(self.attrs) > 0 or \
1057
- self.next.require_space_after_declSpecs()
1058
-
1059
- def _stringify(self, transform: StringifyTransform) -> str:
1060
- res = ['*']
1061
- res.append(transform(self.attrs))
1062
- if len(self.attrs) != 0 and (self.restrict or self.volatile or self.const):
1063
- res.append(' ')
1064
- if self.restrict:
1065
- res.append('restrict')
1066
- if self.volatile:
1067
- if self.restrict:
1068
- res.append(' ')
1069
- res.append('volatile')
1070
- if self.const:
1071
- if self.restrict or self.volatile:
1072
- res.append(' ')
1073
- res.append('const')
1074
- if self.const or self.volatile or self.restrict or len(self.attrs) > 0:
1075
- if self.next.require_space_after_declSpecs():
1076
- res.append(' ')
1077
- res.append(transform(self.next))
1078
- return ''.join(res)
1079
-
1080
- def describe_signature(self, signode: TextElement, mode: str,
1081
- env: BuildEnvironment, symbol: Symbol) -> None:
1082
- verify_description_mode(mode)
1083
- signode += addnodes.desc_sig_punctuation('*', '*')
1084
- self.attrs.describe_signature(signode)
1085
- if len(self.attrs) != 0 and (self.restrict or self.volatile or self.const):
1086
- signode += addnodes.desc_sig_space()
1087
-
1088
- def _add_anno(signode: TextElement, text: str) -> None:
1089
- signode += addnodes.desc_sig_keyword(text, text)
1090
-
1091
- if self.restrict:
1092
- _add_anno(signode, 'restrict')
1093
- if self.volatile:
1094
- if self.restrict:
1095
- signode += addnodes.desc_sig_space()
1096
- _add_anno(signode, 'volatile')
1097
- if self.const:
1098
- if self.restrict or self.volatile:
1099
- signode += addnodes.desc_sig_space()
1100
- _add_anno(signode, 'const')
1101
- if self.const or self.volatile or self.restrict or len(self.attrs) > 0:
1102
- if self.next.require_space_after_declSpecs():
1103
- signode += addnodes.desc_sig_space()
1104
- self.next.describe_signature(signode, mode, env, symbol)
1105
-
1106
-
1107
- class ASTDeclaratorParen(ASTDeclarator):
1108
- def __init__(self, inner: ASTDeclarator, next: ASTDeclarator) -> None:
1109
- assert inner
1110
- assert next
1111
- self.inner = inner
1112
- self.next = next
1113
- # TODO: we assume the name and params are in inner
1114
-
1115
- @property
1116
- def name(self) -> ASTNestedName:
1117
- return self.inner.name
1118
-
1119
- @property
1120
- def function_params(self) -> list[ASTFunctionParameter]:
1121
- return self.inner.function_params
1122
-
1123
- def require_space_after_declSpecs(self) -> bool:
1124
- return True
1125
-
1126
- def _stringify(self, transform: StringifyTransform) -> str:
1127
- res = ['(']
1128
- res.append(transform(self.inner))
1129
- res.append(')')
1130
- res.append(transform(self.next))
1131
- return ''.join(res)
1132
-
1133
- def describe_signature(self, signode: TextElement, mode: str,
1134
- env: BuildEnvironment, symbol: Symbol) -> None:
1135
- verify_description_mode(mode)
1136
- signode += addnodes.desc_sig_punctuation('(', '(')
1137
- self.inner.describe_signature(signode, mode, env, symbol)
1138
- signode += addnodes.desc_sig_punctuation(')', ')')
1139
- self.next.describe_signature(signode, "noneIsName", env, symbol)
1140
-
1141
-
1142
- # Initializer
1143
- ################################################################################
1144
-
1145
- class ASTParenExprList(ASTBaseParenExprList):
1146
- def __init__(self, exprs: list[ASTExpression]) -> None:
1147
- self.exprs = exprs
1148
-
1149
- def _stringify(self, transform: StringifyTransform) -> str:
1150
- exprs = [transform(e) for e in self.exprs]
1151
- return '(%s)' % ', '.join(exprs)
1152
-
1153
- def describe_signature(self, signode: TextElement, mode: str,
1154
- env: BuildEnvironment, symbol: Symbol) -> None:
1155
- verify_description_mode(mode)
1156
- signode += addnodes.desc_sig_punctuation('(', '(')
1157
- first = True
1158
- for e in self.exprs:
1159
- if not first:
1160
- signode += addnodes.desc_sig_punctuation(',', ',')
1161
- signode += addnodes.desc_sig_space()
1162
- else:
1163
- first = False
1164
- e.describe_signature(signode, mode, env, symbol)
1165
- signode += addnodes.desc_sig_punctuation(')', ')')
1166
-
1167
-
1168
- class ASTBracedInitList(ASTBase):
1169
- def __init__(self, exprs: list[ASTExpression], trailingComma: bool) -> None:
1170
- self.exprs = exprs
1171
- self.trailingComma = trailingComma
1172
-
1173
- def _stringify(self, transform: StringifyTransform) -> str:
1174
- exprs = ', '.join(transform(e) for e in self.exprs)
1175
- trailingComma = ',' if self.trailingComma else ''
1176
- return f'{{{exprs}{trailingComma}}}'
1177
-
1178
- def describe_signature(self, signode: TextElement, mode: str,
1179
- env: BuildEnvironment, symbol: Symbol) -> None:
1180
- verify_description_mode(mode)
1181
- signode += addnodes.desc_sig_punctuation('{', '{')
1182
- first = True
1183
- for e in self.exprs:
1184
- if not first:
1185
- signode += addnodes.desc_sig_punctuation(',', ',')
1186
- signode += addnodes.desc_sig_space()
1187
- else:
1188
- first = False
1189
- e.describe_signature(signode, mode, env, symbol)
1190
- if self.trailingComma:
1191
- signode += addnodes.desc_sig_punctuation(',', ',')
1192
- signode += addnodes.desc_sig_punctuation('}', '}')
1193
-
1194
-
1195
- class ASTInitializer(ASTBase):
1196
- def __init__(self, value: ASTBracedInitList | ASTExpression,
1197
- hasAssign: bool = True) -> None:
1198
- self.value = value
1199
- self.hasAssign = hasAssign
1200
-
1201
- def _stringify(self, transform: StringifyTransform) -> str:
1202
- val = transform(self.value)
1203
- if self.hasAssign:
1204
- return ' = ' + val
1205
- else:
1206
- return val
1207
-
1208
- def describe_signature(self, signode: TextElement, mode: str,
1209
- env: BuildEnvironment, symbol: Symbol) -> None:
1210
- verify_description_mode(mode)
1211
- if self.hasAssign:
1212
- signode += addnodes.desc_sig_space()
1213
- signode += addnodes.desc_sig_punctuation('=', '=')
1214
- signode += addnodes.desc_sig_space()
1215
- self.value.describe_signature(signode, 'markType', env, symbol)
1216
-
1217
-
1218
- class ASTType(ASTBase):
1219
- def __init__(self, declSpecs: ASTDeclSpecs, decl: ASTDeclarator) -> None:
1220
- assert declSpecs
1221
- assert decl
1222
- self.declSpecs = declSpecs
1223
- self.decl = decl
1224
-
1225
- @property
1226
- def name(self) -> ASTNestedName:
1227
- return self.decl.name
1228
-
1229
- def get_id(self, version: int, objectType: str, symbol: Symbol) -> str:
1230
- return symbol.get_full_nested_name().get_id(version)
1231
-
1232
- @property
1233
- def function_params(self) -> list[ASTFunctionParameter]:
1234
- return self.decl.function_params
1235
-
1236
- def _stringify(self, transform: StringifyTransform) -> str:
1237
- res = []
1238
- declSpecs = transform(self.declSpecs)
1239
- res.append(declSpecs)
1240
- if self.decl.require_space_after_declSpecs() and len(declSpecs) > 0:
1241
- res.append(' ')
1242
- res.append(transform(self.decl))
1243
- return ''.join(res)
1244
-
1245
- def get_type_declaration_prefix(self) -> str:
1246
- if self.declSpecs.trailingTypeSpec:
1247
- return 'typedef'
1248
- else:
1249
- return 'type'
1250
-
1251
- def describe_signature(self, signode: TextElement, mode: str,
1252
- env: BuildEnvironment, symbol: Symbol) -> None:
1253
- verify_description_mode(mode)
1254
- self.declSpecs.describe_signature(signode, 'markType', env, symbol)
1255
- if (self.decl.require_space_after_declSpecs() and
1256
- len(str(self.declSpecs)) > 0):
1257
- signode += addnodes.desc_sig_space()
1258
- # for parameters that don't really declare new names we get 'markType',
1259
- # this should not be propagated, but be 'noneIsName'.
1260
- if mode == 'markType':
1261
- mode = 'noneIsName'
1262
- self.decl.describe_signature(signode, mode, env, symbol)
1263
-
1264
-
1265
- class ASTTypeWithInit(ASTBase):
1266
- def __init__(self, type: ASTType, init: ASTInitializer) -> None:
1267
- self.type = type
1268
- self.init = init
1269
-
1270
- @property
1271
- def name(self) -> ASTNestedName:
1272
- return self.type.name
1273
-
1274
- def get_id(self, version: int, objectType: str, symbol: Symbol) -> str:
1275
- return self.type.get_id(version, objectType, symbol)
1276
-
1277
- def _stringify(self, transform: StringifyTransform) -> str:
1278
- res = []
1279
- res.append(transform(self.type))
1280
- if self.init:
1281
- res.append(transform(self.init))
1282
- return ''.join(res)
1283
-
1284
- def describe_signature(self, signode: TextElement, mode: str,
1285
- env: BuildEnvironment, symbol: Symbol) -> None:
1286
- verify_description_mode(mode)
1287
- self.type.describe_signature(signode, mode, env, symbol)
1288
- if self.init:
1289
- self.init.describe_signature(signode, mode, env, symbol)
1290
-
1291
-
1292
- class ASTMacroParameter(ASTBase):
1293
- def __init__(self, arg: ASTNestedName | None, ellipsis: bool = False,
1294
- variadic: bool = False) -> None:
1295
- self.arg = arg
1296
- self.ellipsis = ellipsis
1297
- self.variadic = variadic
1298
-
1299
- def _stringify(self, transform: StringifyTransform) -> str:
1300
- if self.ellipsis:
1301
- return '...'
1302
- elif self.variadic:
1303
- return transform(self.arg) + '...'
1304
- else:
1305
- return transform(self.arg)
1306
-
1307
- def describe_signature(self, signode: Any, mode: str,
1308
- env: BuildEnvironment, symbol: Symbol) -> None:
1309
- verify_description_mode(mode)
1310
- if self.ellipsis:
1311
- signode += addnodes.desc_sig_punctuation('...', '...')
1312
- elif self.variadic:
1313
- name = str(self)
1314
- signode += addnodes.desc_sig_name(name, name)
1315
- else:
1316
- self.arg.describe_signature(signode, mode, env, symbol=symbol)
1317
-
1318
-
1319
- class ASTMacro(ASTBase):
1320
- def __init__(self, ident: ASTNestedName, args: list[ASTMacroParameter] | None) -> None:
1321
- self.ident = ident
1322
- self.args = args
1323
-
1324
- @property
1325
- def name(self) -> ASTNestedName:
1326
- return self.ident
1327
-
1328
- def get_id(self, version: int, objectType: str, symbol: Symbol) -> str:
1329
- return symbol.get_full_nested_name().get_id(version)
1330
-
1331
- def _stringify(self, transform: StringifyTransform) -> str:
1332
- res = []
1333
- res.append(transform(self.ident))
1334
- if self.args is not None:
1335
- res.append('(')
1336
- first = True
1337
- for arg in self.args:
1338
- if not first:
1339
- res.append(', ')
1340
- first = False
1341
- res.append(transform(arg))
1342
- res.append(')')
1343
- return ''.join(res)
1344
-
1345
- def describe_signature(self, signode: TextElement, mode: str,
1346
- env: BuildEnvironment, symbol: Symbol) -> None:
1347
- verify_description_mode(mode)
1348
- self.ident.describe_signature(signode, mode, env, symbol)
1349
- if self.args is None:
1350
- return
1351
- paramlist = addnodes.desc_parameterlist()
1352
- for arg in self.args:
1353
- param = addnodes.desc_parameter('', '', noemph=True)
1354
- arg.describe_signature(param, 'param', env, symbol=symbol)
1355
- paramlist += param
1356
- signode += paramlist
1357
-
1358
-
1359
- class ASTStruct(ASTBase):
1360
- def __init__(self, name: ASTNestedName) -> None:
1361
- self.name = name
1362
-
1363
- def get_id(self, version: int, objectType: str, symbol: Symbol) -> str:
1364
- return symbol.get_full_nested_name().get_id(version)
1365
-
1366
- def _stringify(self, transform: StringifyTransform) -> str:
1367
- return transform(self.name)
1368
-
1369
- def describe_signature(self, signode: TextElement, mode: str,
1370
- env: BuildEnvironment, symbol: Symbol) -> None:
1371
- verify_description_mode(mode)
1372
- self.name.describe_signature(signode, mode, env, symbol=symbol)
1373
-
1374
-
1375
- class ASTUnion(ASTBase):
1376
- def __init__(self, name: ASTNestedName) -> None:
1377
- self.name = name
1378
-
1379
- def get_id(self, version: int, objectType: str, symbol: Symbol) -> str:
1380
- return symbol.get_full_nested_name().get_id(version)
1381
-
1382
- def _stringify(self, transform: StringifyTransform) -> str:
1383
- return transform(self.name)
1384
-
1385
- def describe_signature(self, signode: TextElement, mode: str,
1386
- env: BuildEnvironment, symbol: Symbol) -> None:
1387
- verify_description_mode(mode)
1388
- self.name.describe_signature(signode, mode, env, symbol=symbol)
1389
-
1390
-
1391
- class ASTEnum(ASTBase):
1392
- def __init__(self, name: ASTNestedName) -> None:
1393
- self.name = name
1394
-
1395
- def get_id(self, version: int, objectType: str, symbol: Symbol) -> str:
1396
- return symbol.get_full_nested_name().get_id(version)
1397
-
1398
- def _stringify(self, transform: StringifyTransform) -> str:
1399
- return transform(self.name)
1400
-
1401
- def describe_signature(self, signode: TextElement, mode: str,
1402
- env: BuildEnvironment, symbol: Symbol) -> None:
1403
- verify_description_mode(mode)
1404
- self.name.describe_signature(signode, mode, env, symbol=symbol)
1405
-
1406
-
1407
- class ASTEnumerator(ASTBase):
1408
- def __init__(self, name: ASTNestedName, init: ASTInitializer | None,
1409
- attrs: ASTAttributeList) -> None:
1410
- self.name = name
1411
- self.init = init
1412
- self.attrs = attrs
1413
-
1414
- def get_id(self, version: int, objectType: str, symbol: Symbol) -> str:
1415
- return symbol.get_full_nested_name().get_id(version)
1416
-
1417
- def _stringify(self, transform: StringifyTransform) -> str:
1418
- res = []
1419
- res.append(transform(self.name))
1420
- if len(self.attrs) != 0:
1421
- res.append(' ')
1422
- res.append(transform(self.attrs))
1423
- if self.init:
1424
- res.append(transform(self.init))
1425
- return ''.join(res)
1426
-
1427
- def describe_signature(self, signode: TextElement, mode: str,
1428
- env: BuildEnvironment, symbol: Symbol) -> None:
1429
- verify_description_mode(mode)
1430
- self.name.describe_signature(signode, mode, env, symbol)
1431
- if len(self.attrs) != 0:
1432
- signode += addnodes.desc_sig_space()
1433
- self.attrs.describe_signature(signode)
1434
- if self.init:
1435
- self.init.describe_signature(signode, 'markType', env, symbol)
1436
-
1437
-
1438
- class ASTDeclaration(ASTBaseBase):
1439
- def __init__(self, objectType: str, directiveType: str | None,
1440
- declaration: DeclarationType | ASTFunctionParameter,
1441
- semicolon: bool = False) -> None:
1442
- self.objectType = objectType
1443
- self.directiveType = directiveType
1444
- self.declaration = declaration
1445
- self.semicolon = semicolon
1446
-
1447
- self.symbol: Symbol = None
1448
- # set by CObject._add_enumerator_to_parent
1449
- self.enumeratorScopedSymbol: Symbol = None
1450
-
1451
- def clone(self) -> ASTDeclaration:
1452
- return ASTDeclaration(self.objectType, self.directiveType,
1453
- self.declaration.clone(), self.semicolon)
1454
-
1455
- @property
1456
- def name(self) -> ASTNestedName:
1457
- decl = cast(DeclarationType, self.declaration)
1458
- return decl.name
1459
-
1460
- @property
1461
- def function_params(self) -> list[ASTFunctionParameter] | None:
1462
- if self.objectType != 'function':
1463
- return None
1464
- decl = cast(ASTType, self.declaration)
1465
- return decl.function_params
1466
-
1467
- def get_id(self, version: int, prefixed: bool = True) -> str:
1468
- if self.objectType == 'enumerator' and self.enumeratorScopedSymbol:
1469
- return self.enumeratorScopedSymbol.declaration.get_id(version, prefixed)
1470
- id_ = self.declaration.get_id(version, self.objectType, self.symbol)
1471
- if prefixed:
1472
- return _id_prefix[version] + id_
1473
- else:
1474
- return id_
1475
-
1476
- def get_newest_id(self) -> str:
1477
- return self.get_id(_max_id, True)
1478
-
1479
- def _stringify(self, transform: StringifyTransform) -> str:
1480
- res = transform(self.declaration)
1481
- if self.semicolon:
1482
- res += ';'
1483
- return res
1484
-
1485
- def describe_signature(self, signode: TextElement, mode: str,
1486
- env: BuildEnvironment, options: dict) -> None:
1487
- verify_description_mode(mode)
1488
- assert self.symbol
1489
- # The caller of the domain added a desc_signature node.
1490
- # Always enable multiline:
1491
- signode['is_multiline'] = True
1492
- # Put each line in a desc_signature_line node.
1493
- mainDeclNode = addnodes.desc_signature_line()
1494
- mainDeclNode.sphinx_line_type = 'declarator'
1495
- mainDeclNode['add_permalink'] = not self.symbol.isRedeclaration
1496
- signode += mainDeclNode
1497
-
1498
- if self.objectType in {'member', 'function', 'macro'}:
1499
- pass
1500
- elif self.objectType == 'struct':
1501
- mainDeclNode += addnodes.desc_sig_keyword('struct', 'struct')
1502
- mainDeclNode += addnodes.desc_sig_space()
1503
- elif self.objectType == 'union':
1504
- mainDeclNode += addnodes.desc_sig_keyword('union', 'union')
1505
- mainDeclNode += addnodes.desc_sig_space()
1506
- elif self.objectType == 'enum':
1507
- mainDeclNode += addnodes.desc_sig_keyword('enum', 'enum')
1508
- mainDeclNode += addnodes.desc_sig_space()
1509
- elif self.objectType == 'enumerator':
1510
- mainDeclNode += addnodes.desc_sig_keyword('enumerator', 'enumerator')
1511
- mainDeclNode += addnodes.desc_sig_space()
1512
- elif self.objectType == 'type':
1513
- decl = cast(ASTType, self.declaration)
1514
- prefix = decl.get_type_declaration_prefix()
1515
- mainDeclNode += addnodes.desc_sig_keyword(prefix, prefix)
1516
- mainDeclNode += addnodes.desc_sig_space()
1517
- else:
1518
- raise AssertionError
1519
- self.declaration.describe_signature(mainDeclNode, mode, env, self.symbol)
1520
- if self.semicolon:
1521
- mainDeclNode += addnodes.desc_sig_punctuation(';', ';')
1522
-
1523
-
1524
- class SymbolLookupResult:
1525
- def __init__(self, symbols: Iterator[Symbol], parentSymbol: Symbol,
1526
- ident: ASTIdentifier) -> None:
1527
- self.symbols = symbols
1528
- self.parentSymbol = parentSymbol
1529
- self.ident = ident
1530
-
1531
-
1532
- class LookupKey:
1533
- def __init__(self, data: list[tuple[ASTIdentifier, str]]) -> None:
1534
- self.data = data
1535
-
1536
- def __str__(self) -> str:
1537
- inner = ', '.join(f"({ident}, {id_})" for ident, id_ in self.data)
1538
- return f'[{inner}]'
1539
-
1540
-
1541
- class Symbol:
1542
- debug_indent = 0
1543
- debug_indent_string = " "
1544
- debug_lookup = False
1545
- debug_show_tree = False
1546
-
1547
- def __copy__(self):
1548
- raise AssertionError # shouldn't happen
1549
-
1550
- def __deepcopy__(self, memo):
1551
- if self.parent:
1552
- raise AssertionError # shouldn't happen
1553
- # the domain base class makes a copy of the initial data, which is fine
1554
- return Symbol(None, None, None, None, None)
1555
-
1556
- @staticmethod
1557
- def debug_print(*args: Any) -> None:
1558
- logger.debug(Symbol.debug_indent_string * Symbol.debug_indent, end="")
1559
- logger.debug(*args)
1560
-
1561
- def _assert_invariants(self) -> None:
1562
- if not self.parent:
1563
- # parent == None means global scope, so declaration means a parent
1564
- assert not self.declaration
1565
- assert not self.docname
1566
- else:
1567
- if self.declaration:
1568
- assert self.docname
1569
-
1570
- def __setattr__(self, key: str, value: Any) -> None:
1571
- if key == "children":
1572
- raise AssertionError
1573
- return super().__setattr__(key, value)
1574
-
1575
- def __init__(
1576
- self,
1577
- parent: Symbol,
1578
- ident: ASTIdentifier,
1579
- declaration: ASTDeclaration | None,
1580
- docname: str | None,
1581
- line: int | None,
1582
- ) -> None:
1583
- self.parent = parent
1584
- # declarations in a single directive are linked together
1585
- self.siblingAbove: Symbol = None
1586
- self.siblingBelow: Symbol = None
1587
- self.ident = ident
1588
- self.declaration = declaration
1589
- self.docname = docname
1590
- self.line = line
1591
- self.isRedeclaration = False
1592
- self._assert_invariants()
1593
-
1594
- # Remember to modify Symbol.remove if modifications to the parent change.
1595
- self._children: list[Symbol] = []
1596
- self._anonChildren: list[Symbol] = []
1597
- # note: _children includes _anonChildren
1598
- if self.parent:
1599
- self.parent._children.append(self)
1600
- if self.declaration:
1601
- self.declaration.symbol = self
1602
-
1603
- # Do symbol addition after self._children has been initialised.
1604
- self._add_function_params()
1605
-
1606
- def _fill_empty(self, declaration: ASTDeclaration, docname: str, line: int) -> None:
1607
- self._assert_invariants()
1608
- assert self.declaration is None
1609
- assert self.docname is None
1610
- assert self.line is None
1611
- assert declaration is not None
1612
- assert docname is not None
1613
- assert line is not None
1614
- self.declaration = declaration
1615
- self.declaration.symbol = self
1616
- self.docname = docname
1617
- self.line = line
1618
- self._assert_invariants()
1619
- # and symbol addition should be done as well
1620
- self._add_function_params()
1621
-
1622
- def _add_function_params(self) -> None:
1623
- if Symbol.debug_lookup:
1624
- Symbol.debug_indent += 1
1625
- Symbol.debug_print("_add_function_params:")
1626
- # Note: we may be called from _fill_empty, so the symbols we want
1627
- # to add may actually already be present (as empty symbols).
1628
-
1629
- # add symbols for function parameters, if any
1630
- if self.declaration is not None and self.declaration.function_params is not None:
1631
- for p in self.declaration.function_params:
1632
- if p.arg is None:
1633
- continue
1634
- nn = p.arg.name
1635
- if nn is None:
1636
- continue
1637
- # (comparing to the template params: we have checked that we are a declaration)
1638
- decl = ASTDeclaration('functionParam', None, p)
1639
- assert not nn.rooted
1640
- assert len(nn.names) == 1
1641
- self._add_symbols(nn, decl, self.docname, self.line)
1642
- if Symbol.debug_lookup:
1643
- Symbol.debug_indent -= 1
1644
-
1645
- def remove(self) -> None:
1646
- if self.parent is None:
1647
- return
1648
- assert self in self.parent._children
1649
- self.parent._children.remove(self)
1650
- self.parent = None
1651
-
1652
- def clear_doc(self, docname: str) -> None:
1653
- for sChild in self._children:
1654
- sChild.clear_doc(docname)
1655
- if sChild.declaration and sChild.docname == docname:
1656
- sChild.declaration = None
1657
- sChild.docname = None
1658
- sChild.line = None
1659
- if sChild.siblingAbove is not None:
1660
- sChild.siblingAbove.siblingBelow = sChild.siblingBelow
1661
- if sChild.siblingBelow is not None:
1662
- sChild.siblingBelow.siblingAbove = sChild.siblingAbove
1663
- sChild.siblingAbove = None
1664
- sChild.siblingBelow = None
1665
-
1666
- def get_all_symbols(self) -> Iterator[Symbol]:
1667
- yield self
1668
- for sChild in self._children:
1669
- yield from sChild.get_all_symbols()
1670
-
1671
- @property
1672
- def children(self) -> Iterator[Symbol]:
1673
- yield from self._children
1674
-
1675
- @property
1676
- def children_recurse_anon(self) -> Iterator[Symbol]:
1677
- for c in self._children:
1678
- yield c
1679
- if not c.ident.is_anon():
1680
- continue
1681
- yield from c.children_recurse_anon
1682
-
1683
- def get_lookup_key(self) -> LookupKey:
1684
- # The pickle files for the environment and for each document are distinct.
1685
- # The environment has all the symbols, but the documents has xrefs that
1686
- # must know their scope. A lookup key is essentially a specification of
1687
- # how to find a specific symbol.
1688
- symbols = []
1689
- s = self
1690
- while s.parent:
1691
- symbols.append(s)
1692
- s = s.parent
1693
- symbols.reverse()
1694
- key = []
1695
- for s in symbols:
1696
- if s.declaration is not None:
1697
- # TODO: do we need the ID?
1698
- key.append((s.ident, s.declaration.get_newest_id()))
1699
- else:
1700
- key.append((s.ident, None))
1701
- return LookupKey(key)
1702
-
1703
- def get_full_nested_name(self) -> ASTNestedName:
1704
- symbols = []
1705
- s = self
1706
- while s.parent:
1707
- symbols.append(s)
1708
- s = s.parent
1709
- symbols.reverse()
1710
- names = []
1711
- for s in symbols:
1712
- names.append(s.ident)
1713
- return ASTNestedName(names, rooted=False)
1714
-
1715
- def _find_first_named_symbol(self, ident: ASTIdentifier,
1716
- matchSelf: bool, recurseInAnon: bool) -> Symbol | None:
1717
- # TODO: further simplification from C++ to C
1718
- if Symbol.debug_lookup:
1719
- Symbol.debug_print("_find_first_named_symbol ->")
1720
- res = self._find_named_symbols(ident, matchSelf, recurseInAnon,
1721
- searchInSiblings=False)
1722
- try:
1723
- return next(res)
1724
- except StopIteration:
1725
- return None
1726
-
1727
- def _find_named_symbols(self, ident: ASTIdentifier,
1728
- matchSelf: bool, recurseInAnon: bool,
1729
- searchInSiblings: bool) -> Iterator[Symbol]:
1730
- # TODO: further simplification from C++ to C
1731
- if Symbol.debug_lookup:
1732
- Symbol.debug_indent += 1
1733
- Symbol.debug_print("_find_named_symbols:")
1734
- Symbol.debug_indent += 1
1735
- Symbol.debug_print("self:")
1736
- logger.debug(self.to_string(Symbol.debug_indent + 1), end="")
1737
- Symbol.debug_print("ident: ", ident)
1738
- Symbol.debug_print("matchSelf: ", matchSelf)
1739
- Symbol.debug_print("recurseInAnon: ", recurseInAnon)
1740
- Symbol.debug_print("searchInSiblings: ", searchInSiblings)
1741
-
1742
- def candidates() -> Generator[Symbol, None, None]:
1743
- s = self
1744
- if Symbol.debug_lookup:
1745
- Symbol.debug_print("searching in self:")
1746
- logger.debug(s.to_string(Symbol.debug_indent + 1), end="")
1747
- while True:
1748
- if matchSelf:
1749
- yield s
1750
- if recurseInAnon:
1751
- yield from s.children_recurse_anon
1752
- else:
1753
- yield from s._children
1754
-
1755
- if s.siblingAbove is None:
1756
- break
1757
- s = s.siblingAbove
1758
- if Symbol.debug_lookup:
1759
- Symbol.debug_print("searching in sibling:")
1760
- logger.debug(s.to_string(Symbol.debug_indent + 1), end="")
1761
-
1762
- for s in candidates():
1763
- if Symbol.debug_lookup:
1764
- Symbol.debug_print("candidate:")
1765
- logger.debug(s.to_string(Symbol.debug_indent + 1), end="")
1766
- if s.ident == ident:
1767
- if Symbol.debug_lookup:
1768
- Symbol.debug_indent += 1
1769
- Symbol.debug_print("matches")
1770
- Symbol.debug_indent -= 3
1771
- yield s
1772
- if Symbol.debug_lookup:
1773
- Symbol.debug_indent += 2
1774
- if Symbol.debug_lookup:
1775
- Symbol.debug_indent -= 2
1776
-
1777
- def _symbol_lookup(
1778
- self,
1779
- nestedName: ASTNestedName,
1780
- onMissingQualifiedSymbol: Callable[[Symbol, ASTIdentifier], Symbol | None],
1781
- ancestorLookupType: str | None,
1782
- matchSelf: bool,
1783
- recurseInAnon: bool,
1784
- searchInSiblings: bool,
1785
- ) -> SymbolLookupResult | None:
1786
- # TODO: further simplification from C++ to C
1787
- # ancestorLookupType: if not None, specifies the target type of the lookup
1788
- if Symbol.debug_lookup:
1789
- Symbol.debug_indent += 1
1790
- Symbol.debug_print("_symbol_lookup:")
1791
- Symbol.debug_indent += 1
1792
- Symbol.debug_print("self:")
1793
- logger.debug(self.to_string(Symbol.debug_indent + 1), end="")
1794
- Symbol.debug_print("nestedName: ", nestedName)
1795
- Symbol.debug_print("ancestorLookupType:", ancestorLookupType)
1796
- Symbol.debug_print("matchSelf: ", matchSelf)
1797
- Symbol.debug_print("recurseInAnon: ", recurseInAnon)
1798
- Symbol.debug_print("searchInSiblings: ", searchInSiblings)
1799
-
1800
- names = nestedName.names
1801
-
1802
- # find the right starting point for lookup
1803
- parentSymbol = self
1804
- if nestedName.rooted:
1805
- while parentSymbol.parent:
1806
- parentSymbol = parentSymbol.parent
1807
- if ancestorLookupType is not None:
1808
- # walk up until we find the first identifier
1809
- firstName = names[0]
1810
- while parentSymbol.parent:
1811
- if parentSymbol.find_identifier(firstName,
1812
- matchSelf=matchSelf,
1813
- recurseInAnon=recurseInAnon,
1814
- searchInSiblings=searchInSiblings):
1815
- break
1816
- parentSymbol = parentSymbol.parent
1817
-
1818
- if Symbol.debug_lookup:
1819
- Symbol.debug_print("starting point:")
1820
- logger.debug(parentSymbol.to_string(Symbol.debug_indent + 1), end="")
1821
-
1822
- # and now the actual lookup
1823
- for ident in names[:-1]:
1824
- symbol = parentSymbol._find_first_named_symbol(
1825
- ident, matchSelf=matchSelf, recurseInAnon=recurseInAnon)
1826
- if symbol is None:
1827
- symbol = onMissingQualifiedSymbol(parentSymbol, ident)
1828
- if symbol is None:
1829
- if Symbol.debug_lookup:
1830
- Symbol.debug_indent -= 2
1831
- return None
1832
- # We have now matched part of a nested name, and need to match more
1833
- # so even if we should matchSelf before, we definitely shouldn't
1834
- # even more. (see also issue #2666)
1835
- matchSelf = False
1836
- parentSymbol = symbol
1837
-
1838
- if Symbol.debug_lookup:
1839
- Symbol.debug_print("handle last name from:")
1840
- logger.debug(parentSymbol.to_string(Symbol.debug_indent + 1), end="")
1841
-
1842
- # handle the last name
1843
- ident = names[-1]
1844
-
1845
- symbols = parentSymbol._find_named_symbols(
1846
- ident, matchSelf=matchSelf,
1847
- recurseInAnon=recurseInAnon,
1848
- searchInSiblings=searchInSiblings)
1849
- if Symbol.debug_lookup:
1850
- symbols = list(symbols) # type: ignore[assignment]
1851
- Symbol.debug_indent -= 2
1852
- return SymbolLookupResult(symbols, parentSymbol, ident)
1853
-
1854
- def _add_symbols(
1855
- self,
1856
- nestedName: ASTNestedName,
1857
- declaration: ASTDeclaration | None,
1858
- docname: str | None,
1859
- line: int | None,
1860
- ) -> Symbol:
1861
- # TODO: further simplification from C++ to C
1862
- # Used for adding a whole path of symbols, where the last may or may not
1863
- # be an actual declaration.
1864
-
1865
- if Symbol.debug_lookup:
1866
- Symbol.debug_indent += 1
1867
- Symbol.debug_print("_add_symbols:")
1868
- Symbol.debug_indent += 1
1869
- Symbol.debug_print("nn: ", nestedName)
1870
- Symbol.debug_print("decl: ", declaration)
1871
- Symbol.debug_print(f"location: {docname}:{line}")
1872
-
1873
- def onMissingQualifiedSymbol(parentSymbol: Symbol, ident: ASTIdentifier) -> Symbol:
1874
- if Symbol.debug_lookup:
1875
- Symbol.debug_indent += 1
1876
- Symbol.debug_print("_add_symbols, onMissingQualifiedSymbol:")
1877
- Symbol.debug_indent += 1
1878
- Symbol.debug_print("ident: ", ident)
1879
- Symbol.debug_indent -= 2
1880
- return Symbol(parent=parentSymbol, ident=ident,
1881
- declaration=None, docname=None, line=None)
1882
-
1883
- lookupResult = self._symbol_lookup(nestedName,
1884
- onMissingQualifiedSymbol,
1885
- ancestorLookupType=None,
1886
- matchSelf=False,
1887
- recurseInAnon=False,
1888
- searchInSiblings=False)
1889
- assert lookupResult is not None # we create symbols all the way, so that can't happen
1890
- symbols = list(lookupResult.symbols)
1891
- if len(symbols) == 0:
1892
- if Symbol.debug_lookup:
1893
- Symbol.debug_print("_add_symbols, result, no symbol:")
1894
- Symbol.debug_indent += 1
1895
- Symbol.debug_print("ident: ", lookupResult.ident)
1896
- Symbol.debug_print("declaration: ", declaration)
1897
- Symbol.debug_print(f"location: {docname}:{line}")
1898
- Symbol.debug_indent -= 1
1899
- symbol = Symbol(parent=lookupResult.parentSymbol,
1900
- ident=lookupResult.ident,
1901
- declaration=declaration,
1902
- docname=docname, line=line)
1903
- if Symbol.debug_lookup:
1904
- Symbol.debug_indent -= 2
1905
- return symbol
1906
-
1907
- if Symbol.debug_lookup:
1908
- Symbol.debug_print("_add_symbols, result, symbols:")
1909
- Symbol.debug_indent += 1
1910
- Symbol.debug_print("number symbols:", len(symbols))
1911
- Symbol.debug_indent -= 1
1912
-
1913
- if not declaration:
1914
- if Symbol.debug_lookup:
1915
- Symbol.debug_print("no declaration")
1916
- Symbol.debug_indent -= 2
1917
- # good, just a scope creation
1918
- # TODO: what if we have more than one symbol?
1919
- return symbols[0]
1920
-
1921
- noDecl = []
1922
- withDecl = []
1923
- dupDecl = []
1924
- for s in symbols:
1925
- if s.declaration is None:
1926
- noDecl.append(s)
1927
- elif s.isRedeclaration:
1928
- dupDecl.append(s)
1929
- else:
1930
- withDecl.append(s)
1931
- if Symbol.debug_lookup:
1932
- Symbol.debug_print("#noDecl: ", len(noDecl))
1933
- Symbol.debug_print("#withDecl:", len(withDecl))
1934
- Symbol.debug_print("#dupDecl: ", len(dupDecl))
1935
-
1936
- # With partial builds we may start with a large symbol tree stripped of declarations.
1937
- # Essentially any combination of noDecl, withDecl, and dupDecls seems possible.
1938
- # TODO: make partial builds fully work. What should happen when the primary symbol gets
1939
- # deleted, and other duplicates exist? The full document should probably be rebuild.
1940
-
1941
- # First check if one of those with a declaration matches.
1942
- # If it's a function, we need to compare IDs,
1943
- # otherwise there should be only one symbol with a declaration.
1944
- def makeCandSymbol() -> Symbol:
1945
- if Symbol.debug_lookup:
1946
- Symbol.debug_print("begin: creating candidate symbol")
1947
- symbol = Symbol(parent=lookupResult.parentSymbol,
1948
- ident=lookupResult.ident,
1949
- declaration=declaration,
1950
- docname=docname, line=line)
1951
- if Symbol.debug_lookup:
1952
- Symbol.debug_print("end: creating candidate symbol")
1953
- return symbol
1954
-
1955
- if len(withDecl) == 0:
1956
- candSymbol = None
1957
- else:
1958
- candSymbol = makeCandSymbol()
1959
-
1960
- def handleDuplicateDeclaration(symbol: Symbol, candSymbol: Symbol) -> None:
1961
- if Symbol.debug_lookup:
1962
- Symbol.debug_indent += 1
1963
- Symbol.debug_print("redeclaration")
1964
- Symbol.debug_indent -= 1
1965
- Symbol.debug_indent -= 2
1966
- # Redeclaration of the same symbol.
1967
- # Let the new one be there, but raise an error to the client
1968
- # so it can use the real symbol as subscope.
1969
- # This will probably result in a duplicate id warning.
1970
- candSymbol.isRedeclaration = True
1971
- raise _DuplicateSymbolError(symbol, declaration)
1972
-
1973
- if declaration.objectType != "function":
1974
- assert len(withDecl) <= 1
1975
- handleDuplicateDeclaration(withDecl[0], candSymbol)
1976
- # (not reachable)
1977
-
1978
- # a function, so compare IDs
1979
- candId = declaration.get_newest_id()
1980
- if Symbol.debug_lookup:
1981
- Symbol.debug_print("candId:", candId)
1982
- for symbol in withDecl:
1983
- oldId = symbol.declaration.get_newest_id()
1984
- if Symbol.debug_lookup:
1985
- Symbol.debug_print("oldId: ", oldId)
1986
- if candId == oldId:
1987
- handleDuplicateDeclaration(symbol, candSymbol)
1988
- # (not reachable)
1989
- # no candidate symbol found with matching ID
1990
- # if there is an empty symbol, fill that one
1991
- if len(noDecl) == 0:
1992
- if Symbol.debug_lookup:
1993
- Symbol.debug_print(
1994
- "no match, no empty, candSybmol is not None?:", candSymbol is not None,
1995
- )
1996
- Symbol.debug_indent -= 2
1997
- if candSymbol is not None:
1998
- return candSymbol
1999
- else:
2000
- return makeCandSymbol()
2001
- else:
2002
- if Symbol.debug_lookup:
2003
- Symbol.debug_print(
2004
- "no match, but fill an empty declaration, candSybmol is not None?:",
2005
- candSymbol is not None)
2006
- Symbol.debug_indent -= 2
2007
- if candSymbol is not None:
2008
- candSymbol.remove()
2009
- # assert len(noDecl) == 1
2010
- # TODO: enable assertion when we at some point find out how to do cleanup
2011
- # for now, just take the first one, it should work fine ... right?
2012
- symbol = noDecl[0]
2013
- # If someone first opened the scope, and then later
2014
- # declares it, e.g,
2015
- # .. namespace:: Test
2016
- # .. namespace:: nullptr
2017
- # .. class:: Test
2018
- symbol._fill_empty(declaration, docname, line)
2019
- return symbol
2020
-
2021
- def merge_with(self, other: Symbol, docnames: list[str],
2022
- env: BuildEnvironment) -> None:
2023
- if Symbol.debug_lookup:
2024
- Symbol.debug_indent += 1
2025
- Symbol.debug_print("merge_with:")
2026
- assert other is not None
2027
- for otherChild in other._children:
2028
- ourChild = self._find_first_named_symbol(
2029
- ident=otherChild.ident, matchSelf=False,
2030
- recurseInAnon=False)
2031
- if ourChild is None:
2032
- # TODO: hmm, should we prune by docnames?
2033
- self._children.append(otherChild)
2034
- otherChild.parent = self
2035
- otherChild._assert_invariants()
2036
- continue
2037
- if otherChild.declaration and otherChild.docname in docnames:
2038
- if not ourChild.declaration:
2039
- ourChild._fill_empty(otherChild.declaration,
2040
- otherChild.docname, otherChild.line)
2041
- elif ourChild.docname != otherChild.docname:
2042
- name = str(ourChild.declaration)
2043
- msg = __("Duplicate C declaration, also defined at %s:%s.\n"
2044
- "Declaration is '.. c:%s:: %s'.")
2045
- msg = msg % (ourChild.docname, ourChild.line,
2046
- ourChild.declaration.directiveType, name)
2047
- logger.warning(msg, location=(otherChild.docname, otherChild.line))
2048
- else:
2049
- # Both have declarations, and in the same docname.
2050
- # This can apparently happen, it should be safe to
2051
- # just ignore it, right?
2052
- pass
2053
- ourChild.merge_with(otherChild, docnames, env)
2054
- if Symbol.debug_lookup:
2055
- Symbol.debug_indent -= 1
2056
-
2057
- def add_name(self, nestedName: ASTNestedName) -> Symbol:
2058
- if Symbol.debug_lookup:
2059
- Symbol.debug_indent += 1
2060
- Symbol.debug_print("add_name:")
2061
- res = self._add_symbols(nestedName, declaration=None, docname=None, line=None)
2062
- if Symbol.debug_lookup:
2063
- Symbol.debug_indent -= 1
2064
- return res
2065
-
2066
- def add_declaration(self, declaration: ASTDeclaration,
2067
- docname: str, line: int) -> Symbol:
2068
- if Symbol.debug_lookup:
2069
- Symbol.debug_indent += 1
2070
- Symbol.debug_print("add_declaration:")
2071
- assert declaration is not None
2072
- assert docname is not None
2073
- assert line is not None
2074
- nestedName = declaration.name
2075
- res = self._add_symbols(nestedName, declaration, docname, line)
2076
- if Symbol.debug_lookup:
2077
- Symbol.debug_indent -= 1
2078
- return res
2079
-
2080
- def find_identifier(self, ident: ASTIdentifier,
2081
- matchSelf: bool, recurseInAnon: bool, searchInSiblings: bool,
2082
- ) -> Symbol | None:
2083
- if Symbol.debug_lookup:
2084
- Symbol.debug_indent += 1
2085
- Symbol.debug_print("find_identifier:")
2086
- Symbol.debug_indent += 1
2087
- Symbol.debug_print("ident: ", ident)
2088
- Symbol.debug_print("matchSelf: ", matchSelf)
2089
- Symbol.debug_print("recurseInAnon: ", recurseInAnon)
2090
- Symbol.debug_print("searchInSiblings:", searchInSiblings)
2091
- logger.debug(self.to_string(Symbol.debug_indent + 1), end="")
2092
- Symbol.debug_indent -= 2
2093
- current = self
2094
- while current is not None:
2095
- if Symbol.debug_lookup:
2096
- Symbol.debug_indent += 2
2097
- Symbol.debug_print("trying:")
2098
- logger.debug(current.to_string(Symbol.debug_indent + 1), end="")
2099
- Symbol.debug_indent -= 2
2100
- if matchSelf and current.ident == ident:
2101
- return current
2102
- children = current.children_recurse_anon if recurseInAnon else current._children
2103
- for s in children:
2104
- if s.ident == ident:
2105
- return s
2106
- if not searchInSiblings:
2107
- break
2108
- current = current.siblingAbove
2109
- return None
2110
-
2111
- def direct_lookup(self, key: LookupKey) -> Symbol | None:
2112
- if Symbol.debug_lookup:
2113
- Symbol.debug_indent += 1
2114
- Symbol.debug_print("direct_lookup:")
2115
- Symbol.debug_indent += 1
2116
- s = self
2117
- for name, id_ in key.data:
2118
- res = None
2119
- for cand in s._children:
2120
- if cand.ident == name:
2121
- res = cand
2122
- break
2123
- s = res
2124
- if Symbol.debug_lookup:
2125
- Symbol.debug_print("name: ", name)
2126
- Symbol.debug_print("id: ", id_)
2127
- if s is not None:
2128
- logger.debug(s.to_string(Symbol.debug_indent + 1), end="")
2129
- else:
2130
- Symbol.debug_print("not found")
2131
- if s is None:
2132
- if Symbol.debug_lookup:
2133
- Symbol.debug_indent -= 2
2134
- return None
2135
- if Symbol.debug_lookup:
2136
- Symbol.debug_indent -= 2
2137
- return s
2138
-
2139
- def find_declaration(self, nestedName: ASTNestedName, typ: str,
2140
- matchSelf: bool, recurseInAnon: bool) -> Symbol | None:
2141
- # templateShorthand: missing template parameter lists for templates is ok
2142
- if Symbol.debug_lookup:
2143
- Symbol.debug_indent += 1
2144
- Symbol.debug_print("find_declaration:")
2145
-
2146
- def onMissingQualifiedSymbol(
2147
- parentSymbol: Symbol,
2148
- ident: ASTIdentifier,
2149
- ) -> Symbol | None:
2150
- return None
2151
-
2152
- lookupResult = self._symbol_lookup(nestedName,
2153
- onMissingQualifiedSymbol,
2154
- ancestorLookupType=typ,
2155
- matchSelf=matchSelf,
2156
- recurseInAnon=recurseInAnon,
2157
- searchInSiblings=False)
2158
- if Symbol.debug_lookup:
2159
- Symbol.debug_indent -= 1
2160
- if lookupResult is None:
2161
- return None
2162
-
2163
- symbols = list(lookupResult.symbols)
2164
- if len(symbols) == 0:
2165
- return None
2166
- return symbols[0]
2167
-
2168
- def to_string(self, indent: int) -> str:
2169
- res = [Symbol.debug_indent_string * indent]
2170
- if not self.parent:
2171
- res.append('::')
2172
- else:
2173
- if self.ident:
2174
- res.append(str(self.ident))
2175
- else:
2176
- res.append(str(self.declaration))
2177
- if self.declaration:
2178
- res.append(": ")
2179
- if self.isRedeclaration:
2180
- res.append('!!duplicate!! ')
2181
- res.append(str(self.declaration))
2182
- if self.docname:
2183
- res.append('\t(')
2184
- res.append(self.docname)
2185
- res.append(')')
2186
- res.append('\n')
2187
- return ''.join(res)
2188
-
2189
- def dump(self, indent: int) -> str:
2190
- res = [self.to_string(indent)]
2191
- for c in self._children:
2192
- res.append(c.dump(indent + 1))
2193
- return ''.join(res)
2194
-
2195
-
2196
- class DefinitionParser(BaseParser):
2197
- @property
2198
- def language(self) -> str:
2199
- return 'C'
2200
-
2201
- @property
2202
- def id_attributes(self):
2203
- return self.config.c_id_attributes
2204
-
2205
- @property
2206
- def paren_attributes(self):
2207
- return self.config.c_paren_attributes
2208
-
2209
- def _parse_string(self) -> str | None:
2210
- if self.current_char != '"':
2211
- return None
2212
- startPos = self.pos
2213
- self.pos += 1
2214
- escape = False
2215
- while True:
2216
- if self.eof:
2217
- self.fail("Unexpected end during inside string.")
2218
- elif self.current_char == '"' and not escape:
2219
- self.pos += 1
2220
- break
2221
- elif self.current_char == '\\':
2222
- escape = True
2223
- else:
2224
- escape = False
2225
- self.pos += 1
2226
- return self.definition[startPos:self.pos]
2227
-
2228
- def _parse_literal(self) -> ASTLiteral | None:
2229
- # -> integer-literal
2230
- # | character-literal
2231
- # | floating-literal
2232
- # | string-literal
2233
- # | boolean-literal -> "false" | "true"
2234
- self.skip_ws()
2235
- if self.skip_word('true'):
2236
- return ASTBooleanLiteral(True)
2237
- if self.skip_word('false'):
2238
- return ASTBooleanLiteral(False)
2239
- pos = self.pos
2240
- if self.match(float_literal_re):
2241
- self.match(float_literal_suffix_re)
2242
- return ASTNumberLiteral(self.definition[pos:self.pos])
2243
- for regex in [binary_literal_re, hex_literal_re,
2244
- integer_literal_re, octal_literal_re]:
2245
- if self.match(regex):
2246
- self.match(integers_literal_suffix_re)
2247
- return ASTNumberLiteral(self.definition[pos:self.pos])
2248
-
2249
- string = self._parse_string()
2250
- if string is not None:
2251
- return ASTStringLiteral(string)
2252
-
2253
- # character-literal
2254
- if self.match(char_literal_re):
2255
- prefix = self.last_match.group(1) # may be None when no prefix
2256
- data = self.last_match.group(2)
2257
- try:
2258
- return ASTCharLiteral(prefix, data)
2259
- except UnicodeDecodeError as e:
2260
- self.fail("Can not handle character literal. Internal error was: %s" % e)
2261
- except UnsupportedMultiCharacterCharLiteral:
2262
- self.fail("Can not handle character literal"
2263
- " resulting in multiple decoded characters.")
2264
- return None
2265
-
2266
- def _parse_paren_expression(self) -> ASTExpression | None:
2267
- # "(" expression ")"
2268
- if self.current_char != '(':
2269
- return None
2270
- self.pos += 1
2271
- res = self._parse_expression()
2272
- self.skip_ws()
2273
- if not self.skip_string(')'):
2274
- self.fail("Expected ')' in end of parenthesized expression.")
2275
- return ASTParenExpr(res)
2276
-
2277
- def _parse_primary_expression(self) -> ASTExpression | None:
2278
- # literal
2279
- # "(" expression ")"
2280
- # id-expression -> we parse this with _parse_nested_name
2281
- self.skip_ws()
2282
- res: ASTExpression | None = self._parse_literal()
2283
- if res is not None:
2284
- return res
2285
- res = self._parse_paren_expression()
2286
- if res is not None:
2287
- return res
2288
- nn = self._parse_nested_name()
2289
- if nn is not None:
2290
- return ASTIdExpression(nn)
2291
- return None
2292
-
2293
- def _parse_initializer_list(self, name: str, open: str, close: str,
2294
- ) -> tuple[list[ASTExpression], bool]:
2295
- # Parse open and close with the actual initializer-list in between
2296
- # -> initializer-clause '...'[opt]
2297
- # | initializer-list ',' initializer-clause '...'[opt]
2298
- # TODO: designators
2299
- self.skip_ws()
2300
- if not self.skip_string_and_ws(open):
2301
- return None, None
2302
- if self.skip_string(close):
2303
- return [], False
2304
-
2305
- exprs = []
2306
- trailingComma = False
2307
- while True:
2308
- self.skip_ws()
2309
- expr = self._parse_expression()
2310
- self.skip_ws()
2311
- exprs.append(expr)
2312
- self.skip_ws()
2313
- if self.skip_string(close):
2314
- break
2315
- if not self.skip_string_and_ws(','):
2316
- self.fail(f"Error in {name}, expected ',' or '{close}'.")
2317
- if self.current_char == close and close == '}':
2318
- self.pos += 1
2319
- trailingComma = True
2320
- break
2321
- return exprs, trailingComma
2322
-
2323
- def _parse_paren_expression_list(self) -> ASTParenExprList | None:
2324
- # -> '(' expression-list ')'
2325
- # though, we relax it to also allow empty parens
2326
- # as it's needed in some cases
2327
- #
2328
- # expression-list
2329
- # -> initializer-list
2330
- exprs, trailingComma = self._parse_initializer_list("parenthesized expression-list",
2331
- '(', ')')
2332
- if exprs is None:
2333
- return None
2334
- return ASTParenExprList(exprs)
2335
-
2336
- def _parse_braced_init_list(self) -> ASTBracedInitList | None:
2337
- # -> '{' initializer-list ','[opt] '}'
2338
- # | '{' '}'
2339
- exprs, trailingComma = self._parse_initializer_list("braced-init-list", '{', '}')
2340
- if exprs is None:
2341
- return None
2342
- return ASTBracedInitList(exprs, trailingComma)
2343
-
2344
- def _parse_postfix_expression(self) -> ASTPostfixExpr:
2345
- # -> primary
2346
- # | postfix "[" expression "]"
2347
- # | postfix "[" braced-init-list [opt] "]"
2348
- # | postfix "(" expression-list [opt] ")"
2349
- # | postfix "." id-expression // taken care of in primary by nested name
2350
- # | postfix "->" id-expression
2351
- # | postfix "++"
2352
- # | postfix "--"
2353
-
2354
- prefix = self._parse_primary_expression()
2355
-
2356
- # and now parse postfixes
2357
- postFixes: list[ASTPostfixOp] = []
2358
- while True:
2359
- self.skip_ws()
2360
- if self.skip_string_and_ws('['):
2361
- expr = self._parse_expression()
2362
- self.skip_ws()
2363
- if not self.skip_string(']'):
2364
- self.fail("Expected ']' in end of postfix expression.")
2365
- postFixes.append(ASTPostfixArray(expr))
2366
- continue
2367
- if self.skip_string('->'):
2368
- if self.skip_string('*'):
2369
- # don't steal the arrow
2370
- self.pos -= 3
2371
- else:
2372
- name = self._parse_nested_name()
2373
- postFixes.append(ASTPostfixMemberOfPointer(name))
2374
- continue
2375
- if self.skip_string('++'):
2376
- postFixes.append(ASTPostfixInc())
2377
- continue
2378
- if self.skip_string('--'):
2379
- postFixes.append(ASTPostfixDec())
2380
- continue
2381
- lst = self._parse_paren_expression_list()
2382
- if lst is not None:
2383
- postFixes.append(ASTPostfixCallExpr(lst))
2384
- continue
2385
- break
2386
- return ASTPostfixExpr(prefix, postFixes)
2387
-
2388
- def _parse_unary_expression(self) -> ASTExpression:
2389
- # -> postfix
2390
- # | "++" cast
2391
- # | "--" cast
2392
- # | unary-operator cast -> (* | & | + | - | ! | ~) cast
2393
- # The rest:
2394
- # | "sizeof" unary
2395
- # | "sizeof" "(" type-id ")"
2396
- # | "alignof" "(" type-id ")"
2397
- self.skip_ws()
2398
- for op in _expression_unary_ops:
2399
- # TODO: hmm, should we be able to backtrack here?
2400
- if op[0] in 'cn':
2401
- res = self.skip_word(op)
2402
- else:
2403
- res = self.skip_string(op)
2404
- if res:
2405
- expr = self._parse_cast_expression()
2406
- return ASTUnaryOpExpr(op, expr)
2407
- if self.skip_word_and_ws('sizeof'):
2408
- if self.skip_string_and_ws('('):
2409
- typ = self._parse_type(named=False)
2410
- self.skip_ws()
2411
- if not self.skip_string(')'):
2412
- self.fail("Expecting ')' to end 'sizeof'.")
2413
- return ASTSizeofType(typ)
2414
- expr = self._parse_unary_expression()
2415
- return ASTSizeofExpr(expr)
2416
- if self.skip_word_and_ws('alignof'):
2417
- if not self.skip_string_and_ws('('):
2418
- self.fail("Expecting '(' after 'alignof'.")
2419
- typ = self._parse_type(named=False)
2420
- self.skip_ws()
2421
- if not self.skip_string(')'):
2422
- self.fail("Expecting ')' to end 'alignof'.")
2423
- return ASTAlignofExpr(typ)
2424
- return self._parse_postfix_expression()
2425
-
2426
- def _parse_cast_expression(self) -> ASTExpression:
2427
- # -> unary | "(" type-id ")" cast
2428
- pos = self.pos
2429
- self.skip_ws()
2430
- if self.skip_string('('):
2431
- try:
2432
- typ = self._parse_type(False)
2433
- if not self.skip_string(')'):
2434
- self.fail("Expected ')' in cast expression.")
2435
- expr = self._parse_cast_expression()
2436
- return ASTCastExpr(typ, expr)
2437
- except DefinitionError as exCast:
2438
- self.pos = pos
2439
- try:
2440
- return self._parse_unary_expression()
2441
- except DefinitionError as exUnary:
2442
- errs = []
2443
- errs.append((exCast, "If type cast expression"))
2444
- errs.append((exUnary, "If unary expression"))
2445
- raise self._make_multi_error(errs,
2446
- "Error in cast expression.") from exUnary
2447
- else:
2448
- return self._parse_unary_expression()
2449
-
2450
- def _parse_logical_or_expression(self) -> ASTExpression:
2451
- # logical-or = logical-and ||
2452
- # logical-and = inclusive-or &&
2453
- # inclusive-or = exclusive-or |
2454
- # exclusive-or = and ^
2455
- # and = equality &
2456
- # equality = relational ==, !=
2457
- # relational = shift <, >, <=, >=
2458
- # shift = additive <<, >>
2459
- # additive = multiplicative +, -
2460
- # multiplicative = pm *, /, %
2461
- # pm = cast .*, ->*
2462
- def _parse_bin_op_expr(self, opId):
2463
- if opId + 1 == len(_expression_bin_ops):
2464
- def parser() -> ASTExpression:
2465
- return self._parse_cast_expression()
2466
- else:
2467
- def parser() -> ASTExpression:
2468
- return _parse_bin_op_expr(self, opId + 1)
2469
- exprs = []
2470
- ops = []
2471
- exprs.append(parser())
2472
- while True:
2473
- self.skip_ws()
2474
- pos = self.pos
2475
- oneMore = False
2476
- for op in _expression_bin_ops[opId]:
2477
- if op[0] in 'abcnox':
2478
- if not self.skip_word(op):
2479
- continue
2480
- else:
2481
- if not self.skip_string(op):
2482
- continue
2483
- if op == '&' and self.current_char == '&':
2484
- # don't split the && 'token'
2485
- self.pos -= 1
2486
- # and btw. && has lower precedence, so we are done
2487
- break
2488
- try:
2489
- expr = parser()
2490
- exprs.append(expr)
2491
- ops.append(op)
2492
- oneMore = True
2493
- break
2494
- except DefinitionError:
2495
- self.pos = pos
2496
- if not oneMore:
2497
- break
2498
- return ASTBinOpExpr(exprs, ops)
2499
- return _parse_bin_op_expr(self, 0)
2500
-
2501
- def _parse_conditional_expression_tail(self, orExprHead: Any) -> ASTExpression | None:
2502
- # -> "?" expression ":" assignment-expression
2503
- return None
2504
-
2505
- def _parse_assignment_expression(self) -> ASTExpression:
2506
- # -> conditional-expression
2507
- # | logical-or-expression assignment-operator initializer-clause
2508
- # -> conditional-expression ->
2509
- # logical-or-expression
2510
- # | logical-or-expression "?" expression ":" assignment-expression
2511
- # | logical-or-expression assignment-operator initializer-clause
2512
- exprs = []
2513
- ops = []
2514
- orExpr = self._parse_logical_or_expression()
2515
- exprs.append(orExpr)
2516
- # TODO: handle ternary with _parse_conditional_expression_tail
2517
- while True:
2518
- oneMore = False
2519
- self.skip_ws()
2520
- for op in _expression_assignment_ops:
2521
- if op[0] in 'abcnox':
2522
- if not self.skip_word(op):
2523
- continue
2524
- else:
2525
- if not self.skip_string(op):
2526
- continue
2527
- expr = self._parse_logical_or_expression()
2528
- exprs.append(expr)
2529
- ops.append(op)
2530
- oneMore = True
2531
- if not oneMore:
2532
- break
2533
- return ASTAssignmentExpr(exprs, ops)
2534
-
2535
- def _parse_constant_expression(self) -> ASTExpression:
2536
- # -> conditional-expression
2537
- orExpr = self._parse_logical_or_expression()
2538
- # TODO: use _parse_conditional_expression_tail
2539
- return orExpr
2540
-
2541
- def _parse_expression(self) -> ASTExpression:
2542
- # -> assignment-expression
2543
- # | expression "," assignment-expression
2544
- # TODO: actually parse the second production
2545
- return self._parse_assignment_expression()
2546
-
2547
- def _parse_expression_fallback(
2548
- self, end: list[str],
2549
- parser: Callable[[], ASTExpression],
2550
- allow: bool = True) -> ASTExpression:
2551
- # Stupidly "parse" an expression.
2552
- # 'end' should be a list of characters which ends the expression.
2553
-
2554
- # first try to use the provided parser
2555
- prevPos = self.pos
2556
- try:
2557
- return parser()
2558
- except DefinitionError as e:
2559
- # some places (e.g., template parameters) we really don't want to use fallback,
2560
- # and for testing we may want to globally disable it
2561
- if not allow or not self.allowFallbackExpressionParsing:
2562
- raise
2563
- self.warn("Parsing of expression failed. Using fallback parser."
2564
- " Error was:\n%s" % e)
2565
- self.pos = prevPos
2566
- # and then the fallback scanning
2567
- assert end is not None
2568
- self.skip_ws()
2569
- startPos = self.pos
2570
- if self.match(_string_re):
2571
- value = self.matched_text
2572
- else:
2573
- # TODO: add handling of more bracket-like things, and quote handling
2574
- brackets = {'(': ')', '{': '}', '[': ']'}
2575
- symbols: list[str] = []
2576
- while not self.eof:
2577
- if (len(symbols) == 0 and self.current_char in end):
2578
- break
2579
- if self.current_char in brackets:
2580
- symbols.append(brackets[self.current_char])
2581
- elif len(symbols) > 0 and self.current_char == symbols[-1]:
2582
- symbols.pop()
2583
- self.pos += 1
2584
- if len(end) > 0 and self.eof:
2585
- self.fail("Could not find end of expression starting at %d."
2586
- % startPos)
2587
- value = self.definition[startPos:self.pos].strip()
2588
- return ASTFallbackExpr(value.strip())
2589
-
2590
- def _parse_nested_name(self) -> ASTNestedName:
2591
- names: list[Any] = []
2592
-
2593
- self.skip_ws()
2594
- rooted = False
2595
- if self.skip_string('.'):
2596
- rooted = True
2597
- while 1:
2598
- self.skip_ws()
2599
- if not self.match(identifier_re):
2600
- self.fail("Expected identifier in nested name.")
2601
- identifier = self.matched_text
2602
- # make sure there isn't a keyword
2603
- if identifier in _keywords:
2604
- self.fail("Expected identifier in nested name, "
2605
- "got keyword: %s" % identifier)
2606
- if self.matched_text in self.config.c_extra_keywords:
2607
- msg = "Expected identifier, got user-defined keyword: %s." \
2608
- + " Remove it from c_extra_keywords to allow it as identifier.\n" \
2609
- + "Currently c_extra_keywords is %s."
2610
- self.fail(msg % (self.matched_text,
2611
- str(self.config.c_extra_keywords)))
2612
- ident = ASTIdentifier(identifier)
2613
- names.append(ident)
2614
-
2615
- self.skip_ws()
2616
- if not self.skip_string('.'):
2617
- break
2618
- return ASTNestedName(names, rooted)
2619
-
2620
- def _parse_simple_type_specifier(self) -> str | None:
2621
- if self.match(_simple_type_specifiers_re):
2622
- return self.matched_text
2623
- for t in ('bool', 'complex', 'imaginary'):
2624
- if t in self.config.c_extra_keywords:
2625
- if self.skip_word(t):
2626
- return t
2627
- return None
2628
-
2629
- def _parse_simple_type_specifiers(self) -> ASTTrailingTypeSpecFundamental | None:
2630
- names: list[str] = []
2631
-
2632
- self.skip_ws()
2633
- while True:
2634
- t = self._parse_simple_type_specifier()
2635
- if t is None:
2636
- break
2637
- names.append(t)
2638
- self.skip_ws()
2639
- if len(names) == 0:
2640
- return None
2641
- return ASTTrailingTypeSpecFundamental(names)
2642
-
2643
- def _parse_trailing_type_spec(self) -> ASTTrailingTypeSpec:
2644
- # fundamental types, https://en.cppreference.com/w/c/language/type
2645
- # and extensions
2646
- self.skip_ws()
2647
- res = self._parse_simple_type_specifiers()
2648
- if res is not None:
2649
- return res
2650
-
2651
- # prefixed
2652
- prefix = None
2653
- self.skip_ws()
2654
- for k in ('struct', 'enum', 'union'):
2655
- if self.skip_word_and_ws(k):
2656
- prefix = k
2657
- break
2658
-
2659
- nestedName = self._parse_nested_name()
2660
- return ASTTrailingTypeSpecName(prefix, nestedName)
2661
-
2662
- def _parse_parameters(self, paramMode: str) -> ASTParameters | None:
2663
- self.skip_ws()
2664
- if not self.skip_string('('):
2665
- if paramMode == 'function':
2666
- self.fail('Expecting "(" in parameters.')
2667
- else:
2668
- return None
2669
-
2670
- args = []
2671
- self.skip_ws()
2672
- if not self.skip_string(')'):
2673
- while 1:
2674
- self.skip_ws()
2675
- if self.skip_string('...'):
2676
- args.append(ASTFunctionParameter(None, True))
2677
- self.skip_ws()
2678
- if not self.skip_string(')'):
2679
- self.fail('Expected ")" after "..." in parameters.')
2680
- break
2681
- # note: it seems that function arguments can always be named,
2682
- # even in function pointers and similar.
2683
- arg = self._parse_type_with_init(outer=None, named='single')
2684
- # TODO: parse default parameters # TODO: didn't we just do that?
2685
- args.append(ASTFunctionParameter(arg))
2686
-
2687
- self.skip_ws()
2688
- if self.skip_string(','):
2689
- continue
2690
- if self.skip_string(')'):
2691
- break
2692
- self.fail(f'Expecting "," or ")" in parameters, got "{self.current_char}".')
2693
-
2694
- attrs = self._parse_attribute_list()
2695
- return ASTParameters(args, attrs)
2696
-
2697
- def _parse_decl_specs_simple(
2698
- self, outer: str | None, typed: bool,
2699
- ) -> ASTDeclSpecsSimple:
2700
- """Just parse the simple ones."""
2701
- storage = None
2702
- threadLocal = None
2703
- inline = None
2704
- restrict = None
2705
- volatile = None
2706
- const = None
2707
- attrs = []
2708
- while 1: # accept any permutation of a subset of some decl-specs
2709
- self.skip_ws()
2710
- if not storage:
2711
- if outer == 'member':
2712
- if self.skip_word('auto'):
2713
- storage = 'auto'
2714
- continue
2715
- if self.skip_word('register'):
2716
- storage = 'register'
2717
- continue
2718
- if outer in ('member', 'function'):
2719
- if self.skip_word('static'):
2720
- storage = 'static'
2721
- continue
2722
- if self.skip_word('extern'):
2723
- storage = 'extern'
2724
- continue
2725
- if outer == 'member' and not threadLocal:
2726
- if self.skip_word('thread_local'):
2727
- threadLocal = 'thread_local'
2728
- continue
2729
- if self.skip_word('_Thread_local'):
2730
- threadLocal = '_Thread_local'
2731
- continue
2732
- if outer == 'function' and not inline:
2733
- inline = self.skip_word('inline')
2734
- if inline:
2735
- continue
2736
-
2737
- if not restrict and typed:
2738
- restrict = self.skip_word('restrict')
2739
- if restrict:
2740
- continue
2741
- if not volatile and typed:
2742
- volatile = self.skip_word('volatile')
2743
- if volatile:
2744
- continue
2745
- if not const and typed:
2746
- const = self.skip_word('const')
2747
- if const:
2748
- continue
2749
- attr = self._parse_attribute()
2750
- if attr:
2751
- attrs.append(attr)
2752
- continue
2753
- break
2754
- return ASTDeclSpecsSimple(storage, threadLocal, inline,
2755
- restrict, volatile, const, ASTAttributeList(attrs))
2756
-
2757
- def _parse_decl_specs(self, outer: str | None, typed: bool = True) -> ASTDeclSpecs:
2758
- if outer:
2759
- if outer not in ('type', 'member', 'function'):
2760
- raise Exception('Internal error, unknown outer "%s".' % outer)
2761
- leftSpecs = self._parse_decl_specs_simple(outer, typed)
2762
- rightSpecs = None
2763
-
2764
- if typed:
2765
- trailing = self._parse_trailing_type_spec()
2766
- rightSpecs = self._parse_decl_specs_simple(outer, typed)
2767
- else:
2768
- trailing = None
2769
- return ASTDeclSpecs(outer, leftSpecs, rightSpecs, trailing)
2770
-
2771
- def _parse_declarator_name_suffix(
2772
- self, named: bool | str, paramMode: str, typed: bool,
2773
- ) -> ASTDeclarator:
2774
- assert named in (True, False, 'single')
2775
- # now we should parse the name, and then suffixes
2776
- if named == 'single':
2777
- if self.match(identifier_re):
2778
- if self.matched_text in _keywords:
2779
- self.fail("Expected identifier, "
2780
- "got keyword: %s" % self.matched_text)
2781
- if self.matched_text in self.config.c_extra_keywords:
2782
- msg = "Expected identifier, got user-defined keyword: %s." \
2783
- + " Remove it from c_extra_keywords to allow it as identifier.\n" \
2784
- + "Currently c_extra_keywords is %s."
2785
- self.fail(msg % (self.matched_text,
2786
- str(self.config.c_extra_keywords)))
2787
- identifier = ASTIdentifier(self.matched_text)
2788
- declId = ASTNestedName([identifier], rooted=False)
2789
- else:
2790
- declId = None
2791
- elif named:
2792
- declId = self._parse_nested_name()
2793
- else:
2794
- declId = None
2795
- arrayOps = []
2796
- while 1:
2797
- self.skip_ws()
2798
- if typed and self.skip_string('['):
2799
- self.skip_ws()
2800
- static = False
2801
- const = False
2802
- volatile = False
2803
- restrict = False
2804
- while True:
2805
- if not static:
2806
- if self.skip_word_and_ws('static'):
2807
- static = True
2808
- continue
2809
- if not const:
2810
- if self.skip_word_and_ws('const'):
2811
- const = True
2812
- continue
2813
- if not volatile:
2814
- if self.skip_word_and_ws('volatile'):
2815
- volatile = True
2816
- continue
2817
- if not restrict:
2818
- if self.skip_word_and_ws('restrict'):
2819
- restrict = True
2820
- continue
2821
- break
2822
- vla = False if static else self.skip_string_and_ws('*')
2823
- if vla:
2824
- if not self.skip_string(']'):
2825
- self.fail("Expected ']' in end of array operator.")
2826
- size = None
2827
- else:
2828
- if self.skip_string(']'):
2829
- size = None
2830
- else:
2831
-
2832
- def parser():
2833
- return self._parse_expression()
2834
- size = self._parse_expression_fallback([']'], parser)
2835
- self.skip_ws()
2836
- if not self.skip_string(']'):
2837
- self.fail("Expected ']' in end of array operator.")
2838
- arrayOps.append(ASTArray(static, const, volatile, restrict, vla, size))
2839
- else:
2840
- break
2841
- param = self._parse_parameters(paramMode)
2842
- if param is None and len(arrayOps) == 0:
2843
- # perhaps a bit-field
2844
- if named and paramMode == 'type' and typed:
2845
- self.skip_ws()
2846
- if self.skip_string(':'):
2847
- size = self._parse_constant_expression()
2848
- return ASTDeclaratorNameBitField(declId=declId, size=size)
2849
- return ASTDeclaratorNameParam(declId=declId, arrayOps=arrayOps,
2850
- param=param)
2851
-
2852
- def _parse_declarator(self, named: bool | str, paramMode: str,
2853
- typed: bool = True) -> ASTDeclarator:
2854
- # 'typed' here means 'parse return type stuff'
2855
- if paramMode not in ('type', 'function'):
2856
- raise Exception(
2857
- "Internal error, unknown paramMode '%s'." % paramMode)
2858
- prevErrors = []
2859
- self.skip_ws()
2860
- if typed and self.skip_string('*'):
2861
- self.skip_ws()
2862
- restrict = False
2863
- volatile = False
2864
- const = False
2865
- attrs = []
2866
- while 1:
2867
- if not restrict:
2868
- restrict = self.skip_word_and_ws('restrict')
2869
- if restrict:
2870
- continue
2871
- if not volatile:
2872
- volatile = self.skip_word_and_ws('volatile')
2873
- if volatile:
2874
- continue
2875
- if not const:
2876
- const = self.skip_word_and_ws('const')
2877
- if const:
2878
- continue
2879
- attr = self._parse_attribute()
2880
- if attr is not None:
2881
- attrs.append(attr)
2882
- continue
2883
- break
2884
- next = self._parse_declarator(named, paramMode, typed)
2885
- return ASTDeclaratorPtr(next=next,
2886
- restrict=restrict, volatile=volatile, const=const,
2887
- attrs=ASTAttributeList(attrs))
2888
- if typed and self.current_char == '(': # note: peeking, not skipping
2889
- # maybe this is the beginning of params, try that first,
2890
- # otherwise assume it's noptr->declarator > ( ptr-declarator )
2891
- pos = self.pos
2892
- try:
2893
- # assume this is params
2894
- res = self._parse_declarator_name_suffix(named, paramMode,
2895
- typed)
2896
- return res
2897
- except DefinitionError as exParamQual:
2898
- msg = "If declarator-id with parameters"
2899
- if paramMode == 'function':
2900
- msg += " (e.g., 'void f(int arg)')"
2901
- prevErrors.append((exParamQual, msg))
2902
- self.pos = pos
2903
- try:
2904
- assert self.current_char == '('
2905
- self.skip_string('(')
2906
- # TODO: hmm, if there is a name, it must be in inner, right?
2907
- # TODO: hmm, if there must be parameters, they must b
2908
- # inside, right?
2909
- inner = self._parse_declarator(named, paramMode, typed)
2910
- if not self.skip_string(')'):
2911
- self.fail("Expected ')' in \"( ptr-declarator )\"")
2912
- next = self._parse_declarator(named=False,
2913
- paramMode="type",
2914
- typed=typed)
2915
- return ASTDeclaratorParen(inner=inner, next=next)
2916
- except DefinitionError as exNoPtrParen:
2917
- self.pos = pos
2918
- msg = "If parenthesis in noptr-declarator"
2919
- if paramMode == 'function':
2920
- msg += " (e.g., 'void (*f(int arg))(double)')"
2921
- prevErrors.append((exNoPtrParen, msg))
2922
- header = "Error in declarator"
2923
- raise self._make_multi_error(prevErrors, header) from exNoPtrParen
2924
- pos = self.pos
2925
- try:
2926
- return self._parse_declarator_name_suffix(named, paramMode, typed)
2927
- except DefinitionError as e:
2928
- self.pos = pos
2929
- prevErrors.append((e, "If declarator-id"))
2930
- header = "Error in declarator or parameters"
2931
- raise self._make_multi_error(prevErrors, header) from e
2932
-
2933
- def _parse_initializer(self, outer: str | None = None, allowFallback: bool = True,
2934
- ) -> ASTInitializer | None:
2935
- self.skip_ws()
2936
- if outer == 'member' and False: # NoQA: SIM223 # TODO
2937
- bracedInit = self._parse_braced_init_list()
2938
- if bracedInit is not None:
2939
- return ASTInitializer(bracedInit, hasAssign=False)
2940
-
2941
- if not self.skip_string('='):
2942
- return None
2943
-
2944
- bracedInit = self._parse_braced_init_list()
2945
- if bracedInit is not None:
2946
- return ASTInitializer(bracedInit)
2947
-
2948
- if outer == 'member':
2949
- fallbackEnd: list[str] = []
2950
- elif outer is None: # function parameter
2951
- fallbackEnd = [',', ')']
2952
- else:
2953
- self.fail("Internal error, initializer for outer '%s' not "
2954
- "implemented." % outer)
2955
-
2956
- def parser():
2957
- return self._parse_assignment_expression()
2958
-
2959
- value = self._parse_expression_fallback(fallbackEnd, parser, allow=allowFallback)
2960
- return ASTInitializer(value)
2961
-
2962
- def _parse_type(self, named: bool | str, outer: str | None = None) -> ASTType:
2963
- """
2964
- named=False|'single'|True: 'single' is e.g., for function objects which
2965
- doesn't need to name the arguments, but otherwise is a single name
2966
- """
2967
- if outer: # always named
2968
- if outer not in ('type', 'member', 'function'):
2969
- raise Exception('Internal error, unknown outer "%s".' % outer)
2970
- assert named
2971
-
2972
- if outer == 'type':
2973
- # We allow type objects to just be a name.
2974
- prevErrors = []
2975
- startPos = self.pos
2976
- # first try without the type
2977
- try:
2978
- declSpecs = self._parse_decl_specs(outer=outer, typed=False)
2979
- decl = self._parse_declarator(named=True, paramMode=outer,
2980
- typed=False)
2981
- self.assert_end(allowSemicolon=True)
2982
- except DefinitionError as exUntyped:
2983
- desc = "If just a name"
2984
- prevErrors.append((exUntyped, desc))
2985
- self.pos = startPos
2986
- try:
2987
- declSpecs = self._parse_decl_specs(outer=outer)
2988
- decl = self._parse_declarator(named=True, paramMode=outer)
2989
- except DefinitionError as exTyped:
2990
- self.pos = startPos
2991
- desc = "If typedef-like declaration"
2992
- prevErrors.append((exTyped, desc))
2993
- # Retain the else branch for easier debugging.
2994
- # TODO: it would be nice to save the previous stacktrace
2995
- # and output it here.
2996
- if True:
2997
- header = "Type must be either just a name or a "
2998
- header += "typedef-like declaration."
2999
- raise self._make_multi_error(prevErrors, header) from exTyped
3000
- else: # NoQA: RET506
3001
- # For testing purposes.
3002
- # do it again to get the proper traceback (how do you
3003
- # reliably save a traceback when an exception is
3004
- # constructed?)
3005
- self.pos = startPos
3006
- typed = True
3007
- declSpecs = self._parse_decl_specs(outer=outer, typed=typed)
3008
- decl = self._parse_declarator(named=True, paramMode=outer,
3009
- typed=typed)
3010
- elif outer == 'function':
3011
- declSpecs = self._parse_decl_specs(outer=outer)
3012
- decl = self._parse_declarator(named=True, paramMode=outer)
3013
- else:
3014
- paramMode = 'type'
3015
- if outer == 'member': # i.e., member
3016
- named = True
3017
- declSpecs = self._parse_decl_specs(outer=outer)
3018
- decl = self._parse_declarator(named=named, paramMode=paramMode)
3019
- return ASTType(declSpecs, decl)
3020
-
3021
- def _parse_type_with_init(self, named: bool | str, outer: str | None) -> ASTTypeWithInit:
3022
- if outer:
3023
- assert outer in ('type', 'member', 'function')
3024
- type = self._parse_type(outer=outer, named=named)
3025
- init = self._parse_initializer(outer=outer)
3026
- return ASTTypeWithInit(type, init)
3027
-
3028
- def _parse_macro(self) -> ASTMacro:
3029
- self.skip_ws()
3030
- ident = self._parse_nested_name()
3031
- if ident is None:
3032
- self.fail("Expected identifier in macro definition.")
3033
- self.skip_ws()
3034
- if not self.skip_string_and_ws('('):
3035
- return ASTMacro(ident, None)
3036
- if self.skip_string(')'):
3037
- return ASTMacro(ident, [])
3038
- args = []
3039
- while 1:
3040
- self.skip_ws()
3041
- if self.skip_string('...'):
3042
- args.append(ASTMacroParameter(None, True))
3043
- self.skip_ws()
3044
- if not self.skip_string(')'):
3045
- self.fail('Expected ")" after "..." in macro parameters.')
3046
- break
3047
- if not self.match(identifier_re):
3048
- self.fail("Expected identifier in macro parameters.")
3049
- nn = ASTNestedName([ASTIdentifier(self.matched_text)], rooted=False)
3050
- # Allow named variadic args:
3051
- # https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
3052
- self.skip_ws()
3053
- if self.skip_string_and_ws('...'):
3054
- args.append(ASTMacroParameter(nn, False, True))
3055
- self.skip_ws()
3056
- if not self.skip_string(')'):
3057
- self.fail('Expected ")" after "..." in macro parameters.')
3058
- break
3059
- args.append(ASTMacroParameter(nn))
3060
- if self.skip_string_and_ws(','):
3061
- continue
3062
- if self.skip_string_and_ws(')'):
3063
- break
3064
- self.fail("Expected identifier, ')', or ',' in macro parameter list.")
3065
- return ASTMacro(ident, args)
3066
-
3067
- def _parse_struct(self) -> ASTStruct:
3068
- name = self._parse_nested_name()
3069
- return ASTStruct(name)
3070
-
3071
- def _parse_union(self) -> ASTUnion:
3072
- name = self._parse_nested_name()
3073
- return ASTUnion(name)
3074
-
3075
- def _parse_enum(self) -> ASTEnum:
3076
- name = self._parse_nested_name()
3077
- return ASTEnum(name)
3078
-
3079
- def _parse_enumerator(self) -> ASTEnumerator:
3080
- name = self._parse_nested_name()
3081
- attrs = self._parse_attribute_list()
3082
- self.skip_ws()
3083
- init = None
3084
- if self.skip_string('='):
3085
- self.skip_ws()
3086
-
3087
- def parser() -> ASTExpression:
3088
- return self._parse_constant_expression()
3089
-
3090
- initVal = self._parse_expression_fallback([], parser)
3091
- init = ASTInitializer(initVal)
3092
- return ASTEnumerator(name, init, attrs)
3093
-
3094
- def parse_declaration(self, objectType: str, directiveType: str) -> ASTDeclaration:
3095
- if objectType not in ('function', 'member',
3096
- 'macro', 'struct', 'union', 'enum', 'enumerator', 'type'):
3097
- raise Exception('Internal error, unknown objectType "%s".' % objectType)
3098
- if directiveType not in ('function', 'member', 'var',
3099
- 'macro', 'struct', 'union', 'enum', 'enumerator', 'type'):
3100
- raise Exception('Internal error, unknown directiveType "%s".' % directiveType)
3101
-
3102
- declaration: DeclarationType = None
3103
- if objectType == 'member':
3104
- declaration = self._parse_type_with_init(named=True, outer='member')
3105
- elif objectType == 'function':
3106
- declaration = self._parse_type(named=True, outer='function')
3107
- elif objectType == 'macro':
3108
- declaration = self._parse_macro()
3109
- elif objectType == 'struct':
3110
- declaration = self._parse_struct()
3111
- elif objectType == 'union':
3112
- declaration = self._parse_union()
3113
- elif objectType == 'enum':
3114
- declaration = self._parse_enum()
3115
- elif objectType == 'enumerator':
3116
- declaration = self._parse_enumerator()
3117
- elif objectType == 'type':
3118
- declaration = self._parse_type(named=True, outer='type')
3119
- else:
3120
- raise AssertionError
3121
- if objectType != 'macro':
3122
- self.skip_ws()
3123
- semicolon = self.skip_string(';')
3124
- else:
3125
- semicolon = False
3126
- return ASTDeclaration(objectType, directiveType, declaration, semicolon)
3127
-
3128
- def parse_namespace_object(self) -> ASTNestedName:
3129
- return self._parse_nested_name()
3130
-
3131
- def parse_xref_object(self) -> ASTNestedName:
3132
- name = self._parse_nested_name()
3133
- # if there are '()' left, just skip them
3134
- self.skip_ws()
3135
- self.skip_string('()')
3136
- self.assert_end()
3137
- return name
3138
-
3139
- def parse_expression(self) -> ASTExpression | ASTType:
3140
- pos = self.pos
3141
- res: ASTExpression | ASTType = None
3142
- try:
3143
- res = self._parse_expression()
3144
- self.skip_ws()
3145
- self.assert_end()
3146
- except DefinitionError as exExpr:
3147
- self.pos = pos
3148
- try:
3149
- res = self._parse_type(False)
3150
- self.skip_ws()
3151
- self.assert_end()
3152
- except DefinitionError as exType:
3153
- header = "Error when parsing (type) expression."
3154
- errs = []
3155
- errs.append((exExpr, "If expression"))
3156
- errs.append((exType, "If type"))
3157
- raise self._make_multi_error(errs, header) from exType
3158
- return res
3159
-
3160
-
3161
- def _make_phony_error_name() -> ASTNestedName:
3162
- return ASTNestedName([ASTIdentifier("PhonyNameDueToError")], rooted=False)
3163
-
3164
-
3165
- class CObject(ObjectDescription[ASTDeclaration]):
3166
- """
3167
- Description of a C language object.
3168
- """
3169
-
3170
- option_spec: OptionSpec = {
3171
- 'no-index-entry': directives.flag,
3172
- 'no-contents-entry': directives.flag,
3173
- 'no-typesetting': directives.flag,
3174
- 'noindexentry': directives.flag,
3175
- 'nocontentsentry': directives.flag,
3176
- 'single-line-parameter-list': directives.flag,
3177
- }
3178
-
3179
- def _add_enumerator_to_parent(self, ast: ASTDeclaration) -> None:
3180
- assert ast.objectType == 'enumerator'
3181
- # find the parent, if it exists && is an enum
3182
- # then add the name to the parent scope
3183
- symbol = ast.symbol
3184
- assert symbol
3185
- assert symbol.ident is not None
3186
- parentSymbol = symbol.parent
3187
- assert parentSymbol
3188
- if parentSymbol.parent is None:
3189
- # TODO: we could warn, but it is somewhat equivalent to
3190
- # enumeratorss, without the enum
3191
- return # no parent
3192
- parentDecl = parentSymbol.declaration
3193
- if parentDecl is None:
3194
- # the parent is not explicitly declared
3195
- # TODO: we could warn, but?
3196
- return
3197
- if parentDecl.objectType != 'enum':
3198
- # TODO: maybe issue a warning, enumerators in non-enums is weird,
3199
- # but it is somewhat equivalent to enumeratorss, without the enum
3200
- return
3201
- if parentDecl.directiveType != 'enum':
3202
- return
3203
-
3204
- targetSymbol = parentSymbol.parent
3205
- s = targetSymbol.find_identifier(symbol.ident, matchSelf=False, recurseInAnon=True,
3206
- searchInSiblings=False)
3207
- if s is not None:
3208
- # something is already declared with that name
3209
- return
3210
- declClone = symbol.declaration.clone()
3211
- declClone.enumeratorScopedSymbol = symbol
3212
- Symbol(parent=targetSymbol, ident=symbol.ident,
3213
- declaration=declClone,
3214
- docname=self.env.docname, line=self.get_source_info()[1])
3215
-
3216
- def add_target_and_index(self, ast: ASTDeclaration, sig: str,
3217
- signode: TextElement) -> None:
3218
- ids = []
3219
- for i in range(1, _max_id + 1):
3220
- try:
3221
- id = ast.get_id(version=i)
3222
- ids.append(id)
3223
- except NoOldIdError:
3224
- assert i < _max_id
3225
- # let's keep the newest first
3226
- ids = list(reversed(ids))
3227
- newestId = ids[0]
3228
- assert newestId # shouldn't be None
3229
-
3230
- name = ast.symbol.get_full_nested_name().get_display_string().lstrip('.')
3231
- if newestId not in self.state.document.ids:
3232
- # always add the newest id
3233
- assert newestId
3234
- signode['ids'].append(newestId)
3235
- # only add compatibility ids when there are no conflicts
3236
- for id in ids[1:]:
3237
- if not id: # is None when the element didn't exist in that version
3238
- continue
3239
- if id not in self.state.document.ids:
3240
- signode['ids'].append(id)
3241
-
3242
- self.state.document.note_explicit_target(signode)
3243
-
3244
- if 'no-index-entry' not in self.options:
3245
- indexText = self.get_index_text(name)
3246
- self.indexnode['entries'].append(('single', indexText, newestId, '', None))
3247
-
3248
- @property
3249
- def object_type(self) -> str:
3250
- raise NotImplementedError
3251
-
3252
- @property
3253
- def display_object_type(self) -> str:
3254
- return self.object_type
3255
-
3256
- def get_index_text(self, name: str) -> str:
3257
- return _('%s (C %s)') % (name, self.display_object_type)
3258
-
3259
- def parse_definition(self, parser: DefinitionParser) -> ASTDeclaration:
3260
- return parser.parse_declaration(self.object_type, self.objtype)
3261
-
3262
- def describe_signature(self, signode: TextElement, ast: ASTDeclaration,
3263
- options: dict) -> None:
3264
- ast.describe_signature(signode, 'lastIsName', self.env, options)
3265
-
3266
- def run(self) -> list[Node]:
3267
- env = self.state.document.settings.env # from ObjectDescription.run
3268
- if 'c:parent_symbol' not in env.temp_data:
3269
- root = env.domaindata['c']['root_symbol']
3270
- env.temp_data['c:parent_symbol'] = root
3271
- env.ref_context['c:parent_key'] = root.get_lookup_key()
3272
-
3273
- # When multiple declarations are made in the same directive
3274
- # they need to know about each other to provide symbol lookup for function parameters.
3275
- # We use last_symbol to store the latest added declaration in a directive.
3276
- env.temp_data['c:last_symbol'] = None
3277
- return super().run()
3278
-
3279
- def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration:
3280
- parentSymbol: Symbol = self.env.temp_data['c:parent_symbol']
3281
-
3282
- max_len = (self.env.config.c_maximum_signature_line_length
3283
- or self.env.config.maximum_signature_line_length
3284
- or 0)
3285
- signode['multi_line_parameter_list'] = (
3286
- 'single-line-parameter-list' not in self.options
3287
- and (len(sig) > max_len > 0)
3288
- )
3289
-
3290
- parser = DefinitionParser(sig, location=signode, config=self.env.config)
3291
- try:
3292
- ast = self.parse_definition(parser)
3293
- parser.assert_end()
3294
- except DefinitionError as e:
3295
- logger.warning(e, location=signode)
3296
- # It is easier to assume some phony name than handling the error in
3297
- # the possibly inner declarations.
3298
- name = _make_phony_error_name()
3299
- symbol = parentSymbol.add_name(name)
3300
- self.env.temp_data['c:last_symbol'] = symbol
3301
- raise ValueError from e
3302
-
3303
- try:
3304
- symbol = parentSymbol.add_declaration(
3305
- ast, docname=self.env.docname, line=self.get_source_info()[1])
3306
- # append the new declaration to the sibling list
3307
- assert symbol.siblingAbove is None
3308
- assert symbol.siblingBelow is None
3309
- symbol.siblingAbove = self.env.temp_data['c:last_symbol']
3310
- if symbol.siblingAbove is not None:
3311
- assert symbol.siblingAbove.siblingBelow is None
3312
- symbol.siblingAbove.siblingBelow = symbol
3313
- self.env.temp_data['c:last_symbol'] = symbol
3314
- except _DuplicateSymbolError as e:
3315
- # Assume we are actually in the old symbol,
3316
- # instead of the newly created duplicate.
3317
- self.env.temp_data['c:last_symbol'] = e.symbol
3318
- msg = __("Duplicate C declaration, also defined at %s:%s.\n"
3319
- "Declaration is '.. c:%s:: %s'.")
3320
- msg = msg % (e.symbol.docname, e.symbol.line, self.display_object_type, sig)
3321
- logger.warning(msg, location=signode)
3322
-
3323
- if ast.objectType == 'enumerator':
3324
- self._add_enumerator_to_parent(ast)
3325
-
3326
- # note: handle_signature may be called multiple time per directive,
3327
- # if it has multiple signatures, so don't mess with the original options.
3328
- options = dict(self.options)
3329
- self.describe_signature(signode, ast, options)
3330
- return ast
3331
-
3332
- def before_content(self) -> None:
3333
- lastSymbol: Symbol = self.env.temp_data['c:last_symbol']
3334
- assert lastSymbol
3335
- self.oldParentSymbol = self.env.temp_data['c:parent_symbol']
3336
- self.oldParentKey: LookupKey = self.env.ref_context['c:parent_key']
3337
- self.env.temp_data['c:parent_symbol'] = lastSymbol
3338
- self.env.ref_context['c:parent_key'] = lastSymbol.get_lookup_key()
3339
-
3340
- def after_content(self) -> None:
3341
- self.env.temp_data['c:parent_symbol'] = self.oldParentSymbol
3342
- self.env.ref_context['c:parent_key'] = self.oldParentKey
3343
-
3344
-
3345
- class CMemberObject(CObject):
3346
- object_type = 'member'
3347
-
3348
- @property
3349
- def display_object_type(self) -> str:
3350
- # the distinction between var and member is only cosmetic
3351
- assert self.objtype in ('member', 'var')
3352
- return self.objtype
3353
-
3354
-
3355
- _function_doc_field_types = [
3356
- TypedField('parameter', label=_('Parameters'),
3357
- names=('param', 'parameter', 'arg', 'argument'),
3358
- typerolename='expr', typenames=('type',)),
3359
- GroupedField('retval', label=_('Return values'),
3360
- names=('retvals', 'retval'),
3361
- can_collapse=True),
3362
- Field('returnvalue', label=_('Returns'), has_arg=False,
3363
- names=('returns', 'return')),
3364
- Field('returntype', label=_('Return type'), has_arg=False,
3365
- names=('rtype',)),
3366
- ]
3367
-
3368
-
3369
- class CFunctionObject(CObject):
3370
- object_type = 'function'
3371
-
3372
- doc_field_types = _function_doc_field_types.copy()
3373
-
3374
-
3375
- class CMacroObject(CObject):
3376
- object_type = 'macro'
3377
-
3378
- doc_field_types = _function_doc_field_types.copy()
3379
-
3380
-
3381
- class CStructObject(CObject):
3382
- object_type = 'struct'
3383
-
3384
-
3385
- class CUnionObject(CObject):
3386
- object_type = 'union'
3387
-
3388
-
3389
- class CEnumObject(CObject):
3390
- object_type = 'enum'
3391
-
3392
-
3393
- class CEnumeratorObject(CObject):
3394
- object_type = 'enumerator'
3395
-
3396
-
3397
- class CTypeObject(CObject):
3398
- object_type = 'type'
3399
-
3400
-
3401
- class CNamespaceObject(SphinxDirective):
3402
- """
3403
- This directive is just to tell Sphinx that we're documenting stuff in
3404
- namespace foo.
3405
- """
3406
-
3407
- has_content = False
3408
- required_arguments = 1
3409
- optional_arguments = 0
3410
- final_argument_whitespace = True
3411
- option_spec: OptionSpec = {}
3412
-
3413
- def run(self) -> list[Node]:
3414
- rootSymbol = self.env.domaindata['c']['root_symbol']
3415
- if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
3416
- symbol = rootSymbol
3417
- stack: list[Symbol] = []
3418
- else:
3419
- parser = DefinitionParser(self.arguments[0],
3420
- location=self.get_location(),
3421
- config=self.env.config)
3422
- try:
3423
- name = parser.parse_namespace_object()
3424
- parser.assert_end()
3425
- except DefinitionError as e:
3426
- logger.warning(e, location=self.get_location())
3427
- name = _make_phony_error_name()
3428
- symbol = rootSymbol.add_name(name)
3429
- stack = [symbol]
3430
- self.env.temp_data['c:parent_symbol'] = symbol
3431
- self.env.temp_data['c:namespace_stack'] = stack
3432
- self.env.ref_context['c:parent_key'] = symbol.get_lookup_key()
3433
- return []
3434
-
3435
-
3436
- class CNamespacePushObject(SphinxDirective):
3437
- has_content = False
3438
- required_arguments = 1
3439
- optional_arguments = 0
3440
- final_argument_whitespace = True
3441
- option_spec: OptionSpec = {}
3442
-
3443
- def run(self) -> list[Node]:
3444
- if self.arguments[0].strip() in ('NULL', '0', 'nullptr'):
3445
- return []
3446
- parser = DefinitionParser(self.arguments[0],
3447
- location=self.get_location(),
3448
- config=self.env.config)
3449
- try:
3450
- name = parser.parse_namespace_object()
3451
- parser.assert_end()
3452
- except DefinitionError as e:
3453
- logger.warning(e, location=self.get_location())
3454
- name = _make_phony_error_name()
3455
- oldParent = self.env.temp_data.get('c:parent_symbol', None)
3456
- if not oldParent:
3457
- oldParent = self.env.domaindata['c']['root_symbol']
3458
- symbol = oldParent.add_name(name)
3459
- stack = self.env.temp_data.get('c:namespace_stack', [])
3460
- stack.append(symbol)
3461
- self.env.temp_data['c:parent_symbol'] = symbol
3462
- self.env.temp_data['c:namespace_stack'] = stack
3463
- self.env.ref_context['c:parent_key'] = symbol.get_lookup_key()
3464
- return []
3465
-
3466
-
3467
- class CNamespacePopObject(SphinxDirective):
3468
- has_content = False
3469
- required_arguments = 0
3470
- optional_arguments = 0
3471
- final_argument_whitespace = True
3472
- option_spec: OptionSpec = {}
3473
-
3474
- def run(self) -> list[Node]:
3475
- stack = self.env.temp_data.get('c:namespace_stack', None)
3476
- if not stack or len(stack) == 0:
3477
- logger.warning("C namespace pop on empty stack. Defaulting to global scope.",
3478
- location=self.get_location())
3479
- stack = []
3480
- else:
3481
- stack.pop()
3482
- if len(stack) > 0:
3483
- symbol = stack[-1]
3484
- else:
3485
- symbol = self.env.domaindata['c']['root_symbol']
3486
- self.env.temp_data['c:parent_symbol'] = symbol
3487
- self.env.temp_data['c:namespace_stack'] = stack
3488
- self.env.ref_context['cp:parent_key'] = symbol.get_lookup_key()
3489
- return []
3490
-
3491
-
3492
- class AliasNode(nodes.Element):
3493
- def __init__(
3494
- self,
3495
- sig: str,
3496
- aliasOptions: dict,
3497
- document: Any,
3498
- env: BuildEnvironment | None = None,
3499
- parentKey: LookupKey | None = None,
3500
- ) -> None:
3501
- super().__init__()
3502
- self.sig = sig
3503
- self.aliasOptions = aliasOptions
3504
- self.document = document
3505
- if env is not None:
3506
- if 'c:parent_symbol' not in env.temp_data:
3507
- root = env.domaindata['c']['root_symbol']
3508
- env.temp_data['c:parent_symbol'] = root
3509
- env.ref_context['c:parent_key'] = root.get_lookup_key()
3510
- self.parentKey = env.ref_context['c:parent_key']
3511
- else:
3512
- assert parentKey is not None
3513
- self.parentKey = parentKey
3514
-
3515
- def copy(self) -> AliasNode:
3516
- return self.__class__(self.sig, self.aliasOptions, self.document,
3517
- env=None, parentKey=self.parentKey)
3518
-
3519
-
3520
- class AliasTransform(SphinxTransform):
3521
- default_priority = ReferencesResolver.default_priority - 1
3522
-
3523
- def _render_symbol(self, s: Symbol, maxdepth: int, skipThis: bool,
3524
- aliasOptions: dict, renderOptions: dict,
3525
- document: Any) -> list[Node]:
3526
- if maxdepth == 0:
3527
- recurse = True
3528
- elif maxdepth == 1:
3529
- recurse = False
3530
- else:
3531
- maxdepth -= 1
3532
- recurse = True
3533
-
3534
- nodes: list[Node] = []
3535
- if not skipThis:
3536
- signode = addnodes.desc_signature('', '')
3537
- nodes.append(signode)
3538
- s.declaration.describe_signature(signode, 'markName', self.env, renderOptions)
3539
-
3540
- if recurse:
3541
- if skipThis:
3542
- childContainer: list[Node] | addnodes.desc = nodes
3543
- else:
3544
- content = addnodes.desc_content()
3545
- desc = addnodes.desc()
3546
- content.append(desc)
3547
- desc.document = document
3548
- desc['domain'] = 'c'
3549
- # 'desctype' is a backwards compatible attribute
3550
- desc['objtype'] = desc['desctype'] = 'alias'
3551
- desc['no-index'] = True
3552
- childContainer = desc
3553
-
3554
- for sChild in s.children:
3555
- if sChild.declaration is None:
3556
- continue
3557
- childNodes = self._render_symbol(
3558
- sChild, maxdepth=maxdepth, skipThis=False,
3559
- aliasOptions=aliasOptions, renderOptions=renderOptions,
3560
- document=document)
3561
- childContainer.extend(childNodes)
3562
-
3563
- if not skipThis and len(desc.children) != 0:
3564
- nodes.append(content)
3565
- return nodes
3566
-
3567
- def apply(self, **kwargs: Any) -> None:
3568
- for node in self.document.findall(AliasNode):
3569
- sig = node.sig
3570
- parentKey = node.parentKey
3571
- try:
3572
- parser = DefinitionParser(sig, location=node,
3573
- config=self.env.config)
3574
- name = parser.parse_xref_object()
3575
- except DefinitionError as e:
3576
- logger.warning(e, location=node)
3577
- name = None
3578
-
3579
- if name is None:
3580
- # could not be parsed, so stop here
3581
- signode = addnodes.desc_signature(sig, '')
3582
- signode.clear()
3583
- signode += addnodes.desc_name(sig, sig)
3584
- node.replace_self(signode)
3585
- continue
3586
-
3587
- rootSymbol: Symbol = self.env.domains['c'].data['root_symbol']
3588
- parentSymbol: Symbol = rootSymbol.direct_lookup(parentKey)
3589
- if not parentSymbol:
3590
- logger.debug("Target: %s", sig)
3591
- logger.debug("ParentKey: %s", parentKey)
3592
- logger.debug(rootSymbol.dump(1))
3593
- assert parentSymbol # should be there
3594
-
3595
- s = parentSymbol.find_declaration(
3596
- name, 'any',
3597
- matchSelf=True, recurseInAnon=True)
3598
- if s is None:
3599
- signode = addnodes.desc_signature(sig, '')
3600
- node.append(signode)
3601
- signode.clear()
3602
- signode += addnodes.desc_name(sig, sig)
3603
-
3604
- logger.warning("Could not find C declaration for alias '%s'." % name,
3605
- location=node)
3606
- node.replace_self(signode)
3607
- continue
3608
- # Declarations like .. var:: int Missing::var
3609
- # may introduce symbols without declarations.
3610
- # But if we skip the root then it is ok to start recursion from it.
3611
- if not node.aliasOptions['noroot'] and s.declaration is None:
3612
- signode = addnodes.desc_signature(sig, '')
3613
- node.append(signode)
3614
- signode.clear()
3615
- signode += addnodes.desc_name(sig, sig)
3616
-
3617
- logger.warning(
3618
- "Can not render C declaration for alias '%s'. No such declaration." % name,
3619
- location=node)
3620
- node.replace_self(signode)
3621
- continue
3622
-
3623
- nodes = self._render_symbol(s, maxdepth=node.aliasOptions['maxdepth'],
3624
- skipThis=node.aliasOptions['noroot'],
3625
- aliasOptions=node.aliasOptions,
3626
- renderOptions={}, document=node.document)
3627
- node.replace_self(nodes)
3628
-
3629
-
3630
- class CAliasObject(ObjectDescription):
3631
- option_spec: OptionSpec = {
3632
- 'maxdepth': directives.nonnegative_int,
3633
- 'noroot': directives.flag,
3634
- }
3635
-
3636
- def run(self) -> list[Node]:
3637
- """
3638
- On purpose this doesn't call the ObjectDescription version, but is based on it.
3639
- Each alias signature may expand into multiple real signatures if 'noroot'.
3640
- The code is therefore based on the ObjectDescription version.
3641
- """
3642
- if ':' in self.name:
3643
- self.domain, self.objtype = self.name.split(':', 1)
3644
- else:
3645
- self.domain, self.objtype = '', self.name
3646
-
3647
- node = addnodes.desc()
3648
- node.document = self.state.document
3649
- node['domain'] = self.domain
3650
- # 'desctype' is a backwards compatible attribute
3651
- node['objtype'] = node['desctype'] = self.objtype
3652
- node['no-index'] = True
3653
-
3654
- self.names: list[str] = []
3655
- aliasOptions = {
3656
- 'maxdepth': self.options.get('maxdepth', 1),
3657
- 'noroot': 'noroot' in self.options,
3658
- }
3659
- if aliasOptions['noroot'] and aliasOptions['maxdepth'] == 1:
3660
- logger.warning("Error in C alias declaration."
3661
- " Requested 'noroot' but 'maxdepth' 1."
3662
- " When skipping the root declaration,"
3663
- " need 'maxdepth' 0 for infinite or at least 2.",
3664
- location=self.get_location())
3665
- for sig in self.get_signatures():
3666
- node.append(AliasNode(sig, aliasOptions, self.state.document, env=self.env))
3667
- return [node]
3668
-
3669
-
3670
- class CXRefRole(XRefRole):
3671
- def process_link(self, env: BuildEnvironment, refnode: Element,
3672
- has_explicit_title: bool, title: str, target: str) -> tuple[str, str]:
3673
- refnode.attributes.update(env.ref_context)
3674
-
3675
- if not has_explicit_title:
3676
- # major hax: replace anon names via simple string manipulation.
3677
- # Can this actually fail?
3678
- title = anon_identifier_re.sub("[anonymous]", str(title))
3679
-
3680
- if not has_explicit_title:
3681
- target = target.lstrip('~') # only has a meaning for the title
3682
- # if the first character is a tilde, don't display the module/class
3683
- # parts of the contents
3684
- if title[0:1] == '~':
3685
- title = title[1:]
3686
- dot = title.rfind('.')
3687
- if dot != -1:
3688
- title = title[dot + 1:]
3689
- return title, target
3690
-
3691
-
3692
- class CExprRole(SphinxRole):
3693
- def __init__(self, asCode: bool) -> None:
3694
- super().__init__()
3695
- if asCode:
3696
- # render the expression as inline code
3697
- self.class_type = 'c-expr'
3698
- else:
3699
- # render the expression as inline text
3700
- self.class_type = 'c-texpr'
3701
-
3702
- def run(self) -> tuple[list[Node], list[system_message]]:
3703
- text = self.text.replace('\n', ' ')
3704
- parser = DefinitionParser(text, location=self.get_location(),
3705
- config=self.env.config)
3706
- # attempt to mimic XRefRole classes, except that...
3707
- try:
3708
- ast = parser.parse_expression()
3709
- except DefinitionError as ex:
3710
- logger.warning('Unparseable C expression: %r\n%s', text, ex,
3711
- location=self.get_location())
3712
- # see below
3713
- return [addnodes.desc_inline('c', text, text, classes=[self.class_type])], []
3714
- parentSymbol = self.env.temp_data.get('c:parent_symbol', None)
3715
- if parentSymbol is None:
3716
- parentSymbol = self.env.domaindata['c']['root_symbol']
3717
- # ...most if not all of these classes should really apply to the individual references,
3718
- # not the container node
3719
- signode = addnodes.desc_inline('c', classes=[self.class_type])
3720
- ast.describe_signature(signode, 'markType', self.env, parentSymbol)
3721
- return [signode], []
3722
-
3723
-
3724
- class CDomain(Domain):
3725
- """C language domain."""
3726
- name = 'c'
3727
- label = 'C'
3728
- object_types = {
3729
- # 'identifier' is the one used for xrefs generated in signatures, not in roles
3730
- 'member': ObjType(_('member'), 'var', 'member', 'data', 'identifier'),
3731
- 'var': ObjType(_('variable'), 'var', 'member', 'data', 'identifier'),
3732
- 'function': ObjType(_('function'), 'func', 'identifier', 'type'),
3733
- 'macro': ObjType(_('macro'), 'macro', 'identifier'),
3734
- 'struct': ObjType(_('struct'), 'struct', 'identifier', 'type'),
3735
- 'union': ObjType(_('union'), 'union', 'identifier', 'type'),
3736
- 'enum': ObjType(_('enum'), 'enum', 'identifier', 'type'),
3737
- 'enumerator': ObjType(_('enumerator'), 'enumerator', 'identifier'),
3738
- 'type': ObjType(_('type'), 'identifier', 'type'),
3739
- # generated object types
3740
- 'functionParam': ObjType(_('function parameter'), 'identifier', 'var', 'member', 'data'), # noqa: E501
3741
- }
3742
-
3743
- directives = {
3744
- 'member': CMemberObject,
3745
- 'var': CMemberObject,
3746
- 'function': CFunctionObject,
3747
- 'macro': CMacroObject,
3748
- 'struct': CStructObject,
3749
- 'union': CUnionObject,
3750
- 'enum': CEnumObject,
3751
- 'enumerator': CEnumeratorObject,
3752
- 'type': CTypeObject,
3753
- # scope control
3754
- 'namespace': CNamespaceObject,
3755
- 'namespace-push': CNamespacePushObject,
3756
- 'namespace-pop': CNamespacePopObject,
3757
- # other
3758
- 'alias': CAliasObject,
3759
- }
3760
- roles = {
3761
- 'member': CXRefRole(),
3762
- 'data': CXRefRole(),
3763
- 'var': CXRefRole(),
3764
- 'func': CXRefRole(fix_parens=True),
3765
- 'macro': CXRefRole(),
3766
- 'struct': CXRefRole(),
3767
- 'union': CXRefRole(),
3768
- 'enum': CXRefRole(),
3769
- 'enumerator': CXRefRole(),
3770
- 'type': CXRefRole(),
3771
- 'expr': CExprRole(asCode=True),
3772
- 'texpr': CExprRole(asCode=False),
3773
- }
3774
- initial_data: dict[str, Symbol | dict[str, tuple[str, str, str]]] = {
3775
- 'root_symbol': Symbol(None, None, None, None, None),
3776
- 'objects': {}, # fullname -> docname, node_id, objtype
3777
- }
3778
-
3779
- def clear_doc(self, docname: str) -> None:
3780
- if Symbol.debug_show_tree:
3781
- logger.debug("clear_doc: %s", docname)
3782
- logger.debug("\tbefore:")
3783
- logger.debug(self.data['root_symbol'].dump(1))
3784
- logger.debug("\tbefore end")
3785
-
3786
- rootSymbol = self.data['root_symbol']
3787
- rootSymbol.clear_doc(docname)
3788
-
3789
- if Symbol.debug_show_tree:
3790
- logger.debug("\tafter:")
3791
- logger.debug(self.data['root_symbol'].dump(1))
3792
- logger.debug("\tafter end")
3793
- logger.debug("clear_doc end: %s", docname)
3794
-
3795
- def process_doc(self, env: BuildEnvironment, docname: str,
3796
- document: nodes.document) -> None:
3797
- if Symbol.debug_show_tree:
3798
- logger.debug("process_doc: %s", docname)
3799
- logger.debug(self.data['root_symbol'].dump(0))
3800
- logger.debug("process_doc end: %s", docname)
3801
-
3802
- def process_field_xref(self, pnode: pending_xref) -> None:
3803
- pnode.attributes.update(self.env.ref_context)
3804
-
3805
- def merge_domaindata(self, docnames: list[str], otherdata: dict) -> None:
3806
- if Symbol.debug_show_tree:
3807
- logger.debug("merge_domaindata:")
3808
- logger.debug("\tself:")
3809
- logger.debug(self.data['root_symbol'].dump(1))
3810
- logger.debug("\tself end")
3811
- logger.debug("\tother:")
3812
- logger.debug(otherdata['root_symbol'].dump(1))
3813
- logger.debug("\tother end")
3814
- logger.debug("merge_domaindata end")
3815
-
3816
- self.data['root_symbol'].merge_with(otherdata['root_symbol'],
3817
- docnames, self.env)
3818
- ourObjects = self.data['objects']
3819
- for fullname, (fn, id_, objtype) in otherdata['objects'].items():
3820
- if fn in docnames:
3821
- if fullname not in ourObjects:
3822
- ourObjects[fullname] = (fn, id_, objtype)
3823
- # no need to warn on duplicates, the symbol merge already does that
3824
-
3825
- def _resolve_xref_inner(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
3826
- typ: str, target: str, node: pending_xref,
3827
- contnode: Element) -> tuple[Element | None, str | None]:
3828
- parser = DefinitionParser(target, location=node, config=env.config)
3829
- try:
3830
- name = parser.parse_xref_object()
3831
- except DefinitionError as e:
3832
- logger.warning('Unparseable C cross-reference: %r\n%s', target, e,
3833
- location=node)
3834
- return None, None
3835
- parentKey: LookupKey = node.get("c:parent_key", None)
3836
- rootSymbol = self.data['root_symbol']
3837
- if parentKey:
3838
- parentSymbol: Symbol = rootSymbol.direct_lookup(parentKey)
3839
- if not parentSymbol:
3840
- logger.debug("Target: %s", target)
3841
- logger.debug("ParentKey: %s", parentKey)
3842
- logger.debug(rootSymbol.dump(1))
3843
- assert parentSymbol # should be there
3844
- else:
3845
- parentSymbol = rootSymbol
3846
- s = parentSymbol.find_declaration(name, typ,
3847
- matchSelf=True, recurseInAnon=True)
3848
- if s is None or s.declaration is None:
3849
- return None, None
3850
-
3851
- # TODO: check role type vs. object type
3852
-
3853
- declaration = s.declaration
3854
- displayName = name.get_display_string()
3855
- docname = s.docname
3856
- assert docname
3857
-
3858
- return make_refnode(builder, fromdocname, docname,
3859
- declaration.get_newest_id(), contnode, displayName,
3860
- ), declaration.objectType
3861
-
3862
- def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
3863
- typ: str, target: str, node: pending_xref,
3864
- contnode: Element) -> Element | None:
3865
- return self._resolve_xref_inner(env, fromdocname, builder, typ,
3866
- target, node, contnode)[0]
3867
-
3868
- def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
3869
- target: str, node: pending_xref, contnode: Element,
3870
- ) -> list[tuple[str, Element]]:
3871
- with logging.suppress_logging():
3872
- retnode, objtype = self._resolve_xref_inner(env, fromdocname, builder,
3873
- 'any', target, node, contnode)
3874
- if retnode:
3875
- return [('c:' + self.role_for_objtype(objtype), retnode)]
3876
- return []
3877
-
3878
- def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
3879
- rootSymbol = self.data['root_symbol']
3880
- for symbol in rootSymbol.get_all_symbols():
3881
- if symbol.declaration is None:
3882
- continue
3883
- assert symbol.docname
3884
- fullNestedName = symbol.get_full_nested_name()
3885
- name = str(fullNestedName).lstrip('.')
3886
- dispname = fullNestedName.get_display_string().lstrip('.')
3887
- objectType = symbol.declaration.objectType
3888
- docname = symbol.docname
3889
- newestId = symbol.declaration.get_newest_id()
3890
- yield (name, dispname, objectType, docname, newestId, 1)
3891
-
3892
-
3893
- def setup(app: Sphinx) -> dict[str, Any]:
3894
- app.add_domain(CDomain)
3895
- app.add_config_value("c_id_attributes", [], 'env')
3896
- app.add_config_value("c_paren_attributes", [], 'env')
3897
- app.add_config_value("c_extra_keywords", _macroKeywords, 'env')
3898
- app.add_config_value("c_maximum_signature_line_length", None, 'env', types={int, None})
3899
- app.add_post_transform(AliasTransform)
3900
-
3901
- return {
3902
- 'version': 'builtin',
3903
- 'env_version': 3,
3904
- 'parallel_read_safe': True,
3905
- 'parallel_write_safe': True,
3906
- }