kunaltyagi / nsiqcppstyle

Cpp style checker in python
GNU General Public License v2.0
26 stars 24 forks source link

Need to support GCC function attribute specifier when specifier follows function name #58

Closed mosherubin closed 1 month ago

mosherubin commented 2 months ago

The Problem

NsiqCppStyle misidentifies the function name, in both declarations and definitions, when GCC's function attribute specifier follows the true function name. In the following code snippets:

static void finiUtracer_1() __attribute__((destructor));
void finiUtracer_2() __attribute__((destructor)) {}

NsiqCppStyle incorrectly identifies the token "__attribute__" as the function name.

This is contrasted by NsiqCppStyle correctly identifying the function name when the GCC attribute specifier precedes the true function name:

static void __attribute__((destructor)) finiUtracer_3();
void __attribute__((destructor)) finiUtracer_4() {}

Technical Background (source: GitHub Copilot)

The __attribute__ modifier in GCC is a way to specify special attributes of functions or variables. This feature allows developers to attach characteristics to function declarations to allow the compiler to perform more error checking or code optimization.

Here are a few examples of what you can do with __attribute__:

Remember that __attribute__ is specific to GCC and may not work with other compilers. If you're writing portable code, you should use it conditionally.

trace-callback.py shows the error

I created a test file ~/junk/test.cpp:

static void finiUtracer_1() __attribute__((destructor));
void finiUtracer_2() __attribute__((destructor)) {}

static void __attribute__((destructor)) finiUtracer_3();
void __attribute__((destructor)) finiUtracer_4() {}

CD-ing to the nsiqcppstyle root folder and running the following command:

/usr/bin/python3.11 trace-callbacks.py ~/junk/test.cpp > ~/junk/test.log

produced a full trace of all NsiqCppStyle callback function parameter lists (the complete output file is attached to this ticket). Here is the relevant output to illustrate this problem:

======================================================================================
Processing:  /homes/mosheru/junk/test.cpp
FileStart     (lexer, filename='test.cpp', dirname='/homes/mosheru/junk')
--------------------------------------------------
Line          (lexer, line='static void finiUtracer_1() __attribute__((destructor));', lineNumber=1)
Token         (lexer,
               contextStack (empty),
               token=LexToken(STATIC,'static',1,1,0, False, None))
Token         (lexer,
               contextStack (empty),
               token=LexToken(VOID,'void',1,8,7, False, None))
Token         (lexer,
               contextStack (empty),
               token=LexToken(ID,'finiUtracer_1',1,13,12, False, None))
Token         (lexer,
               contextStack (1),
                   PARENBLOCK, '', 25, 26
               token=LexToken(LPAREN,'(',1,26,25, False, None))
Token         (lexer,
               contextStack (empty),
               token=LexToken(RPAREN,')',1,27,26, False, None))
FunctionName  (lexer,
               fullName='__attribute__',
               decl='True',
               contextStack (empty),
                   context='None')
Token         (lexer,
               contextStack (empty),
               token=LexToken(FUNCTION,'__attribute__',1,29,28, False, None))
Token         (lexer,
               contextStack (1),
                   PARENBLOCK, '', 41, 54
               token=LexToken(LPAREN,'(',1,42,41, False, None))
Token         (lexer,
               contextStack (2),
                   PARENBLOCK, '', 41, 54
                   PARENBLOCK, '', 42, 53
               token=LexToken(LPAREN,'(',1,43,42, False, None))
Token         (lexer,
               contextStack (empty),
               token=LexToken(ID,'destructor',1,44,43, False, None))
Token         (lexer,
               contextStack (1),
                   PARENBLOCK, '', 41, 54
               token=LexToken(RPAREN,')',1,54,53, False, None))
Token         (lexer,
               contextStack (empty),
               token=LexToken(RPAREN,')',1,55,54, False, None))
Token         (lexer,
               contextStack (empty),
               token=LexToken(SEMI,';',1,56,55, False, None))

The true function name ("finiUtracer_1") is identified as a regular token:

Token         (lexer,
               contextStack (empty),
               token=LexToken(ID,'finiUtracer_1',1,13,12, False, None))

while the "__attribute__" value is identified as the FunctionName:

FunctionName  (lexer,
               fullName='__attribute__',
               decl='True',
               contextStack (empty),
                   context='None')

Thoughts

I have not investigated NsiqCppStyle's parser code, but I would wager that the function attribute specifier ("\_\_attribute\_\_((destructor))") looks too much like a function name and a parenthesized argument list. NsiqCppStyle might be taking the latest such match on the line as the function name.