FazBrowse GitHub Viewer | Trending |
URL:
| Home
Tools: [Download Repo ZIP]   [Original HTTPS Page]

fix(iwyu): suggest C headers instead of C++ ones for C files · cpplint/cpplint@6858cbe · GitHub

Commit 6858cbe

Browse files
committed
fix(iwyu): suggest C headers instead of C++ ones for C files
For C files (.c/.cu or LINT_C_FILE), build/include_what_you_use suggested the C++ C-library header (e.g. <cstdio> for printf). It now suggests the C equivalent (<stdio.h>). The C-file determination is factored into _IsCFile (single source, shared by ProcessGlobalSuppressions and the IWYU check) and threaded into CheckForIncludeWhatYouUse as an is_c_file parameter rather than a module global. A whitelist maps the C++ C-library headers to their C counterparts, so non-C-library <c...> headers and C++ files are unaffected. Fixes #399
1 parent 96db7ae commit 6858cbe

3 files changed

Lines changed: 84 additions & 10 deletions

File tree

‎CHANGELOG.rst‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ TBA
77

88
* Fixed a whitespace/newline false positive for control conditions containing lambdas. (#410)
99
* We now error on relative include paths (``./``, ``../``). (#432)
10+
* For C files, build/include_what_you_use now suggests the C header (e.g. ``<stdio.h>``) instead of its C++ counterpart (e.g. ``<cstdio>``). (#399)
1011
* This makes ``#include "./foo.h"`` produce two separate errors: that foo.cpp should include foo.h and that relative paths are not allowed.
1112

1213
2.0.2 (2025-04-08)

‎cpplint.py‎

Lines changed: 55 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -916,6 +916,45 @@
916916
r"vim?:\s*.*(\s*|:)filetype=c(\s*|:|$))"
917917
)
918918

919+
# Maps the C++ C-library headers to their C equivalents so that, for C files,
920+
# build/include_what_you_use suggests the C header (e.g. <stdio.h> rather than
921+
# <cstdio>). See https://github.com/cpplint/cpplint/issues/399.
922+
_C_LIBRARY_CPP_HEADERS = {
923+
"<cassert>": "<assert.h>",
924+
"<cctype>": "<ctype.h>",
925+
"<cerrno>": "<errno.h>",
926+
"<cfenv>": "<fenv.h>",
927+
"<cfloat>": "<float.h>",
928+
"<cinttypes>": "<inttypes.h>",
929+
"<climits>": "<limits.h>",
930+
"<clocale>": "<locale.h>",
931+
"<cmath>": "<math.h>",
932+
"<csetjmp>": "<setjmp.h>",
933+
"<csignal>": "<signal.h>",
934+
"<cstdarg>": "<stdarg.h>",
935+
"<cstddef>": "<stddef.h>",
936+
"<cstdint>": "<stdint.h>",
937+
"<cstdio>": "<stdio.h>",
938+
"<cstdlib>": "<stdlib.h>",
939+
"<cstring>": "<string.h>",
940+
"<ctime>": "<time.h>",
941+
"<cuchar>": "<uchar.h>",
942+
"<cwchar>": "<wchar.h>",
943+
"<cwctype>": "<wctype.h>",
944+
}
945+
946+
947+
def _IsCFile(filename: str, lines: list[str]) -> bool:
948+
"""Whether the file is a C file: a .c/.cu extension or a LINT_C_FILE marker.
949+
950+
Single source of truth shared by the C-specific error suppression and the
951+
C-vs-C++ header suggestion, so the two never drift. See #399.
952+
"""
953+
return filename.lower().endswith((".c", ".cu")) or any(
954+
_SEARCH_C_FILE.search(line) for line in lines
955+
)
956+
957+
919958
# Match string that indicates we're working on a Linux Kernel file.
920959
_SEARCH_KERNEL_FILE = re.compile(r"\b(?:LINT_KERNEL_FILE)")
921960

@@ -1206,10 +1245,10 @@ def ProcessGlobalSuppressions(filename: str, lines: list[str]) -> None:
12061245
last element being empty if the file is terminated with a newline.
12071246
filename: str, the name of the input file.
12081247
"""
1248+
if _IsCFile(filename, lines):
1249+
for category in _DEFAULT_C_SUPPRESSED_CATEGORIES:
1250+
_error_suppressions.AddGlobalSuppression(category)
12091251
for line in lines:
1210-
if _SEARCH_C_FILE.search(line) or filename.lower().endswith((".c", ".cu")):
1211-
for category in _DEFAULT_C_SUPPRESSED_CATEGORIES:
1212-
_error_suppressions.AddGlobalSuppression(category)
12131252
if _SEARCH_KERNEL_FILE.search(line):
12141253
for category in _DEFAULT_KERNEL_SUPPRESSED_CATEGORIES:
12151254
_error_suppressions.AddGlobalSuppression(category)
@@ -7119,7 +7158,7 @@ def FilesBelongToSameModule(filename_cc, filename_h):
71197158
return files_belong_to_same_module, common_path
71207159

71217160

7122-
def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, io=codecs):
7161+
def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, io=codecs, is_c_file=False):
71237162
"""Reports for missing stl includes.
71247163
71257164
This function will output warnings to make sure you are including the headers
@@ -7183,12 +7222,19 @@ def CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error, io=co
71837222
if header_stripped not in include_dict and not (
71847223
header_stripped[0] == "c" and (header_stripped[1:] + ".h") in include_dict
71857224
):
7225+
# For C files, suggest the C header (e.g. <stdio.h>) rather than
7226+
# the C++ one (e.g. <cstdio>). See #399.
7227+
suggested = (
7228+
_C_LIBRARY_CPP_HEADERS[header]
7229+
if is_c_file and header in _C_LIBRARY_CPP_HEADERS
7230+
else header
7231+
)
71867232
error(
71877233
filename,
71887234
required[header][0],
71897235
"build/include_what_you_use",
71907236
4,
7191-
"Add #include " + header + " for " + template,
7237+
"Add #include " + suggested + " for " + template,
71927238
)
71937239

71947240

@@ -7578,7 +7624,10 @@ def ProcessFileData(filename, file_extension, lines, error, extra_check_function
75787624
"NONLINT block never ended",
75797625
)
75807626

7581-
CheckForIncludeWhatYouUse(filename, clean_lines, include_state, error)
7627+
is_c_file = _IsCFile(filename, clean_lines.raw_lines)
7628+
CheckForIncludeWhatYouUse(
7629+
filename, clean_lines, include_state, error, is_c_file=is_c_file
7630+
)
75827631

75837632
# Check that the .cc file has included its header if it exists.
75847633
if _IsSourceExtension(file_extension):

‎cpplint_unittest.py‎

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,8 @@ def PerformIncludeWhatYouUse(self, code, filename="foo.h", io=codecs):
223223
error_collector = ErrorCollector(self.assertTrue)
224224
include_state = cpplint._IncludeState()
225225
nesting_state = cpplint.NestingState()
226-
lines = code.split("\n")
226+
raw_lines = code.split("\n")
227+
lines = raw_lines
227228
cpplint.RemoveMultiLineComments(filename, lines, error_collector)
228229
lines = cpplint.CleansedLines(lines)
229230
for i in range(lines.NumLines()):
@@ -235,7 +236,10 @@ def PerformIncludeWhatYouUse(self, code, filename="foo.h", io=codecs):
235236
# have language problems.
236237

237238
# Second, look for missing includes.
238-
cpplint.CheckForIncludeWhatYouUse(filename, lines, include_state, error_collector, io)
239+
is_c_file = cpplint._IsCFile(filename, raw_lines)
240+
cpplint.CheckForIncludeWhatYouUse(
241+
filename, lines, include_state, error_collector, io, is_c_file=is_c_file
242+
)
239243
return error_collector.Results()
240244

241245
# Perform lint and make sure one of the errors is what we want
@@ -259,8 +263,8 @@ def TestMultiLineLintRE(self, code, expected_message_re):
259263
def TestLanguageRulesCheck(self, file_name, code, expected_message):
260264
assert expected_message == self.PerformLanguageRulesCheck(file_name, code)
261265

262-
def TestIncludeWhatYouUse(self, code, expected_message):
263-
assert expected_message == self.PerformIncludeWhatYouUse(code)
266+
def TestIncludeWhatYouUse(self, code, expected_message, filename="foo.h"):
267+
assert expected_message == self.PerformIncludeWhatYouUse(code, filename=filename)
264268

265269
def TestBlankLinesCheck(self, lines, start_errors, end_errors):
266270
for extension in ["c", "cc", "cpp", "cxx", "c++", "cu"]:
@@ -1290,6 +1294,26 @@ def testIncludeWhatYouUse(self):
12901294
printf("hello world");""",
12911295
"",
12921296
) # Avoid false positives w/ c-style include
1297+
# C files should be told to include the C header (e.g. <stdio.h>) rather
1298+
# than its C++ counterpart (e.g. <cstdio>). See #399.
1299+
self.TestIncludeWhatYouUse(
1300+
'printf("hello world");',
1301+
"Add #include <stdio.h> for printf [build/include_what_you_use] [4]",
1302+
filename="foo.c",
1303+
)
1304+
# C++ files are unaffected.
1305+
self.TestIncludeWhatYouUse(
1306+
'printf("hello world");',
1307+
"Add #include <cstdio> for printf [build/include_what_you_use] [4]",
1308+
filename="foo.cpp",
1309+
)
1310+
# The "C header already included" behaviour is preserved for C files.
1311+
self.TestIncludeWhatYouUse(
1312+
"""#include <stdio.h>
1313+
printf("hello world");""",
1314+
"",
1315+
filename="foo.c",
1316+
)
12931317
self.TestIncludeWhatYouUse(
12941318
"void a(const string &foobar);",
12951319
"Add #include <string> for string [build/include_what_you_use] [4]",

0 commit comments

Comments
 (0)

Back | FazBrowse Home | New Git URL