Skip to content

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

Closed
@mosherubin

Description

@mosherubin

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__:

  • __attribute__((constructor)) and __attribute__((destructor)): These attributes, when used with a function, ensure that the function is called before main() when the program starts, or after main() when the program exits, respectively.

  • __attribute__((deprecated)): This attribute, when used with a function, produces a warning whenever the function is used elsewhere in the code, indicating that the function is deprecated.

  • __attribute__((packed)): This attribute, when used with a structure or union type definition, specifies that each member (after the first) is placed to minimize the memory required.

  • __attribute__((noreturn)): This attribute, when used with a function, tells the compiler that the function does not return. The compiler can then optimize the code with this assumption.

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.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions