tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

commit 60e21469f7c368c2780b0c62b6927e8aed873447
parent 9eb9358a1ef702c07b6b7f5df44501a6163f3038
Author: serge-sans-paille <sguelton@mozilla.com>
Date:   Wed,  7 Jan 2026 06:31:45 +0000

Bug 2007348 - Minimal C api linter r=ahal

Make sure stdio.h and stdlib.h are not included when they are not
actually used.

Differential Revision: https://phabricator.services.mozilla.com/D277318

Diffstat:
Mtools/lint/includes/__init__.py | 37+++++++++++++++++++++++++++++++++++++
Mtools/lint/includes/std.py | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Atools/lint/test/files/includes/correct_cstdio.h | 5+++++
Atools/lint/test/files/includes/correct_stdio.h | 5+++++
Atools/lint/test/files/includes/incorrect_cstdio.h | 5+++++
Atools/lint/test/files/includes/incorrect_stdio.h | 5+++++
Mtools/lint/test/test_includes.py | 20++++++++++++++++++++
7 files changed, 226 insertions(+), 0 deletions(-)

diff --git a/tools/lint/includes/__init__.py b/tools/lint/includes/__init__.py @@ -11,6 +11,7 @@ from mozlint import result from mozlint.pathutils import expand_exclusions from .std import api as std_api +from .std import capi as std_capi here = os.path.dirname(__file__) with open(os.path.join(here, "..", "..", "..", "mfbt", "api.yml")) as fd: @@ -133,6 +134,41 @@ def lint_std_headers(results, path, raw_content, config, fix): ) +def lint_cstd_headers(results, path, raw_content, config, fix): + symbol_pattern = r"\b((std)?::)?{}\b" + + for header, symbols in std_capi.items(): + headerline = rf"#\s*include <({header}|c{header[:-2]})>" + if not (match := re.search(headerline, raw_content)): + continue + if re.search( + "|".join(symbol_pattern.format(symbol) for symbol in symbols), raw_content + ): + continue + + msg = ( + f"{path} includes <{match.group(1)}> but does not reference any of its API" + ) + lineno = 1 + raw_content.count("\n", 0, match.start()) + + if fix: + fix_includes(path, raw_content, lineno) + results["fixed"] += 1 + else: + diff = generate_diff(path, raw_content, lineno) + + results["results"].append( + result.from_config( + config, + path=path, + message=msg, + level="error", + lineno=lineno, + diff=diff, + ) + ) + + def lint(paths, config, **lintargs): results = {"results": [], "fixed": 0} paths = list(expand_exclusions(paths, config, lintargs["root"])) @@ -147,5 +183,6 @@ def lint(paths, config, **lintargs): lint_mfbt_headers(results, path, raw_content, config, fix) lint_std_headers(results, path, raw_content, config, fix) + lint_cstd_headers(results, path, raw_content, config, fix) return results diff --git a/tools/lint/includes/std.py b/tools/lint/includes/std.py @@ -422,3 +422,152 @@ api = { api["type_traits"].extend( [f"{k}_v" for k in api["type_traits"]] + [f"{k}_t" for k in api["type_traits"]] ) + +capi = { + "stdio.h": [ + # macros + "BUFSIZ", + "EOF", + "FILENAME_MAX", + "FOPEN_MAX", + "L_ctermid", + "L_cuserid", + "L_tmpnam", + "NULL", + "SEEK_CUR", + "SEEK_END", + "SEEK_SET", + "TMP_MAX", + "clearerr", + "feof", + "ferror", + "fileno", + "getc", + "getchar", + "putc", + "putchar", + "stderr", + "stdin", + "stdout", + # typedef + "FILE", + # functions + "clearerr", + "fclose", + "fdopen", + "feof", + "ferror", + "fflush", + "fgetc", + "fgetpos", + "fgets", + "fileno", + "fmemopen", + "fopen", + "fopencookie", + "fprintf", + "fpurge", + "fputc", + "fputs", + "fread", + "freopen", + "fscanf", + "fseek", + "fseeko", + "fsetpos", + "ftell", + "ftello", + "fwrite", + "getc", + "getchar", + "gets", + "getw", + "mktemp", + "open_memstream", + "open_wmemstream", + "perror", + "printf", + "putc", + "putchar", + "puts", + "putw", + "remove", + "rewind", + "scanf", + "setbuf", + "setbuffer", + "setlinebuf", + "setvbuf", + "snprintf", + "snwprintf", + "sprintf", + "sscanf", + "strerror", + "sys_errlist", + "sys_nerr", + "tempnam", + "tmpfile", + "tmpnam", + "ungetc", + "vfprintf", + "vfscanf", + "vprintf", + "vscanf", + "vsnprintf", + "vsprintf", + "vsscanf", + ], + "stdlib.h": [ + "_Exit", + "_exit", + "_wtoi", + "_wtoi_l", + "abort", + "abs", + "aligned_alloc", + "at_quick_exit", + "atexit", + "atof", + "atoi", + "atol", + "atoll", + "bsearch", + "call_once", + "calloc", + "div", + "div_t", + "exit", + "free", + "free_aligned_sized", + "free_sized", + "getenv", + "labs", + "ldiv", + "llabs", + "lldiv", + "malloc", + "mblen", + "mbstowcs", + "mbtowc", + "memalignment", + "putenv", + "qsort", + "quick_exit", + "rand", + "realloc", + "srand", + "strfromd", + "strfromf", + "strfroml", + "strtod", + "strtof", + "strtol", + "strtold", + "strtoll", + "strtoul", + "strtoull", + "system", + "wcstombs", + "wctomb", + ], +} diff --git a/tools/lint/test/files/includes/correct_cstdio.h b/tools/lint/test/files/includes/correct_cstdio.h @@ -0,0 +1,5 @@ +#include <cstdio> + +auto foo() { + fputs("yeah"); +} diff --git a/tools/lint/test/files/includes/correct_stdio.h b/tools/lint/test/files/includes/correct_stdio.h @@ -0,0 +1,5 @@ +#include <stdio.h> + +auto foo() { + fputs("yeah"); +} diff --git a/tools/lint/test/files/includes/incorrect_cstdio.h b/tools/lint/test/files/includes/incorrect_cstdio.h @@ -0,0 +1,5 @@ +#include <cstdio> + +auto foo() { + return 1; +} diff --git a/tools/lint/test/files/includes/incorrect_stdio.h b/tools/lint/test/files/includes/incorrect_stdio.h @@ -0,0 +1,5 @@ +#include <stdio.h> + +auto foo() { + return 0; +} diff --git a/tools/lint/test/test_includes.py b/tools/lint/test/test_includes.py @@ -91,5 +91,25 @@ def test_lint_std_includes(lint, paths): ) +def test_lint_c_std_includes(lint, paths): + results = lint(paths("correct_stdio.h")) + assert not results + + results = lint(paths("correct_cstdio.h")) + assert not results + + results = lint(paths("incorrect_stdio.h")) + assert len(results) == 1 + assert results[0].message.endswith( + "incorrect_stdio.h includes <stdio.h> but does not reference any of its API" + ) + + results = lint(paths("incorrect_cstdio.h")) + assert len(results) == 1 + assert results[0].message.endswith( + "incorrect_cstdio.h includes <cstdio> but does not reference any of its API" + ) + + if __name__ == "__main__": mozunit.main()