llvm-project/clang/unittests/Format/NamespaceEndCommentsFixerTest.cpp
Francois Ferrand a98a95cca7 clang-format: fix block OpeningLineIndex around preprocessor
Summary:
The current code would return an incorrect value when a preprocessor
directive is present immediately after the opening brace: this causes
the nanespace end comment fixer to break in some places, for exemple it
would not add the comment in this case:

  namespace a {
  #define FOO
  }

Fixing the computation is simple enough, but it was breaking a feature,
as it would cause comments to be added also when the namespace
declaration was dependant on conditional compilation.

To fix this, a hash of the current preprocessor stack/branches is
computed at the beginning of parseBlock(), so that we explicitely do not
store the OpeningLineIndex when the beginning and end of the block are
not in the same preprocessor conditions.

Tthe hash is computed based on the line, but this could propbably be
improved by using the actual condition, so that clang-format would be
able to match multiple identical #ifdef blocks.

Reviewers: krasimir, djasper

Reviewed By: krasimir

Subscribers: klimek, cfe-commits

Differential Revision: https://reviews.llvm.org/D35483

llvm-svn: 309369
2017-07-28 07:56:14 +00:00

787 lines
29 KiB
C++

//===- NamespaceEndCommentsFixerTest.cpp - Formatting unit tests ----------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
#include "clang/Format/Format.h"
#include "clang/Frontend/TextDiagnosticPrinter.h"
#include "llvm/Support/Debug.h"
#include "gtest/gtest.h"
#define DEBUG_TYPE "namespace-end-comments-fixer-test"
namespace clang {
namespace format {
namespace {
class NamespaceEndCommentsFixerTest : public ::testing::Test {
protected:
std::string
fixNamespaceEndComments(llvm::StringRef Code,
const std::vector<tooling::Range> &Ranges,
const FormatStyle &Style = getLLVMStyle()) {
DEBUG(llvm::errs() << "---\n");
DEBUG(llvm::errs() << Code << "\n\n");
tooling::Replacements Replaces =
clang::format::fixNamespaceEndComments(Style, Code, Ranges, "<stdin>");
auto Result = applyAllReplacements(Code, Replaces);
EXPECT_TRUE(static_cast<bool>(Result));
DEBUG(llvm::errs() << "\n" << *Result << "\n\n");
return *Result;
}
std::string
fixNamespaceEndComments(llvm::StringRef Code,
const FormatStyle &Style = getLLVMStyle()) {
return fixNamespaceEndComments(
Code,
/*Ranges=*/{1, tooling::Range(0, Code.size())}, Style);
}
};
TEST_F(NamespaceEndCommentsFixerTest, AddsEndComment) {
EXPECT_EQ("namespace {\n"
" int i;\n"
" int j;\n"
"}// namespace",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
" int j;\n"
"}"));
EXPECT_EQ("namespace {\n"
" int i;\n"
" int j;\n"
"}// namespace\n",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
" int j;\n"
"}\n"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
" int j;\n"
"}// namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
" int j;\n"
"}"));
EXPECT_EQ("inline namespace A {\n"
" int i;\n"
" int j;\n"
"}// namespace A",
fixNamespaceEndComments("inline namespace A {\n"
" int i;\n"
" int j;\n"
"}"));
EXPECT_EQ("namespace ::A {\n"
" int i;\n"
" int j;\n"
"}// namespace ::A",
fixNamespaceEndComments("namespace ::A {\n"
" int i;\n"
" int j;\n"
"}"));
EXPECT_EQ("namespace ::A::B {\n"
" int i;\n"
" int j;\n"
"}// namespace ::A::B",
fixNamespaceEndComments("namespace ::A::B {\n"
" int i;\n"
" int j;\n"
"}"));
EXPECT_EQ("namespace /**/::/**/A/**/::/**/B/**/ {\n"
" int i;\n"
" int j;\n"
"}// namespace ::A::B",
fixNamespaceEndComments("namespace /**/::/**/A/**/::/**/B/**/ {\n"
" int i;\n"
" int j;\n"
"}"));
EXPECT_EQ("namespace A {\n"
"namespace B {\n"
" int i;\n"
"}\n"
"}// namespace A",
fixNamespaceEndComments("namespace A {\n"
"namespace B {\n"
" int i;\n"
"}\n"
"}"));
EXPECT_EQ("namespace A {\n"
"namespace B {\n"
" int i;\n"
" int j;\n"
"}// namespace B\n"
"}// namespace A",
fixNamespaceEndComments("namespace A {\n"
"namespace B {\n"
" int i;\n"
" int j;\n"
"}\n"
"}"));
EXPECT_EQ("namespace A {\n"
" int a;\n"
" int b;\n"
"}// namespace A\n"
"namespace B {\n"
" int b;\n"
" int a;\n"
"}// namespace B",
fixNamespaceEndComments("namespace A {\n"
" int a;\n"
" int b;\n"
"}\n"
"namespace B {\n"
" int b;\n"
" int a;\n"
"}"));
EXPECT_EQ("namespace A {\n"
" int a1;\n"
" int a2;\n"
"}// namespace A\n"
"namespace A {\n"
" int a2;\n"
" int a1;\n"
"}// namespace A",
fixNamespaceEndComments("namespace A {\n"
" int a1;\n"
" int a2;\n"
"}\n"
"namespace A {\n"
" int a2;\n"
" int a1;\n"
"}"));
EXPECT_EQ("namespace A {\n"
" int a;\n"
" int b;\n"
"}// namespace A\n"
"// comment about b\n"
"int b;",
fixNamespaceEndComments("namespace A {\n"
" int a;\n"
" int b;\n"
"}\n"
"// comment about b\n"
"int b;"));
EXPECT_EQ("namespace A {\n"
"namespace B {\n"
"namespace C {\n"
"namespace D {\n"
"}\n"
"}// namespace C\n"
"}// namespace B\n"
"}// namespace A",
fixNamespaceEndComments("namespace A {\n"
"namespace B {\n"
"namespace C {\n"
"namespace D {\n"
"}\n"
"}\n"
"}\n"
"}"));
// Add comment for namespaces which will be 'compacted'
FormatStyle CompactNamespacesStyle = getLLVMStyle();
CompactNamespacesStyle.CompactNamespaces = true;
EXPECT_EQ("namespace out { namespace in {\n"
"int i;\n"
"int j;\n"
"}}// namespace out::in",
fixNamespaceEndComments("namespace out { namespace in {\n"
"int i;\n"
"int j;\n"
"}}",
CompactNamespacesStyle));
EXPECT_EQ("namespace out {\n"
"namespace in {\n"
"int i;\n"
"int j;\n"
"}\n"
"}// namespace out::in",
fixNamespaceEndComments("namespace out {\n"
"namespace in {\n"
"int i;\n"
"int j;\n"
"}\n"
"}",
CompactNamespacesStyle));
EXPECT_EQ("namespace out { namespace in {\n"
"int i;\n"
"int j;\n"
"};}// namespace out::in",
fixNamespaceEndComments("namespace out { namespace in {\n"
"int i;\n"
"int j;\n"
"};}",
CompactNamespacesStyle));
// Adds an end comment after a semicolon.
EXPECT_EQ("namespace {\n"
" int i;\n"
" int j;\n"
"};// namespace",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
" int j;\n"
"};"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
" int j;\n"
"};// namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
" int j;\n"
"};"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
" int j;\n"
"};// namespace A\n"
"// unrelated",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
" int j;\n"
"};\n"
"// unrelated"));
}
TEST_F(NamespaceEndCommentsFixerTest, AddsNewlineIfNeeded) {
EXPECT_EQ("namespace A {\n"
" int i;\n"
" int j;\n"
"}// namespace A\n"
" int k;",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
" int j;\n"
"} int k;"));
EXPECT_EQ("namespace {\n"
" int i;\n"
" int j;\n"
"}// namespace\n"
" int k;",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
" int j;\n"
"} int k;"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
" int j;\n"
"}// namespace A\n"
" namespace B {\n"
" int j;\n"
" int k;\n"
"}// namespace B",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
" int j;\n"
"} namespace B {\n"
" int j;\n"
" int k;\n"
"}"));
EXPECT_EQ("namespace {\n"
" int i;\n"
" int j;\n"
"};// namespace\n"
"int k;",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
" int j;\n"
"};int k;"));
EXPECT_EQ("namespace {\n"
" int i;\n"
" int j;\n"
"};// namespace\n"
";",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
" int j;\n"
"};;"));
}
TEST_F(NamespaceEndCommentsFixerTest, DoesNotAddEndCommentForShortNamespace) {
EXPECT_EQ("namespace {}", fixNamespaceEndComments("namespace {}"));
EXPECT_EQ("namespace A {}", fixNamespaceEndComments("namespace A {}"));
EXPECT_EQ("namespace A { a }",
fixNamespaceEndComments("namespace A { a }"));
EXPECT_EQ("namespace A { a };",
fixNamespaceEndComments("namespace A { a };"));
}
TEST_F(NamespaceEndCommentsFixerTest, DoesNotAddCommentAfterUnaffectedRBrace) {
EXPECT_EQ("namespace A {\n"
" int i;\n"
"}",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"}",
// The range (16, 3) spans the 'int' above.
/*Ranges=*/{1, tooling::Range(16, 3)}));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"};",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"};",
// The range (16, 3) spans the 'int' above.
/*Ranges=*/{1, tooling::Range(16, 3)}));
}
TEST_F(NamespaceEndCommentsFixerTest, DoesNotAddCommentAfterRBraceInPPDirective) {
EXPECT_EQ("#define SAD \\\n"
"namespace A { \\\n"
" int i; \\\n"
"}",
fixNamespaceEndComments("#define SAD \\\n"
"namespace A { \\\n"
" int i; \\\n"
"}"));
}
TEST_F(NamespaceEndCommentsFixerTest, KeepsValidEndComment) {
EXPECT_EQ("namespace {\n"
" int i;\n"
"} // end anonymous namespace",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
"} // end anonymous namespace"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"} /* end of namespace A */",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"} /* end of namespace A */"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"} // namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"} // namespace A"));
EXPECT_EQ("namespace A::B {\n"
" int i;\n"
"} // end namespace A::B",
fixNamespaceEndComments("namespace A::B {\n"
" int i;\n"
"} // end namespace A::B"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"}; // end namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"}; // end namespace A"));
EXPECT_EQ("namespace {\n"
" int i;\n"
"}; /* unnamed namespace */",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
"}; /* unnamed namespace */"));
}
TEST_F(NamespaceEndCommentsFixerTest, UpdatesInvalidEndLineComment) {
EXPECT_EQ("namespace {\n"
" int i;\n"
"} // namespace",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
"} // namespace A"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"} // namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"} // namespace"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"} // namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"} //"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"} // namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"} //"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"} // namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"} // banamespace A"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"}; // namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"}; // banamespace A"));
// Updates invalid line comments even for short namespaces.
EXPECT_EQ("namespace A {} // namespace A",
fixNamespaceEndComments("namespace A {} // namespace"));
EXPECT_EQ("namespace A {}; // namespace A",
fixNamespaceEndComments("namespace A {}; // namespace"));
// Update invalid comments for compacted namespaces.
FormatStyle CompactNamespacesStyle = getLLVMStyle();
CompactNamespacesStyle.CompactNamespaces = true;
EXPECT_EQ("namespace out { namespace in {\n"
"}} // namespace out::in",
fixNamespaceEndComments("namespace out { namespace in {\n"
"}} // namespace out",
CompactNamespacesStyle));
EXPECT_EQ("namespace out { namespace in {\n"
"}} // namespace out::in",
fixNamespaceEndComments("namespace out { namespace in {\n"
"}} // namespace in",
CompactNamespacesStyle));
EXPECT_EQ("namespace out { namespace in {\n"
"}\n"
"} // namespace out::in",
fixNamespaceEndComments("namespace out { namespace in {\n"
"}// banamespace in\n"
"} // namespace out",
CompactNamespacesStyle));
}
TEST_F(NamespaceEndCommentsFixerTest, UpdatesInvalidEndBlockComment) {
EXPECT_EQ("namespace {\n"
" int i;\n"
"} // namespace",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
"} /* namespace A */"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"} // namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"} /* end namespace */"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"} // namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"} /**/"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"} // namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"} /* end unnamed namespace */"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"} // namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"} /* banamespace A */"));
EXPECT_EQ("namespace A {\n"
" int i;\n"
"}; // namespace A",
fixNamespaceEndComments("namespace A {\n"
" int i;\n"
"}; /* banamespace A */"));
EXPECT_EQ("namespace A {} // namespace A",
fixNamespaceEndComments("namespace A {} /**/"));
EXPECT_EQ("namespace A {}; // namespace A",
fixNamespaceEndComments("namespace A {}; /**/"));
}
TEST_F(NamespaceEndCommentsFixerTest,
DoesNotAddEndCommentForNamespacesControlledByMacros) {
EXPECT_EQ("#ifdef 1\n"
"namespace A {\n"
"#elseif\n"
"namespace B {\n"
"#endif\n"
" int i;\n"
"}\n"
"}\n",
fixNamespaceEndComments("#ifdef 1\n"
"namespace A {\n"
"#elseif\n"
"namespace B {\n"
"#endif\n"
" int i;\n"
"}\n"
"}\n"));
}
TEST_F(NamespaceEndCommentsFixerTest, AddEndCommentForNamespacesAroundMacros) {
// Conditional blocks around are fine
EXPECT_EQ("namespace A {\n"
"#if 1\n"
"int i;\n"
"#endif\n"
"}// namespace A",
fixNamespaceEndComments("namespace A {\n"
"#if 1\n"
"int i;\n"
"#endif\n"
"}"));
EXPECT_EQ("#if 1\n"
"#endif\n"
"namespace A {\n"
"int i;\n"
"int j;\n"
"}// namespace A",
fixNamespaceEndComments("#if 1\n"
"#endif\n"
"namespace A {\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ("namespace A {\n"
"int i;\n"
"int j;\n"
"}// namespace A\n"
"#if 1\n"
"#endif",
fixNamespaceEndComments("namespace A {\n"
"int i;\n"
"int j;\n"
"}\n"
"#if 1\n"
"#endif"));
EXPECT_EQ("#if 1\n"
"namespace A {\n"
"int i;\n"
"int j;\n"
"}// namespace A\n"
"#endif",
fixNamespaceEndComments("#if 1\n"
"namespace A {\n"
"int i;\n"
"int j;\n"
"}\n"
"#endif"));
// Macro definition has no impact
EXPECT_EQ("namespace A {\n"
"#define FOO\n"
"int i;\n"
"}// namespace A",
fixNamespaceEndComments("namespace A {\n"
"#define FOO\n"
"int i;\n"
"}"));
EXPECT_EQ("#define FOO\n"
"namespace A {\n"
"int i;\n"
"int j;\n"
"}// namespace A",
fixNamespaceEndComments("#define FOO\n"
"namespace A {\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ("namespace A {\n"
"int i;\n"
"int j;\n"
"}// namespace A\n"
"#define FOO\n",
fixNamespaceEndComments("namespace A {\n"
"int i;\n"
"int j;\n"
"}\n"
"#define FOO\n"));
// No replacement if open & close in different conditional blocks
EXPECT_EQ("#if 1\n"
"namespace A {\n"
"#endif\n"
"int i;\n"
"int j;\n"
"#if 1\n"
"}\n"
"#endif",
fixNamespaceEndComments("#if 1\n"
"namespace A {\n"
"#endif\n"
"int i;\n"
"int j;\n"
"#if 1\n"
"}\n"
"#endif"));
EXPECT_EQ("#ifdef A\n"
"namespace A {\n"
"#endif\n"
"int i;\n"
"int j;\n"
"#ifdef B\n"
"}\n"
"#endif",
fixNamespaceEndComments("#ifdef A\n"
"namespace A {\n"
"#endif\n"
"int i;\n"
"int j;\n"
"#ifdef B\n"
"}\n"
"#endif"));
// No replacement inside unreachable conditional block
EXPECT_EQ("#if 0\n"
"namespace A {\n"
"int i;\n"
"int j;\n"
"}\n"
"#endif",
fixNamespaceEndComments("#if 0\n"
"namespace A {\n"
"int i;\n"
"int j;\n"
"}\n"
"#endif"));
}
TEST_F(NamespaceEndCommentsFixerTest,
DoesNotAddEndCommentForNamespacesInMacroDeclarations) {
EXPECT_EQ("#ifdef 1\n"
"namespace A {\n"
"#elseif\n"
"namespace B {\n"
"#endif\n"
" int i;\n"
"}\n"
"}\n",
fixNamespaceEndComments("#ifdef 1\n"
"namespace A {\n"
"#elseif\n"
"namespace B {\n"
"#endif\n"
" int i;\n"
"}\n"
"}\n"));
EXPECT_EQ("namespace {\n"
" int i;\n"
" int j;\n"
"}// namespace\n"
"#if A\n"
" int i;\n"
"#else\n"
" int j;\n"
"#endif",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
" int j;\n"
"}\n"
"#if A\n"
" int i;\n"
"#else\n"
" int j;\n"
"#endif"));
EXPECT_EQ("#if A\n"
"namespace A {\n"
"#else\n"
"namespace B {\n"
"#endif\n"
"int i;\n"
"int j;\n"
"}",
fixNamespaceEndComments("#if A\n"
"namespace A {\n"
"#else\n"
"namespace B {\n"
"#endif\n"
"int i;\n"
"int j;\n"
"}"));
EXPECT_EQ("#if A\n"
"namespace A {\n"
"#else\n"
"namespace B {\n"
"#endif\n"
"int i;\n"
"int j;\n"
"} // namespace A",
fixNamespaceEndComments("#if A\n"
"namespace A {\n"
"#else\n"
"namespace B {\n"
"#endif\n"
"int i;\n"
"int j;\n"
"} // namespace A"));
EXPECT_EQ("#if A\n"
"namespace A {\n"
"#else\n"
"namespace B {\n"
"#endif\n"
"int i;\n"
"int j;\n"
"} // namespace B",
fixNamespaceEndComments("#if A\n"
"namespace A {\n"
"#else\n"
"namespace B {\n"
"#endif\n"
"int i;\n"
"int j;\n"
"} // namespace B"));
EXPECT_EQ("namespace A\n"
"int i;\n"
"int j;\n"
"#if A\n"
"}\n"
"#else\n"
"}\n"
"#endif",
fixNamespaceEndComments("namespace A\n"
"int i;\n"
"int j;\n"
"#if A\n"
"}\n"
"#else\n"
"}\n"
"#endif"));
EXPECT_EQ("namespace A\n"
"int i;\n"
"int j;\n"
"#if A\n"
"} // namespace A\n"
"#else\n"
"} // namespace A\n"
"#endif",
fixNamespaceEndComments("namespace A\n"
"int i;\n"
"int j;\n"
"#if A\n"
"} // namespace A\n"
"#else\n"
"} // namespace A\n"
"#endif"));
}
TEST_F(NamespaceEndCommentsFixerTest,
DoesNotAddEndCommentForUnbalancedRBracesAfterNamespaceEnd) {
EXPECT_EQ("namespace {\n"
" int i;\n"
"} // namespace\n"
"}",
fixNamespaceEndComments("namespace {\n"
" int i;\n"
"} // namespace\n"
"}"));
}
TEST_F(NamespaceEndCommentsFixerTest, HandlesInlineAtEndOfLine_PR32438) {
EXPECT_EQ("template <int> struct a {};\n"
"struct a<bool{}> b() {\n"
"}\n"
"#define c inline\n"
"void d() {\n"
"}\n",
fixNamespaceEndComments("template <int> struct a {};\n"
"struct a<bool{}> b() {\n"
"}\n"
"#define c inline\n"
"void d() {\n"
"}\n"));
}
} // end namespace
} // end namespace format
} // end namespace clang