This is an archive of the discontinued LLVM Phabricator instance.

[clang-format] Fix ObjC keywords following try/catch getting split.
Needs RevisionPublic

Authored by Bigcheese on Dec 9 2019, 5:49 PM.

Details

Summary

Previously things like:

int main() {
  @try {
  } @catch (NSException *e) {
  }

  @try {
  } @catch (NSException *e) {
  }
}

Would be formatted like:

int main() {
  @try {
  } @catch (NSException *e) {
  }

  @
  try {
  } @catch (NSException *e) {
  }
}

because UnwrappedLineParser::parseTryCatch() would consume the @ as
part of checking for another catch or finally. This patch fixes that
by doing a lookahead.

I'm not super happy about the way the lookhead works, but this is the only thing that worked.

Diff Detail

Event Timeline

Bigcheese created this revision.Dec 9 2019, 5:49 PM
Herald added a project: Restricted Project. · View Herald TranscriptDec 9 2019, 5:49 PM
Herald added a subscriber: dexonsmith. · View Herald Transcript

Build result: pass - 60659 tests passed, 0 failed and 726 were skipped.

Log files: console-log.txt, CMakeCache.txt

MyDeveloperDay retitled this revision from [clang][Format] Fix ObjC keywords following try/catch getting split. to [clang-format] Fix ObjC keywords following try/catch getting split..Dec 10 2019, 10:52 AM
MyDeveloperDay added a reviewer: MyDeveloperDay.
clang/lib/Format/UnwrappedLineParser.cpp
1849

can you use FormatTok->getNextNonComment()?

clang/unittests/Format/FormatTestObjC.cpp
200

Nit: Could you not keep the original test? just add a new testcase? I get uncomfortable about changing tests no matter how trivial

Bigcheese marked an inline comment as done.Dec 10 2019, 2:47 PM
Bigcheese added inline comments.
clang/lib/Format/UnwrappedLineParser.cpp
1849

No, Next has not been setup at this point, so it will always return nullptr.

clang/unittests/Format/FormatTestObjC.cpp
200

Is there something special about clang-format that causes this concern? I'm all for testing separate things separately, but the additions to this test are testing the same leaf lines of code.

MyDeveloperDay added inline comments.Dec 11 2019, 7:05 AM
clang/lib/Format/UnwrappedLineParser.cpp
1849

There is something about what we are doing here which doesn't feel correct...If you look in FormatTokenLexer why are we not doing something similar here for @try and @catch as we are doing for @"ABC" (see below tryMergeNSStringLiteral)

The FormatTokenLexer can merge together a number of tokens into a new single token and combined text, after that nothing can split that token, meaning the @ and try will never become separated and will simply be treated as a "@try"

This can be useful because you can set the new setKind() to be "tok::try" after which @try will behave just like a normal try for all rules without the constant need to keep checking if the previous token is an @

bool FormatTokenLexer::tryMergeNSStringLiteral() {
  if (Tokens.size() < 2)
    return false;
  auto &At = *(Tokens.end() - 2);
  auto &String = *(Tokens.end() - 1);
  if (!At->is(tok::at) || !String->is(tok::string_literal))
    return false;
  At->Tok.setKind(tok::string_literal);
  At->TokenText = StringRef(At->TokenText.begin(),
                            String->TokenText.end() - At->TokenText.begin());
  At->ColumnWidth += String->ColumnWidth;
  At->Type = TT_ObjCStringLiteral;
  Tokens.erase(Tokens.end() - 1);
  return true;
}
clang/unittests/Format/FormatTestObjC.cpp
200

as you are scanning back, I'd like to know we haven't broken the previous tests behaviour.

bool FormatTokenLexer::tryMergeAtTry() {
  if (Tokens.size() < 2)
    return false;
  auto &At = *(Tokens.end() - 2);
  auto &Try = *(Tokens.end() - 1);
  if (!At->is(tok::at) || !String->is(tok::try))
    return false;
  At->Tok.setKind(tok::try);
  At->TokenText = StringRef(At->TokenText.begin(),
                            String->TokenText.end() - At->TokenText.begin());
  At->ColumnWidth += String->ColumnWidth;
  Tokens.erase(Tokens.end() - 1);
  return true;
}

bool FormatTokenLexer::tryMergeAtCatch() {
  if (Tokens.size() < 2)
    return false;
  auto &At = *(Tokens.end() - 2);
  auto &Try = *(Tokens.end() - 1);
  if (!At->is(tok::at) || !String->is(tok::catch))
    return false;
  At->Tok.setKind(tok::catch);
  At->TokenText = StringRef(At->TokenText.begin(),
                            String->TokenText.end() - At->TokenText.begin());
  At->ColumnWidth += String->ColumnWidth;
  Tokens.erase(Tokens.end() - 1);
  return true;
}

I think adding these 2 functions could help to always treat @try and @catch the same as you we do try and catch (and any rules they have like BeforeCatch etc..

Did you consider doing that or are you not pursuing this patch any more?

MyDeveloperDay requested changes to this revision.May 5 2020, 3:08 AM
This revision now requires changes to proceed.May 5 2020, 3:08 AM