From 65e926cc57717b9d11fcf4dee9b8985adc715eb3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 16:04:19 +0100 Subject: [PATCH 1/3] #536: Include line number when complaining about missing reference targets in nitpicky mode. --- CHANGES | 3 +++ sphinx/environment.py | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CHANGES b/CHANGES index 15b6e31bd..6fdae5466 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.0.7 (in development) ============================== +* #536: Include line number when complaining about missing reference + targets in nitpicky mode. + * #590: Fix inline display of graphviz diagrams in LaTeX output. * #589: Build using app.build() in setup command. diff --git a/sphinx/environment.py b/sphinx/environment.py index 2c0dc99ea..61e017f25 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -1364,7 +1364,7 @@ class BuildEnvironment: 'reference target not found: %stype %s, target %s' % (node.get('refdomain') and 'domain %s, ' % node['refdomain'] or '', - typ, target)) + typ, target), node.line) except NoUri: newnode = contnode node.replace_self(newnode or contnode) From fad48a6a0fe9c25fd30eb630cb684d4d6ac9a497 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 16:06:11 +0100 Subject: [PATCH 2/3] Complain about unparseable C++ references. --- sphinx/domains/cpp.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/sphinx/domains/cpp.py b/sphinx/domains/cpp.py index c7074fb83..f07037e45 100644 --- a/sphinx/domains/cpp.py +++ b/sphinx/domains/cpp.py @@ -1066,13 +1066,15 @@ class CPPDomain(Domain): contnode, name) parser = DefinitionParser(target) - # XXX: warn? try: expr = parser.parse_type().get_name() parser.skip_ws() if not parser.eof or expr is None: - return None + raise DefinitionError('') except DefinitionError: + refdoc = node.get('refdoc', fromdocname) + env.warn(refdoc, 'unparseable C++ definition: %r' % target, + node.line) return None parent = node['cpp:parent'] From 388079984d8bd5c44a4ec2c8ce24d8e8bc013ea3 Mon Sep 17 00:00:00 2001 From: Georg Brandl Date: Sat, 8 Jan 2011 16:41:15 +0100 Subject: [PATCH 3/3] #572: Show warnings by default when reference labels cannot be found. --- CHANGES | 3 +++ sphinx/domains/__init__.py | 2 ++ sphinx/domains/std.py | 45 +++++++++++++++----------------------- sphinx/environment.py | 20 +++++++++++------ sphinx/roles.py | 4 +++- 5 files changed, 39 insertions(+), 35 deletions(-) diff --git a/CHANGES b/CHANGES index 6fdae5466..bc74696f8 100644 --- a/CHANGES +++ b/CHANGES @@ -1,6 +1,9 @@ Release 1.0.7 (in development) ============================== +* #572: Show warnings by default when reference labels cannot be + found. + * #536: Include line number when complaining about missing reference targets in nitpicky mode. diff --git a/sphinx/domains/__init__.py b/sphinx/domains/__init__.py index b16e61095..1d04a5933 100644 --- a/sphinx/domains/__init__.py +++ b/sphinx/domains/__init__.py @@ -132,6 +132,8 @@ class Domain(object): roles = {} #: a list of Index subclasses indices = [] + #: role name -> a warning message if reference is missing + dangling_warnings = {} #: data value for a fresh environment initial_data = {} diff --git a/sphinx/domains/std.py b/sphinx/domains/std.py index 0625a4516..1fd015ac6 100644 --- a/sphinx/domains/std.py +++ b/sphinx/domains/std.py @@ -347,11 +347,13 @@ class StandardDomain(Domain): # links to tokens in grammar productions 'token': XRefRole(), # links to terms in glossary - 'term': XRefRole(lowercase=True, innernodeclass=nodes.emphasis), + 'term': XRefRole(lowercase=True, innernodeclass=nodes.emphasis, + warn_dangling=True), # links to headings or arbitrary labels - 'ref': XRefRole(lowercase=True, innernodeclass=nodes.emphasis), + 'ref': XRefRole(lowercase=True, innernodeclass=nodes.emphasis, + warn_dangling=True), # links to labels, without a different title - 'keyword': XRefRole(), + 'keyword': XRefRole(warn_dangling=True), } initial_data = { @@ -369,6 +371,13 @@ class StandardDomain(Domain): }, } + dangling_warnings = { + 'term': 'term not in glossary: %(target)s', + 'ref': 'undefined label: %(target)s (if the link has no caption ' + 'the label must precede a section header)', + 'keyword': 'unknown keyword: %(target)s', + } + def clear_doc(self, docname): for key, (fn, _) in self.data['progoptions'].items(): if fn == docname: @@ -426,27 +435,16 @@ class StandardDomain(Domain): def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): if typ == 'ref': - #refdoc = node.get('refdoc', fromdocname) if node['refexplicit']: # reference to anonymous label; the reference uses # the supplied link caption docname, labelid = self.data['anonlabels'].get(target, ('','')) sectname = node.astext() - # XXX warn somehow if not resolved by intersphinx - #if not docname: - # env.warn(refdoc, 'undefined label: %s' % - # target, node.line) else: # reference to named label; the final node will # contain the section name after the label docname, labelid, sectname = self.data['labels'].get(target, ('','','')) - # XXX warn somehow if not resolved by intersphinx - #if not docname: - # env.warn(refdoc, - # 'undefined label: %s' % target + ' -- if you ' - # 'don\'t give a link caption the label must ' - # 'precede a section header.', node.line) if not docname: return None newnode = nodes.reference('', '', internal=True) @@ -470,20 +468,17 @@ class StandardDomain(Domain): # keywords are oddballs: they are referenced by named labels docname, labelid, _ = self.data['labels'].get(target, ('','','')) if not docname: - #env.warn(refdoc, 'unknown keyword: %s' % target) return None - else: - return make_refnode(builder, fromdocname, docname, - labelid, contnode) + return make_refnode(builder, fromdocname, docname, + labelid, contnode) elif typ == 'option': progname = node['refprogram'] docname, labelid = self.data['progoptions'].get((progname, target), ('', '')) if not docname: return None - else: - return make_refnode(builder, fromdocname, docname, - labelid, contnode) + return make_refnode(builder, fromdocname, docname, + labelid, contnode) else: objtypes = self.objtypes_for_role(typ) or [] for objtype in objtypes: @@ -493,13 +488,9 @@ class StandardDomain(Domain): else: docname, labelid = '', '' if not docname: - if typ == 'term': - env.warn(node.get('refdoc', fromdocname), - 'term not in glossary: %s' % target, node.line) return None - else: - return make_refnode(builder, fromdocname, docname, - labelid, contnode) + return make_refnode(builder, fromdocname, docname, + labelid, contnode) def get_objects(self): for (prog, option), info in self.data['progoptions'].iteritems(): diff --git a/sphinx/environment.py b/sphinx/environment.py index 61e017f25..65f98e66e 100644 --- a/sphinx/environment.py +++ b/sphinx/environment.py @@ -1315,9 +1315,10 @@ class BuildEnvironment: target = node['reftarget'] refdoc = node.get('refdoc', fromdocname) warned = False + domain = None try: - if node.has_key('refdomain') and node['refdomain']: + if 'refdomain' in node and node['refdomain']: # let the domain try to resolve the reference try: domain = self.domains[node['refdomain']] @@ -1359,12 +1360,17 @@ class BuildEnvironment: newnode = builder.app.emit_firstresult( 'missing-reference', self, node, contnode) # still not found? warn if in nit-picky mode - if newnode is None and not warned and self.config.nitpicky: - self.warn(refdoc, - 'reference target not found: %stype %s, target %s' - % (node.get('refdomain') and - 'domain %s, ' % node['refdomain'] or '', - typ, target), node.line) + if newnode is None and not warned and \ + (self.config.nitpicky or node.get('refwarn')): + if domain and typ in domain.dangling_warnings: + msg = domain.dangling_warnings[typ] + elif node.get('refdomain') != 'std': + msg = '%s:%s reference target not found: ' \ + '%%(target)s' % (node['refdomain'], typ) + else: + msg = '%s reference target not found: ' \ + '%%(target)s' % typ + self.warn(refdoc, msg % {'target': target}, node.line) except NoUri: newnode = contnode node.replace_self(newnode or contnode) diff --git a/sphinx/roles.py b/sphinx/roles.py index fd3da416f..32d02da14 100644 --- a/sphinx/roles.py +++ b/sphinx/roles.py @@ -69,9 +69,10 @@ class XRefRole(object): innernodeclass = nodes.literal def __init__(self, fix_parens=False, lowercase=False, - nodeclass=None, innernodeclass=None): + nodeclass=None, innernodeclass=None, warn_dangling=False): self.fix_parens = fix_parens self.lowercase = lowercase + self.warn_dangling = warn_dangling if nodeclass is not None: self.nodeclass = nodeclass if innernodeclass is not None: @@ -133,6 +134,7 @@ class XRefRole(object): refnode += self.innernodeclass(rawtext, title, classes=classes) # we also need the source document refnode['refdoc'] = env.docname + refnode['refwarn'] = self.warn_dangling # result_nodes allow further modification of return values return self.result_nodes(inliner.document, env, refnode, is_ref=True)