emitted_diagnostics_test.cpp 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. // Part of the Carbon Language project, under the Apache License v2.0 with LLVM
  2. // Exceptions. See /LICENSE for license information.
  3. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  4. #include <gtest/gtest.h>
  5. #include <fstream>
  6. #include <string>
  7. #include "absl/flags/flag.h"
  8. #include "common/set.h"
  9. #include "llvm/ADT/StringExtras.h"
  10. #include "re2/re2.h"
  11. #include "toolchain/diagnostics/diagnostic_kind.h"
  12. ABSL_FLAG(std::string, testdata_manifest, "",
  13. "A path to a file containing repo-relative names of test files.");
  14. namespace Carbon {
  15. namespace {
  16. constexpr DiagnosticKind Diagnostics[] = {
  17. #define CARBON_DIAGNOSTIC_KIND(Name) DiagnosticKind::Name,
  18. #include "toolchain/diagnostics/diagnostic_kind.def"
  19. };
  20. // Returns true for diagnostics which have no tests. In general, diagnostics
  21. // should be tested.
  22. static auto IsUntestedDiagnostic(DiagnosticKind diagnostic_kind) -> bool {
  23. switch (diagnostic_kind) {
  24. case DiagnosticKind::TestDiagnostic:
  25. case DiagnosticKind::TestDiagnosticNote:
  26. // These exist only for unit tests.
  27. return true;
  28. case DiagnosticKind::ErrorReadingFile:
  29. case DiagnosticKind::ErrorStattingFile:
  30. case DiagnosticKind::FileTooLarge:
  31. // These diagnose filesystem issues that are hard to unit test.
  32. return true;
  33. case DiagnosticKind::ArrayBoundTooLarge:
  34. // Int literals are currently limited to i32. Once that's fixed, this
  35. // should be tested.
  36. return true;
  37. case DiagnosticKind::ExternLibraryInImporter:
  38. case DiagnosticKind::ExternLibraryOnDefinition:
  39. case DiagnosticKind::HexadecimalEscapeMissingDigits:
  40. case DiagnosticKind::ImplOfUndefinedInterface:
  41. case DiagnosticKind::IncompleteTypeInFunctionParam:
  42. case DiagnosticKind::InvalidDigit:
  43. case DiagnosticKind::InvalidDigitSeparator:
  44. case DiagnosticKind::InvalidHorizontalWhitespaceInString:
  45. case DiagnosticKind::MismatchedIndentInString:
  46. case DiagnosticKind::ModifierPrivateNotAllowed:
  47. case DiagnosticKind::MultiLineStringWithDoubleQuotes:
  48. case DiagnosticKind::NameAmbiguousDueToExtend:
  49. case DiagnosticKind::TooManyDigits:
  50. case DiagnosticKind::UnaryOperatorRequiresWhitespace:
  51. case DiagnosticKind::UnicodeEscapeSurrogate:
  52. case DiagnosticKind::UnicodeEscapeTooLarge:
  53. case DiagnosticKind::UnknownBaseSpecifier:
  54. case DiagnosticKind::UnsupportedCRLineEnding:
  55. case DiagnosticKind::UnsupportedLFCRLineEnding:
  56. // TODO: Should look closer at these, but adding tests is a high risk of
  57. // loss in merge conflicts due to the amount of tests being changed right
  58. // now.
  59. return true;
  60. case DiagnosticKind::TooManyTokens:
  61. // This isn't feasible to test with a normal testcase, but is tested in
  62. // lex/tokenized_buffer_test.cpp.
  63. return true;
  64. default:
  65. return false;
  66. }
  67. }
  68. TEST(EmittedDiagnostics, Verify) {
  69. std::ifstream manifest_in(absl::GetFlag(FLAGS_testdata_manifest));
  70. ASSERT_TRUE(manifest_in.good());
  71. RE2 diagnostic_re(R"(^ *// CHECK:STDERR: .*\.carbon:.* \[(\w+)\]$)");
  72. ASSERT_TRUE(diagnostic_re.ok()) << diagnostic_re.error();
  73. Set<std::string> emitted_diagnostics;
  74. std::string test_filename;
  75. while (std::getline(manifest_in, test_filename)) {
  76. std::ifstream test_in(test_filename);
  77. ASSERT_TRUE(test_in.good());
  78. std::string line;
  79. while (std::getline(test_in, line)) {
  80. std::string diagnostic;
  81. if (RE2::PartialMatch(line, diagnostic_re, &diagnostic)) {
  82. emitted_diagnostics.Insert(diagnostic);
  83. }
  84. }
  85. }
  86. llvm::SmallVector<llvm::StringRef> missing_diagnostics;
  87. for (auto diagnostic_kind : Diagnostics) {
  88. if (IsUntestedDiagnostic(diagnostic_kind)) {
  89. EXPECT_FALSE(emitted_diagnostics.Erase(diagnostic_kind.name()))
  90. << diagnostic_kind
  91. << " was previously untested, and is now tested. That's good, but "
  92. "please remove it from IsUntestedDiagnostic.";
  93. continue;
  94. }
  95. if (!emitted_diagnostics.Erase(diagnostic_kind.name())) {
  96. missing_diagnostics.push_back(diagnostic_kind.name());
  97. }
  98. }
  99. constexpr llvm::StringLiteral Bullet = "\n - ";
  100. std::sort(missing_diagnostics.begin(), missing_diagnostics.end());
  101. EXPECT_TRUE(missing_diagnostics.empty())
  102. << "Some diagnostics have no tests:" << Bullet
  103. << llvm::join(missing_diagnostics, Bullet);
  104. llvm::SmallVector<std::string> unexpected_matches;
  105. emitted_diagnostics.ForEach(
  106. [&](const std::string& match) { unexpected_matches.push_back(match); });
  107. std::sort(unexpected_matches.begin(), unexpected_matches.end());
  108. EXPECT_TRUE(unexpected_matches.empty())
  109. << "Matched things that don't appear to be diagnostics:" << Bullet
  110. << llvm::join(unexpected_matches, Bullet);
  111. }
  112. } // namespace
  113. } // namespace Carbon