Skip to content

Commit 9e27d06

Browse files
committed
Add definition checks for \ref and \iref
1 parent 50fc65d commit 9e27d06

3 files changed

Lines changed: 67 additions & 12 deletions

File tree

source/intro.tex

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -326,8 +326,7 @@
326326
behavior, for a \termref{defns.well.formed}{well-formed program}{} construct and correct data, that
327327
depends on the implementation and that each implementation documents
328328

329-
\definition{implementation-defined strict total order over pointers}
330-
{defns.order.ptr}
329+
\definition{implementation-defined strict total order over pointers}{defns.order.ptr}
331330
\indexdefn{pointer!strict total order}%
332331
\defncontext{library}
333332
\impldef{strict total order over pointer values}

tools/check-source.py

Lines changed: 59 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1700,26 +1700,78 @@ def __init__(
17001700
self.definition_pattern = definition_pattern
17011701
self.usage_pattern = usage_pattern
17021702
self.defined: Set[str] = set()
1703-
self.used: Dict[str, List[Tuple[str, int]]] = defaultdict(list)
1703+
self.used: Dict[str, List[Tuple[str, int, int, int]]] = defaultdict(list)
17041704

17051705
def check_line(self, line_num: int, line: str) -> None:
17061706
for m in self.definition_pattern.finditer(line):
17071707
self.defined.add(m.group(1))
17081708
for pattern in self.usage_pattern:
17091709
for m in pattern.finditer(line):
1710-
name = m.group(1)
1711-
self.used[name].append((os.path.relpath(self.file_path), line_num))
1710+
for name in m.group(1).split(","):
1711+
name = name.strip()
1712+
if not name or name.startswith("\\"):
1713+
continue
1714+
self.used[name].append(
1715+
(
1716+
os.path.relpath(self.file_path),
1717+
line_num,
1718+
m.start(1),
1719+
m.end(1),
1720+
)
1721+
)
17121722

17131723
def end_checks(self) -> None:
17141724
for name, locations in sorted(self.used.items()):
17151725
if name not in self.defined:
1716-
file_name, line_num = locations[0]
1726+
file_name, line_num, column_start, column_end = locations[0]
17171727
emit_check_failure(
17181728
self,
17191729
file_name,
17201730
line_num,
1721-
0,
1722-
0,
1731+
column_start,
1732+
column_end,
1733+
f"`{name}` has no definition.",
1734+
)
1735+
1736+
1737+
class RefUndefinedCheck(Check):
1738+
"""Checks whether any uses of `\\ref` or `\\iref` are dangling."""
1739+
1740+
SECTION_PATTERN = re.compile(r"\\rSec[0-9]\[([^\]]+)\]")
1741+
DEFINITION_PATTERN = re.compile(r"\\definition\{[^\}]*?\}\{(.+?)\}")
1742+
ETC_PATTERN = re.compile(r"\\(?:infannex|normannex|label)\{(.+?)\}")
1743+
DEFINING_PATTERNS = [SECTION_PATTERN, DEFINITION_PATTERN, ETC_PATTERN]
1744+
1745+
# TODO: Excluding `:` means that `tab:...` references are currently unchecked.
1746+
# Maybe that could be added in the future,
1747+
# but there are lots of different kinds of tables that could be defining.
1748+
REF_IREF_PATTERN = re.compile(r"\\i?ref\{([^}\\:]+)\}")
1749+
1750+
def __init__(self, check_id: str):
1751+
self.id = check_id
1752+
self.defined: Set[str] = set()
1753+
self.used: Dict[str, List[Tuple[str, int, int, int]]] = defaultdict(list)
1754+
1755+
def check_line(self, line_num: int, line: str) -> None:
1756+
for defining_pattern in self.DEFINING_PATTERNS:
1757+
for m in defining_pattern.finditer(line):
1758+
self.defined.add(m.group(1))
1759+
for m in self.REF_IREF_PATTERN.finditer(line):
1760+
for usage in m.group(1).split(","):
1761+
self.used[usage.strip()].append(
1762+
(os.path.relpath(self.file_path), line_num, m.start(1), m.end(1))
1763+
)
1764+
1765+
def end_checks(self) -> None:
1766+
for name, locations in sorted(self.used.items()):
1767+
if name not in self.defined:
1768+
file_name, line_num, column_start, column_end = locations[0]
1769+
emit_check_failure(
1770+
self,
1771+
file_name,
1772+
line_num,
1773+
column_start,
1774+
column_end,
17231775
f"`{name}` has no definition.",
17241776
)
17251777

@@ -1895,6 +1947,7 @@ def end_checks(self) -> None:
18951947
SubclausesWithoutSiblingsCheck("base-lonely-subclause"),
18961948
UnknownCommandCheck("base-unknown-command"),
18971949
SectionSelfReferenceCheck("base-self-ref"),
1950+
RefUndefinedCheck("base-ref-undef"),
18981951
UseOfUndefinedCheck(
18991952
"base-grammarterm-undef",
19001953
definition_pattern=re.compile(r"\\nontermdef\{([^}]+)\}"),

tools/test-checks.tex

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,14 @@
7575
\tcode{\exposid{kebak-case}}
7676

7777
%EXPECTCHECKNEXTLINE(base-ref-in-parens)
78-
See the section.(\ref{some.label})
78+
See the section.(\ref{expr.const})
7979
%EXPECTCHECKNEXTLINE(base-ref-in-parens)
80-
Also see (\ref{other.label}) for details.
80+
Also see (\ref{expr.const}) for details.
8181

8282
%EXPECTCHECKNEXTLINE(base-iref-location)
83-
\iref{some.label} at start of line.
83+
\iref{expr.const} at start of line.
8484
%EXPECTCHECKNEXTLINE(base-iref-location)
85-
See \iref{some.label} for details.
85+
See \iref{expr.const} for details.
8686

8787
%EXPECTCHECKNEXTLINE(base-xrefc)
8888
ISO C 7.42.0 specifies this.
@@ -190,6 +190,9 @@
190190
%EXPECTCHECKNEXTLINE(base-self-ref)
191191
More text\iref{test.selfref2} there.
192192

193+
%EXPECTCHECKNEXTLINE(base-ref-undef)
194+
\ref{lex.whatever}
195+
193196
%EXPECTCHECKNEXTLINE(base-grammarterm-undef)
194197
\grammarterm{test-nonexistent-grammar-term}
195198
%EXPECTCHECKNEXTLINE(base-grammarterm-undef)

0 commit comments

Comments
 (0)