[clang-tools-extra] r323448 - [clangd] Add ClangdUnit diagnostics tests using annotated code.

Sam McCall via cfe-commits cfe-commits at lists.llvm.org
Thu Jan 25 09:29:18 PST 2018


Author: sammccall
Date: Thu Jan 25 09:29:17 2018
New Revision: 323448

URL: http://llvm.org/viewvc/llvm-project?rev=323448&view=rev
Log:
[clangd] Add ClangdUnit diagnostics tests using annotated code.

Summary:
This adds checks that our diagnostics emit correct ranges in a bunch of cases,
as promised in D41118.

The diagnostics-preamble test is also converted and extended to be a little more
precise.

diagnostics.test stays around as the smoke test for this feature.

Reviewers: ilya-biryukov

Subscribers: klimek, mgorny, cfe-commits

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

Added:
    clang-tools-extra/trunk/unittests/clangd/ClangdUnitTests.cpp
Removed:
    clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test
Modified:
    clang-tools-extra/trunk/clangd/Protocol.cpp
    clang-tools-extra/trunk/clangd/Protocol.h
    clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt

Modified: clang-tools-extra/trunk/clangd/Protocol.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.cpp?rev=323448&r1=323447&r2=323448&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.cpp (original)
+++ clang-tools-extra/trunk/clangd/Protocol.cpp Thu Jan 25 09:29:17 2018
@@ -137,6 +137,12 @@ json::Expr toJSON(const TextEdit &P) {
   };
 }
 
+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 @@ bool fromJSON(const json::Expr &Params,
   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) &&

Modified: clang-tools-extra/trunk/clangd/Protocol.h
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/clangd/Protocol.h?rev=323448&r1=323447&r2=323448&view=diff
==============================================================================
--- clang-tools-extra/trunk/clangd/Protocol.h (original)
+++ clang-tools-extra/trunk/clangd/Protocol.h Thu Jan 25 09:29:17 2018
@@ -150,6 +150,7 @@ struct TextEdit {
 };
 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 @@ struct LSPDiagnosticCompare {
   }
 };
 bool fromJSON(const json::Expr &, Diagnostic &);
+llvm::raw_ostream &operator<<(llvm::raw_ostream &, const Diagnostic &);
 
 struct CodeActionContext {
   /// An array of diagnostics.

Removed: clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test?rev=323447&view=auto
==============================================================================
--- clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test (original)
+++ clang-tools-extra/trunk/test/clangd/diagnostics-preamble.test (removed)
@@ -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"}

Modified: clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt?rev=323448&r1=323447&r2=323448&view=diff
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt (original)
+++ clang-tools-extra/trunk/unittests/clangd/CMakeLists.txt Thu Jan 25 09:29:17 2018
@@ -11,6 +11,7 @@ include_directories(
 add_extra_unittest(ClangdTests
   Annotations.cpp
   ClangdTests.cpp
+  ClangdUnitTests.cpp
   CodeCompleteTests.cpp
   CodeCompletionStringsTests.cpp
   ContextTests.cpp

Added: clang-tools-extra/trunk/unittests/clangd/ClangdUnitTests.cpp
URL: http://llvm.org/viewvc/llvm-project/clang-tools-extra/trunk/unittests/clangd/ClangdUnitTests.cpp?rev=323448&view=auto
==============================================================================
--- clang-tools-extra/trunk/unittests/clangd/ClangdUnitTests.cpp (added)
+++ clang-tools-extra/trunk/unittests/clangd/ClangdUnitTests.cpp Thu Jan 25 09:29:17 2018
@@ -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




More information about the cfe-commits mailing list