cxxheaderparser 1.7.0__tar.gz → 1.7.2__tar.gz

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.
Files changed (24) hide show
  1. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/PKG-INFO +1 -1
  2. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/gentest.py +3 -0
  3. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/parser.py +246 -45
  4. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/parserstate.py +14 -3
  5. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/types.py +60 -1
  6. cxxheaderparser-1.7.2/cxxheaderparser/version.py +24 -0
  7. cxxheaderparser-1.7.0/cxxheaderparser/version.py +0 -34
  8. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/.gitignore +0 -0
  9. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/LICENSE.txt +0 -0
  10. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/README.md +0 -0
  11. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/__init__.py +0 -0
  12. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/__main__.py +0 -0
  13. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/_ply/__init__.py +0 -0
  14. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/_ply/lex.py +0 -0
  15. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/dump.py +0 -0
  16. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/errors.py +0 -0
  17. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/lexer.py +0 -0
  18. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/options.py +0 -0
  19. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/preprocessor.py +0 -0
  20. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/py.typed +0 -0
  21. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/simple.py +0 -0
  22. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/tokfmt.py +0 -0
  23. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/cxxheaderparser/visitor.py +0 -0
  24. {cxxheaderparser-1.7.0 → cxxheaderparser-1.7.2}/pyproject.toml +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: cxxheaderparser
3
- Version: 1.7.0
3
+ Version: 1.7.2
4
4
  Summary: Modern C++ header parser
5
5
  Project-URL: Source code, https://github.com/robotpy/cxxheaderparser
6
6
  Author-email: Dustin Spicuzza <robotpy@googlegroups.com>
@@ -1,5 +1,6 @@
1
1
  import argparse
2
2
  import dataclasses
3
+ import enum
3
4
  import inspect
4
5
  import subprocess
5
6
  import textwrap
@@ -44,6 +45,8 @@ def nondefault_repr(data: typing.Any) -> str:
44
45
  for k, v in o.items():
45
46
  vals.append(f'"{k}": {_inner_repr(v)}')
46
47
  return "{" + ",".join(vals) + "}"
48
+ elif isinstance(o, enum.Enum):
49
+ return f"{o.__class__.__qualname__}.{o.name}"
47
50
  else:
48
51
  return repr(o)
49
52
 
@@ -19,6 +19,8 @@ from .parserstate import (
19
19
  from .types import (
20
20
  AnonymousName,
21
21
  Array,
22
+ Attribute,
23
+ AttributeStyle,
22
24
  AutoSpecifier,
23
25
  BaseClass,
24
26
  ClassDecl,
@@ -104,6 +106,7 @@ class CxxParser:
104
106
  None, self.lex.current_location(), global_ns
105
107
  )
106
108
  self.anon_id = 0
109
+ self._pending_attributes: typing.List[Attribute] = []
107
110
 
108
111
  self.verbose = self.options.verbose
109
112
  if self.verbose:
@@ -907,38 +910,116 @@ class CxxParser:
907
910
  }
908
911
  _attribute_start_tokens |= _attribute_specifier_seq_start_types
909
912
 
910
- def _consume_attribute(self, tok: LexToken) -> None:
913
+ def _record_attributes(self, attrs: typing.Iterable[Attribute]) -> None:
914
+ self._pending_attributes.extend(attrs)
915
+
916
+ def _take_pending_attributes(self) -> typing.List[Attribute]:
917
+ attrs = self._pending_attributes
918
+ self._pending_attributes = []
919
+ return attrs
920
+
921
+ def _parse_attribute_list_tokens(
922
+ self, kind: AttributeStyle, *init_tokens: LexToken
923
+ ) -> typing.List[Attribute]:
924
+
925
+ match_stack = [self._balanced_token_map[tok.type] for tok in init_tokens]
926
+ get_token = self.lex.token
927
+
928
+ attrs = []
929
+ name_tokens: typing.List[LexToken] = []
930
+
931
+ def _commit(value: typing.Optional[Value]) -> None:
932
+ name = "".join(tok.value for tok in name_tokens).strip()
933
+ name_tokens.clear()
934
+ if name:
935
+ attrs.append(Attribute(style=kind, name=name, value=value))
936
+ elif value is not None:
937
+ raise self._parse_error(None, expected="missing attribute name")
938
+
939
+ while True:
940
+ tok = get_token()
941
+ if tok.type in self._end_balanced_tokens:
942
+ expected = match_stack[-1]
943
+ if tok.type != expected:
944
+ raise self._parse_error(tok, expected)
945
+
946
+ if name_tokens:
947
+ _commit(None)
948
+
949
+ if len(match_stack) == 2:
950
+ self._next_token_must_be(match_stack[0])
951
+
952
+ break
953
+
954
+ elif tok.type in self._balanced_token_map:
955
+ _commit(self._create_value(self._consume_balanced_tokens(tok)[1:-1]))
956
+ elif tok.type == ",":
957
+ _commit(None)
958
+ else:
959
+ name_tokens.append(tok)
960
+
961
+ return attrs
962
+
963
+ def _consume_attribute(
964
+ self,
965
+ tok: LexToken,
966
+ doxygen: typing.Optional[str] = None,
967
+ *,
968
+ record: bool = True,
969
+ ) -> typing.List[Attribute]:
911
970
  if tok.type == "__attribute__":
912
- self._consume_gcc_attribute(tok)
971
+ attrs = self._consume_gcc_attribute(tok, doxygen, record=record)
913
972
  elif tok.type == "__declspec":
914
- self._consume_declspec(tok)
973
+ attrs = self._consume_declspec(tok, doxygen, record=record)
915
974
  elif tok.type in self._attribute_specifier_seq_start_types:
916
- self._consume_attribute_specifier_seq(tok)
975
+ attrs = self._consume_attribute_specifier_seq(tok, doxygen, record=record)
917
976
  else:
918
977
  raise CxxParseError("internal error")
978
+ return attrs
919
979
 
920
980
  def _consume_gcc_attribute(
921
- self, tok: typing.Optional[LexToken], doxygen: typing.Optional[str] = None
922
- ) -> None:
981
+ self,
982
+ tok: typing.Optional[LexToken],
983
+ doxygen: typing.Optional[str] = None,
984
+ *,
985
+ record: bool = True,
986
+ ) -> typing.List[Attribute]:
987
+ attrs: typing.List[Attribute] = []
923
988
  while True:
924
989
  tok1 = self._next_token_must_be("(")
925
990
  tok2 = self._next_token_must_be("(")
926
- self._consume_balanced_tokens(tok1, tok2)
991
+ attrs.extend(
992
+ self._parse_attribute_list_tokens(AttributeStyle.GCC, tok1, tok2)
993
+ )
927
994
 
928
995
  # Apparently you can have multiple attributes chained together?
929
996
  tok = self.lex.token_if("__attribute__")
930
997
  if tok is None:
931
998
  break
999
+ if record:
1000
+ self._record_attributes(attrs)
1001
+ return attrs
932
1002
 
933
1003
  def _consume_declspec(
934
- self, tok: LexToken, doxygen: typing.Optional[str] = None
935
- ) -> None:
1004
+ self,
1005
+ tok: LexToken,
1006
+ doxygen: typing.Optional[str] = None,
1007
+ *,
1008
+ record: bool = True,
1009
+ ) -> typing.List[Attribute]:
936
1010
  tok = self._next_token_must_be("(")
937
- self._consume_balanced_tokens(tok)
1011
+ attrs = self._parse_attribute_list_tokens(AttributeStyle.MSVC, tok)
1012
+ if record:
1013
+ self._record_attributes(attrs)
1014
+ return attrs
938
1015
 
939
1016
  def _consume_attribute_specifier_seq(
940
- self, tok: LexToken, doxygen: typing.Optional[str] = None
941
- ) -> None:
1017
+ self,
1018
+ tok: LexToken,
1019
+ doxygen: typing.Optional[str] = None,
1020
+ *,
1021
+ record: bool = True,
1022
+ ) -> typing.List[Attribute]:
942
1023
  """
943
1024
  attribute_specifier_seq: attribute_specifier
944
1025
  | attribute_specifier_seq attribute_specifier
@@ -974,17 +1055,20 @@ class CxxParser:
974
1055
  | token
975
1056
  """
976
1057
 
977
- # TODO: retain the attributes and do something with them
978
- # attrs = []
979
-
1058
+ attrs: typing.List[Attribute] = []
980
1059
  while True:
981
1060
  if tok.type == "DBL_LBRACKET":
982
- tokens = self._consume_balanced_tokens(tok)
983
- # attrs.append(Attribute(tokens))
1061
+ attrs.extend(self._parse_attribute_list_tokens(AttributeStyle.CXX, tok))
984
1062
  elif tok.type == "alignas":
985
1063
  next_tok = self._next_token_must_be("(")
986
1064
  tokens = self._consume_balanced_tokens(next_tok)
987
- # attrs.append(AlignasAttribute(tokens))
1065
+ attrs.append(
1066
+ Attribute(
1067
+ style=AttributeStyle.CXX,
1068
+ name="alignas",
1069
+ value=self._create_value(tokens[1:-1]),
1070
+ )
1071
+ )
988
1072
  else:
989
1073
  self.lex.return_token(tok)
990
1074
  break
@@ -996,7 +1080,18 @@ class CxxParser:
996
1080
 
997
1081
  tok = maybe_tok
998
1082
 
999
- # TODO return attrs
1083
+ if record:
1084
+ self._record_attributes(attrs)
1085
+ return attrs
1086
+
1087
+ def _consume_decl_attributes(self) -> typing.List[Attribute]:
1088
+ attrs: typing.List[Attribute] = []
1089
+ while True:
1090
+ tok = self.lex.token_if_in_set(self._attribute_start_tokens)
1091
+ if not tok:
1092
+ break
1093
+ attrs.extend(self._consume_attribute(tok, record=False))
1094
+ return attrs
1000
1095
 
1001
1096
  #
1002
1097
  # Using directive/declaration/typealias
@@ -1109,6 +1204,7 @@ class CxxParser:
1109
1204
  is_typedef: bool,
1110
1205
  location: Location,
1111
1206
  mods: ParsedTypeModifiers,
1207
+ attributes: typing.List[Attribute],
1112
1208
  ) -> None:
1113
1209
  """
1114
1210
  opaque_enum_declaration: enum_key [attribute_specifier_seq] IDENTIFIER [enum_base] ";"
@@ -1148,18 +1244,29 @@ class CxxParser:
1148
1244
 
1149
1245
  # enum forward declaration with base
1150
1246
  fdecl = ForwardDecl(
1151
- typename, None, doxygen, base, access=self._current_access
1247
+ typename,
1248
+ None,
1249
+ doxygen,
1250
+ attributes=attributes,
1251
+ enum_base=base,
1252
+ access=self._current_access,
1152
1253
  )
1153
1254
  self.visitor.on_forward_decl(self.state, fdecl)
1154
1255
  return
1155
1256
 
1156
1257
  values = self._parse_enumerator_list()
1157
1258
 
1158
- enum = EnumDecl(typename, values, base, doxygen, self._current_access)
1259
+ enum = EnumDecl(
1260
+ typename, values, base, doxygen, attributes, self._current_access
1261
+ )
1159
1262
  self.visitor.on_enum(self.state, enum)
1160
1263
 
1161
1264
  # Finish it up
1162
- self._finish_class_or_enum(enum.typename, is_typedef, mods, "enum")
1265
+ attrs_after = self._finish_class_or_enum(
1266
+ enum.typename, is_typedef, mods, "enum"
1267
+ )
1268
+ if attrs_after:
1269
+ enum.attributes.extend(attrs_after)
1163
1270
 
1164
1271
  def _parse_enumerator_list(self) -> typing.List[Enumerator]:
1165
1272
  """
@@ -1186,17 +1293,18 @@ class CxxParser:
1186
1293
 
1187
1294
  name = name_tok.value
1188
1295
  value = None
1296
+ attributes = []
1189
1297
 
1190
1298
  tok = self._next_token_must_be("}", ",", "=", "DBL_LBRACKET")
1191
1299
  if tok.type == "DBL_LBRACKET":
1192
- self._consume_attribute_specifier_seq(tok)
1300
+ attributes = self._consume_attribute_specifier_seq(tok, record=False)
1193
1301
  tok = self._next_token_must_be("}", ",", "=")
1194
1302
 
1195
1303
  if tok.type == "=":
1196
1304
  value = self._create_value(self._consume_value_until([], ",", "}"))
1197
1305
  tok = self._next_token_must_be("}", ",")
1198
1306
 
1199
- values.append(Enumerator(name, value, doxygen))
1307
+ values.append(Enumerator(name, value, doxygen, attributes))
1200
1308
 
1201
1309
  if tok.type == "}":
1202
1310
  break
@@ -1235,8 +1343,9 @@ class CxxParser:
1235
1343
  tok_type = tok.type
1236
1344
 
1237
1345
  # might start with attributes
1346
+ attributes = []
1238
1347
  if tok.type in self._attribute_specifier_seq_start_types:
1239
- self._consume_attribute_specifier_seq(tok)
1348
+ attributes = self._consume_attribute_specifier_seq(tok, record=False)
1240
1349
  tok = self.lex.token()
1241
1350
  tok_type = tok.type
1242
1351
 
@@ -1261,7 +1370,9 @@ class CxxParser:
1261
1370
  if self.lex.token_if("ELLIPSIS"):
1262
1371
  parameter_pack = True
1263
1372
 
1264
- bases.append(BaseClass(access, typename, virtual, parameter_pack))
1373
+ bases.append(
1374
+ BaseClass(access, typename, virtual, parameter_pack, attributes)
1375
+ )
1265
1376
 
1266
1377
  if not self.lex.token_if(","):
1267
1378
  break
@@ -1277,6 +1388,7 @@ class CxxParser:
1277
1388
  typedef: bool,
1278
1389
  location: Location,
1279
1390
  mods: ParsedTypeModifiers,
1391
+ attributes: typing.List[Attribute],
1280
1392
  ) -> None:
1281
1393
  """
1282
1394
  class_specifier: class_head "{" [member_specification] "}"
@@ -1329,7 +1441,14 @@ class CxxParser:
1329
1441
  raise self._parse_error(tok, "{")
1330
1442
 
1331
1443
  clsdecl = ClassDecl(
1332
- typename, bases, template, explicit, final, doxygen, self._current_access
1444
+ typename,
1445
+ bases,
1446
+ template,
1447
+ explicit,
1448
+ final,
1449
+ doxygen,
1450
+ attributes,
1451
+ self._current_access,
1333
1452
  )
1334
1453
  state: ClassBlockState = ClassBlockState(
1335
1454
  self.state, location, clsdecl, default_access, typedef, mods
@@ -1340,12 +1459,14 @@ class CxxParser:
1340
1459
  self.visitor = null_visitor
1341
1460
 
1342
1461
  def _finish_class_decl(self, state: ClassBlockState) -> None:
1343
- self._finish_class_or_enum(
1462
+ attrs_after = self._finish_class_or_enum(
1344
1463
  state.class_decl.typename,
1345
1464
  state.typedef,
1346
1465
  state.mods,
1347
1466
  state.class_decl.classkey,
1348
1467
  )
1468
+ if attrs_after:
1469
+ state.class_decl.attributes.extend(attrs_after)
1349
1470
 
1350
1471
  def _process_access_specifier(
1351
1472
  self, tok: LexToken, doxygen: typing.Optional[str]
@@ -1433,6 +1554,7 @@ class CxxParser:
1433
1554
  doxygen: typing.Optional[str],
1434
1555
  location: Location,
1435
1556
  is_typedef: bool,
1557
+ attributes: typing.List[Attribute],
1436
1558
  ) -> None:
1437
1559
  state = self.state
1438
1560
  state.location = location
@@ -1498,7 +1620,7 @@ class CxxParser:
1498
1620
  if is_typedef:
1499
1621
  if not name:
1500
1622
  raise self._parse_error(None)
1501
- typedef = Typedef(dtype, name, self._current_access)
1623
+ typedef = Typedef(dtype, name, self._current_access, attributes)
1502
1624
  self.visitor.on_typedef(state, typedef)
1503
1625
  else:
1504
1626
  props = dict.fromkeys(mods.both.keys(), True)
@@ -1515,13 +1637,20 @@ class CxxParser:
1515
1637
  value=default,
1516
1638
  bits=bits,
1517
1639
  doxygen=doxygen,
1640
+ attributes=attributes,
1518
1641
  **props,
1519
1642
  )
1520
1643
  self.visitor.on_class_field(class_state, f)
1521
1644
  else:
1522
1645
  assert pqname is not None
1523
1646
  v = Variable(
1524
- pqname, dtype, default, doxygen=doxygen, template=template, **props
1647
+ pqname,
1648
+ dtype,
1649
+ default,
1650
+ doxygen=doxygen,
1651
+ template=template,
1652
+ attributes=attributes,
1653
+ **props,
1525
1654
  )
1526
1655
  self.visitor.on_variable(state, v)
1527
1656
 
@@ -2000,6 +2129,10 @@ class CxxParser:
2000
2129
  fn.has_trailing_return = True
2001
2130
  fn.return_type = return_type
2002
2131
 
2132
+ extra_attrs = self._consume_decl_attributes()
2133
+ if extra_attrs:
2134
+ fn.attributes.extend(extra_attrs)
2135
+
2003
2136
  if self.lex.token_if("{"):
2004
2137
  self._discard_contents("{", "}")
2005
2138
  fn.has_body = True
@@ -2073,6 +2206,11 @@ class CxxParser:
2073
2206
  self.lex.return_token(tok)
2074
2207
  break
2075
2208
 
2209
+ if not method.has_body:
2210
+ extra_attrs = self._consume_decl_attributes()
2211
+ if extra_attrs:
2212
+ method.attributes.extend(extra_attrs)
2213
+
2076
2214
  def _parse_function(
2077
2215
  self,
2078
2216
  mods: ParsedTypeModifiers,
@@ -2088,6 +2226,7 @@ class CxxParser:
2088
2226
  is_typedef: bool,
2089
2227
  msvc_convention: typing.Optional[LexToken],
2090
2228
  is_guide: bool = False,
2229
+ attributes: typing.Optional[typing.List[Attribute]] = None,
2091
2230
  ) -> bool:
2092
2231
  """
2093
2232
  Assumes the caller has already consumed the return type and name, this consumes the
@@ -2129,6 +2268,11 @@ class CxxParser:
2129
2268
 
2130
2269
  if (is_class_block or multiple_name_segments) and not is_typedef:
2131
2270
  props.update(dict.fromkeys(mods.meths.keys(), True))
2271
+ if mods.explicit_value is not None:
2272
+ props["explicit"] = mods.explicit_value
2273
+
2274
+ if attributes is None:
2275
+ attributes = []
2132
2276
 
2133
2277
  method = Method(
2134
2278
  return_type,
@@ -2136,6 +2280,7 @@ class CxxParser:
2136
2280
  params,
2137
2281
  vararg,
2138
2282
  doxygen=doxygen,
2283
+ attributes=attributes,
2139
2284
  constructor=constructor,
2140
2285
  destructor=destructor,
2141
2286
  template=template,
@@ -2183,12 +2328,15 @@ class CxxParser:
2183
2328
  return False
2184
2329
  else:
2185
2330
  assert return_type is not None
2331
+ if attributes is None:
2332
+ attributes = []
2186
2333
  fn = Function(
2187
2334
  return_type,
2188
2335
  pqname,
2189
2336
  params,
2190
2337
  vararg,
2191
2338
  doxygen=doxygen,
2339
+ attributes=attributes,
2192
2340
  template=template,
2193
2341
  operator=op,
2194
2342
  **props,
@@ -2230,7 +2378,7 @@ class CxxParser:
2230
2378
  msvc_convention=fn.msvc_convention,
2231
2379
  )
2232
2380
 
2233
- typedef = Typedef(fntype, name, self._current_access)
2381
+ typedef = Typedef(fntype, name, self._current_access, attributes or [])
2234
2382
  self.visitor.on_typedef(state, typedef)
2235
2383
  return False
2236
2384
  else:
@@ -2416,6 +2564,7 @@ class CxxParser:
2416
2564
  vars: typing.Dict[str, LexToken] = {} # only found on variables
2417
2565
  both: typing.Dict[str, LexToken] = {} # found on either
2418
2566
  meths: typing.Dict[str, LexToken] = {} # only found on methods
2567
+ explicit_value: typing.Optional[Value] = None
2419
2568
 
2420
2569
  get_token = self.lex.token
2421
2570
 
@@ -2424,6 +2573,7 @@ class CxxParser:
2424
2573
 
2425
2574
  pqname: typing.Optional[PQName] = None
2426
2575
  pqname_optional = False
2576
+ friend_tok: typing.Optional[LexToken] = None
2427
2577
 
2428
2578
  _pqname_start_tokens = self._pqname_start_tokens
2429
2579
  _attribute_start = self._attribute_start_tokens
@@ -2448,6 +2598,8 @@ class CxxParser:
2448
2598
  break
2449
2599
  elif tok_type == "const":
2450
2600
  const = True
2601
+ elif tok_type == "friend" and pqname is None:
2602
+ friend_tok = tok
2451
2603
  elif tok_type in self._type_kwd_both:
2452
2604
  if tok_type == "extern":
2453
2605
  # TODO: store linkage
@@ -2455,6 +2607,13 @@ class CxxParser:
2455
2607
  both[tok_type] = tok
2456
2608
  elif tok_type in self._type_kwd_meth:
2457
2609
  meths[tok_type] = tok
2610
+ if tok_type == "explicit":
2611
+ # C++20: explicit(<bool-constant-expression>)
2612
+ otok = self.lex.token_if("(")
2613
+ if otok:
2614
+ explicit_value = self._create_value(
2615
+ self._consume_balanced_tokens(otok)[1:-1]
2616
+ )
2458
2617
  elif tok_type == "mutable":
2459
2618
  vars["mutable"] = tok
2460
2619
  elif tok_type == "volatile":
@@ -2479,7 +2638,7 @@ class CxxParser:
2479
2638
  self.lex.return_token(tok)
2480
2639
 
2481
2640
  # Always return the modifiers
2482
- mods = ParsedTypeModifiers(vars, both, meths)
2641
+ mods = ParsedTypeModifiers(vars, both, meths, explicit_value, friend_tok)
2483
2642
  return parsed_type, mods
2484
2643
 
2485
2644
  def _parse_decl(
@@ -2491,6 +2650,7 @@ class CxxParser:
2491
2650
  template: TemplateDeclTypeVar,
2492
2651
  is_typedef: bool,
2493
2652
  is_friend: bool,
2653
+ attributes: typing.List[Attribute],
2494
2654
  ) -> bool:
2495
2655
  toks = []
2496
2656
 
@@ -2596,6 +2756,7 @@ class CxxParser:
2596
2756
  is_typedef,
2597
2757
  msvc_convention,
2598
2758
  is_guide,
2759
+ attributes,
2599
2760
  )
2600
2761
  elif msvc_convention:
2601
2762
  raise self._parse_error(msvc_convention)
@@ -2612,6 +2773,7 @@ class CxxParser:
2612
2773
  parsed_type.typename,
2613
2774
  template,
2614
2775
  doxygen,
2776
+ attributes=attributes,
2615
2777
  access=self._current_access,
2616
2778
  )
2617
2779
  friend = FriendDecl(cls=fwd)
@@ -2627,7 +2789,9 @@ class CxxParser:
2627
2789
  if isinstance(template, list):
2628
2790
  raise CxxParseError("multiple template declarations on a field")
2629
2791
 
2630
- self._parse_field(mods, dtype, pqname, template, doxygen, location, is_typedef)
2792
+ self._parse_field(
2793
+ mods, dtype, pqname, template, doxygen, location, is_typedef, attributes
2794
+ )
2631
2795
  return False
2632
2796
 
2633
2797
  def _parse_operator_conversion(
@@ -2638,6 +2802,7 @@ class CxxParser:
2638
2802
  template: TemplateDeclTypeVar,
2639
2803
  is_typedef: bool,
2640
2804
  is_friend: bool,
2805
+ attributes: typing.List[Attribute],
2641
2806
  ) -> None:
2642
2807
  tok = self._next_token_must_be("operator")
2643
2808
 
@@ -2675,6 +2840,8 @@ class CxxParser:
2675
2840
  is_friend,
2676
2841
  False,
2677
2842
  None,
2843
+ False,
2844
+ attributes,
2678
2845
  ):
2679
2846
  # has function body and handled it
2680
2847
  return
@@ -2701,15 +2868,31 @@ class CxxParser:
2701
2868
 
2702
2869
  location = tok.location
2703
2870
 
2871
+ attributes = self._take_pending_attributes()
2872
+
2704
2873
  # Almost always starts out with some kind of type name or a modifier
2705
2874
  parsed_type, mods = self._parse_type(tok, operator_ok=True)
2875
+ attributes.extend(self._take_pending_attributes())
2876
+
2877
+ if mods.friend is not None:
2878
+ if is_friend or is_typedef or not isinstance(self.state, ClassBlockState):
2879
+ raise self._parse_error(mods.friend)
2880
+ is_friend = True
2881
+ mods = mods._replace(friend=None)
2706
2882
 
2707
2883
  # Check to see if this might be a class/enum declaration
2708
2884
  if (
2709
2885
  parsed_type is not None
2710
2886
  and parsed_type.typename.classkey
2711
2887
  and self._maybe_parse_class_enum_decl(
2712
- parsed_type, mods, doxygen, template, is_typedef, is_friend, location
2888
+ parsed_type,
2889
+ mods,
2890
+ doxygen,
2891
+ template,
2892
+ is_typedef,
2893
+ is_friend,
2894
+ location,
2895
+ attributes,
2713
2896
  )
2714
2897
  ):
2715
2898
  return
@@ -2742,14 +2925,21 @@ class CxxParser:
2742
2925
  if parsed_type is None:
2743
2926
  # this means an operator was encountered, deal with the special case
2744
2927
  self._parse_operator_conversion(
2745
- mods, location, doxygen, template, is_typedef, is_friend
2928
+ mods, location, doxygen, template, is_typedef, is_friend, attributes
2746
2929
  )
2747
2930
  return
2748
2931
 
2749
2932
  # Ok, dealing with a variable or function/method
2750
2933
  while True:
2751
2934
  if self._parse_decl(
2752
- parsed_type, mods, location, doxygen, template, is_typedef, is_friend
2935
+ parsed_type,
2936
+ mods,
2937
+ location,
2938
+ doxygen,
2939
+ template,
2940
+ is_typedef,
2941
+ is_friend,
2942
+ attributes,
2753
2943
  ):
2754
2944
  # if it returns True then it handled the end of the statement
2755
2945
  break
@@ -2777,6 +2967,7 @@ class CxxParser:
2777
2967
  is_typedef: bool,
2778
2968
  is_friend: bool,
2779
2969
  location: Location,
2970
+ attributes: typing.List[Attribute],
2780
2971
  ) -> bool:
2781
2972
  # check for forward declaration or friend declaration
2782
2973
  if self.lex.token_if(";"):
@@ -2801,7 +2992,11 @@ class CxxParser:
2801
2992
  raise self._parse_error(None)
2802
2993
 
2803
2994
  fdecl = ForwardDecl(
2804
- parsed_type.typename, template, doxygen, access=self._current_access
2995
+ parsed_type.typename,
2996
+ template,
2997
+ doxygen,
2998
+ attributes=attributes,
2999
+ access=self._current_access,
2805
3000
  )
2806
3001
  state = self.state
2807
3002
  state.location = location
@@ -2830,14 +3025,21 @@ class CxxParser:
2830
3025
 
2831
3026
  if typename.classkey in ("class", "struct", "union"):
2832
3027
  self._parse_class_decl(
2833
- typename, tok, doxygen, template, is_typedef, location, mods
3028
+ typename,
3029
+ tok,
3030
+ doxygen,
3031
+ template,
3032
+ is_typedef,
3033
+ location,
3034
+ mods,
3035
+ attributes,
2834
3036
  )
2835
3037
  else:
2836
3038
  if template:
2837
3039
  # enum cannot have a template
2838
3040
  raise self._parse_error(tok)
2839
3041
  self._parse_enum_decl(
2840
- typename, tok, doxygen, is_typedef, location, mods
3042
+ typename, tok, doxygen, is_typedef, location, mods, attributes
2841
3043
  )
2842
3044
 
2843
3045
  return True
@@ -2851,12 +3053,10 @@ class CxxParser:
2851
3053
  is_typedef: bool,
2852
3054
  mods: ParsedTypeModifiers,
2853
3055
  classkey: typing.Optional[str],
2854
- ) -> None:
3056
+ ) -> typing.Optional[typing.List[Attribute]]:
2855
3057
  parsed_type = Type(name)
2856
3058
 
2857
- tok = self.lex.token_if("__attribute__")
2858
- if tok:
2859
- self._consume_gcc_attribute(tok)
3059
+ attrs_after = self._consume_decl_attributes()
2860
3060
 
2861
3061
  if not is_typedef and self.lex.token_if(";"):
2862
3062
  # if parent scope is a class, add the anonymous
@@ -2876,12 +3076,12 @@ class CxxParser:
2876
3076
  access=access,
2877
3077
  )
2878
3078
  self.visitor.on_class_field(class_state, f)
2879
- return
3079
+ return attrs_after
2880
3080
 
2881
3081
  while True:
2882
3082
  location = self.lex.current_location()
2883
3083
  if self._parse_decl(
2884
- parsed_type, mods, location, None, None, is_typedef, False
3084
+ parsed_type, mods, location, None, None, is_typedef, False, attrs_after
2885
3085
  ):
2886
3086
  # if it returns True then it handled the end of the statement
2887
3087
  break
@@ -2890,3 +3090,4 @@ class CxxParser:
2890
3090
  tok = self._next_token_must_be(",", ";")
2891
3091
  if tok.type == ";":
2892
3092
  break
3093
+ return None
@@ -5,15 +5,23 @@ if typing.TYPE_CHECKING:
5
5
 
6
6
  from .errors import CxxParseError
7
7
  from .lexer import LexToken, Location
8
- from .types import ClassDecl, NamespaceDecl
8
+ from .types import ClassDecl, NamespaceDecl, Value
9
9
 
10
10
 
11
11
  class ParsedTypeModifiers(typing.NamedTuple):
12
12
  vars: typing.Dict[str, LexToken] # only found on variables
13
13
  both: typing.Dict[str, LexToken] # found on either variables or functions
14
14
  meths: typing.Dict[str, LexToken] # only found on methods
15
-
16
- def validate(self, *, var_ok: bool, meth_ok: bool, msg: str) -> None:
15
+ #: For C++20 ``explicit(<expr>)``: the constant expression inside the
16
+ #: parens (omitting the parens themselves). ``None`` if absent or if
17
+ #: ``explicit`` was used as a bare keyword.
18
+ explicit_value: typing.Optional[Value] = None
19
+ #: ``friend`` if encountered while parsing declaration specifiers.
20
+ friend: typing.Optional[LexToken] = None
21
+
22
+ def validate(
23
+ self, *, var_ok: bool, meth_ok: bool, msg: str, friend_ok: bool = False
24
+ ) -> None:
17
25
  # Almost there! Do any checks the caller asked for
18
26
  if not var_ok and self.vars:
19
27
  for tok in self.vars.values():
@@ -27,6 +35,9 @@ class ParsedTypeModifiers(typing.NamedTuple):
27
35
  for tok in self.both.values():
28
36
  raise CxxParseError(f"{msg}: unexpected '{tok.value}'")
29
37
 
38
+ if not friend_ok and self.friend is not None:
39
+ raise CxxParseError(f"{msg}: unexpected '{self.friend.value}'")
40
+
30
41
 
31
42
  #: custom user data for this state type
32
43
  T = typing.TypeVar("T")
@@ -1,4 +1,5 @@
1
1
  import typing
2
+ from enum import Enum
2
3
  from dataclasses import dataclass, field
3
4
 
4
5
  from .tokfmt import tokfmt, Token
@@ -22,6 +23,41 @@ class Value:
22
23
  return tokfmt(self.tokens)
23
24
 
24
25
 
26
+ class AttributeStyle(Enum):
27
+ """
28
+ Indicates what style of attribute was found
29
+ """
30
+
31
+ #: ``[[deprecated]]``
32
+ CXX = "cxx"
33
+
34
+ #: ``__attribute__((deprecated))``
35
+ GCC = "gcc"
36
+
37
+ #: ``__declspec(deprecated)``
38
+ MSVC = "msvc"
39
+
40
+
41
+ @dataclass
42
+ class Attribute:
43
+ """
44
+ An attribute
45
+
46
+ .. code-block:: c++
47
+
48
+ [[deprecated("message")]]
49
+ ~~~~~~~~~ ~~~~~~~~~~~
50
+ """
51
+
52
+ #: Indicates what style of attribute this is
53
+ style: AttributeStyle
54
+
55
+ name: str
56
+
57
+ #: Portion of the attribute inside the parens
58
+ value: typing.Optional[Value] = None
59
+
60
+
25
61
  @dataclass
26
62
  class NamespaceAlias:
27
63
  """
@@ -427,6 +463,9 @@ class Enumerator:
427
463
  #: Documentation if present
428
464
  doxygen: typing.Optional[str] = None
429
465
 
466
+ #: Any attributes attached to this enumerator
467
+ attributes: typing.List[Attribute] = field(default_factory=list)
468
+
430
469
 
431
470
  @dataclass
432
471
  class EnumDecl:
@@ -442,6 +481,8 @@ class EnumDecl:
442
481
 
443
482
  #: Documentation if present
444
483
  doxygen: typing.Optional[str] = None
484
+ #: Any attributes attached to this enum declaration
485
+ attributes: typing.List[Attribute] = field(default_factory=list)
445
486
 
446
487
  #: If within a class, the access level for this decl
447
488
  access: typing.Optional[str] = None
@@ -610,6 +651,8 @@ class ForwardDecl:
610
651
  typename: PQName
611
652
  template: TemplateDeclTypeVar = None
612
653
  doxygen: typing.Optional[str] = None
654
+ #: Any attributes attached to this forward declaration
655
+ attributes: typing.List[Attribute] = field(default_factory=list)
613
656
 
614
657
  #: Set if this is a forward declaration of an enum and it has a base
615
658
  enum_base: typing.Optional[PQName] = None
@@ -635,6 +678,8 @@ class BaseClass:
635
678
 
636
679
  #: Contains a ``...``
637
680
  param_pack: bool = False
681
+ #: Any attributes attached to this base specifier
682
+ attributes: typing.List[Attribute] = field(default_factory=list)
638
683
 
639
684
 
640
685
  @dataclass
@@ -652,6 +697,8 @@ class ClassDecl:
652
697
  final: bool = False
653
698
 
654
699
  doxygen: typing.Optional[str] = None
700
+ #: Any attributes attached to this class declaration
701
+ attributes: typing.List[Attribute] = field(default_factory=list)
655
702
 
656
703
  #: If within a class, the access level for this decl
657
704
  access: typing.Optional[str] = None
@@ -701,6 +748,8 @@ class Function:
701
748
  vararg: bool = False
702
749
 
703
750
  doxygen: typing.Optional[str] = None
751
+ #: Any attributes attached to this function declaration
752
+ attributes: typing.List[Attribute] = field(default_factory=list)
704
753
 
705
754
  constexpr: bool = False
706
755
  extern: typing.Union[bool, str] = False
@@ -773,7 +822,11 @@ class Method(Function):
773
822
  ref_qualifier: typing.Optional[str] = None
774
823
 
775
824
  constructor: bool = False
776
- explicit: bool = False
825
+
826
+ #: True if the method was declared ``explicit``. For C++20
827
+ #: ``explicit(<expr>)``, this holds the expression as a Value (omitting
828
+ #: the outer parentheses).
829
+ explicit: typing.Union[bool, Value] = False
777
830
  default: bool = False
778
831
 
779
832
  destructor: bool = False
@@ -825,6 +878,8 @@ class Typedef:
825
878
 
826
879
  #: If within a class, the access level for this decl
827
880
  access: typing.Optional[str] = None
881
+ #: Any attributes attached to this typedef
882
+ attributes: typing.List[Attribute] = field(default_factory=list)
828
883
 
829
884
 
830
885
  @dataclass
@@ -847,6 +902,8 @@ class Variable:
847
902
  template: typing.Optional[TemplateDecl] = None
848
903
 
849
904
  doxygen: typing.Optional[str] = None
905
+ #: Any attributes attached to this variable
906
+ attributes: typing.List[Attribute] = field(default_factory=list)
850
907
 
851
908
 
852
909
  @dataclass
@@ -870,6 +927,8 @@ class Field:
870
927
  inline: bool = False
871
928
 
872
929
  doxygen: typing.Optional[str] = None
930
+ #: Any attributes attached to this field
931
+ attributes: typing.List[Attribute] = field(default_factory=list)
873
932
 
874
933
 
875
934
  @dataclass
@@ -0,0 +1,24 @@
1
+ # file generated by vcs-versioning
2
+ # don't change, don't track in version control
3
+ from __future__ import annotations
4
+
5
+ __all__ = [
6
+ "__version__",
7
+ "__version_tuple__",
8
+ "version",
9
+ "version_tuple",
10
+ "__commit_id__",
11
+ "commit_id",
12
+ ]
13
+
14
+ version: str
15
+ __version__: str
16
+ __version_tuple__: tuple[int | str, ...]
17
+ version_tuple: tuple[int | str, ...]
18
+ commit_id: str | None
19
+ __commit_id__: str | None
20
+
21
+ __version__ = version = '1.7.2'
22
+ __version_tuple__ = version_tuple = (1, 7, 2)
23
+
24
+ __commit_id__ = commit_id = None
@@ -1,34 +0,0 @@
1
- # file generated by setuptools-scm
2
- # don't change, don't track in version control
3
-
4
- __all__ = [
5
- "__version__",
6
- "__version_tuple__",
7
- "version",
8
- "version_tuple",
9
- "__commit_id__",
10
- "commit_id",
11
- ]
12
-
13
- TYPE_CHECKING = False
14
- if TYPE_CHECKING:
15
- from typing import Tuple
16
- from typing import Union
17
-
18
- VERSION_TUPLE = Tuple[Union[int, str], ...]
19
- COMMIT_ID = Union[str, None]
20
- else:
21
- VERSION_TUPLE = object
22
- COMMIT_ID = object
23
-
24
- version: str
25
- __version__: str
26
- __version_tuple__: VERSION_TUPLE
27
- version_tuple: VERSION_TUPLE
28
- commit_id: COMMIT_ID
29
- __commit_id__: COMMIT_ID
30
-
31
- __version__ = version = '1.7.0'
32
- __version_tuple__ = version_tuple = (1, 7, 0)
33
-
34
- __commit_id__ = commit_id = None