Enable automatic formatting for `sphinx/domains/c/`

This commit is contained in:
Adam Turner 2024-12-30 02:09:50 +00:00
parent e47d12862e
commit 3e2f89b820
6 changed files with 1027 additions and 656 deletions

View File

@ -394,11 +394,6 @@ preview = true
quote-style = "single"
exclude = [
"sphinx/builders/latex/constants.py",
"sphinx/domains/c/_parser.py",
"sphinx/domains/c/_ids.py",
"sphinx/domains/c/__init__.py",
"sphinx/domains/c/_symbol.py",
"sphinx/domains/c/_ast.py",
"sphinx/domains/changeset.py",
"sphinx/domains/citation.py",
"sphinx/domains/cpp/_parser.py",

View File

@ -101,7 +101,7 @@ logger = logging.getLogger(__name__)
def _make_phony_error_name() -> ASTNestedName:
return ASTNestedName([ASTIdentifier("PhonyNameDueToError")], rooted=False)
return ASTNestedName([ASTIdentifier('PhonyNameDueToError')], rooted=False)
class CObject(ObjectDescription[ASTDeclaration]):
@ -125,38 +125,44 @@ class CObject(ObjectDescription[ASTDeclaration]):
symbol = ast.symbol
assert symbol
assert symbol.ident is not None
parentSymbol = symbol.parent
assert parentSymbol
if parentSymbol.parent is None:
parent_symbol = symbol.parent
assert parent_symbol
if parent_symbol.parent is None:
# TODO: we could warn, but it is somewhat equivalent to
# enumeratorss, without the enum
return # no parent
parentDecl = parentSymbol.declaration
if parentDecl is None:
parent_decl = parent_symbol.declaration
if parent_decl is None:
# the parent is not explicitly declared
# TODO: we could warn, but?
return
if parentDecl.objectType != 'enum':
if parent_decl.objectType != 'enum':
# TODO: maybe issue a warning, enumerators in non-enums is weird,
# but it is somewhat equivalent to enumeratorss, without the enum
return
if parentDecl.directiveType != 'enum':
if parent_decl.directiveType != 'enum':
return
targetSymbol = parentSymbol.parent
s = targetSymbol.find_identifier(symbol.ident, matchSelf=False, recurseInAnon=True,
searchInSiblings=False)
target_symbol = parent_symbol.parent
s = target_symbol.find_identifier(
symbol.ident, matchSelf=False, recurseInAnon=True, searchInSiblings=False
)
if s is not None:
# something is already declared with that name
return
declClone = symbol.declaration.clone()
declClone.enumeratorScopedSymbol = symbol
Symbol(parent=targetSymbol, ident=symbol.ident,
declaration=declClone,
docname=self.env.docname, line=self.get_source_info()[1])
decl_clone = symbol.declaration.clone()
decl_clone.enumeratorScopedSymbol = symbol
Symbol(
parent=target_symbol,
ident=symbol.ident,
declaration=decl_clone,
docname=self.env.docname,
line=self.get_source_info()[1],
)
def add_target_and_index(self, ast: ASTDeclaration, sig: str,
signode: TextElement) -> None:
def add_target_and_index(
self, ast: ASTDeclaration, sig: str, signode: TextElement
) -> None:
ids = []
for i in range(1, _max_id + 1):
try:
@ -166,14 +172,14 @@ class CObject(ObjectDescription[ASTDeclaration]):
assert i < _max_id
# let's keep the newest first
ids.reverse()
newestId = ids[0]
assert newestId # shouldn't be None
newest_id = ids[0]
assert newest_id # shouldn't be None
name = ast.symbol.get_full_nested_name().get_display_string().lstrip('.')
if newestId not in self.state.document.ids:
if newest_id not in self.state.document.ids:
# always add the newest id
assert newestId
signode['ids'].append(newestId)
assert newest_id
signode['ids'].append(newest_id)
# only add compatibility ids when there are no conflicts
for id in ids[1:]:
if not id: # is None when the element didn't exist in that version
@ -184,8 +190,14 @@ class CObject(ObjectDescription[ASTDeclaration]):
self.state.document.note_explicit_target(signode)
if 'no-index-entry' not in self.options:
indexText = self.get_index_text(name)
self.indexnode['entries'].append(('single', indexText, newestId, '', None))
index_text = self.get_index_text(name)
self.indexnode['entries'].append((
'single',
index_text,
newest_id,
'',
None,
))
@property
def object_type(self) -> str:
@ -201,8 +213,9 @@ class CObject(ObjectDescription[ASTDeclaration]):
def parse_definition(self, parser: DefinitionParser) -> ASTDeclaration:
return parser.parse_declaration(self.object_type, self.objtype)
def describe_signature(self, signode: TextElement, ast: ASTDeclaration,
options: dict) -> None:
def describe_signature(
self, signode: TextElement, ast: ASTDeclaration, options: dict
) -> None:
ast.describe_signature(signode, 'lastIsName', self.env, options)
def run(self) -> list[Node]:
@ -219,11 +232,13 @@ class CObject(ObjectDescription[ASTDeclaration]):
return super().run()
def handle_signature(self, sig: str, signode: TextElement) -> ASTDeclaration:
parentSymbol: Symbol = self.env.temp_data['c:parent_symbol']
parent_symbol: Symbol = self.env.temp_data['c:parent_symbol']
max_len = (self.env.config.c_maximum_signature_line_length
or self.env.config.maximum_signature_line_length
or 0)
max_len = (
self.env.config.c_maximum_signature_line_length
or self.env.config.maximum_signature_line_length
or 0
)
signode['multi_line_parameter_list'] = (
'single-line-parameter-list' not in self.options
and (len(sig) > max_len > 0)
@ -238,13 +253,14 @@ class CObject(ObjectDescription[ASTDeclaration]):
# It is easier to assume some phony name than handling the error in
# the possibly inner declarations.
name = _make_phony_error_name()
symbol = parentSymbol.add_name(name)
symbol = parent_symbol.add_name(name)
self.env.temp_data['c:last_symbol'] = symbol
raise ValueError from e
try:
symbol = parentSymbol.add_declaration(
ast, docname=self.env.docname, line=self.get_source_info()[1])
symbol = parent_symbol.add_declaration(
ast, docname=self.env.docname, line=self.get_source_info()[1]
)
# append the new declaration to the sibling list
assert symbol.siblingAbove is None
assert symbol.siblingBelow is None
@ -257,8 +273,10 @@ class CObject(ObjectDescription[ASTDeclaration]):
# Assume we are actually in the old symbol,
# instead of the newly created duplicate.
self.env.temp_data['c:last_symbol'] = e.symbol
msg = __("Duplicate C declaration, also defined at %s:%s.\n"
"Declaration is '.. c:%s:: %s'.")
msg = __(
'Duplicate C declaration, also defined at %s:%s.\n'
"Declaration is '.. c:%s:: %s'."
)
logger.warning(
msg,
e.symbol.docname,
@ -278,12 +296,12 @@ class CObject(ObjectDescription[ASTDeclaration]):
return ast
def before_content(self) -> None:
lastSymbol: Symbol = self.env.temp_data['c:last_symbol']
assert lastSymbol
last_symbol: Symbol = self.env.temp_data['c:last_symbol']
assert last_symbol
self.oldParentSymbol = self.env.temp_data['c:parent_symbol']
self.oldParentKey: LookupKey = self.env.ref_context['c:parent_key']
self.env.temp_data['c:parent_symbol'] = lastSymbol
self.env.ref_context['c:parent_key'] = lastSymbol.get_lookup_key()
self.env.temp_data['c:parent_symbol'] = last_symbol
self.env.ref_context['c:parent_key'] = last_symbol.get_lookup_key()
def after_content(self) -> None:
self.env.temp_data['c:parent_symbol'] = self.oldParentSymbol
@ -301,16 +319,31 @@ class CMemberObject(CObject):
_function_doc_field_types = [
TypedField('parameter', label=_('Parameters'),
names=('param', 'parameter', 'arg', 'argument'),
typerolename='expr', typenames=('type',)),
GroupedField('retval', label=_('Return values'),
names=('retvals', 'retval'),
can_collapse=True),
Field('returnvalue', label=_('Returns'), has_arg=False,
names=('returns', 'return')),
Field('returntype', label=_('Return type'), has_arg=False,
names=('rtype',)),
TypedField(
'parameter',
label=_('Parameters'),
names=('param', 'parameter', 'arg', 'argument'),
typerolename='expr',
typenames=('type',),
),
GroupedField(
'retval',
label=_('Return values'),
names=('retvals', 'retval'),
can_collapse=True,
),
Field(
'returnvalue',
label=_('Returns'),
has_arg=False,
names=('returns', 'return'),
),
Field(
'returntype',
label=_('Return type'),
has_arg=False,
names=('rtype',),
),
]
@ -359,21 +392,21 @@ class CNamespaceObject(SphinxDirective):
option_spec: ClassVar[OptionSpec] = {}
def run(self) -> list[Node]:
rootSymbol = self.env.domaindata['c']['root_symbol']
root_symbol = self.env.domaindata['c']['root_symbol']
if self.arguments[0].strip() in {'NULL', '0', 'nullptr'}:
symbol = rootSymbol
symbol = root_symbol
stack: list[Symbol] = []
else:
parser = DefinitionParser(self.arguments[0],
location=self.get_location(),
config=self.env.config)
parser = DefinitionParser(
self.arguments[0], location=self.get_location(), config=self.env.config
)
try:
name = parser.parse_namespace_object()
parser.assert_end()
except DefinitionError as e:
logger.warning(e, location=self.get_location())
name = _make_phony_error_name()
symbol = rootSymbol.add_name(name)
symbol = root_symbol.add_name(name)
stack = [symbol]
self.env.temp_data['c:parent_symbol'] = symbol
self.env.temp_data['c:namespace_stack'] = stack
@ -391,19 +424,19 @@ class CNamespacePushObject(SphinxDirective):
def run(self) -> list[Node]:
if self.arguments[0].strip() in {'NULL', '0', 'nullptr'}:
return []
parser = DefinitionParser(self.arguments[0],
location=self.get_location(),
config=self.env.config)
parser = DefinitionParser(
self.arguments[0], location=self.get_location(), config=self.env.config
)
try:
name = parser.parse_namespace_object()
parser.assert_end()
except DefinitionError as e:
logger.warning(e, location=self.get_location())
name = _make_phony_error_name()
oldParent = self.env.temp_data.get('c:parent_symbol', None)
if not oldParent:
oldParent = self.env.domaindata['c']['root_symbol']
symbol = oldParent.add_name(name)
old_parent = self.env.temp_data.get('c:parent_symbol', None)
if not old_parent:
old_parent = self.env.domaindata['c']['root_symbol']
symbol = old_parent.add_name(name)
stack = self.env.temp_data.get('c:namespace_stack', [])
stack.append(symbol)
self.env.temp_data['c:parent_symbol'] = symbol
@ -422,8 +455,10 @@ class CNamespacePopObject(SphinxDirective):
def run(self) -> list[Node]:
stack = self.env.temp_data.get('c:namespace_stack', None)
if not stack or len(stack) == 0:
logger.warning("C namespace pop on empty stack. Defaulting to global scope.",
location=self.get_location())
logger.warning(
'C namespace pop on empty stack. Defaulting to global scope.',
location=self.get_location(),
)
stack = []
else:
stack.pop()
@ -461,16 +496,27 @@ class AliasNode(nodes.Element):
self.parentKey = parentKey
def copy(self) -> AliasNode:
return self.__class__(self.sig, self.aliasOptions, self.document,
env=None, parentKey=self.parentKey)
return self.__class__(
self.sig,
self.aliasOptions,
self.document,
env=None,
parentKey=self.parentKey,
)
class AliasTransform(SphinxTransform):
default_priority = ReferencesResolver.default_priority - 1
def _render_symbol(self, s: Symbol, maxdepth: int, skipThis: bool,
aliasOptions: dict, renderOptions: dict,
document: Any) -> list[Node]:
def _render_symbol(
self,
s: Symbol,
maxdepth: int,
skip_this: bool,
alias_options: dict,
render_options: dict,
document: Any,
) -> list[Node]:
if maxdepth == 0:
recurse = True
elif maxdepth == 1:
@ -480,14 +526,16 @@ class AliasTransform(SphinxTransform):
recurse = True
nodes: list[Node] = []
if not skipThis:
if not skip_this:
signode = addnodes.desc_signature('', '')
nodes.append(signode)
s.declaration.describe_signature(signode, 'markName', self.env, renderOptions)
s.declaration.describe_signature(
signode, 'markName', self.env, render_options
)
if recurse:
if skipThis:
childContainer: list[Node] | addnodes.desc = nodes
if skip_this:
child_container: list[Node] | addnodes.desc = nodes
else:
content = addnodes.desc_content()
desc = addnodes.desc()
@ -497,28 +545,31 @@ class AliasTransform(SphinxTransform):
# 'desctype' is a backwards compatible attribute
desc['objtype'] = desc['desctype'] = 'alias'
desc['no-index'] = True
childContainer = desc
child_container = desc
for sChild in s.children:
if sChild.declaration is None:
for s_child in s.children:
if s_child.declaration is None:
continue
childNodes = self._render_symbol(
sChild, maxdepth=maxdepth, skipThis=False,
aliasOptions=aliasOptions, renderOptions=renderOptions,
document=document)
childContainer.extend(childNodes)
child_nodes = self._render_symbol(
s_child,
maxdepth=maxdepth,
skip_this=False,
alias_options=alias_options,
render_options=render_options,
document=document,
)
child_container.extend(child_nodes)
if not skipThis and len(desc.children) != 0:
if not skip_this and len(desc.children) != 0:
nodes.append(content)
return nodes
def apply(self, **kwargs: Any) -> None:
for node in self.document.findall(AliasNode):
sig = node.sig
parentKey = node.parentKey
parent_key = node.parentKey
try:
parser = DefinitionParser(sig, location=node,
config=self.env.config)
parser = DefinitionParser(sig, location=node, config=self.env.config)
name = parser.parse_xref_object()
except DefinitionError as e:
logger.warning(e, location=node)
@ -532,25 +583,26 @@ class AliasTransform(SphinxTransform):
node.replace_self(signode)
continue
rootSymbol: Symbol = self.env.domains.c_domain.data['root_symbol']
parentSymbol: Symbol | None = rootSymbol.direct_lookup(parentKey)
if not parentSymbol:
logger.debug("Target: %s", sig)
logger.debug("ParentKey: %s", parentKey)
logger.debug(rootSymbol.dump(1))
assert parentSymbol # should be there
root_symbol: Symbol = self.env.domains.c_domain.data['root_symbol']
parent_symbol: Symbol | None = root_symbol.direct_lookup(parent_key)
if not parent_symbol:
logger.debug('Target: %s', sig)
logger.debug('ParentKey: %s', parent_key)
logger.debug(root_symbol.dump(1))
assert parent_symbol # should be there
s = parentSymbol.find_declaration(
name, 'any',
matchSelf=True, recurseInAnon=True)
s = parent_symbol.find_declaration(
name, 'any', matchSelf=True, recurseInAnon=True
)
if s is None:
signode = addnodes.desc_signature(sig, '')
node.append(signode)
signode.clear()
signode += addnodes.desc_name(sig, sig)
logger.warning("Could not find C declaration for alias '%s'.", name,
location=node)
logger.warning(
"Could not find C declaration for alias '%s'.", name, location=node
)
node.replace_self(signode)
continue
# Declarations like .. var:: int Missing::var
@ -563,15 +615,21 @@ class AliasTransform(SphinxTransform):
signode += addnodes.desc_name(sig, sig)
logger.warning(
"Can not render C declaration for alias '%s'. No such declaration.", name,
location=node)
"Can not render C declaration for alias '%s'. No such declaration.",
name,
location=node,
)
node.replace_self(signode)
continue
nodes = self._render_symbol(s, maxdepth=node.aliasOptions['maxdepth'],
skipThis=node.aliasOptions['noroot'],
aliasOptions=node.aliasOptions,
renderOptions={}, document=node.document)
nodes = self._render_symbol(
s,
maxdepth=node.aliasOptions['maxdepth'],
skip_this=node.aliasOptions['noroot'],
alias_options=node.aliasOptions,
render_options={},
document=node.document,
)
node.replace_self(nodes)
@ -600,30 +658,40 @@ class CAliasObject(ObjectDescription):
node['no-index'] = True
self.names: list[str] = []
aliasOptions = {
alias_options = {
'maxdepth': self.options.get('maxdepth', 1),
'noroot': 'noroot' in self.options,
}
if aliasOptions['noroot'] and aliasOptions['maxdepth'] == 1:
logger.warning("Error in C alias declaration."
" Requested 'noroot' but 'maxdepth' 1."
" When skipping the root declaration,"
" need 'maxdepth' 0 for infinite or at least 2.",
location=self.get_location())
if alias_options['noroot'] and alias_options['maxdepth'] == 1:
logger.warning(
'Error in C alias declaration.'
" Requested 'noroot' but 'maxdepth' 1."
' When skipping the root declaration,'
" need 'maxdepth' 0 for infinite or at least 2.",
location=self.get_location(),
)
for sig in self.get_signatures():
node.append(AliasNode(sig, aliasOptions, self.state.document, env=self.env))
node.append(
AliasNode(sig, alias_options, self.state.document, env=self.env)
)
return [node]
class CXRefRole(XRefRole):
def process_link(self, env: BuildEnvironment, refnode: Element,
has_explicit_title: bool, title: str, target: str) -> tuple[str, str]:
def process_link(
self,
env: BuildEnvironment,
refnode: Element,
has_explicit_title: bool,
title: str,
target: str,
) -> tuple[str, str]:
refnode.attributes.update(env.ref_context)
if not has_explicit_title:
# major hax: replace anon names via simple string manipulation.
# Can this actually fail?
title = anon_identifier_re.sub("[anonymous]", str(title))
title = anon_identifier_re.sub('[anonymous]', str(title))
if not has_explicit_title:
target = target.lstrip('~') # only has a meaning for the title
@ -633,7 +701,7 @@ class CXRefRole(XRefRole):
title = title[1:]
dot = title.rfind('.')
if dot != -1:
title = title[dot + 1:]
title = title[dot + 1 :]
return title, target
@ -649,23 +717,29 @@ class CExprRole(SphinxRole):
def run(self) -> tuple[list[Node], list[system_message]]:
text = self.text.replace('\n', ' ')
parser = DefinitionParser(text, location=self.get_location(),
config=self.env.config)
parser = DefinitionParser(
text, location=self.get_location(), config=self.env.config
)
# attempt to mimic XRefRole classes, except that...
try:
ast = parser.parse_expression()
except DefinitionError as ex:
logger.warning('Unparseable C expression: %r\n%s', text, ex,
location=self.get_location())
logger.warning(
'Unparseable C expression: %r\n%s',
text,
ex,
location=self.get_location(),
)
# see below
return [addnodes.desc_inline('c', text, text, classes=[self.class_type])], []
parentSymbol = self.env.temp_data.get('c:parent_symbol', None)
if parentSymbol is None:
parentSymbol = self.env.domaindata['c']['root_symbol']
node = addnodes.desc_inline('c', text, text, classes=[self.class_type])
return [node], []
parent_symbol = self.env.temp_data.get('c:parent_symbol', None)
if parent_symbol is None:
parent_symbol = self.env.domaindata['c']['root_symbol']
# ...most if not all of these classes should really apply to the individual references,
# not the container node
signode = addnodes.desc_inline('c', classes=[self.class_type])
ast.describe_signature(signode, 'markType', self.env, parentSymbol)
ast.describe_signature(signode, 'markType', self.env, parent_symbol)
return [signode], []
@ -677,16 +751,18 @@ class CDomain(Domain):
object_types = {
# 'identifier' is the one used for xrefs generated in signatures, not in roles
'member': ObjType(_('member'), 'var', 'member', 'data', 'identifier'),
'var': ObjType(_('variable'), 'var', 'member', 'data', 'identifier'),
'function': ObjType(_('function'), 'func', 'identifier', 'type'),
'macro': ObjType(_('macro'), 'macro', 'identifier'),
'struct': ObjType(_('struct'), 'struct', 'identifier', 'type'),
'union': ObjType(_('union'), 'union', 'identifier', 'type'),
'enum': ObjType(_('enum'), 'enum', 'identifier', 'type'),
'enumerator': ObjType(_('enumerator'), 'enumerator', 'identifier'),
'type': ObjType(_('type'), 'identifier', 'type'),
'var': ObjType(_('variable'), 'var', 'member', 'data', 'identifier'),
'function': ObjType(_('function'), 'func', 'identifier', 'type'),
'macro': ObjType(_('macro'), 'macro', 'identifier'),
'struct': ObjType(_('struct'), 'struct', 'identifier', 'type'),
'union': ObjType(_('union'), 'union', 'identifier', 'type'),
'enum': ObjType(_('enum'), 'enum', 'identifier', 'type'),
'enumerator': ObjType(_('enumerator'), 'enumerator', 'identifier'),
'type': ObjType(_('type'), 'identifier', 'type'),
# generated object types
'functionParam': ObjType(_('function parameter'), 'identifier', 'var', 'member', 'data'), # NoQA: E501
'functionParam': ObjType(
_('function parameter'), 'identifier', 'var', 'member', 'data'
), # NoQA: E501
}
directives = {
@ -727,126 +803,156 @@ class CDomain(Domain):
def clear_doc(self, docname: str) -> None:
if Symbol.debug_show_tree:
logger.debug("clear_doc: %s", docname)
logger.debug("\tbefore:")
logger.debug('clear_doc: %s', docname)
logger.debug('\tbefore:')
logger.debug(self.data['root_symbol'].dump(1))
logger.debug("\tbefore end")
logger.debug('\tbefore end')
rootSymbol = self.data['root_symbol']
rootSymbol.clear_doc(docname)
root_symbol = self.data['root_symbol']
root_symbol.clear_doc(docname)
if Symbol.debug_show_tree:
logger.debug("\tafter:")
logger.debug('\tafter:')
logger.debug(self.data['root_symbol'].dump(1))
logger.debug("\tafter end")
logger.debug("clear_doc end: %s", docname)
logger.debug('\tafter end')
logger.debug('clear_doc end: %s', docname)
def process_doc(self, env: BuildEnvironment, docname: str,
document: nodes.document) -> None:
def process_doc(
self, env: BuildEnvironment, docname: str, document: nodes.document
) -> None:
if Symbol.debug_show_tree:
logger.debug("process_doc: %s", docname)
logger.debug('process_doc: %s', docname)
logger.debug(self.data['root_symbol'].dump(0))
logger.debug("process_doc end: %s", docname)
logger.debug('process_doc end: %s', docname)
def process_field_xref(self, pnode: pending_xref) -> None:
pnode.attributes.update(self.env.ref_context)
def merge_domaindata(self, docnames: Set[str], otherdata: dict[str, Any]) -> None:
if Symbol.debug_show_tree:
logger.debug("merge_domaindata:")
logger.debug("\tself:")
logger.debug('merge_domaindata:')
logger.debug('\tself:')
logger.debug(self.data['root_symbol'].dump(1))
logger.debug("\tself end")
logger.debug("\tother:")
logger.debug('\tself end')
logger.debug('\tother:')
logger.debug(otherdata['root_symbol'].dump(1))
logger.debug("\tother end")
logger.debug("merge_domaindata end")
logger.debug('\tother end')
logger.debug('merge_domaindata end')
self.data['root_symbol'].merge_with(otherdata['root_symbol'],
docnames, self.env)
ourObjects = self.data['objects']
self.data['root_symbol'].merge_with(
otherdata['root_symbol'], docnames, self.env
)
our_objects = self.data['objects']
for fullname, (fn, id_, objtype) in otherdata['objects'].items():
if fn in docnames:
if fullname not in ourObjects:
ourObjects[fullname] = (fn, id_, objtype)
if fullname not in our_objects:
our_objects[fullname] = (fn, id_, objtype)
# no need to warn on duplicates, the symbol merge already does that
def _resolve_xref_inner(
self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref, contnode: Element
self,
env: BuildEnvironment,
fromdocname: str,
builder: Builder,
typ: str,
target: str,
node: pending_xref,
contnode: Element,
) -> tuple[nodes.reference, str] | tuple[None, None]:
parser = DefinitionParser(target, location=node, config=env.config)
try:
name = parser.parse_xref_object()
except DefinitionError as e:
logger.warning('Unparseable C cross-reference: %r\n%s', target, e,
location=node)
logger.warning(
'Unparseable C cross-reference: %r\n%s', target, e, location=node
)
return None, None
parentKey: LookupKey = node.get("c:parent_key", None)
rootSymbol = self.data['root_symbol']
if parentKey:
parentSymbol: Symbol = rootSymbol.direct_lookup(parentKey)
if not parentSymbol:
logger.debug("Target: %s", target)
logger.debug("ParentKey: %s", parentKey)
logger.debug(rootSymbol.dump(1))
assert parentSymbol # should be there
parent_key: LookupKey = node.get('c:parent_key', None)
root_symbol = self.data['root_symbol']
if parent_key:
parent_symbol: Symbol = root_symbol.direct_lookup(parent_key)
if not parent_symbol:
logger.debug('Target: %s', target)
logger.debug('ParentKey: %s', parent_key)
logger.debug(root_symbol.dump(1))
assert parent_symbol # should be there
else:
parentSymbol = rootSymbol
s = parentSymbol.find_declaration(name, typ,
matchSelf=True, recurseInAnon=True)
parent_symbol = root_symbol
s = parent_symbol.find_declaration(
name, typ, matchSelf=True, recurseInAnon=True
)
if s is None or s.declaration is None:
return None, None
# TODO: check role type vs. object type
declaration = s.declaration
displayName = name.get_display_string()
display_name = name.get_display_string()
docname = s.docname
assert docname
return make_refnode(builder, fromdocname, docname,
declaration.get_newest_id(), contnode, displayName,
), declaration.objectType
return make_refnode(
builder,
fromdocname,
docname,
declaration.get_newest_id(),
contnode,
display_name,
), declaration.objectType
def resolve_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
typ: str, target: str, node: pending_xref,
contnode: Element) -> nodes.reference | None:
return self._resolve_xref_inner(env, fromdocname, builder, typ,
target, node, contnode)[0]
def resolve_xref(
self,
env: BuildEnvironment,
fromdocname: str,
builder: Builder,
typ: str,
target: str,
node: pending_xref,
contnode: Element,
) -> nodes.reference | None:
return self._resolve_xref_inner(
env, fromdocname, builder, typ, target, node, contnode
)[0]
def resolve_any_xref(self, env: BuildEnvironment, fromdocname: str, builder: Builder,
target: str, node: pending_xref, contnode: Element,
) -> list[tuple[str, nodes.reference]]:
def resolve_any_xref(
self,
env: BuildEnvironment,
fromdocname: str,
builder: Builder,
target: str,
node: pending_xref,
contnode: Element,
) -> list[tuple[str, nodes.reference]]:
with logging.suppress_logging():
retnode, objtype = self._resolve_xref_inner(env, fromdocname, builder,
'any', target, node, contnode)
retnode, objtype = self._resolve_xref_inner(
env, fromdocname, builder, 'any', target, node, contnode
)
if retnode:
return [('c:' + self.role_for_objtype(objtype), retnode)]
return []
def get_objects(self) -> Iterator[tuple[str, str, str, str, str, int]]:
rootSymbol = self.data['root_symbol']
for symbol in rootSymbol.get_all_symbols():
root_symbol = self.data['root_symbol']
for symbol in root_symbol.get_all_symbols():
if symbol.declaration is None:
continue
assert symbol.docname
fullNestedName = symbol.get_full_nested_name()
name = str(fullNestedName).lstrip('.')
dispname = fullNestedName.get_display_string().lstrip('.')
objectType = symbol.declaration.objectType
full_nested_name = symbol.get_full_nested_name()
name = str(full_nested_name).lstrip('.')
dispname = full_nested_name.get_display_string().lstrip('.')
object_type = symbol.declaration.objectType
docname = symbol.docname
newestId = symbol.declaration.get_newest_id()
yield name, dispname, objectType, docname, newestId, 1
newest_id = symbol.declaration.get_newest_id()
yield name, dispname, object_type, docname, newest_id, 1
def setup(app: Sphinx) -> ExtensionMetadata:
app.add_domain(CDomain)
app.add_config_value("c_id_attributes", [], 'env', types={list, tuple})
app.add_config_value("c_paren_attributes", [], 'env', types={list, tuple})
app.add_config_value("c_extra_keywords", _macroKeywords, 'env', types={set, list})
app.add_config_value('c_id_attributes', [], 'env', types={list, tuple})
app.add_config_value('c_paren_attributes', [], 'env', types={list, tuple})
app.add_config_value('c_extra_keywords', _macroKeywords, 'env', types={set, list})
app.add_config_value(
"c_maximum_signature_line_length", None, 'env', types={int, type(None)}
'c_maximum_signature_line_length', None, 'env', types={int, type(None)}
)
app.add_post_transform(AliasTransform)

View File

@ -26,20 +26,27 @@ if TYPE_CHECKING:
from sphinx.util.cfamily import StringifyTransform
DeclarationType: TypeAlias = Union[ # NoQA: UP007
"ASTStruct", "ASTUnion", "ASTEnum", "ASTEnumerator",
"ASTType", "ASTTypeWithInit", "ASTMacro",
'ASTStruct',
'ASTUnion',
'ASTEnum',
'ASTEnumerator',
'ASTType',
'ASTTypeWithInit',
'ASTMacro',
]
class ASTBase(ASTBaseBase):
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
raise NotImplementedError(repr(self))
# Names
################################################################################
class ASTIdentifier(ASTBaseBase):
def __init__(self, name: str) -> None:
if not isinstance(name, str) or len(name) == 0:
@ -66,25 +73,35 @@ class ASTIdentifier(ASTBaseBase):
return self.name
def get_display_string(self) -> str:
return "[anonymous]" if self.is_anonymous else self.name
return '[anonymous]' if self.is_anonymous else self.name
def _stringify(self, transform: StringifyTransform) -> str:
return transform(self.get_display_string())
def describe_signature(self, signode: TextElement, mode: str, env: BuildEnvironment,
prefix: str, symbol: Symbol) -> None:
def describe_signature(
self,
signode: TextElement,
mode: str,
env: BuildEnvironment,
prefix: str,
symbol: Symbol,
) -> None:
# note: slightly different signature of describe_signature due to the prefix
verify_description_mode(mode)
if self.is_anonymous:
node = addnodes.desc_sig_name(text="[anonymous]")
node = addnodes.desc_sig_name(text='[anonymous]')
else:
node = addnodes.desc_sig_name(self.name, self.name)
if mode == 'markType':
target_text = prefix + self.name
pnode = addnodes.pending_xref('', refdomain='c',
reftype='identifier',
reftarget=target_text, modname=None,
classname=None)
pnode = addnodes.pending_xref(
'',
refdomain='c',
reftype='identifier',
reftarget=target_text,
modname=None,
classname=None,
)
pnode['c:parent_key'] = symbol.get_lookup_key()
pnode += node
signode += pnode
@ -101,7 +118,8 @@ class ASTIdentifier(ASTBaseBase):
def identifier(self) -> str:
warnings.warn(
'`ASTIdentifier.identifier` is deprecated, use `ASTIdentifier.name` instead',
DeprecationWarning, stacklevel=2,
DeprecationWarning,
stacklevel=2,
)
return self.name
@ -134,18 +152,19 @@ class ASTNestedName(ASTBase):
else:
return res
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
# just print the name part, with template args, not template params
if mode == 'noneIsName':
if self.rooted:
unreachable = "Can this happen?"
unreachable = 'Can this happen?'
raise AssertionError(unreachable) # TODO
signode += nodes.Text('.')
for i in range(len(self.names)):
if i != 0:
unreachable = "Can this happen?"
unreachable = 'Can this happen?'
raise AssertionError(unreachable) # TODO
signode += nodes.Text('.')
n = self.names[i]
@ -197,6 +216,7 @@ class ASTNestedName(ASTBase):
# Expressions
################################################################################
class ASTExpression(ASTBase):
pass
@ -204,6 +224,7 @@ class ASTExpression(ASTBase):
# Primary expressions
################################################################################
class ASTLiteral(ASTExpression):
pass
@ -226,8 +247,9 @@ class ASTBooleanLiteral(ASTLiteral):
else:
return 'false'
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
txt = str(self)
signode += addnodes.desc_sig_keyword(txt, txt)
@ -247,8 +269,9 @@ class ASTNumberLiteral(ASTLiteral):
def _stringify(self, transform: StringifyTransform) -> str:
return self.data
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
txt = str(self)
signode += addnodes.desc_sig_literal_number(txt, txt)
@ -266,10 +289,7 @@ class ASTCharLiteral(ASTLiteral):
def __eq__(self, other: object) -> bool:
if not isinstance(other, ASTCharLiteral):
return NotImplemented
return (
self.prefix == other.prefix
and self.value == other.value
)
return self.prefix == other.prefix and self.value == other.value
def __hash__(self) -> int:
return hash((self.prefix, self.value))
@ -280,8 +300,9 @@ class ASTCharLiteral(ASTLiteral):
else:
return self.prefix + "'" + self.data + "'"
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
txt = str(self)
signode += addnodes.desc_sig_literal_char(txt, txt)
@ -301,8 +322,9 @@ class ASTStringLiteral(ASTLiteral):
def _stringify(self, transform: StringifyTransform) -> str:
return self.data
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
txt = str(self)
signode += addnodes.desc_sig_literal_string(txt, txt)
@ -326,8 +348,9 @@ class ASTIdExpression(ASTExpression):
def get_id(self, version: int) -> str:
return self.name.get_id(version)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
self.name.describe_signature(signode, mode, env, symbol)
@ -349,8 +372,9 @@ class ASTParenExpr(ASTExpression):
def get_id(self, version: int) -> str:
return self.expr.get_id(version) # type: ignore[attr-defined]
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
signode += addnodes.desc_sig_punctuation('(', '(')
self.expr.describe_signature(signode, mode, env, symbol)
signode += addnodes.desc_sig_punctuation(')', ')')
@ -359,6 +383,7 @@ class ASTParenExpr(ASTExpression):
# Postfix expressions
################################################################################
class ASTPostfixOp(ASTBase):
pass
@ -378,8 +403,9 @@ class ASTPostfixCallExpr(ASTPostfixOp):
def _stringify(self, transform: StringifyTransform) -> str:
return transform(self.lst)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
self.lst.describe_signature(signode, mode, env, symbol)
@ -398,8 +424,9 @@ class ASTPostfixArray(ASTPostfixOp):
def _stringify(self, transform: StringifyTransform) -> str:
return '[' + transform(self.expr) + ']'
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
signode += addnodes.desc_sig_punctuation('[', '[')
self.expr.describe_signature(signode, mode, env, symbol)
signode += addnodes.desc_sig_punctuation(']', ']')
@ -409,8 +436,9 @@ class ASTPostfixInc(ASTPostfixOp):
def _stringify(self, transform: StringifyTransform) -> str:
return '++'
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
signode += addnodes.desc_sig_operator('++', '++')
@ -418,8 +446,9 @@ class ASTPostfixDec(ASTPostfixOp):
def _stringify(self, transform: StringifyTransform) -> str:
return '--'
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
signode += addnodes.desc_sig_operator('--', '--')
@ -438,8 +467,9 @@ class ASTPostfixMemberOfPointer(ASTPostfixOp):
def _stringify(self, transform: StringifyTransform) -> str:
return '->' + transform(self.name)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
signode += addnodes.desc_sig_operator('->', '->')
self.name.describe_signature(signode, 'noneIsName', env, symbol)
@ -458,10 +488,14 @@ class ASTPostfixExpr(ASTExpression):
return hash((self.prefix, self.postFixes))
def _stringify(self, transform: StringifyTransform) -> str:
return ''.join([transform(self.prefix), *(transform(p) for p in self.postFixes)])
return ''.join([
transform(self.prefix),
*(transform(p) for p in self.postFixes),
])
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
self.prefix.describe_signature(signode, mode, env, symbol)
for p in self.postFixes:
p.describe_signature(signode, mode, env, symbol)
@ -470,6 +504,7 @@ class ASTPostfixExpr(ASTExpression):
# Unary expressions
################################################################################
class ASTUnaryOpExpr(ASTExpression):
def __init__(self, op: str, expr: ASTExpression) -> None:
self.op = op
@ -485,12 +520,13 @@ class ASTUnaryOpExpr(ASTExpression):
def _stringify(self, transform: StringifyTransform) -> str:
if self.op[0] in 'cn':
return self.op + " " + transform(self.expr)
return self.op + ' ' + transform(self.expr)
else:
return self.op + transform(self.expr)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
if self.op[0] in 'cn':
signode += addnodes.desc_sig_keyword(self.op, self.op)
signode += addnodes.desc_sig_space()
@ -512,10 +548,11 @@ class ASTSizeofType(ASTExpression):
return hash(self.typ)
def _stringify(self, transform: StringifyTransform) -> str:
return "sizeof(" + transform(self.typ) + ")"
return 'sizeof(' + transform(self.typ) + ')'
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
signode += addnodes.desc_sig_keyword('sizeof', 'sizeof')
signode += addnodes.desc_sig_punctuation('(', '(')
self.typ.describe_signature(signode, mode, env, symbol)
@ -535,10 +572,11 @@ class ASTSizeofExpr(ASTExpression):
return hash(self.expr)
def _stringify(self, transform: StringifyTransform) -> str:
return "sizeof " + transform(self.expr)
return 'sizeof ' + transform(self.expr)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
signode += addnodes.desc_sig_keyword('sizeof', 'sizeof')
signode += addnodes.desc_sig_space()
self.expr.describe_signature(signode, mode, env, symbol)
@ -557,10 +595,11 @@ class ASTAlignofExpr(ASTExpression):
return hash(self.typ)
def _stringify(self, transform: StringifyTransform) -> str:
return "alignof(" + transform(self.typ) + ")"
return 'alignof(' + transform(self.typ) + ')'
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
signode += addnodes.desc_sig_keyword('alignof', 'alignof')
signode += addnodes.desc_sig_punctuation('(', '(')
self.typ.describe_signature(signode, mode, env, symbol)
@ -570,6 +609,7 @@ class ASTAlignofExpr(ASTExpression):
# Other expressions
################################################################################
class ASTCastExpr(ASTExpression):
def __init__(self, typ: ASTType, expr: ASTExpression) -> None:
self.typ = typ
@ -578,10 +618,7 @@ class ASTCastExpr(ASTExpression):
def __eq__(self, other: object) -> bool:
if not isinstance(other, ASTCastExpr):
return NotImplemented
return (
self.typ == other.typ
and self.expr == other.expr
)
return self.typ == other.typ and self.expr == other.expr
def __hash__(self) -> int:
return hash((self.typ, self.expr))
@ -593,8 +630,9 @@ class ASTCastExpr(ASTExpression):
res.append(transform(self.expr))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
signode += addnodes.desc_sig_punctuation('(', '(')
self.typ.describe_signature(signode, mode, env, symbol)
signode += addnodes.desc_sig_punctuation(')', ')')
@ -611,10 +649,7 @@ class ASTBinOpExpr(ASTBase):
def __eq__(self, other: object) -> bool:
if not isinstance(other, ASTBinOpExpr):
return NotImplemented
return (
self.exprs == other.exprs
and self.ops == other.ops
)
return self.exprs == other.exprs and self.ops == other.ops
def __hash__(self) -> int:
return hash((self.exprs, self.ops))
@ -629,8 +664,9 @@ class ASTBinOpExpr(ASTBase):
res.append(transform(self.exprs[i]))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
self.exprs[0].describe_signature(signode, mode, env, symbol)
for i in range(1, len(self.exprs)):
signode += addnodes.desc_sig_space()
@ -653,10 +689,7 @@ class ASTAssignmentExpr(ASTExpression):
def __eq__(self, other: object) -> bool:
if not isinstance(other, ASTAssignmentExpr):
return NotImplemented
return (
self.exprs == other.exprs
and self.ops == other.ops
)
return self.exprs == other.exprs and self.ops == other.ops
def __hash__(self) -> int:
return hash((self.exprs, self.ops))
@ -671,8 +704,9 @@ class ASTAssignmentExpr(ASTExpression):
res.append(transform(self.exprs[i]))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
self.exprs[0].describe_signature(signode, mode, env, symbol)
for i in range(1, len(self.exprs)):
signode += addnodes.desc_sig_space()
@ -703,8 +737,9 @@ class ASTFallbackExpr(ASTExpression):
def get_id(self, version: int) -> str:
return str(self.expr)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
signode += nodes.literal(self.expr, self.expr)
@ -712,6 +747,7 @@ class ASTFallbackExpr(ASTExpression):
# Types
################################################################################
class ASTTrailingTypeSpec(ASTBase):
pass
@ -732,8 +768,9 @@ class ASTTrailingTypeSpecFundamental(ASTTrailingTypeSpec):
def _stringify(self, transform: StringifyTransform) -> str:
return ' '.join(self.names)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
first = True
for n in self.names:
if not first:
@ -751,10 +788,7 @@ class ASTTrailingTypeSpecName(ASTTrailingTypeSpec):
def __eq__(self, other: object) -> bool:
if not isinstance(other, ASTTrailingTypeSpecName):
return NotImplemented
return (
self.prefix == other.prefix
and self.nestedName == other.nestedName
)
return self.prefix == other.prefix and self.nestedName == other.nestedName
def __hash__(self) -> int:
return hash((self.prefix, self.nestedName))
@ -771,8 +805,9 @@ class ASTTrailingTypeSpecName(ASTTrailingTypeSpec):
res.append(transform(self.nestedName))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
if self.prefix:
signode += addnodes.desc_sig_keyword(self.prefix, self.prefix)
signode += addnodes.desc_sig_space()
@ -802,8 +837,9 @@ class ASTFunctionParameter(ASTBase):
else:
return transform(self.arg)
def describe_signature(self, signode: Any, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: Any, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
if self.ellipsis:
signode += addnodes.desc_sig_punctuation('...', '...')
@ -812,7 +848,9 @@ class ASTFunctionParameter(ASTBase):
class ASTParameters(ASTBase):
def __init__(self, args: list[ASTFunctionParameter], attrs: ASTAttributeList) -> None:
def __init__(
self, args: list[ASTFunctionParameter], attrs: ASTAttributeList
) -> None:
self.args = args
self.attrs = attrs
@ -843,8 +881,9 @@ class ASTParameters(ASTBase):
res.append(transform(self.attrs))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
multi_line_parameter_list = False
test_node: Element = signode
@ -852,7 +891,9 @@ class ASTParameters(ASTBase):
if not isinstance(test_node, addnodes.desc_signature):
test_node = test_node.parent
continue
multi_line_parameter_list = test_node.get('multi_line_parameter_list', False)
multi_line_parameter_list = test_node.get(
'multi_line_parameter_list', False
)
break
# only use the desc_parameterlist for the outer list, not for inner lists
@ -881,8 +922,16 @@ class ASTParameters(ASTBase):
class ASTDeclSpecsSimple(ASTBaseBase):
def __init__(self, storage: str, threadLocal: str, inline: bool,
restrict: bool, volatile: bool, const: bool, attrs: ASTAttributeList) -> None:
def __init__(
self,
storage: str,
threadLocal: str,
inline: bool,
restrict: bool,
volatile: bool,
const: bool,
attrs: ASTAttributeList,
) -> None:
self.storage = storage
self.threadLocal = threadLocal
self.inline = inline
@ -918,13 +967,15 @@ class ASTDeclSpecsSimple(ASTBaseBase):
def mergeWith(self, other: ASTDeclSpecsSimple) -> ASTDeclSpecsSimple:
if not other:
return self
return ASTDeclSpecsSimple(self.storage or other.storage,
self.threadLocal or other.threadLocal,
self.inline or other.inline,
self.volatile or other.volatile,
self.const or other.const,
self.restrict or other.restrict,
self.attrs + other.attrs)
return ASTDeclSpecsSimple(
self.storage or other.storage,
self.threadLocal or other.threadLocal,
self.inline or other.inline,
self.volatile or other.volatile,
self.const or other.const,
self.restrict or other.restrict,
self.attrs + other.attrs,
)
def _stringify(self, transform: StringifyTransform) -> str:
res: list[str] = []
@ -970,10 +1021,13 @@ class ASTDeclSpecsSimple(ASTBaseBase):
class ASTDeclSpecs(ASTBase):
def __init__(self, outer: str,
leftSpecs: ASTDeclSpecsSimple,
rightSpecs: ASTDeclSpecsSimple,
trailing: ASTTrailingTypeSpec) -> None:
def __init__(
self,
outer: str,
leftSpecs: ASTDeclSpecsSimple,
rightSpecs: ASTDeclSpecsSimple,
trailing: ASTTrailingTypeSpec,
) -> None:
# leftSpecs and rightSpecs are used for output
# allSpecs are used for id generation TODO: remove?
self.outer = outer
@ -1007,17 +1061,18 @@ class ASTDeclSpecs(ASTBase):
res.append(l)
if self.trailingTypeSpec:
if len(res) > 0:
res.append(" ")
res.append(' ')
res.append(transform(self.trailingTypeSpec))
r = str(self.rightSpecs)
if len(r) > 0:
if len(res) > 0:
res.append(" ")
res.append(' ')
res.append(r)
return "".join(res)
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
modifiers: list[Node] = []
@ -1028,8 +1083,7 @@ class ASTDeclSpecs(ASTBase):
if self.trailingTypeSpec:
if len(modifiers) > 0:
signode += addnodes.desc_sig_space()
self.trailingTypeSpec.describe_signature(signode, mode, env,
symbol=symbol)
self.trailingTypeSpec.describe_signature(signode, mode, env, symbol=symbol)
modifiers = []
self.rightSpecs.describe_signature(modifiers)
if len(modifiers) > 0:
@ -1041,9 +1095,17 @@ class ASTDeclSpecs(ASTBase):
# Declarator
################################################################################
class ASTArray(ASTBase):
def __init__(self, static: bool, const: bool, volatile: bool, restrict: bool,
vla: bool, size: ASTExpression) -> None:
def __init__(
self,
static: bool,
const: bool,
volatile: bool,
restrict: bool,
vla: bool,
size: ASTExpression,
) -> None:
self.static = static
self.const = const
self.volatile = volatile
@ -1093,8 +1155,9 @@ class ASTArray(ASTBase):
el.append(transform(self.size))
return '[' + ' '.join(el) + ']'
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
signode += addnodes.desc_sig_punctuation('[', '[')
add_space = False
@ -1136,8 +1199,9 @@ class ASTDeclarator(ASTBase):
class ASTDeclaratorNameParam(ASTDeclarator):
def __init__(self, declId: ASTNestedName,
arrayOps: list[ASTArray], param: ASTParameters) -> None:
def __init__(
self, declId: ASTNestedName, arrayOps: list[ASTArray], param: ASTParameters
) -> None:
self.declId = declId
self.arrayOps = arrayOps
self.param = param
@ -1176,8 +1240,9 @@ class ASTDeclaratorNameParam(ASTDeclarator):
res.append(transform(self.param))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
if self.declId:
self.declId.describe_signature(signode, mode, env, symbol)
@ -1213,12 +1278,13 @@ class ASTDeclaratorNameBitField(ASTDeclarator):
res = []
if self.declId:
res.append(transform(self.declId))
res.append(" : ")
res.append(' : ')
res.append(transform(self.size))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
if self.declId:
self.declId.describe_signature(signode, mode, env, symbol)
@ -1229,8 +1295,14 @@ class ASTDeclaratorNameBitField(ASTDeclarator):
class ASTDeclaratorPtr(ASTDeclarator):
def __init__(self, next: ASTDeclarator, restrict: bool, volatile: bool, const: bool,
attrs: ASTAttributeList) -> None:
def __init__(
self,
next: ASTDeclarator,
restrict: bool,
volatile: bool,
const: bool,
attrs: ASTAttributeList,
) -> None:
assert next
self.next = next
self.restrict = restrict
@ -1261,9 +1333,13 @@ class ASTDeclaratorPtr(ASTDeclarator):
return self.next.function_params
def require_space_after_declSpecs(self) -> bool:
return self.const or self.volatile or self.restrict or \
len(self.attrs) > 0 or \
self.next.require_space_after_declSpecs()
return (
self.const
or self.volatile
or self.restrict
or len(self.attrs) > 0
or self.next.require_space_after_declSpecs()
)
def _stringify(self, transform: StringifyTransform) -> str:
res = ['*']
@ -1286,8 +1362,9 @@ class ASTDeclaratorPtr(ASTDeclarator):
res.append(transform(self.next))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
signode += addnodes.desc_sig_punctuation('*', '*')
self.attrs.describe_signature(signode)
@ -1347,18 +1424,20 @@ class ASTDeclaratorParen(ASTDeclarator):
res.append(transform(self.next))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
signode += addnodes.desc_sig_punctuation('(', '(')
self.inner.describe_signature(signode, mode, env, symbol)
signode += addnodes.desc_sig_punctuation(')', ')')
self.next.describe_signature(signode, "noneIsName", env, symbol)
self.next.describe_signature(signode, 'noneIsName', env, symbol)
# Initializer
################################################################################
class ASTParenExprList(ASTBaseParenExprList):
def __init__(self, exprs: list[ASTExpression]) -> None:
self.exprs = exprs
@ -1375,8 +1454,9 @@ class ASTParenExprList(ASTBaseParenExprList):
exprs = [transform(e) for e in self.exprs]
return '(%s)' % ', '.join(exprs)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
signode += addnodes.desc_sig_punctuation('(', '(')
first = True
@ -1408,8 +1488,9 @@ class ASTBracedInitList(ASTBase):
trailing_comma = ',' if self.trailingComma else ''
return f'{{{exprs}{trailing_comma}}}'
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
signode += addnodes.desc_sig_punctuation('{', '{')
first = True
@ -1426,8 +1507,9 @@ class ASTBracedInitList(ASTBase):
class ASTInitializer(ASTBase):
def __init__(self, value: ASTBracedInitList | ASTExpression,
hasAssign: bool = True) -> None:
def __init__(
self, value: ASTBracedInitList | ASTExpression, hasAssign: bool = True
) -> None:
self.value = value
self.hasAssign = hasAssign
@ -1446,8 +1528,9 @@ class ASTInitializer(ASTBase):
else:
return val
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
if self.hasAssign:
signode += addnodes.desc_sig_space()
@ -1497,12 +1580,12 @@ class ASTType(ASTBase):
else:
return 'type'
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
self.declSpecs.describe_signature(signode, 'markType', env, symbol)
if (self.decl.require_space_after_declSpecs() and
len(str(self.declSpecs)) > 0):
if self.decl.require_space_after_declSpecs() and len(str(self.declSpecs)) > 0:
signode += addnodes.desc_sig_space()
# for parameters that don't really declare new names we get 'markType',
# this should not be propagated, but be 'noneIsName'.
@ -1538,8 +1621,9 @@ class ASTTypeWithInit(ASTBase):
res.append(transform(self.init))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
self.type.describe_signature(signode, mode, env, symbol)
if self.init:
@ -1547,8 +1631,9 @@ class ASTTypeWithInit(ASTBase):
class ASTMacroParameter(ASTBase):
def __init__(self, arg: ASTNestedName | None, ellipsis: bool = False,
variadic: bool = False) -> None:
def __init__(
self, arg: ASTNestedName | None, ellipsis: bool = False, variadic: bool = False
) -> None:
self.arg = arg
self.ellipsis = ellipsis
self.variadic = variadic
@ -1573,8 +1658,9 @@ class ASTMacroParameter(ASTBase):
else:
return transform(self.arg)
def describe_signature(self, signode: Any, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: Any, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
if self.ellipsis:
signode += addnodes.desc_sig_punctuation('...', '...')
@ -1586,7 +1672,9 @@ class ASTMacroParameter(ASTBase):
class ASTMacro(ASTBase):
def __init__(self, ident: ASTNestedName, args: list[ASTMacroParameter] | None) -> None:
def __init__(
self, ident: ASTNestedName, args: list[ASTMacroParameter] | None
) -> None:
self.ident = ident
self.args = args
@ -1619,8 +1707,9 @@ class ASTMacro(ASTBase):
res.append(')')
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
self.ident.describe_signature(signode, mode, env, symbol)
if self.args is None:
@ -1651,8 +1740,9 @@ class ASTStruct(ASTBase):
def _stringify(self, transform: StringifyTransform) -> str:
return transform(self.name)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
self.name.describe_signature(signode, mode, env, symbol=symbol)
@ -1675,8 +1765,9 @@ class ASTUnion(ASTBase):
def _stringify(self, transform: StringifyTransform) -> str:
return transform(self.name)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
self.name.describe_signature(signode, mode, env, symbol=symbol)
@ -1699,15 +1790,17 @@ class ASTEnum(ASTBase):
def _stringify(self, transform: StringifyTransform) -> str:
return transform(self.name)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
self.name.describe_signature(signode, mode, env, symbol=symbol)
class ASTEnumerator(ASTBase):
def __init__(self, name: ASTNestedName, init: ASTInitializer | None,
attrs: ASTAttributeList) -> None:
def __init__(
self, name: ASTNestedName, init: ASTInitializer | None, attrs: ASTAttributeList
) -> None:
self.name = name
self.init = init
self.attrs = attrs
@ -1737,8 +1830,9 @@ class ASTEnumerator(ASTBase):
res.append(transform(self.init))
return ''.join(res)
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, symbol: Symbol) -> None:
def describe_signature(
self, signode: TextElement, mode: str, env: BuildEnvironment, symbol: Symbol
) -> None:
verify_description_mode(mode)
self.name.describe_signature(signode, mode, env, symbol)
if len(self.attrs) != 0:
@ -1749,9 +1843,13 @@ class ASTEnumerator(ASTBase):
class ASTDeclaration(ASTBaseBase):
def __init__(self, objectType: str, directiveType: str | None,
declaration: DeclarationType | ASTFunctionParameter,
semicolon: bool = False) -> None:
def __init__(
self,
objectType: str,
directiveType: str | None,
declaration: DeclarationType | ASTFunctionParameter,
semicolon: bool = False,
) -> None:
self.objectType = objectType
self.directiveType = directiveType
self.declaration = declaration
@ -1788,8 +1886,12 @@ class ASTDeclaration(ASTBaseBase):
))
def clone(self) -> ASTDeclaration:
return ASTDeclaration(self.objectType, self.directiveType,
self.declaration.clone(), self.semicolon)
return ASTDeclaration(
self.objectType,
self.directiveType,
self.declaration.clone(),
self.semicolon,
)
@property
def name(self) -> ASTNestedName:
@ -1823,8 +1925,13 @@ class ASTDeclaration(ASTBaseBase):
res += ';'
return res
def describe_signature(self, signode: TextElement, mode: str,
env: BuildEnvironment, options: dict[str, bool]) -> None:
def describe_signature(
self,
signode: TextElement,
mode: str,
env: BuildEnvironment,
options: dict[str, bool],
) -> None:
verify_description_mode(mode)
assert self.symbol
# The caller of the domain added a desc_signature node.

View File

@ -4,18 +4,41 @@ import re
# https://en.cppreference.com/w/c/keyword
_keywords = [
'auto', 'break', 'case', 'char', 'const', 'continue', 'default', 'do', 'double',
'else', 'enum', 'extern', 'float', 'for', 'goto', 'if', 'inline', 'int', 'long',
'register', 'restrict', 'return', 'short', 'signed', 'sizeof', 'static', 'struct',
'switch', 'typedef', 'union', 'unsigned', 'void', 'volatile', 'while',
'_Alignas', '_Alignof', '_Atomic', '_Bool', '_Complex',
'auto',
'break',
'case', 'char', 'const', 'continue',
'default', 'do', 'double',
'else', 'enum', 'extern',
'float', 'for',
'goto',
'if', 'inline', 'int',
'long',
'register', 'restrict', 'return',
'short', 'signed', 'sizeof', 'static', 'struct', 'switch',
'typedef',
'union', 'unsigned',
'void', 'volatile',
'while',
'_Alignas', '_Alignof', '_Atomic',
'_Bool',
'_Complex',
'_Decimal32', '_Decimal64', '_Decimal128',
'_Generic', '_Imaginary', '_Noreturn', '_Static_assert', '_Thread_local',
]
'_Generic',
'_Imaginary',
'_Noreturn',
'_Static_assert',
'_Thread_local',
] # fmt: skip
# These are only keyword'y when the corresponding headers are included.
# They are used as default value for c_extra_keywords.
_macroKeywords = [
'alignas', 'alignof', 'bool', 'complex', 'imaginary', 'noreturn', 'static_assert',
'alignas',
'alignof',
'bool',
'complex',
'imaginary',
'noreturn',
'static_assert',
'thread_local',
]
@ -33,20 +56,49 @@ _expression_bin_ops = [
['*', '/', '%'],
['.*', '->*'],
]
_expression_unary_ops = ["++", "--", "*", "&", "+", "-", "!", "not", "~", "compl"]
_expression_assignment_ops = ["=", "*=", "/=", "%=", "+=", "-=",
">>=", "<<=", "&=", "and_eq", "^=", "xor_eq", "|=", "or_eq"]
_expression_unary_ops = [
'++',
'--',
'*',
'&',
'+',
'-',
'!',
'not',
'~',
'compl',
]
_expression_assignment_ops = [
'=',
'*=',
'/=',
'%=',
'+=',
'-=',
'>>=',
'<<=',
'&=',
'and_eq',
'^=',
'xor_eq',
'|=',
'or_eq',
]
_max_id = 1
_id_prefix = [None, 'c.', 'Cv2.']
# Ids are used in lookup keys which are used across pickled files,
# so when _max_id changes, make sure to update the ENV_VERSION.
_string_re = re.compile(r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
r'|"([^"\\]*(?:\\.[^"\\]*)*)")', re.DOTALL)
_string_re = re.compile(
r"[LuU8]?('([^'\\]*(?:\\.[^'\\]*)*)'"
r'|"([^"\\]*(?:\\.[^"\\]*)*)")',
re.DOTALL,
)
# bool, complex, and imaginary are macro "keywords", so they are handled separately
_simple_type_specifiers_re = re.compile(r"""
_simple_type_specifiers_re = re.compile(
r"""
\b(
void|_Bool
|signed|unsigned
@ -62,4 +114,6 @@ _simple_type_specifiers_re = re.compile(r"""
|__fp16 # extension
|_Sat|_Fract|fract|_Accum|accum # extension
)\b
""", re.VERBOSE)
""",
re.VERBOSE,
)

View File

@ -105,7 +105,7 @@ class DefinitionParser(BaseParser):
escape = False
while True:
if self.eof:
self.fail("Unexpected end during inside string.")
self.fail('Unexpected end during inside string.')
elif self.current_char == '"' and not escape:
self.pos += 1
break
@ -114,7 +114,7 @@ class DefinitionParser(BaseParser):
else:
escape = False
self.pos += 1
return self.definition[start_pos:self.pos]
return self.definition[start_pos : self.pos]
def _parse_literal(self) -> ASTLiteral | None:
# -> integer-literal
@ -130,12 +130,16 @@ class DefinitionParser(BaseParser):
pos = self.pos
if self.match(float_literal_re):
self.match(float_literal_suffix_re)
return ASTNumberLiteral(self.definition[pos:self.pos])
for regex in (binary_literal_re, hex_literal_re,
integer_literal_re, octal_literal_re):
return ASTNumberLiteral(self.definition[pos : self.pos])
for regex in (
binary_literal_re,
hex_literal_re,
integer_literal_re,
octal_literal_re,
):
if self.match(regex):
self.match(integers_literal_suffix_re)
return ASTNumberLiteral(self.definition[pos:self.pos])
return ASTNumberLiteral(self.definition[pos : self.pos])
string = self._parse_string()
if string is not None:
@ -148,10 +152,14 @@ class DefinitionParser(BaseParser):
try:
return ASTCharLiteral(prefix, data)
except UnicodeDecodeError as e:
self.fail("Can not handle character literal. Internal error was: %s" % e)
self.fail(
'Can not handle character literal. Internal error was: %s' % e
)
except UnsupportedMultiCharacterCharLiteral:
self.fail("Can not handle character literal"
" resulting in multiple decoded characters.")
self.fail(
'Can not handle character literal'
' resulting in multiple decoded characters.'
)
return None
def _parse_paren_expression(self) -> ASTExpression | None:
@ -181,8 +189,9 @@ class DefinitionParser(BaseParser):
return ASTIdExpression(nn)
return None
def _parse_initializer_list(self, name: str, open: str, close: str,
) -> tuple[list[ASTExpression] | None, bool | None]:
def _parse_initializer_list(
self, name: str, open: str, close: str
) -> tuple[list[ASTExpression] | None, bool | None]:
# Parse open and close with the actual initializer-list in between
# -> initializer-clause '...'[opt]
# | initializer-list ',' initializer-clause '...'[opt]
@ -218,8 +227,9 @@ class DefinitionParser(BaseParser):
#
# expression-list
# -> initializer-list
exprs, trailing_comma = self._parse_initializer_list("parenthesized expression-list",
'(', ')')
exprs, trailing_comma = self._parse_initializer_list(
'parenthesized expression-list', '(', ')'
)
if exprs is None:
return None
return ASTParenExprList(exprs)
@ -227,7 +237,9 @@ class DefinitionParser(BaseParser):
def _parse_braced_init_list(self) -> ASTBracedInitList | None:
# -> '{' initializer-list ','[opt] '}'
# | '{' '}'
exprs, trailing_comma = self._parse_initializer_list("braced-init-list", '{', '}')
exprs, trailing_comma = self._parse_initializer_list(
'braced-init-list', '{', '}'
)
if exprs is None:
return None
return ASTBracedInitList(exprs, trailing_comma)
@ -331,10 +343,11 @@ class DefinitionParser(BaseParser):
return self._parse_unary_expression()
except DefinitionError as ex_unary:
errs = []
errs.append((ex_cast, "If type cast expression"))
errs.append((ex_unary, "If unary expression"))
raise self._make_multi_error(errs,
"Error in cast expression.") from ex_unary
errs.append((ex_cast, 'If type cast expression'))
errs.append((ex_unary, 'If unary expression'))
raise self._make_multi_error(
errs, 'Error in cast expression.'
) from ex_unary
else:
return self._parse_unary_expression()
@ -352,11 +365,15 @@ class DefinitionParser(BaseParser):
# pm = cast .*, ->*
def _parse_bin_op_expr(self: DefinitionParser, op_id: int) -> ASTExpression:
if op_id + 1 == len(_expression_bin_ops):
def parser() -> ASTExpression:
return self._parse_cast_expression()
else:
def parser() -> ASTExpression:
return _parse_bin_op_expr(self, op_id + 1)
exprs = []
ops = []
exprs.append(parser())
@ -387,9 +404,12 @@ class DefinitionParser(BaseParser):
if not one_more:
break
return ASTBinOpExpr(exprs, ops) # type: ignore[return-value]
return _parse_bin_op_expr(self, 0)
def _parse_conditional_expression_tail(self, or_expr_head: Any) -> ASTExpression | None:
def _parse_conditional_expression_tail(
self, or_expr_head: Any
) -> ASTExpression | None:
# -> "?" expression ":" assignment-expression
return None
@ -436,9 +456,8 @@ class DefinitionParser(BaseParser):
return self._parse_assignment_expression()
def _parse_expression_fallback(
self, end: list[str],
parser: Callable[[], ASTExpression],
allow: bool = True) -> ASTExpression:
self, end: list[str], parser: Callable[[], ASTExpression], allow: bool = True
) -> ASTExpression:
# Stupidly "parse" an expression.
# 'end' should be a list of characters which ends the expression.
@ -451,8 +470,10 @@ class DefinitionParser(BaseParser):
# and for testing we may want to globally disable it
if not allow or not self.allowFallbackExpressionParsing:
raise
self.warn("Parsing of expression failed. Using fallback parser."
" Error was:\n%s" % e)
self.warn(
'Parsing of expression failed. Using fallback parser.'
' Error was:\n%s' % e
)
self.pos = prev_pos
# and then the fallback scanning
assert end is not None
@ -473,9 +494,10 @@ class DefinitionParser(BaseParser):
symbols.pop()
self.pos += 1
if len(end) > 0 and self.eof:
self.fail("Could not find end of expression starting at %d."
% start_pos)
value = self.definition[start_pos:self.pos].strip()
self.fail(
'Could not find end of expression starting at %d.' % start_pos
)
value = self.definition[start_pos : self.pos].strip()
return ASTFallbackExpr(value.strip())
def _parse_nested_name(self) -> ASTNestedName:
@ -488,20 +510,20 @@ class DefinitionParser(BaseParser):
while 1:
self.skip_ws()
if not self.match(identifier_re):
self.fail("Expected identifier in nested name.")
self.fail('Expected identifier in nested name.')
identifier = self.matched_text
# make sure there isn't a keyword
if identifier in _keywords:
self.fail("Expected identifier in nested name, "
"got keyword: %s" % identifier)
self.fail(
'Expected identifier in nested name, got keyword: %s' % identifier
)
if self.matched_text in self.config.c_extra_keywords:
msg = (
'Expected identifier, got user-defined keyword: %s.'
' Remove it from c_extra_keywords to allow it as identifier.\n'
'Currently c_extra_keywords is %s.'
)
self.fail(msg % (self.matched_text,
str(self.config.c_extra_keywords)))
self.fail(msg % (self.matched_text, str(self.config.c_extra_keywords)))
ident = ASTIdentifier(identifier)
names.append(ident)
@ -582,13 +604,15 @@ class DefinitionParser(BaseParser):
continue
if self.skip_string(')'):
break
self.fail(f'Expecting "," or ")" in parameters, got "{self.current_char}".')
self.fail(
f'Expecting "," or ")" in parameters, got "{self.current_char}".'
)
attrs = self._parse_attribute_list()
return ASTParameters(args, attrs)
def _parse_decl_specs_simple(
self, outer: str | None, typed: bool,
self, outer: str | None, typed: bool
) -> ASTDeclSpecsSimple:
"""Just parse the simple ones."""
storage = None
@ -644,8 +668,15 @@ class DefinitionParser(BaseParser):
attrs.append(attr)
continue
break
return ASTDeclSpecsSimple(storage, thread_local, inline,
restrict, volatile, const, ASTAttributeList(attrs))
return ASTDeclSpecsSimple(
storage,
thread_local,
inline,
restrict,
volatile,
const,
ASTAttributeList(attrs),
)
def _parse_decl_specs(self, outer: str | None, typed: bool = True) -> ASTDeclSpecs:
if outer:
@ -662,23 +693,25 @@ class DefinitionParser(BaseParser):
return ASTDeclSpecs(outer, left_specs, right_specs, trailing)
def _parse_declarator_name_suffix(
self, named: bool | str, param_mode: str, typed: bool,
self, named: bool | str, param_mode: str, typed: bool
) -> ASTDeclarator:
assert named in {True, False, 'single'}
# now we should parse the name, and then suffixes
if named == 'single':
if self.match(identifier_re):
if self.matched_text in _keywords:
self.fail("Expected identifier, "
"got keyword: %s" % self.matched_text)
self.fail(
'Expected identifier, got keyword: %s' % self.matched_text
)
if self.matched_text in self.config.c_extra_keywords:
msg = (
'Expected identifier, got user-defined keyword: %s. '
'Remove it from c_extra_keywords to allow it as identifier.\n'
'Currently c_extra_keywords is %s.'
)
self.fail(msg % (self.matched_text,
str(self.config.c_extra_keywords)))
self.fail(
msg % (self.matched_text, str(self.config.c_extra_keywords))
)
identifier = ASTIdentifier(self.matched_text)
decl_id = ASTNestedName([identifier], rooted=False)
else:
@ -726,6 +759,7 @@ class DefinitionParser(BaseParser):
def parser() -> ASTExpression:
return self._parse_expression()
size = self._parse_expression_fallback([']'], parser)
self.skip_ws()
if not self.skip_string(']'):
@ -741,15 +775,14 @@ class DefinitionParser(BaseParser):
if self.skip_string(':'):
size = self._parse_constant_expression()
return ASTDeclaratorNameBitField(declId=decl_id, size=size)
return ASTDeclaratorNameParam(declId=decl_id, arrayOps=array_ops,
param=param)
return ASTDeclaratorNameParam(declId=decl_id, arrayOps=array_ops, param=param)
def _parse_declarator(self, named: bool | str, param_mode: str,
typed: bool = True) -> ASTDeclarator:
def _parse_declarator(
self, named: bool | str, param_mode: str, typed: bool = True
) -> ASTDeclarator:
# 'typed' here means 'parse return type stuff'
if param_mode not in {'type', 'function'}:
raise Exception(
"Internal error, unknown param_mode '%s'." % param_mode)
raise Exception("Internal error, unknown param_mode '%s'." % param_mode)
prev_errors = []
self.skip_ws()
if typed and self.skip_string('*'):
@ -777,20 +810,23 @@ class DefinitionParser(BaseParser):
continue
break
next = self._parse_declarator(named, param_mode, typed)
return ASTDeclaratorPtr(next=next,
restrict=restrict, volatile=volatile, const=const,
attrs=ASTAttributeList(attrs))
return ASTDeclaratorPtr(
next=next,
restrict=restrict,
volatile=volatile,
const=const,
attrs=ASTAttributeList(attrs),
)
if typed and self.current_char == '(': # note: peeking, not skipping
# maybe this is the beginning of params, try that first,
# otherwise assume it's noptr->declarator > ( ptr-declarator )
pos = self.pos
try:
# assume this is params
res = self._parse_declarator_name_suffix(named, param_mode,
typed)
res = self._parse_declarator_name_suffix(named, param_mode, typed)
return res
except DefinitionError as ex_param_qual:
msg = "If declarator-id with parameters"
msg = 'If declarator-id with parameters'
if param_mode == 'function':
msg += " (e.g., 'void f(int arg)')"
prev_errors.append((ex_param_qual, msg))
@ -803,30 +839,33 @@ class DefinitionParser(BaseParser):
# inside, right?
inner = self._parse_declarator(named, param_mode, typed)
if not self.skip_string(')'):
self.fail("Expected ')' in \"( ptr-declarator )\"")
next = self._parse_declarator(named=False,
param_mode="type",
typed=typed)
self.fail('Expected \')\' in "( ptr-declarator )"')
next = self._parse_declarator(
named=False, param_mode='type', typed=typed
)
return ASTDeclaratorParen(inner=inner, next=next)
except DefinitionError as ex_no_ptr_paren:
self.pos = pos
msg = "If parenthesis in noptr-declarator"
msg = 'If parenthesis in noptr-declarator'
if param_mode == 'function':
msg += " (e.g., 'void (*f(int arg))(double)')"
prev_errors.append((ex_no_ptr_paren, msg))
header = "Error in declarator"
raise self._make_multi_error(prev_errors, header) from ex_no_ptr_paren
header = 'Error in declarator'
raise self._make_multi_error(
prev_errors, header
) from ex_no_ptr_paren
pos = self.pos
try:
return self._parse_declarator_name_suffix(named, param_mode, typed)
except DefinitionError as e:
self.pos = pos
prev_errors.append((e, "If declarator-id"))
header = "Error in declarator or parameters"
prev_errors.append((e, 'If declarator-id'))
header = 'Error in declarator or parameters'
raise self._make_multi_error(prev_errors, header) from e
def _parse_initializer(self, outer: str | None = None, allow_fallback: bool = True,
) -> ASTInitializer | None:
def _parse_initializer(
self, outer: str | None = None, allow_fallback: bool = True
) -> ASTInitializer | None:
self.skip_ws()
if outer == 'member' and False: # NoQA: SIM223 # TODO
braced_init = self._parse_braced_init_list()
@ -845,13 +884,16 @@ class DefinitionParser(BaseParser):
elif outer is None: # function parameter
fallback_end = [',', ')']
else:
self.fail("Internal error, initializer for outer '%s' not "
"implemented." % outer)
self.fail(
"Internal error, initializer for outer '%s' not implemented." % outer
)
def parser() -> ASTExpression:
return self._parse_assignment_expression()
value = self._parse_expression_fallback(fallback_end, parser, allow=allow_fallback)
value = self._parse_expression_fallback(
fallback_end, parser, allow=allow_fallback
)
return ASTInitializer(value)
def _parse_type(self, named: bool | str, outer: str | None = None) -> ASTType:
@ -871,11 +913,10 @@ class DefinitionParser(BaseParser):
# first try without the type
try:
decl_specs = self._parse_decl_specs(outer=outer, typed=False)
decl = self._parse_declarator(named=True, param_mode=outer,
typed=False)
decl = self._parse_declarator(named=True, param_mode=outer, typed=False)
self.assert_end(allowSemicolon=True)
except DefinitionError as ex_untyped:
desc = "If just a name"
desc = 'If just a name'
prev_errors.append((ex_untyped, desc))
self.pos = start_pos
try:
@ -883,14 +924,14 @@ class DefinitionParser(BaseParser):
decl = self._parse_declarator(named=True, param_mode=outer)
except DefinitionError as ex_typed:
self.pos = start_pos
desc = "If typedef-like declaration"
desc = 'If typedef-like declaration'
prev_errors.append((ex_typed, desc))
# Retain the else branch for easier debugging.
# TODO: it would be nice to save the previous stacktrace
# and output it here.
if True:
header = "Type must be either just a name or a "
header += "typedef-like declaration."
header = 'Type must be either just a name or a '
header += 'typedef-like declaration.'
raise self._make_multi_error(prev_errors, header) from ex_typed
else: # NoQA: RET506
# For testing purposes.
@ -900,8 +941,9 @@ class DefinitionParser(BaseParser):
self.pos = start_pos
typed = True
decl_specs = self._parse_decl_specs(outer=outer, typed=typed)
decl = self._parse_declarator(named=True, param_mode=outer,
typed=typed)
decl = self._parse_declarator(
named=True, param_mode=outer, typed=typed
)
elif outer == 'function':
decl_specs = self._parse_decl_specs(outer=outer)
decl = self._parse_declarator(named=True, param_mode=outer)
@ -913,7 +955,9 @@ class DefinitionParser(BaseParser):
decl = self._parse_declarator(named=named, param_mode=param_mode)
return ASTType(decl_specs, decl)
def _parse_type_with_init(self, named: bool | str, outer: str | None) -> ASTTypeWithInit:
def _parse_type_with_init(
self, named: bool | str, outer: str | None
) -> ASTTypeWithInit:
if outer:
assert outer in {'type', 'member', 'function'}
type = self._parse_type(outer=outer, named=named)
@ -924,7 +968,7 @@ class DefinitionParser(BaseParser):
self.skip_ws()
ident = self._parse_nested_name()
if ident is None:
self.fail("Expected identifier in macro definition.")
self.fail('Expected identifier in macro definition.')
self.skip_ws()
if not self.skip_string_and_ws('('):
return ASTMacro(ident, None)
@ -940,7 +984,7 @@ class DefinitionParser(BaseParser):
self.fail('Expected ")" after "..." in macro parameters.')
break
if not self.match(identifier_re):
self.fail("Expected identifier in macro parameters.")
self.fail('Expected identifier in macro parameters.')
nn = ASTNestedName([ASTIdentifier(self.matched_text)], rooted=False)
# Allow named variadic args:
# https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
@ -989,12 +1033,31 @@ class DefinitionParser(BaseParser):
def parse_declaration(self, objectType: str, directiveType: str) -> ASTDeclaration:
object_type = objectType
directive_type = directiveType
if object_type not in {'function', 'member',
'macro', 'struct', 'union', 'enum', 'enumerator', 'type'}:
if object_type not in {
'function',
'member',
'macro',
'struct',
'union',
'enum',
'enumerator',
'type',
}:
raise Exception('Internal error, unknown objectType "%s".' % object_type)
if directive_type not in {'function', 'member', 'var',
'macro', 'struct', 'union', 'enum', 'enumerator', 'type'}:
raise Exception('Internal error, unknown directiveType "%s".' % directive_type)
if directive_type not in {
'function',
'member',
'var',
'macro',
'struct',
'union',
'enum',
'enumerator',
'type',
}:
raise Exception(
'Internal error, unknown directiveType "%s".' % directive_type
)
declaration: DeclarationType | None = None
if object_type == 'member':
@ -1047,9 +1110,9 @@ class DefinitionParser(BaseParser):
self.skip_ws()
self.assert_end()
except DefinitionError as ex_type:
header = "Error when parsing (type) expression."
header = 'Error when parsing (type) expression.'
errs = []
errs.append((ex_expr, "If expression"))
errs.append((ex_type, "If type"))
errs.append((ex_expr, 'If expression'))
errs.append((ex_type, 'If type'))
raise self._make_multi_error(errs, header) from ex_type
return res

View File

@ -27,12 +27,13 @@ class _DuplicateSymbolError(Exception):
self.declaration = declaration
def __str__(self) -> str:
return "Internal C duplicate symbol error:\n%s" % self.symbol.dump(0)
return 'Internal C duplicate symbol error:\n%s' % self.symbol.dump(0)
class SymbolLookupResult:
def __init__(self, symbols: Sequence[Symbol], parentSymbol: Symbol,
ident: ASTIdentifier) -> None:
def __init__(
self, symbols: Sequence[Symbol], parentSymbol: Symbol, ident: ASTIdentifier
) -> None:
self.symbols = symbols
self.parentSymbol = parentSymbol
self.ident = ident
@ -43,13 +44,13 @@ class LookupKey:
self.data = data
def __str__(self) -> str:
inner = ', '.join(f"({ident}, {id_})" for ident, id_ in self.data)
inner = ', '.join(f'({ident}, {id_})' for ident, id_ in self.data)
return f'[{inner}]'
class Symbol:
debug_indent = 0
debug_indent_string = " "
debug_indent_string = ' '
debug_lookup = False
debug_show_tree = False
@ -65,7 +66,7 @@ class Symbol:
@staticmethod
def debug_print(*args: Any) -> None:
msg = Symbol.debug_indent_string * Symbol.debug_indent
msg += "".join(str(e) for e in args)
msg += ''.join(str(e) for e in args)
logger.debug(msg)
def _assert_invariants(self) -> None:
@ -78,7 +79,7 @@ class Symbol:
assert self.docname
def __setattr__(self, key: str, value: Any) -> None:
if key == "children":
if key == 'children':
raise AssertionError
return super().__setattr__(key, value)
@ -159,12 +160,15 @@ class Symbol:
def _add_function_params(self) -> None:
if Symbol.debug_lookup:
Symbol.debug_indent += 1
Symbol.debug_print("_add_function_params:")
Symbol.debug_print('_add_function_params:')
# Note: we may be called from _fill_empty, so the symbols we want
# to add may actually already be present (as empty symbols).
# add symbols for function parameters, if any
if self.declaration is not None and self.declaration.function_params is not None:
if (
self.declaration is not None
and self.declaration.function_params is not None
):
for p in self.declaration.function_params:
if p.arg is None:
continue
@ -205,8 +209,8 @@ class Symbol:
def get_all_symbols(self) -> Iterator[Symbol]:
yield self
for sChild in self._children:
yield from sChild.get_all_symbols()
for s_child in self._children:
yield from s_child.get_all_symbols()
@property
def children(self) -> Iterator[Symbol]:
@ -244,70 +248,74 @@ class Symbol:
def _symbol_lookup(
self,
nestedName: ASTNestedName,
onMissingQualifiedSymbol: Callable[[Symbol, ASTIdentifier], Symbol | None],
ancestorLookupType: str | None,
matchSelf: bool,
recurseInAnon: bool,
searchInSiblings: bool,
nested_name: ASTNestedName,
on_missing_qualified_symbol: Callable[[Symbol, ASTIdentifier], Symbol | None],
ancestor_lookup_type: str | None,
match_self: bool,
recurse_in_anon: bool,
search_in_siblings: bool,
) -> SymbolLookupResult | None:
# TODO: further simplification from C++ to C
# ancestorLookupType: if not None, specifies the target type of the lookup
# ancestor_lookup_type: if not None, specifies the target type of the lookup
if Symbol.debug_lookup:
Symbol.debug_indent += 1
Symbol.debug_print("_symbol_lookup:")
Symbol.debug_print('_symbol_lookup:')
Symbol.debug_indent += 1
Symbol.debug_print("self:")
Symbol.debug_print('self:')
logger.debug(self.to_string(Symbol.debug_indent + 1, addEndNewline=False))
Symbol.debug_print("nestedName: ", nestedName)
Symbol.debug_print("ancestorLookupType:", ancestorLookupType)
Symbol.debug_print("matchSelf: ", matchSelf)
Symbol.debug_print("recurseInAnon: ", recurseInAnon)
Symbol.debug_print("searchInSiblings: ", searchInSiblings)
Symbol.debug_print('nested_name: ', nested_name)
Symbol.debug_print('ancestor_lookup_type:', ancestor_lookup_type)
Symbol.debug_print('match_self: ', match_self)
Symbol.debug_print('recurse_in_anon: ', recurse_in_anon)
Symbol.debug_print('search_in_siblings: ', search_in_siblings)
names = nestedName.names
names = nested_name.names
# find the right starting point for lookup
parentSymbol = self
if nestedName.rooted:
while parentSymbol.parent is not None:
parentSymbol = parentSymbol.parent
parent_symbol = self
if nested_name.rooted:
while parent_symbol.parent is not None:
parent_symbol = parent_symbol.parent
if ancestorLookupType is not None:
if ancestor_lookup_type is not None:
# walk up until we find the first identifier
firstName = names[0]
while parentSymbol.parent:
if firstName.name in parentSymbol._children_by_name:
first_name = names[0]
while parent_symbol.parent:
if first_name.name in parent_symbol._children_by_name:
break
parentSymbol = parentSymbol.parent
parent_symbol = parent_symbol.parent
if Symbol.debug_lookup:
Symbol.debug_print("starting point:")
logger.debug(parentSymbol.to_string(Symbol.debug_indent + 1, addEndNewline=False))
Symbol.debug_print('starting point:')
logger.debug(
parent_symbol.to_string(Symbol.debug_indent + 1, addEndNewline=False)
)
# and now the actual lookup
for ident in names[:-1]:
name = ident.name
if name in parentSymbol._children_by_name:
symbol = parentSymbol._children_by_name[name]
if name in parent_symbol._children_by_name:
symbol = parent_symbol._children_by_name[name]
else:
symbol = onMissingQualifiedSymbol(parentSymbol, ident)
symbol = on_missing_qualified_symbol(parent_symbol, ident)
if symbol is None:
if Symbol.debug_lookup:
Symbol.debug_indent -= 2
return None
parentSymbol = symbol
parent_symbol = symbol
if Symbol.debug_lookup:
Symbol.debug_print("handle last name from:")
logger.debug(parentSymbol.to_string(Symbol.debug_indent + 1, addEndNewline=False))
Symbol.debug_print('handle last name from:')
logger.debug(
parent_symbol.to_string(Symbol.debug_indent + 1, addEndNewline=False)
)
# handle the last name
ident = names[-1]
name = ident.name
symbol = parentSymbol._children_by_name.get(name)
if not symbol and recurseInAnon:
for child in parentSymbol._anon_children:
symbol = parent_symbol._children_by_name.get(name)
if not symbol and recurse_in_anon:
for child in parent_symbol._anon_children:
if name in child._children_by_name:
symbol = child._children_by_name[name]
break
@ -316,11 +324,11 @@ class Symbol:
Symbol.debug_indent -= 2
result = [symbol] if symbol else []
return SymbolLookupResult(result, parentSymbol, ident)
return SymbolLookupResult(result, parent_symbol, ident)
def _add_symbols(
self,
nestedName: ASTNestedName,
nested_name: ASTNestedName,
declaration: ASTDeclaration | None,
docname: str | None,
line: int | None,
@ -331,152 +339,172 @@ class Symbol:
if Symbol.debug_lookup:
Symbol.debug_indent += 1
Symbol.debug_print("_add_symbols:")
Symbol.debug_print('_add_symbols:')
Symbol.debug_indent += 1
Symbol.debug_print("nn: ", nestedName)
Symbol.debug_print("decl: ", declaration)
Symbol.debug_print(f"location: {docname}:{line}")
Symbol.debug_print('nn: ', nested_name)
Symbol.debug_print('decl: ', declaration)
Symbol.debug_print(f'location: {docname}:{line}')
def onMissingQualifiedSymbol(parentSymbol: Symbol, ident: ASTIdentifier) -> Symbol:
def on_missing_qualified_symbol(
parent_symbol: Symbol, ident: ASTIdentifier
) -> Symbol:
if Symbol.debug_lookup:
Symbol.debug_indent += 1
Symbol.debug_print("_add_symbols, onMissingQualifiedSymbol:")
Symbol.debug_print('_add_symbols, on_missing_qualified_symbol:')
Symbol.debug_indent += 1
Symbol.debug_print("ident: ", ident)
Symbol.debug_print('ident: ', ident)
Symbol.debug_indent -= 2
return Symbol(parent=parentSymbol, ident=ident,
declaration=None, docname=None, line=None)
return Symbol(
parent=parent_symbol,
ident=ident,
declaration=None,
docname=None,
line=None,
)
lookupResult = self._symbol_lookup(nestedName,
onMissingQualifiedSymbol,
ancestorLookupType=None,
matchSelf=False,
recurseInAnon=False,
searchInSiblings=False)
assert lookupResult is not None # we create symbols all the way, so that can't happen
symbols = list(lookupResult.symbols)
lookup_result = self._symbol_lookup(
nested_name,
on_missing_qualified_symbol,
ancestor_lookup_type=None,
match_self=False,
recurse_in_anon=False,
search_in_siblings=False,
)
# we create symbols all the way, so that can't happen
assert lookup_result is not None
symbols = list(lookup_result.symbols)
if len(symbols) == 0:
if Symbol.debug_lookup:
Symbol.debug_print("_add_symbols, result, no symbol:")
Symbol.debug_print('_add_symbols, result, no symbol:')
Symbol.debug_indent += 1
Symbol.debug_print("ident: ", lookupResult.ident)
Symbol.debug_print("declaration: ", declaration)
Symbol.debug_print(f"location: {docname}:{line}")
Symbol.debug_print('ident: ', lookup_result.ident)
Symbol.debug_print('declaration: ', declaration)
Symbol.debug_print(f'location: {docname}:{line}')
Symbol.debug_indent -= 1
symbol = Symbol(parent=lookupResult.parentSymbol,
ident=lookupResult.ident,
declaration=declaration,
docname=docname, line=line)
symbol = Symbol(
parent=lookup_result.parentSymbol,
ident=lookup_result.ident,
declaration=declaration,
docname=docname,
line=line,
)
if Symbol.debug_lookup:
Symbol.debug_indent -= 2
return symbol
if Symbol.debug_lookup:
Symbol.debug_print("_add_symbols, result, symbols:")
Symbol.debug_print('_add_symbols, result, symbols:')
Symbol.debug_indent += 1
Symbol.debug_print("number symbols:", len(symbols))
Symbol.debug_print('number symbols:', len(symbols))
Symbol.debug_indent -= 1
if not declaration:
if Symbol.debug_lookup:
Symbol.debug_print("no declaration")
Symbol.debug_print('no declaration')
Symbol.debug_indent -= 2
# good, just a scope creation
# TODO: what if we have more than one symbol?
return symbols[0]
noDecl = []
withDecl = []
dupDecl = []
no_decl = []
with_decl = []
dup_decl = []
for s in symbols:
if s.declaration is None:
noDecl.append(s)
no_decl.append(s)
elif s.isRedeclaration:
dupDecl.append(s)
dup_decl.append(s)
else:
withDecl.append(s)
with_decl.append(s)
if Symbol.debug_lookup:
Symbol.debug_print("#noDecl: ", len(noDecl))
Symbol.debug_print("#withDecl:", len(withDecl))
Symbol.debug_print("#dupDecl: ", len(dupDecl))
Symbol.debug_print('#no_decl: ', len(no_decl))
Symbol.debug_print('#with_decl:', len(with_decl))
Symbol.debug_print('#dup_decl: ', len(dup_decl))
# With partial builds we may start with a large symbol tree stripped of declarations.
# Essentially any combination of noDecl, withDecl, and dupDecls seems possible.
# Essentially any combination of no_decl, with_decl, and dup_decls seems possible.
# TODO: make partial builds fully work. What should happen when the primary symbol gets
# deleted, and other duplicates exist? The full document should probably be rebuild.
# First check if one of those with a declaration matches.
# If it's a function, we need to compare IDs,
# otherwise there should be only one symbol with a declaration.
def makeCandSymbol() -> Symbol:
def make_cand_symbol() -> Symbol:
if Symbol.debug_lookup:
Symbol.debug_print("begin: creating candidate symbol")
symbol = Symbol(parent=lookupResult.parentSymbol,
ident=lookupResult.ident,
declaration=declaration,
docname=docname, line=line)
Symbol.debug_print('begin: creating candidate symbol')
symbol = Symbol(
parent=lookup_result.parentSymbol,
ident=lookup_result.ident,
declaration=declaration,
docname=docname,
line=line,
)
if Symbol.debug_lookup:
Symbol.debug_print("end: creating candidate symbol")
Symbol.debug_print('end: creating candidate symbol')
return symbol
if len(withDecl) == 0:
candSymbol = None
if len(with_decl) == 0:
cand_symbol = None
else:
candSymbol = makeCandSymbol()
cand_symbol = make_cand_symbol()
def handleDuplicateDeclaration(symbol: Symbol, candSymbol: Symbol) -> None:
def handle_duplicate_declaration(
symbol: Symbol, cand_symbol: Symbol
) -> None:
if Symbol.debug_lookup:
Symbol.debug_indent += 1
Symbol.debug_print("redeclaration")
Symbol.debug_print('redeclaration')
Symbol.debug_indent -= 1
Symbol.debug_indent -= 2
# Redeclaration of the same symbol.
# Let the new one be there, but raise an error to the client
# so it can use the real symbol as subscope.
# This will probably result in a duplicate id warning.
candSymbol.isRedeclaration = True
cand_symbol.isRedeclaration = True
raise _DuplicateSymbolError(symbol, declaration)
if declaration.objectType != "function":
assert len(withDecl) <= 1
handleDuplicateDeclaration(withDecl[0], candSymbol)
if declaration.objectType != 'function':
assert len(with_decl) <= 1
handle_duplicate_declaration(with_decl[0], cand_symbol)
# (not reachable)
# a function, so compare IDs
candId = declaration.get_newest_id()
cand_id = declaration.get_newest_id()
if Symbol.debug_lookup:
Symbol.debug_print("candId:", candId)
for symbol in withDecl:
oldId = symbol.declaration.get_newest_id()
Symbol.debug_print('cand_id:', cand_id)
for symbol in with_decl:
old_id = symbol.declaration.get_newest_id()
if Symbol.debug_lookup:
Symbol.debug_print("oldId: ", oldId)
if candId == oldId:
handleDuplicateDeclaration(symbol, candSymbol)
Symbol.debug_print('old_id: ', old_id)
if cand_id == old_id:
handle_duplicate_declaration(symbol, cand_symbol)
# (not reachable)
# no candidate symbol found with matching ID
# if there is an empty symbol, fill that one
if len(noDecl) == 0:
if len(no_decl) == 0:
if Symbol.debug_lookup:
Symbol.debug_print(
"no match, no empty, candSybmol is not None?:", candSymbol is not None,
'no match, no empty, cand_sybmol is not None?:',
cand_symbol is not None,
)
Symbol.debug_indent -= 2
if candSymbol is not None:
return candSymbol
if cand_symbol is not None:
return cand_symbol
else:
return makeCandSymbol()
return make_cand_symbol()
else:
if Symbol.debug_lookup:
Symbol.debug_print(
"no match, but fill an empty declaration, candSybmol is not None?:",
candSymbol is not None)
'no match, but fill an empty declaration, cand_sybmol is not None?:',
cand_symbol is not None,
)
Symbol.debug_indent -= 2
if candSymbol is not None:
candSymbol.remove()
# assert len(noDecl) == 1
if cand_symbol is not None:
cand_symbol.remove()
# assert len(no_decl) == 1
# TODO: enable assertion when we at some point find out how to do cleanup
# for now, just take the first one, it should work fine ... right?
symbol = noDecl[0]
symbol = no_decl[0]
# If someone first opened the scope, and then later
# declares it, e.g,
# .. namespace:: Test
@ -485,44 +513,48 @@ class Symbol:
symbol._fill_empty(declaration, docname, line)
return symbol
def merge_with(self, other: Symbol, docnames: list[str],
env: BuildEnvironment) -> None:
def merge_with(
self, other: Symbol, docnames: list[str], env: BuildEnvironment
) -> None:
if Symbol.debug_lookup:
Symbol.debug_indent += 1
Symbol.debug_print("merge_with:")
Symbol.debug_print('merge_with:')
assert other is not None
for otherChild in other._children:
otherName = otherChild.ident.name
if otherName not in self._children_by_name:
for other_child in other._children:
other_name = other_child.ident.name
if other_name not in self._children_by_name:
# TODO: hmm, should we prune by docnames?
otherChild.parent = self
self._add_child(otherChild)
otherChild._assert_invariants()
other_child.parent = self
self._add_child(other_child)
other_child._assert_invariants()
continue
ourChild = self._children_by_name[otherName]
if otherChild.declaration and otherChild.docname in docnames:
if not ourChild.declaration:
ourChild._fill_empty(otherChild.declaration,
otherChild.docname, otherChild.line)
elif ourChild.docname != otherChild.docname:
name = str(ourChild.declaration)
msg = __("Duplicate C declaration, also defined at %s:%s.\n"
"Declaration is '.. c:%s:: %s'.")
our_child = self._children_by_name[other_name]
if other_child.declaration and other_child.docname in docnames:
if not our_child.declaration:
our_child._fill_empty(
other_child.declaration, other_child.docname, other_child.line
)
elif our_child.docname != other_child.docname:
name = str(our_child.declaration)
msg = __(
'Duplicate C declaration, also defined at %s:%s.\n'
"Declaration is '.. c:%s:: %s'."
)
logger.warning(
msg,
ourChild.docname,
ourChild.line,
ourChild.declaration.directiveType,
our_child.docname,
our_child.line,
our_child.declaration.directiveType,
name,
location=(otherChild.docname, otherChild.line),
location=(other_child.docname, other_child.line),
)
else:
# Both have declarations, and in the same docname.
# This can apparently happen, it should be safe to
# just ignore it, right?
pass
ourChild.merge_with(otherChild, docnames, env)
our_child.merge_with(other_child, docnames, env)
if Symbol.debug_lookup:
Symbol.debug_indent -= 1
@ -530,45 +562,52 @@ class Symbol:
def add_name(self, nestedName: ASTNestedName) -> Symbol:
if Symbol.debug_lookup:
Symbol.debug_indent += 1
Symbol.debug_print("add_name:")
Symbol.debug_print('add_name:')
res = self._add_symbols(nestedName, declaration=None, docname=None, line=None)
if Symbol.debug_lookup:
Symbol.debug_indent -= 1
return res
def add_declaration(self, declaration: ASTDeclaration,
docname: str, line: int) -> Symbol:
def add_declaration(
self, declaration: ASTDeclaration, docname: str, line: int
) -> Symbol:
if Symbol.debug_lookup:
Symbol.debug_indent += 1
Symbol.debug_print("add_declaration:")
Symbol.debug_print('add_declaration:')
assert declaration is not None
assert docname is not None
assert line is not None
nestedName = declaration.name
res = self._add_symbols(nestedName, declaration, docname, line)
nested_name = declaration.name
res = self._add_symbols(nested_name, declaration, docname, line)
if Symbol.debug_lookup:
Symbol.debug_indent -= 1
return res
def find_identifier(self, ident: ASTIdentifier,
matchSelf: bool, recurseInAnon: bool, searchInSiblings: bool,
) -> Symbol | None:
def find_identifier(
self,
ident: ASTIdentifier,
matchSelf: bool,
recurseInAnon: bool,
searchInSiblings: bool,
) -> Symbol | None:
if Symbol.debug_lookup:
Symbol.debug_indent += 1
Symbol.debug_print("find_identifier:")
Symbol.debug_print('find_identifier:')
Symbol.debug_indent += 1
Symbol.debug_print("ident: ", ident)
Symbol.debug_print("matchSelf: ", matchSelf)
Symbol.debug_print("recurseInAnon: ", recurseInAnon)
Symbol.debug_print("searchInSiblings:", searchInSiblings)
Symbol.debug_print('ident: ', ident)
Symbol.debug_print('matchSelf: ', matchSelf)
Symbol.debug_print('recurseInAnon: ', recurseInAnon)
Symbol.debug_print('searchInSiblings:', searchInSiblings)
logger.debug(self.to_string(Symbol.debug_indent + 1, addEndNewline=False))
Symbol.debug_indent -= 2
current = self
while current is not None:
if Symbol.debug_lookup:
Symbol.debug_indent += 2
Symbol.debug_print("trying:")
logger.debug(current.to_string(Symbol.debug_indent + 1, addEndNewline=False))
Symbol.debug_print('trying:')
logger.debug(
current.to_string(Symbol.debug_indent + 1, addEndNewline=False)
)
Symbol.debug_indent -= 2
if matchSelf and current.ident == ident:
return current
@ -587,49 +626,53 @@ class Symbol:
def direct_lookup(self, key: LookupKey) -> Symbol | None:
if Symbol.debug_lookup:
Symbol.debug_indent += 1
Symbol.debug_print("direct_lookup:")
Symbol.debug_print('direct_lookup:')
Symbol.debug_indent += 1
s = self
for ident, id_ in key.data:
s = s._children_by_name.get(ident.name)
if Symbol.debug_lookup:
Symbol.debug_print("name: ", ident.name)
Symbol.debug_print("id: ", id_)
Symbol.debug_print('name: ', ident.name)
Symbol.debug_print('id: ', id_)
if s is not None:
logger.debug(s.to_string(Symbol.debug_indent + 1, addEndNewline=False))
logger.debug(
s.to_string(Symbol.debug_indent + 1, addEndNewline=False)
)
else:
Symbol.debug_print("not found")
Symbol.debug_print('not found')
if s is None:
break
if Symbol.debug_lookup:
Symbol.debug_indent -= 2
return s
def find_declaration(self, nestedName: ASTNestedName, typ: str,
matchSelf: bool, recurseInAnon: bool) -> Symbol | None:
def find_declaration(
self, nestedName: ASTNestedName, typ: str, matchSelf: bool, recurseInAnon: bool
) -> Symbol | None:
# templateShorthand: missing template parameter lists for templates is ok
if Symbol.debug_lookup:
Symbol.debug_indent += 1
Symbol.debug_print("find_declaration:")
Symbol.debug_print('find_declaration:')
def onMissingQualifiedSymbol(
parentSymbol: Symbol,
ident: ASTIdentifier,
def on_missing_qualified_symbol(
parent_symbol: Symbol, ident: ASTIdentifier
) -> Symbol | None:
return None
lookupResult = self._symbol_lookup(nestedName,
onMissingQualifiedSymbol,
ancestorLookupType=typ,
matchSelf=matchSelf,
recurseInAnon=recurseInAnon,
searchInSiblings=False)
lookup_result = self._symbol_lookup(
nestedName,
on_missing_qualified_symbol,
ancestor_lookup_type=typ,
match_self=matchSelf,
recurse_in_anon=recurseInAnon,
search_in_siblings=False,
)
if Symbol.debug_lookup:
Symbol.debug_indent -= 1
if lookupResult is None:
if lookup_result is None:
return None
symbols = list(lookupResult.symbols)
symbols = list(lookup_result.symbols)
if len(symbols) == 0:
return None
return symbols[0]
@ -644,7 +687,7 @@ class Symbol:
else:
res.append(str(self.declaration))
if self.declaration:
res.append(": ")
res.append(': ')
if self.isRedeclaration:
res.append('!!duplicate!! ')
res.append(str(self.declaration))
@ -657,4 +700,7 @@ class Symbol:
return ''.join(res)
def dump(self, indent: int) -> str:
return ''.join([self.to_string(indent), *(c.dump(indent + 1) for c in self._children)])
return ''.join([
self.to_string(indent),
*(c.dump(indent + 1) for c in self._children),
])