--- /dev/null
+# REQUIRES: clangd-tidy-checks
+# RUN: clangd -lit-test -clang-tidy-checks=bugprone-sizeof-expression < %s | FileCheck -strict-whitespace %s
+{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
+---
+{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"int main() {\n(void)sizeof(42);\n}"}}}
+# CHECK: "method": "textDocument/publishDiagnostics",
+# CHECK-NEXT: "params": {
+# CHECK-NEXT: "diagnostics": [
+# CHECK-NEXT: {
+# CHECK-NEXT: "code": "bugprone-sizeof-expression",
+# CHECK-NEXT: "message": "Suspicious usage of 'sizeof(K)'; did you mean 'K'?",
+# CHECK-NEXT: "range": {
+# CHECK-NEXT: "end": {
+# CHECK-NEXT: "character": 12,
+# CHECK-NEXT: "line": 1
+# CHECK-NEXT: },
+# CHECK-NEXT: "start": {
+# CHECK-NEXT: "character": 6,
+# CHECK-NEXT: "line": 1
+# CHECK-NEXT: }
+# CHECK-NEXT: },
+# CHECK-NEXT: "severity": 2,
+# CHECK-NEXT: "source": "clang-tidy"
+# CHECK-NEXT: }
+# CHECK-NEXT: ],
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
+# CHECK-NEXT: "version": 0
+# CHECK-NEXT: }
+---
+{"jsonrpc":"2.0","id":2,"method":"sync","params":null}
+---
+{"jsonrpc":"2.0","method":"textDocument/didClose","params":{"textDocument":{"uri":"test:///foo.c"}}}
+# CHECK: "method": "textDocument/publishDiagnostics",
+# CHECK-NEXT: "params": {
+# CHECK-NEXT: "diagnostics": [],
+# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
+# CHECK-NEXT: }
+---
+{"jsonrpc":"2.0","id":5,"method":"shutdown"}
+---
+{"jsonrpc":"2.0","method":"exit"}
+
+++ /dev/null
-# RUN: clangd -lit-test -clang-tidy-checks=bugprone-sizeof-expression < %s | FileCheck -strict-whitespace %s
-{"jsonrpc":"2.0","id":0,"method":"initialize","params":{"processId":123,"rootPath":"clangd","capabilities":{},"trace":"off"}}
----
-{"jsonrpc":"2.0","method":"textDocument/didOpen","params":{"textDocument":{"uri":"test:///foo.c","languageId":"c","text":"void main() {\n(void)sizeof(42);\n}"}}}
-# CHECK: "method": "textDocument/publishDiagnostics",
-# CHECK-NEXT: "params": {
-# CHECK-NEXT: "diagnostics": [
-# CHECK-NEXT: {
-# CHECK-NEXT: "code": "-Wmain-return-type",
-# CHECK-NEXT: "message": "Return type of 'main' is not 'int' (fix available)",
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 4,
-# CHECK-NEXT: "line": 0
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 0,
-# CHECK-NEXT: "line": 0
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "severity": 2,
-# CHECK-NEXT: "source": "clang"
-# CHECK-NEXT: },
-# CHECK-NEXT: {
-# CHECK-NEXT: "code": "bugprone-sizeof-expression",
-# CHECK-NEXT: "message": "Suspicious usage of 'sizeof(K)'; did you mean 'K'?",
-# CHECK-NEXT: "range": {
-# CHECK-NEXT: "end": {
-# CHECK-NEXT: "character": 12,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: },
-# CHECK-NEXT: "start": {
-# CHECK-NEXT: "character": 6,
-# CHECK-NEXT: "line": 1
-# CHECK-NEXT: }
-# CHECK-NEXT: },
-# CHECK-NEXT: "severity": 2,
-# CHECK-NEXT: "source": "clang-tidy"
-# CHECK-NEXT: }
-# CHECK-NEXT: ],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c",
-# CHECK-NEXT: "version": 0
-# CHECK-NEXT: }
----
-{"jsonrpc":"2.0","id":2,"method":"sync","params":null}
----
-{"jsonrpc":"2.0","method":"textDocument/didClose","params":{"textDocument":{"uri":"test:///foo.c"}}}
-# CHECK: "method": "textDocument/publishDiagnostics",
-# CHECK-NEXT: "params": {
-# CHECK-NEXT: "diagnostics": [],
-# CHECK-NEXT: "uri": "file://{{.*}}/foo.c"
-# CHECK-NEXT: }
----
-{"jsonrpc":"2.0","id":5,"method":"shutdown"}
----
-{"jsonrpc":"2.0","method":"exit"}
#include "Config.h"
#include "Diagnostics.h"
#include "FeatureModule.h"
+#include "Features.h"
#include "ParsedAST.h"
#include "Protocol.h"
#include "SourceCode.h"
return Res;
}
+// Normally returns the provided diagnostics matcher.
+// If clang-tidy checks are not linked in, returns a matcher for no diagnostics!
+// This is intended for tests where the diagnostics come from clang-tidy checks.
+// We don't #ifdef each individual test as it's intrusive and we want to ensure
+// that as much of the test is still compiled an run as possible.
+::testing::Matcher<std::vector<clangd::Diag>>
+ifTidyChecks(::testing::Matcher<std::vector<clangd::Diag>> M) {
+ if (!CLANGD_TIDY_CHECKS)
+ return IsEmpty();
+ return M;
+}
+
TEST(DiagnosticsTest, DiagnosticRanges) {
// Check we report correct ranges, including various edge-cases.
Annotations Test(R"cpp(
#define ID(X) X
namespace test{};
void $decl[[foo]]();
- class T{$explicit[[]]$constructor[[T]](int a);};
int main() {
+ struct Container { int* begin(); int* end(); } *container;
+ for (auto i : $insertstar[[]]$range[[container]]) {
+ }
+
$typo[[go\
o]]();
foo()$semicolon[[]]//with comments
}
)cpp");
auto TU = TestTU::withCode(Test.code());
- TU.ClangTidyProvider = addTidyChecks("google-explicit-constructor");
EXPECT_THAT(
*TU.build().getDiagnostics(),
ElementsAre(
+ // Make sure the whole token is highlighted.
+ AllOf(Diag(Test.range("range"),
+ "invalid range expression of type 'struct Container *'; "
+ "did you mean to dereference it with '*'?"),
+ WithFix(Fix(Test.range("insertstar"), "*", "insert '*'"))),
// This range spans lines.
AllOf(Diag(Test.range("typo"),
"use of undeclared identifier 'goo'; did you mean 'foo'?"),
AllOf(Diag(Test.range("macro"),
"use of undeclared identifier 'fod'; did you mean 'foo'?"),
WithFix(Fix(Test.range("macroarg"), "foo",
- "change 'fod' to 'foo'"))),
- // We make sure here that the entire token is highlighted
- AllOf(Diag(Test.range("constructor"),
- "single-argument constructors must be marked explicit to "
- "avoid unintentional implicit conversions"),
- WithFix(Fix(Test.range("explicit"), "explicit ",
- "insert 'explicit '")))));
+ "change 'fod' to 'foo'")))));
}
TEST(DiagnosticsTest, FlagsMatter) {
// Verify that we filter out the duplicated diagnostic message.
EXPECT_THAT(
*TU.build().getDiagnostics(),
- UnorderedElementsAre(::testing::AllOf(
+ ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
Diag(Test.range(),
"floating point literal has suffix 'f', which is not uppercase"),
- DiagSource(Diag::ClangTidy))));
+ DiagSource(Diag::ClangTidy)))));
Test = Annotations(R"cpp(
template<typename T>
// duplicated messages, verify that we deduplicate them.
EXPECT_THAT(
*TU.build().getDiagnostics(),
- UnorderedElementsAre(::testing::AllOf(
+ ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
Diag(Test.range(),
"floating point literal has suffix 'f', which is not uppercase"),
- DiagSource(Diag::ClangTidy))));
+ DiagSource(Diag::ClangTidy)))));
}
TEST(DiagnosticsTest, ClangTidy) {
"modernize-deprecated-headers,"
"modernize-use-trailing-return-type,"
"misc-no-recursion");
+ TU.ExtraArgs.push_back("-Wno-unsequenced");
EXPECT_THAT(
*TU.build().getDiagnostics(),
- UnorderedElementsAre(
+ ifTidyChecks(UnorderedElementsAre(
AllOf(Diag(Test.range("deprecated"),
"inclusion of deprecated C++ header 'assert.h'; consider "
"using 'cassert' instead"),
"change '\"assert.h\"' to '<cassert>'"))),
Diag(Test.range("doubled"),
"suspicious usage of 'sizeof(sizeof(...))'"),
- AllOf(
- Diag(Test.range("macroarg"),
- "side effects in the 1st macro argument 'X' are repeated in "
- "macro expansion"),
- DiagSource(Diag::ClangTidy),
- DiagName("bugprone-macro-repeated-side-effects"),
- WithNote(
- Diag(Test.range("macrodef"), "macro 'SQUARE' defined here"))),
- Diag(Test.range("macroarg"),
- "multiple unsequenced modifications to 'y'"),
- AllOf(
- Diag(Test.range("main"),
- "use a trailing return type for this function"),
- DiagSource(Diag::ClangTidy),
- DiagName("modernize-use-trailing-return-type"),
- // Verify that we don't have "[check-name]" suffix in the message.
- WithFix(
- FixMessage("use a trailing return type for this function"))),
+ AllOf(Diag(Test.range("macroarg"),
+ "side effects in the 1st macro argument 'X' are "
+ "repeated in "
+ "macro expansion"),
+ DiagSource(Diag::ClangTidy),
+ DiagName("bugprone-macro-repeated-side-effects"),
+ WithNote(Diag(Test.range("macrodef"),
+ "macro 'SQUARE' defined here"))),
+ AllOf(Diag(Test.range("main"),
+ "use a trailing return type for this function"),
+ DiagSource(Diag::ClangTidy),
+ DiagName("modernize-use-trailing-return-type"),
+ // Verify there's no "[check-name]" suffix in the message.
+ WithFix(FixMessage(
+ "use a trailing return type for this function"))),
Diag(Test.range("foo"),
"function 'foo' is within a recursive call chain"),
Diag(Test.range("bar"),
- "function 'bar' is within a recursive call chain")));
+ "function 'bar' is within a recursive call chain"))));
}
TEST(DiagnosticsTest, ClangTidyEOF) {
TU.ClangTidyProvider = addTidyChecks("llvm-include-order");
EXPECT_THAT(
*TU.build().getDiagnostics(),
- Contains(AllOf(Diag(Test.range(), "#includes are not sorted properly"),
- DiagSource(Diag::ClangTidy),
- DiagName("llvm-include-order"))));
+ ifTidyChecks(Contains(
+ AllOf(Diag(Test.range(), "#includes are not sorted properly"),
+ DiagSource(Diag::ClangTidy), DiagName("llvm-include-order")))));
}
TEST(DiagnosticTest, TemplatesInHeaders) {
TU.ClangTidyProvider = addTidyChecks("modernize-loop-convert");
EXPECT_THAT(
*TU.build().getDiagnostics(),
- UnorderedElementsAre(::testing::AllOf(
+ ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
Diag(Main.range(), "use range-based for loop instead"),
- DiagSource(Diag::ClangTidy), DiagName("modernize-loop-convert"))));
+ DiagSource(Diag::ClangTidy), DiagName("modernize-loop-convert")))));
}
TEST(DiagnosticTest, RespectsDiagnosticConfig) {
TU.ClangTidyProvider = addTidyChecks("bugprone-integer-division");
EXPECT_THAT(
*TU.build().getDiagnostics(),
- UnorderedElementsAre(::testing::AllOf(
+ ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
Diag(Main.range(), "result of integer division used in a floating "
"point context; possible loss of precision"),
- DiagSource(Diag::ClangTidy), DiagName("bugprone-integer-division"))));
+ DiagSource(Diag::ClangTidy),
+ DiagName("bugprone-integer-division")))));
}
TEST(DiagnosticTest, ClangTidyWarningAsError) {
addTidyChecks("bugprone-integer-division", "bugprone-integer-division");
EXPECT_THAT(
*TU.build().getDiagnostics(),
- UnorderedElementsAre(::testing::AllOf(
+ ifTidyChecks(UnorderedElementsAre(::testing::AllOf(
Diag(Main.range(), "result of integer division used in a floating "
"point context; possible loss of precision"),
DiagSource(Diag::ClangTidy), DiagName("bugprone-integer-division"),
- DiagSeverity(DiagnosticsEngine::Error))));
+ DiagSeverity(DiagnosticsEngine::Error)))));
}
TEST(DiagnosticTest, LongFixMessages) {
)cpp");
TestTU TU = TestTU::withCode(Main.code());
TU.ClangTidyProvider = addTidyChecks("llvm-else-after-return");
- EXPECT_THAT(
- *TU.build().getDiagnostics(),
- ElementsAre(Diag(Main.range(), "do not use 'else' after 'return'")));
+ EXPECT_THAT(*TU.build().getDiagnostics(),
+ ifTidyChecks(ElementsAre(
+ Diag(Main.range(), "do not use 'else' after 'return'"))));
}
TEST(DiagnosticsTest, Preprocessor) {