diff options
author | Owen Pan <owenpiano@gmail.com> | 2024-02-20 21:51:51 -0800 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-02-20 21:51:51 -0800 |
commit | 04fbc461e0fd1c6f2b014761e9c03ca80d17b33b (patch) | |
tree | d9cfa8c462727a05a82e5f63d98738357f372972 | |
parent | ec516ff3e6122069b36f32a6db8bb3dc672133fc (diff) | |
download | llvm-04fbc461e0fd1c6f2b014761e9c03ca80d17b33b.zip llvm-04fbc461e0fd1c6f2b014761e9c03ca80d17b33b.tar.gz llvm-04fbc461e0fd1c6f2b014761e9c03ca80d17b33b.tar.bz2 |
[clang-format] Fix RemoveSemicolon for empty functions (#82278)
Fixes #79833.
-rw-r--r-- | clang/lib/Format/Format.cpp | 23 | ||||
-rw-r--r-- | clang/unittests/Format/FormatTest.cpp | 19 |
2 files changed, 29 insertions, 13 deletions
diff --git a/clang/lib/Format/Format.cpp b/clang/lib/Format/Format.cpp index 2c81512..10ab406 100644 --- a/clang/lib/Format/Format.cpp +++ b/clang/lib/Format/Format.cpp @@ -2261,27 +2261,36 @@ public: 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) @@ -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(); }); } diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp index 24f62af..8282e75 100644 --- a/clang/unittests/Format/FormatTest.cpp +++ b/clang/unittests/Format/FormatTest.cpp @@ -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" |