diff --git a/CHANGES b/CHANGES index 027a4c342..27268df66 100644 --- a/CHANGES +++ b/CHANGES @@ -10,6 +10,9 @@ Incompatible changes 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 -------------- @@ -24,6 +27,12 @@ Features added * #7052: add ``:noindexentry:`` to the Python, C, C++, and Javascript domains. Update the documentation to better reflect the relationship between this 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 ---------- diff --git a/doc/usage/configuration.rst b/doc/usage/configuration.rst index 84e12cbb0..eba6904c4 100644 --- a/doc/usage/configuration.rst +++ b/doc/usage/configuration.rst @@ -2545,6 +2545,23 @@ Options for the C domain .. 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: diff --git a/sphinx/domains/c.py b/sphinx/domains/c.py index 10ced026d..0e7d88e04 100644 --- a/sphinx/domains/c.py +++ b/sphinx/domains/c.py @@ -22,6 +22,7 @@ from sphinx import addnodes from sphinx.addnodes import pending_xref from sphinx.application import Sphinx from sphinx.builders import Builder +from sphinx.deprecation import RemovedInSphinx50Warning from sphinx.directives import ObjectDescription from sphinx.domains import Domain, ObjType from sphinx.environment import BuildEnvironment @@ -2937,6 +2938,23 @@ class DefinitionParser(BaseParser): init = ASTInitializer(initVal) 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: if objectType not in ('function', 'member', 'macro', 'struct', 'union', 'enum', 'enumerator', 'type'): @@ -3114,6 +3132,9 @@ class CObject(ObjectDescription): def parse_definition(self, parser: DefinitionParser) -> ASTDeclaration: 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: ast.describe_signature(signode, 'lastIsName', self.env, options) @@ -3135,8 +3156,27 @@ class CObject(ObjectDescription): parser = DefinitionParser(sig, location=signode, config=self.env.config) try: - ast = self.parse_definition(parser) - parser.assert_end() + try: + 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: logger.warning(e, location=signode) # It is easier to assume some phony name than handling the error in @@ -3445,6 +3485,39 @@ class CXRefRole(XRefRole): title = title[dot + 1:] 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): 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_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 { 'version': 'builtin', 'env_version': 2,