Skip to content

Commit

Permalink
[clang-format] Fix RemoveSemicolon for empty functions (#82278)
Browse files Browse the repository at this point in the history
Fixes #79833.
  • Loading branch information
owenca committed Feb 21, 2024
1 parent ec516ff commit 04fbc46
Show file tree
Hide file tree
Showing 2 changed files with 29 additions and 13 deletions.
23 changes: 16 additions & 7 deletions clang/lib/Format/Format.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2261,27 +2261,36 @@ class SemiRemover : public TokenAnalyzer {
FormatTokenLexer &Tokens) override {
AffectedRangeMgr.computeAffectedLines(AnnotatedLines);
tooling::Replacements Result;
removeSemi(AnnotatedLines, Result);
removeSemi(Annotator, AnnotatedLines, Result);
return {Result, 0};
}

private:
void removeSemi(SmallVectorImpl<AnnotatedLine *> &Lines,
void removeSemi(TokenAnnotator &Annotator,
SmallVectorImpl<AnnotatedLine *> &Lines,
tooling::Replacements &Result) {
auto PrecededByFunctionRBrace = [](const FormatToken &Tok) {
const auto *Prev = Tok.Previous;
if (!Prev || Prev->isNot(tok::r_brace))
return false;
const auto *LBrace = Prev->MatchingParen;
return LBrace && LBrace->is(TT_FunctionLBrace);
};
const auto &SourceMgr = Env.getSourceManager();
const auto End = Lines.end();
for (auto I = Lines.begin(); I != End; ++I) {
const auto Line = *I;
removeSemi(Line->Children, Result);
removeSemi(Annotator, Line->Children, Result);
if (!Line->Affected)
continue;
Annotator.calculateFormattingInformation(*Line);
const auto NextLine = I + 1 == End ? nullptr : I[1];
for (auto Token = Line->First; Token && !Token->Finalized;
Token = Token->Next) {
if (!Token->Optional)
continue;
if (Token->isNot(tok::semi))
if (Token->isNot(tok::semi) ||
(!Token->Optional && !PrecededByFunctionRBrace(*Token))) {
continue;
}
auto Next = Token->Next;
assert(Next || Token == Line->Last);
if (!Next && NextLine)
Expand Down Expand Up @@ -3677,7 +3686,7 @@ reformat(const FormatStyle &Style, StringRef Code,
FormatStyle S = Expanded;
S.RemoveSemicolon = true;
Passes.emplace_back([&, S = std::move(S)](const Environment &Env) {
return SemiRemover(Env, S).process(/*SkipAnnotation=*/true);
return SemiRemover(Env, S).process();
});
}

Expand Down
19 changes: 13 additions & 6 deletions clang/unittests/Format/FormatTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26720,13 +26720,20 @@ TEST_F(FormatTest, RemoveSemicolon) {

verifyIncompleteFormat("class C final [[deprecated(l]] {});", Style);

// These tests are here to show a problem that may not be easily
// solved, our implementation to remove semicolons is only as good
// as our FunctionLBrace detection and this fails for empty braces
// because we can't distringuish this from a bracelist.
// We will enable when that is resolved.
#if 0
verifyFormat("void main() {}", "void main() {};", Style);

verifyFormat("struct Foo {\n"
" Foo() {}\n"
" ~Foo() {}\n"
"};",
"struct Foo {\n"
" Foo() {};\n"
" ~Foo() {};\n"
"};",
Style);

// We can't (and probably shouldn't) support the following.
#if 0
verifyFormat("void foo() {} //\n"
"int bar;",
"void foo() {}; //\n"
Expand Down

0 comments on commit 04fbc46

Please sign in to comment.