diff --git a/CHANGELOG.rst b/CHANGELOG.rst index c8429e9..9a77560 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -6,6 +6,10 @@ TBA === * Fixed a whitespace/newline false positive for control conditions containing lambdas. (#410) +* Fixed false positives in readability/casting for function-pointer typedefs and alias + declarations that use a calling-convention macro before ``*``, including array declarators + (e.g. ``typedef int32_t(CALLCONV* Func)(void);`` and + ``typedef int32_t(CALLCONV* FuncArray[3])(void);``). (https://github.com/cpplint/cpplint/issues/409) * We now error on relative include paths (``./``, ``../``). (#432) * This makes ``#include "./foo.h"`` produce two separate errors: that foo.cpp should include foo.h and that relative paths are not allowed. diff --git a/cpplint.py b/cpplint.py index 41c3bb0..4957453 100755 --- a/cpplint.py +++ b/cpplint.py @@ -6651,7 +6651,8 @@ def CheckCasts(filename, clean_lines, linenum, error): return # Other things to ignore: - # - Function pointers + # - Function pointers (including calling-convention forms like + # (CALLCONV* name)(args) and (CALLCONV *)(args)) # - Casts to pointer types # - Placement new # - Alias declarations @@ -6662,6 +6663,7 @@ def CheckCasts(filename, clean_lines, linenum, error): matched_funcptr and ( re.match(r"\((?:[^() ]+::\s*\*\s*)?[^() ]+\)\s*\(", matched_funcptr) + or re.match(r"\((?:\w+\s*)?\*\s*[^() ]*\)\s*\(", matched_funcptr) or matched_funcptr.startswith("(*)") ) ) @@ -6803,6 +6805,12 @@ def CheckCStyleCast(filename, clean_lines, linenum, cast_type, pattern, error): if re.match(r"^\s*(?:;|const\b|throw\b|final\b|override\b|[=>{),]|->)", remainder): return False + # Function-pointer types in typedef/alias declarations can look like + # pointer casts followed by a parameter list, e.g. + # using Func = int32_t(CALLCONV *)(void); + if re.search(r"\b(?:typedef|using)\b", line) and re.match(r"^\s*\(", remainder): + return False + # At this point, all that should be left is actual casts. error( filename, diff --git a/cpplint_unittest.py b/cpplint_unittest.py index 032ecc5..58f6151 100755 --- a/cpplint_unittest.py +++ b/cpplint_unittest.py @@ -1116,6 +1116,21 @@ def testDeprecatedCast(self): self.TestLint("typedef set SortedIdSet", "") self.TestLint("bool TraverseNode(T *Node, bool(VisitorBase:: *traverse) (T *t)) {}", "") + # Function pointers with a calling-convention macro before '*' should + # not be flagged as deprecated/C-style casts. (https://github.com/cpplint/cpplint/issues/409) + self.TestLint( + "typedef int32_t(NAPI_CDECL* node_api_addon_get_api_version_func)(void);", + "", + ) + self.TestLint( + "using node_api_addon_get_api_version_func = int32_t(NAPI_CDECL *)(void);", + "", + ) + self.TestLint("typedef int32_t(CALLCONV* Func)(int);", "") + self.TestLint("using Func = int32_t(CALLCONV *)(int, int);", "") + self.TestLint("typedef int32_t(CALLCONV* FuncArray[3])(void);", "") + self.TestLint("typedef int32_t(*FuncArray[3])(void);", "") + # The second parameter to a gMock method definition is a function signature # that often looks like a bad cast but should not picked up by lint. def testMockMethod(self):