Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions addons/threadsafety.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,12 @@
'drand48',
'ecvt',
'elf_fill',
'elf_flagdata',
'elf_flagehdr',
'elf_flagelf',
'elf_flagphdr',
'elf_flagscn',
'elf_flagshdr',
'encrypt',
'endfsent',
'endgrent',
Expand Down
69 changes: 69 additions & 0 deletions test/tools/MT-Unsafe.3
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
.TH MT-Unsafe 3 2026-06-21 "CppCheck" "CppCheck Developer Tools"
.SH NAME
MT-Unsafe.py

.SH DESCRIPTION
This man page does
.BI not
document real functions
(although some of the functions it names
.I are
real).

This man page is test data for the
.BI tools/MT-Unsafe.py
script, which is used to convert man pages into data for the
.B cppcheck
addon
.I threadsafety.py\fR.
The output should match
.I test/tools/MT-Unsafe.3.expected\fR.
E.g.:

.I tools/MT-Unsafe.py test/tools/MT-Unsafe.3 | diff - test/tools/MT-Unsafe.3.expected

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

Nothing runs this comparison: no pytest or CI step uses MT-Unsafe.3 / MT-Unsafe.3.expected, so it is only a manual check. A small pytest that runs the script and compares its stdout with the expected file (as danmar suggested for addons/test) would make it a real regression test. Note that running it on a gzipped copy of this page gives different output: the '' type entry is missing.

should produce no output.

.\ .SH SEE ALSO

.SH ATTRIBUTES
For an explanation of the terms used in this section, see
.BR attributes (7).

.TS
allbox;
lbx lb lb
l l l.
Interface Attribute Value
T{
.na
.nh
.BR elf_flagelf (),\~elf_flagehdr(),\~elf_flagphdr(),\~elf_flagscn(),\~elf_flagshdr(),\~elf_flagdata()
T} Thread safety MT-Unsafe
T{
.na
.nh
.BR safe_function (),\~second_safe_function(),third_function()
T} Thread safety MT-Safe
T{
.na
.nh
.BR race_function (),
.BR second_race_function ()
T} Thread safety MT-Unsafe race:argument
.TE


.\ .SH STANDARDS

.\ .SH HISTORY
.\ Original version Andrew C. Aitchison 2026-06-21

.SH AUTHORS
.PP
\fBAndrew Aitchison\fR <\&github@aitchison\&.me\&.uk\&>
.RS 4
Wrote MT-Unsafe.py and this man page\&.
.RE
.SH COPYRIGHT
Copyright (C) 2026 Cppcheck team.
.\ .SH NOTES
15 changes: 15 additions & 0 deletions test/tools/MT-Unsafe.3.expected
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
# Types marked MT-Unsafe
'',
'race:argument',
# APIs marked MT-Unsafe
'elf_flagdata',
'elf_flagehdr',
'elf_flagelf',
'elf_flagphdr',
'elf_flagscn',
'elf_flagshdr',
'race_function',
'second_race_function',
}

26 changes: 18 additions & 8 deletions tools/MT-Unsafe.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,25 +85,35 @@ def man_search(manpage):
apis = set()
for lineread in MANPAGE:
lineread = str(lineread)
dprint(2, '%s' % (lineread))
dprint(1, '%s' % (lineread))
if 'MT-Safe' in lineread:
vprint(1, 'clearing MT-Safe %s', lineread)
apis.clear()

res = re.search(r'\.BR\s+(\w+)\s', lineread)
# vprint(1, '%s for %s' % (res, lineread))
if res:
apis.add(res.group(1))
dprint(1, 'found api %s in %s' % (res.group(1), lineread))
continue
if '.BR' in lineread:
function_names = re.sub(r'^.*\.BR ', '', lineread)
dprint(1, 'function_names 1:\n\t%s\n' % (function_names))
function_names = re.sub(r'\\\\~', '', function_names)
dprint(1, 'function_names 1a:\n\t%s\n' % (function_names))
function_names = function_names.replace(r'\n', '')
dprint(1, 'function_names 2:\n\t%s\n' % (function_names))
function_names = re.sub(r"[~() '\\\n]+", '', function_names)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

AI review

This character class only strips ~ ( ) space ' \\ and newline, so other troff syntax ends up in the function names.

  • .BR \%pthread_attr_setaffinity_np (), gives '%pthread_attr_setaffinity_np'. The \% hyphenation escape is common in current man-pages.
  • .BR "strtok" () gives '"strtok"'.
  • .BR foo_r " and " bar () gives 'foo_r"and"bar'.

The old regex just skipped these lines; now they produce invalid identifiers that would be merged into threadsafety.py. Extracting identifiers with something like re.findall(r'[A-Za-z_]\w*(?=\s*\\?\(|\s*"?\s*\(|,|$)', ...), or at least stripping \% and " before the split, would avoid this.

dprint(1, 'function_names 3:\n\t%s\n' % (function_names))
function_names = re.split(',', function_names)
dprint(1, 'function_names split:\n\t%s\n' % (function_names))

for fn in function_names:
if fn:
apis.add(fn)
dprint(1, 'added function "%s"' % fn)

if 'MT-Unsafe' in lineread:
resUnsafe = re.search("MT-Unsafe\\s+(.*)(\\n\'|$)", lineread)

if resUnsafe:
values = resUnsafe.group(1)
dprint(1, 'a %s' % values)
values = re.sub(r'\\n\'$', '', values)
values = re.sub(r"\\n\'$", '', values)
#
values = values.split(' ')
dprint(1, 'values %s' % list(values))
Expand Down
Loading