This revision was automatically updated to reflect the committed changes.
Closed by commit rCTE323448: [clangd] Add ClangdUnit diagnostics tests using 
annotated code. (authored by sammccall, committed by ).

Changed prior to commit:
  https://reviews.llvm.org/D41454?vs=127843&id=131471#toc

Repository:
  rCTE Clang Tools Extra

https://reviews.llvm.org/D41454

Files:
  clangd/Protocol.cpp
  clangd/Protocol.h
  test/clangd/diagnostics-preamble.test
  unittests/clangd/CMakeLists.txt
  unittests/clangd/ClangdUnitTests.cpp

Index: unittests/clangd/ClangdUnitTests.cpp
===================================================================
--- unittests/clangd/ClangdUnitTests.cpp
+++ unittests/clangd/ClangdUnitTests.cpp
@@ -0,0 +1,130 @@
+//===-- ClangdUnitTests.cpp - ClangdUnit tests ------------------*- C++ -*-===//
+//
+//                     The LLVM Compiler Infrastructure
+//
+// This file is distributed under the University of Illinois Open Source
+// License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+
+#include "ClangdUnit.h"
+#include "Annotations.h"
+#include "TestFS.h"
+#include "clang/Frontend/CompilerInvocation.h"
+#include "clang/Frontend/PCHContainerOperations.h"
+#include "clang/Frontend/Utils.h"
+#include "llvm/Support/ScopedPrinter.h"
+#include "gmock/gmock.h"
+#include "gtest/gtest.h"
+
+namespace clang {
+namespace clangd {
+using namespace llvm;
+void PrintTo(const DiagWithFixIts &D, std::ostream *O) {
+  llvm::raw_os_ostream OS(*O);
+  OS << D.Diag;
+  if (!D.FixIts.empty()) {
+    OS << " {";
+    const char *Sep = "";
+    for (const auto &F : D.FixIts) {
+      OS << Sep << F;
+      Sep = ", ";
+    }
+    OS << "}";
+  }
+}
+
+namespace {
+using testing::ElementsAre;
+
+// FIXME: this is duplicated with FileIndexTests. Share it.
+ParsedAST build(StringRef Code, std::vector<const char*> Flags = {}) {
+  std::vector<const char*> Cmd = {"clang", "main.cpp"};
+  Cmd.insert(Cmd.begin() + 1, Flags.begin(), Flags.end());
+  auto CI = createInvocationFromCommandLine(Cmd);
+  auto Buf = MemoryBuffer::getMemBuffer(Code);
+  auto AST = ParsedAST::Build(
+      Context::empty(), std::move(CI), nullptr, std::move(Buf),
+      std::make_shared<PCHContainerOperations>(), vfs::getRealFileSystem());
+  assert(AST.hasValue());
+  return std::move(*AST);
+}
+
+MATCHER_P2(Diag, Range, Message,
+           "Diagnostic at " + llvm::to_string(Range) + " = [" + Message + "]") {
+  return arg.Diag.range == Range && arg.Diag.message == Message &&
+         arg.FixIts.empty();
+}
+
+MATCHER_P3(Fix, Range, Replacement, Message,
+           "Fix " + llvm::to_string(Range) + " => " +
+               testing::PrintToString(Replacement) + " = [" + Message + "]") {
+  return arg.Diag.range == Range && arg.Diag.message == Message &&
+         arg.FixIts.size() == 1 && arg.FixIts[0].range == Range &&
+         arg.FixIts[0].newText == Replacement;
+}
+
+TEST(DiagnosticsTest, DiagnosticRanges) {
+  // Check we report correct ranges, including various edge-cases.
+  Annotations Test(R"cpp(
+    void $decl[[foo]]();
+    int main() {
+      $typo[[go\
+o]]();
+      foo()$semicolon[[]]
+      $unk[[unknown]]();
+    }
+  )cpp");
+      llvm::errs() << Test.code();
+      EXPECT_THAT(
+          build(Test.code()).getDiagnostics(),
+          ElementsAre(
+              // This range spans lines.
+              Fix(Test.range("typo"), "foo",
+                  "use of undeclared identifier 'goo'; did you mean 'foo'?"),
+              // This is a pretty normal range.
+              Diag(Test.range("decl"), "'foo' declared here"),
+              // This range is zero-width, and at the end of a line.
+              Fix(Test.range("semicolon"), ";",
+                  "expected ';' after expression"),
+              // This range isn't provided by clang, we expand to the token.
+              Diag(Test.range("unk"),
+                   "use of undeclared identifier 'unknown'")));
+}
+
+TEST(DiagnosticsTest, FlagsMatter) {
+  Annotations Test("[[void]] main() {}");
+  EXPECT_THAT(
+      build(Test.code()).getDiagnostics(),
+      ElementsAre(Fix(Test.range(), "int", "'main' must return 'int'")));
+  // Same code built as C gets different diagnostics.
+  EXPECT_THAT(
+      build(Test.code(), {"-x", "c"}).getDiagnostics(),
+      ElementsAre(
+          // FIXME: ideally this would be one diagnostic with a named FixIt.
+          Diag(Test.range(), "return type of 'main' is not 'int'"),
+          Fix(Test.range(), "int", "change return type to 'int'")));
+}
+
+TEST(DiagnosticsTest, Preprocessor) {
+  // This looks like a preamble, but there's an #else in the middle!
+  // Check that:
+  //  - the #else doesn't generate diagnostics (we had this bug)
+  //  - we get diagnostics from the taken branch
+  //  - we get no diagnostics from the not taken branch
+  Annotations Test(R"cpp(
+    #ifndef FOO
+    #define FOO
+      int a = [[b]];
+    #else
+      int x = y;
+    #endif
+    )cpp");
+  EXPECT_THAT(
+      build(Test.code()).getDiagnostics(),
+      ElementsAre(Diag(Test.range(), "use of undeclared identifier 'b'")));
+}
+
+} // namespace
+} // namespace clangd
+} // namespace clang
Index: unittests/clangd/CMakeLists.txt
===================================================================
--- unittests/clangd/CMakeLists.txt
+++ unittests/clangd/CMakeLists.txt
@@ -11,6 +11,7 @@
 add_extra_unittest(ClangdTests
   Annotations.cpp
   ClangdTests.cpp
+  ClangdUnitTests.cpp
   CodeCompleteTests.cpp
   CodeCompletionStringsTests.cpp
   ContextTests.cpp
Index: clangd/Protocol.cpp
===================================================================
--- clangd/Protocol.cpp
+++ clangd/Protocol.cpp
@@ -137,6 +137,12 @@
   };
 }
 
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const TextEdit &TE) {
+  OS << TE.range << " => \"";
+  PrintEscapedString(TE.newText, OS);
+  return OS << '"';
+}
+
 bool fromJSON(const json::Expr &E, TraceLevel &Out) {
   if (auto S = E.asString()) {
     if (*S == "off") {
@@ -255,6 +261,28 @@
   return O && O.map("diagnostics", R.diagnostics);
 }
 
+llvm::raw_ostream &operator<<(llvm::raw_ostream &OS, const Diagnostic &D) {
+  OS << D.range << " [";
+  switch (D.severity) {
+    case 1:
+      OS << "error";
+      break;
+    case 2:
+      OS << "warning";
+      break;
+    case 3:
+      OS << "note";
+      break;
+    case 4:
+      OS << "remark";
+      break;
+    default:
+      OS << "diagnostic";
+      break;
+  }
+  return OS << '(' << D.severity << "): " << D.message << "]";
+}
+
 bool fromJSON(const json::Expr &Params, CodeActionParams &R) {
   json::ObjectMapper O(Params);
   return O && O.map("textDocument", R.textDocument) &&
Index: clangd/Protocol.h
===================================================================
--- clangd/Protocol.h
+++ clangd/Protocol.h
@@ -150,6 +150,7 @@
 };
 bool fromJSON(const json::Expr &, TextEdit &);
 json::Expr toJSON(const TextEdit &);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const TextEdit &);
 
 struct TextDocumentItem {
   /// The text document's URI.
@@ -341,6 +342,7 @@
   }
 };
 bool fromJSON(const json::Expr &, Diagnostic &);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Diagnostic &);
 
 struct CodeActionContext {
   /// An array of diagnostics.
Index: test/clangd/diagnostics-preamble.test
===================================================================
--- test/clangd/diagnostics-preamble.test
+++ test/clangd/diagnostics-preamble.test
@@ -1,22 +0,0 @@
-# RUN: clangd -pretty -run-synchronously < %s | FileCheck -strict-whitespace %s
-# RUN: clangd -pretty -run-synchronously -pch-storage=memory < %s | FileCheck -strict-whitespace %s
-# It is absolutely vital that this file has CRLF line endings.
-#
-Content-Length: 125
-
-{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
-
-Content-Length: 206
-
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"file:///main.cpp","languageId":"cpp","version":1,"text":"#ifndef FOO\n#define FOO\nint a;\n#else\nint a = b;#endif\n\n\n"}}}
-#      CHECK:  "method": "textDocument/publishDiagnostics",
-# CHECK-NEXT:  "params": {
-# CHECK-NEXT:    "diagnostics": [],
-# CHECK-NEXT:    "uri": "file:///{{([A-Z]:/)?}}main.cpp"
-# CHECK-NEXT:  }
-Content-Length: 58
-
-{"jsonrpc":"2.0","id":2,"method":"shutdown","params":null}
-Content-Length: 33
-
-{"jsonrpc":"2.0":"method":"exit"}
_______________________________________________
cfe-commits mailing list
cfe-commits@lists.llvm.org
http://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits

Reply via email to