Merge pull request #7905 from jakobandersen/c_compat

C, add compatibility flag for parsing some pre-v3 input
This commit is contained in:
Jakob Lykke Andersen 2020-07-14 13:23:32 +02:00 committed by GitHub
commit 03e1070888
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 104 additions and 2 deletions

View File

@ -10,6 +10,9 @@ Incompatible changes
Deprecated Deprecated
---------- ----------
* C, parsing of pre-v3 style type directives and roles, along with the options
:confval:`c_allow_pre_v3` and :confval:`c_warn_on_allowed_pre_v3`.
Features added Features added
-------------- --------------
@ -24,6 +27,12 @@ Features added
* #7052: add ``:noindexentry:`` to the Python, C, C++, and Javascript domains. * #7052: add ``:noindexentry:`` to the Python, C, C++, and Javascript domains.
Update the documentation to better reflect the relationship between this option Update the documentation to better reflect the relationship between this option
and the ``:noindex:`` option. and the ``:noindex:`` option.
* #7899: C, add possibility of parsing of some pre-v3 style type directives and
roles and try to convert them to equivalent v3 directives/roles.
Set the new option :confval:`c_allow_pre_v3` to ``True`` to enable this.
The warnings printed from this functionality can be suppressed by setting
:confval:`c_warn_on_allowed_pre_v3`` to ``True``.
The functionality is immediately deprecated.
Bugs fixed Bugs fixed
---------- ----------

View File

@ -2545,6 +2545,23 @@ Options for the C domain
.. versionadded:: 3.0 .. versionadded:: 3.0
.. confval:: c_allow_pre_v3
A boolean (default ``False``) controlling whether to parse and try to
convert pre-v3 style type directives and type roles.
.. versionadded:: 3.2
.. deprecated:: 3.2
Use the directives and roles added in v3.
.. confval:: c_warn_on_allowed_pre_v3
A boolean (default ``True``) controlling whether to warn when a pre-v3
style type directive/role is parsed and converted.
.. versionadded:: 3.2
.. deprecated:: 3.2
Use the directives and roles added in v3.
.. _cpp-config: .. _cpp-config:

View File

@ -22,6 +22,7 @@ from sphinx import addnodes
from sphinx.addnodes import pending_xref from sphinx.addnodes import pending_xref
from sphinx.application import Sphinx from sphinx.application import Sphinx
from sphinx.builders import Builder from sphinx.builders import Builder
from sphinx.deprecation import RemovedInSphinx50Warning
from sphinx.directives import ObjectDescription from sphinx.directives import ObjectDescription
from sphinx.domains import Domain, ObjType from sphinx.domains import Domain, ObjType
from sphinx.environment import BuildEnvironment from sphinx.environment import BuildEnvironment
@ -2937,6 +2938,23 @@ class DefinitionParser(BaseParser):
init = ASTInitializer(initVal) init = ASTInitializer(initVal)
return ASTEnumerator(name, init) return ASTEnumerator(name, init)
def parse_pre_v3_type_definition(self) -> ASTDeclaration:
self.skip_ws()
declaration = None # type: Any
if self.skip_word('struct'):
typ = 'struct'
declaration = self._parse_struct()
elif self.skip_word('union'):
typ = 'union'
declaration = self._parse_union()
elif self.skip_word('enum'):
typ = 'enum'
declaration = self._parse_enum()
else:
self.fail("Could not parse pre-v3 type directive."
" Must start with 'struct', 'union', or 'enum'.")
return ASTDeclaration(typ, typ, declaration, False)
def parse_declaration(self, objectType: str, directiveType: str) -> ASTDeclaration: def parse_declaration(self, objectType: str, directiveType: str) -> ASTDeclaration:
if objectType not in ('function', 'member', if objectType not in ('function', 'member',
'macro', 'struct', 'union', 'enum', 'enumerator', 'type'): 'macro', 'struct', 'union', 'enum', 'enumerator', 'type'):
@ -3114,6 +3132,9 @@ class CObject(ObjectDescription):
def parse_definition(self, parser: DefinitionParser) -> ASTDeclaration: def parse_definition(self, parser: DefinitionParser) -> ASTDeclaration:
return parser.parse_declaration(self.object_type, self.objtype) return parser.parse_declaration(self.object_type, self.objtype)
def parse_pre_v3_type_definition(self, parser: DefinitionParser) -> ASTDeclaration:
return parser.parse_pre_v3_type_definition()
def describe_signature(self, signode: TextElement, ast: Any, options: Dict) -> None: def describe_signature(self, signode: TextElement, ast: Any, options: Dict) -> None:
ast.describe_signature(signode, 'lastIsName', self.env, options) ast.describe_signature(signode, 'lastIsName', self.env, options)
@ -3135,8 +3156,27 @@ class CObject(ObjectDescription):
parser = DefinitionParser(sig, location=signode, config=self.env.config) parser = DefinitionParser(sig, location=signode, config=self.env.config)
try: try:
ast = self.parse_definition(parser) try:
parser.assert_end() ast = self.parse_definition(parser)
parser.assert_end()
except DefinitionError as eOrig:
if not self.env.config['c_allow_pre_v3']:
raise
if self.objtype != 'type':
raise
try:
ast = self.parse_pre_v3_type_definition(parser)
parser.assert_end()
except DefinitionError:
raise eOrig
self.object_type = ast.objectType # type: ignore
if self.env.config['c_warn_on_allowed_pre_v3']:
msg = "{}: Pre-v3 C type directive '.. c:type:: {}' converted to " \
"'.. c:{}:: {}'." \
"\nThe original parsing error was:\n{}"
msg = msg.format(RemovedInSphinx50Warning.__name__,
sig, ast.objectType, ast, eOrig)
logger.warning(msg, location=signode)
except DefinitionError as e: except DefinitionError as e:
logger.warning(e, location=signode) logger.warning(e, location=signode)
# It is easier to assume some phony name than handling the error in # It is easier to assume some phony name than handling the error in
@ -3445,6 +3485,39 @@ class CXRefRole(XRefRole):
title = title[dot + 1:] title = title[dot + 1:]
return title, target return title, target
def run(self) -> Tuple[List[Node], List[system_message]]:
if not self.env.config['c_allow_pre_v3']:
return super().run()
text = self.text.replace('\n', ' ')
parser = DefinitionParser(text, location=self.get_source_info(),
config=self.env.config)
try:
parser.parse_xref_object()
# it suceeded, so let it through
return super().run()
except DefinitionError as eOrig:
# try as if it was an c:expr
parser.pos = 0
try:
ast = parser.parse_expression()
except DefinitionError:
# that didn't go well, just default back
return super().run()
classes = ['xref', 'c', 'c-texpr']
parentSymbol = self.env.temp_data.get('cpp:parent_symbol', None)
if parentSymbol is None:
parentSymbol = self.env.domaindata['c']['root_symbol']
signode = nodes.inline(classes=classes)
ast.describe_signature(signode, 'markType', self.env, parentSymbol)
if self.env.config['c_warn_on_allowed_pre_v3']:
msg = "{}: Pre-v3 C type role ':c:type:`{}`' converted to ':c:expr:`{}`'."
msg += "\nThe original parsing error was:\n{}"
msg = msg.format(RemovedInSphinx50Warning.__name__, text, text, eOrig)
logger.warning(msg, location=self.get_source_info())
return [signode], []
class CExprRole(SphinxRole): class CExprRole(SphinxRole):
def __init__(self, asCode: bool) -> None: def __init__(self, asCode: bool) -> None:
@ -3646,6 +3719,9 @@ def setup(app: Sphinx) -> Dict[str, Any]:
app.add_config_value("c_paren_attributes", [], 'env') app.add_config_value("c_paren_attributes", [], 'env')
app.add_post_transform(AliasTransform) app.add_post_transform(AliasTransform)
app.add_config_value("c_allow_pre_v3", False, 'env')
app.add_config_value("c_warn_on_allowed_pre_v3", True, 'env')
return { return {
'version': 'builtin', 'version': 'builtin',
'env_version': 2, 'env_version': 2,