1 | //===-- ConfigTesting.h - Helpers for configuration tests -------*- C++ -*-===// |
2 | // |
3 | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | // See https://llvm.org/LICENSE.txt for license information. |
5 | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | // |
7 | //===----------------------------------------------------------------------===// |
8 | |
9 | #ifndef LLVM_CLANG_TOOLS_EXTRA_CLANGD_UNITTESTS_CONFIGTESTING_H |
10 | #define |
11 | |
12 | #include "Protocol.h" |
13 | #include "llvm/Support/ScopedPrinter.h" |
14 | #include "llvm/Support/SourceMgr.h" |
15 | #include "gmock/gmock.h" |
16 | #include <functional> |
17 | #include <optional> |
18 | |
19 | namespace clang { |
20 | namespace clangd { |
21 | namespace config { |
22 | |
23 | // Provides a DiagnosticsCallback that records diganostics. |
24 | // Unlike just pushing them into a vector, underlying storage need not survive. |
25 | struct CapturedDiags { |
26 | std::function<void(const llvm::SMDiagnostic &)> callback() { |
27 | return [this](const llvm::SMDiagnostic &D) { |
28 | if (Files.empty() || Files.back() != D.getFilename()) |
29 | Files.push_back(x: D.getFilename().str()); |
30 | |
31 | if (D.getKind() > llvm::SourceMgr::DK_Warning) |
32 | return; |
33 | |
34 | Diagnostics.emplace_back(); |
35 | Diag &Out = Diagnostics.back(); |
36 | Out.Message = D.getMessage().str(); |
37 | Out.Kind = D.getKind(); |
38 | Out.Pos.line = D.getLineNo() - 1; |
39 | Out.Pos.character = D.getColumnNo(); // Zero-based - bug in SourceMgr? |
40 | if (!D.getRanges().empty()) { |
41 | const auto &R = D.getRanges().front(); |
42 | Out.Rng.emplace(); |
43 | Out.Rng->start.line = Out.Rng->end.line = Out.Pos.line; |
44 | Out.Rng->start.character = R.first; |
45 | Out.Rng->end.character = R.second; |
46 | } |
47 | }; |
48 | } |
49 | struct Diag { |
50 | std::string Message; |
51 | llvm::SourceMgr::DiagKind Kind; |
52 | Position Pos; |
53 | std::optional<Range> Rng; |
54 | |
55 | friend void PrintTo(const Diag &D, std::ostream *OS) { |
56 | *OS << (D.Kind == llvm::SourceMgr::DK_Error ? "error: " : "warning: " ) |
57 | << D.Message << "@" << llvm::to_string(Value: D.Pos); |
58 | } |
59 | }; |
60 | std::vector<Diag> Diagnostics; // Warning or higher. |
61 | std::vector<std::string> Files; // Filename from diagnostics including notes. |
62 | |
63 | void clear() { |
64 | Diagnostics.clear(); |
65 | Files.clear(); |
66 | } |
67 | }; |
68 | |
69 | MATCHER_P(diagMessage, M, "" ) { return arg.Message == M; } |
70 | MATCHER_P(diagKind, K, "" ) { return arg.Kind == K; } |
71 | MATCHER_P(diagPos, P, "" ) { return arg.Pos == P; } |
72 | MATCHER_P(diagRange, R, "" ) { return arg.Rng == R; } |
73 | |
74 | inline Position toPosition(llvm::SMLoc L, const llvm::SourceMgr &SM) { |
75 | auto LineCol = SM.getLineAndColumn(Loc: L); |
76 | Position P; |
77 | P.line = LineCol.first - 1; |
78 | P.character = LineCol.second - 1; |
79 | return P; |
80 | } |
81 | |
82 | inline Range toRange(llvm::SMRange R, const llvm::SourceMgr &SM) { |
83 | return Range{.start: toPosition(L: R.Start, SM), .end: toPosition(L: R.End, SM)}; |
84 | } |
85 | |
86 | } // namespace config |
87 | } // namespace clangd |
88 | } // namespace clang |
89 | |
90 | #endif |
91 | |