Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

clang-format adds a space after not inside macros #78166

Closed
ilya-biryukov opened this issue Jan 15, 2024 · 9 comments · Fixed by #90161
Closed

clang-format adds a space after not inside macros #78166

ilya-biryukov opened this issue Jan 15, 2024 · 9 comments · Fixed by #90161
Assignees
Labels
clang-format invalid-code-generation Tool (e.g. clang-format) produced invalid code that no longer compiles

Comments

@ilya-biryukov
Copy link
Contributor

Expected formatting:

#define ASSEMBLER_INSTRUCTION_LIST(V) \
  V(and)                              \
  V(not)                              \
  V(other)

Actual formatting:

#define ASSEMBLER_INSTRUCTION_LIST(V) \
  V(and)                              \
  V(not )                             \
  V(other)

This can even change the meaning of some code in rare circumstances:

#define ASSEMBLER_INSTRUCTION_LIST(V) \
    V(and)                            \
    V(not !) /* was not! before formatting*/ \
    V(other)

#define STRINGIFY(X) #X

const char* foo = ASSEMBLER_INSTRUCTION_LIST(STRINGIFY);
/* foo was "andnot!other" before formatting 
    and is "andnot !other" after formatting */
@llvmbot
Copy link
Collaborator

llvmbot commented Jan 15, 2024

@llvm/issue-subscribers-clang-format

Author: Ilya Biryukov (ilya-biryukov)

Expected formatting: ```cpp #define ASSEMBLER_INSTRUCTION_LIST(V) \ V(and) \ V(not) \ V(other) ```

Actual formatting:

#define ASSEMBLER_INSTRUCTION_LIST(V) \
  V(and)                              \
  V(not )                             \
  V(other)

This can even change the meaning of some code in rare circumstances:

#define ASSEMBLER_INSTRUCTION_LIST(V) \
    V(and)                            \
    V(not !) /* was not! before formatting*/ \
    V(other)

#define STRINGIFY(X) #X

const char* foo = ASSEMBLER_INSTRUCTION_LIST(STRINGIFY);
/* foo was "andnot!other" before formatting 
    and is "andnot !other" after formatting */

@rymiel rymiel added the invalid-code-generation Tool (e.g. clang-format) produced invalid code that no longer compiles label Jan 15, 2024
@rymiel
Copy link
Member

rymiel commented Jan 15, 2024

Actually, not sure if this is invalid-code-generation, I think this is why the whitespace senitive macros option exist (not sure what exactly the policy is, someone else feel free to remove the tag)
It's still a formatting bug though

@mydeveloperday
Copy link
Contributor

AnnotatedTokens(L=1, P=0, T=5, C=0):
 M=0 C=0 T=FunctionLikeOrFreestandingMacro S=1 F=0 B=0 BK=0 P=0 Name=identifier L=1 PPK=2 FakeLParens= FakeRParens=0 II=0x22cff6623a8 Text='V'
 M=0 C=0 T=Unknown S=0 F=0 B=0 BK=0 P=23 Name=l_paren L=2 PPK=2 FakeLParens= FakeRParens=0 II=0x0 Text='('
 M=0 C=1 T=UnaryOperator S=0 F=0 B=0 BK=0 P=59 Name=exclaim L=5 PPK=2 FakeLParens=0/ FakeRParens=1 II=0x22cff66e8b0 Text='not'
 M=0 C=0 T=Unknown S=1 F=0 B=0 BK=0 P=100 Name=r_paren L=7 PPK=2 FakeLParens= FakeRParens=0 II=0x0 Text=')'

Space before the ) indicated by S=1

@mydeveloperday
Copy link
Contributor

AnnotatedTokens(L=1, P=0, T=5, C=0):
 M=0 C=0 T=FunctionLikeOrFreestandingMacro S=1 F=0 B=0 BK=0 P=0 Name=identifier L=1 PPK=2 FakeLParens= FakeRParens=0 II=0x207d9b42748 Text='V'
 M=0 C=0 T=Unknown S=0 F=0 B=0 BK=0 P=23 Name=l_paren L=2 PPK=2 FakeLParens= FakeRParens=0 II=0x0 Text='('
 M=0 C=1 T=UnaryOperator S=0 F=0 B=0 BK=0 P=59 Name=exclaim L=5 PPK=2 FakeLParens=0/ FakeRParens=0 II=0x207d9b3b5a0 Text='not'
 M=0 C=0 T=UnaryOperator S=1 F=0 B=0 BK=0 P=100 Name=exclaim L=7 PPK=2 FakeLParens=0/ FakeRParens=2 II=0x0 Text='!'
 M=0 C=0 T=Unknown S=0 F=0 B=0 BK=0 P=100 Name=r_paren L=8 PPK=2 FakeLParens= FakeRParens=0 II=0x0 Text=')'

S=1 for ! in V(not!) case

@mydeveloperday
Copy link
Contributor

its comes down to this piece of code

if (Left.is(TT_UnaryOperator)) {
    if (Right.isNot(tok::l_paren)) {
      // The alternative operators for ~ and ! are "compl" and "not".
      // If they are used instead, we do not want to combine them with
      // the token to the right, unless that is a left paren.
      if (Left.is(tok::exclaim) && Left.TokenText == "not")
        return true;
      if (Left.is(tok::tilde) && Left.TokenText == "compl")
        return true;
      // Lambda captures allow for a lone &, so "&]" needs to be properly
      // handled.
      if (Left.is(tok::amp) && Right.is(tok::r_square))
        return Style.SpacesInSquareBrackets;
    }
    return (Style.SpaceAfterLogicalNot && Left.is(tok::exclaim)) ||
           Right.is(TT_BinaryOperator);

@mydeveloperday
Copy link
Contributor

its purpose was to prevent this failure in the test

int a not 5; 

becoming:

int a not5;

I think we can probably resolve this by adding extra logic

@mydeveloperday
Copy link
Contributor

if (!Right.isOneOf(tok::r_paren, tok::l_paren, tok::exclaim)) {

would fix this use case

@danleh
Copy link

danleh commented Mar 11, 2024

Friendly ping: Is there something blocking your proposed fix in

if (!Right.isOneOf(tok::r_paren, tok::l_paren, tok::exclaim)) {

would fix this use case

@ilya-biryukov
Copy link
Contributor Author

@danleh check out the discussion in #78176, there's no final conclusion there yet (although I think everyone agrees there should not be a space before ).

As for the codegen issues, WhitespaceSensitiveMacros configuration can be used to workaround the issue (although in this case, we'd have to put V, which does not look like a great idea)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang-format invalid-code-generation Tool (e.g. clang-format) produced invalid code that no longer compiles
Projects
None yet
6 participants