1 #include "clang_utils.h"
2
3 #include "platform.h"
4
5 #include <doctest/doctest.h>
6 #include <loguru.hpp>
7
8 namespace {
9
GetLsRangeForFixIt(const CXSourceRange & range)10 lsRange GetLsRangeForFixIt(const CXSourceRange& range) {
11 CXSourceLocation start = clang_getRangeStart(range);
12 CXSourceLocation end = clang_getRangeEnd(range);
13
14 unsigned int start_line, start_column;
15 clang_getSpellingLocation(start, nullptr, &start_line, &start_column,
16 nullptr);
17 unsigned int end_line, end_column;
18 clang_getSpellingLocation(end, nullptr, &end_line, &end_column, nullptr);
19
20 return lsRange(lsPosition(start_line - 1, start_column - 1) /*start*/,
21 lsPosition(end_line - 1, end_column) /*end*/);
22 }
23
24 } // namespace
25
26 // See clang_formatDiagnostic
BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,const AbsolutePath & path)27 optional<lsDiagnostic> BuildAndDisposeDiagnostic(CXDiagnostic diagnostic,
28 const AbsolutePath& path) {
29 // Get diagnostic location.
30 CXFile file;
31 unsigned start_line, start_column;
32 clang_getSpellingLocation(clang_getDiagnosticLocation(diagnostic), &file,
33 &start_line, &start_column, nullptr);
34
35 // No diagnostic file.
36 if (!file) {
37 clang_disposeDiagnostic(diagnostic);
38 return nullopt;
39 }
40 // Diagnostic path does not match our required path.
41 optional<AbsolutePath> cx_file_path = FileName(file);
42 if (!cx_file_path || path != *cx_file_path) {
43 clang_disposeDiagnostic(diagnostic);
44 return nullopt;
45 }
46
47 unsigned end_line = start_line, end_column = start_column,
48 num_ranges = clang_getDiagnosticNumRanges(diagnostic);
49 for (unsigned i = 0; i < num_ranges; i++) {
50 CXFile file0, file1;
51 unsigned line0, column0, line1, column1;
52 CXSourceRange range = clang_getDiagnosticRange(diagnostic, i);
53 clang_getSpellingLocation(clang_getRangeStart(range), &file0, &line0,
54 &column0, nullptr);
55 clang_getSpellingLocation(clang_getRangeEnd(range), &file1, &line1,
56 &column1, nullptr);
57 if (file0 != file1 || file0 != file)
58 continue;
59 if (line0 < start_line || (line0 == start_line && column0 < start_column)) {
60 start_line = line0;
61 start_column = column0;
62 }
63 if (line1 > end_line || (line1 == end_line && column1 > end_column)) {
64 end_line = line1;
65 end_column = column1;
66 }
67 }
68
69 // Build diagnostic.
70 lsDiagnostic ls_diagnostic;
71 ls_diagnostic.range = lsRange(lsPosition(start_line - 1, start_column - 1),
72 lsPosition(end_line - 1, end_column - 1));
73
74 ls_diagnostic.message = ToString(clang_getDiagnosticSpelling(diagnostic));
75
76 // Append the flag that enables this diagnostic, ie, [-Wswitch]
77 std::string enabling_flag =
78 ToString(clang_getDiagnosticOption(diagnostic, nullptr));
79 if (!enabling_flag.empty())
80 ls_diagnostic.message += " [" + enabling_flag + "]";
81
82 ls_diagnostic.code = clang_getDiagnosticCategory(diagnostic);
83
84 switch (clang_getDiagnosticSeverity(diagnostic)) {
85 case CXDiagnostic_Ignored:
86 // llvm_unreachable
87 break;
88 case CXDiagnostic_Note:
89 ls_diagnostic.severity = lsDiagnosticSeverity::Information;
90 break;
91 case CXDiagnostic_Warning:
92 ls_diagnostic.severity = lsDiagnosticSeverity::Warning;
93 break;
94 case CXDiagnostic_Error:
95 case CXDiagnostic_Fatal:
96 ls_diagnostic.severity = lsDiagnosticSeverity::Error;
97 break;
98 }
99
100 // Report fixits
101 unsigned num_fixits = clang_getDiagnosticNumFixIts(diagnostic);
102 for (unsigned i = 0; i < num_fixits; ++i) {
103 CXSourceRange replacement_range;
104 CXString text = clang_getDiagnosticFixIt(diagnostic, i, &replacement_range);
105
106 lsTextEdit edit;
107 edit.newText = ToString(text);
108 edit.range = GetLsRangeForFixIt(replacement_range);
109 ls_diagnostic.fixits_.push_back(edit);
110 }
111
112 clang_disposeDiagnostic(diagnostic);
113
114 return ls_diagnostic;
115 }
116
FileName(CXFile file)117 optional<AbsolutePath> FileName(CXFile file) {
118 CXString cx_name = clang_getFileName(file);
119 std::string name = ToString(cx_name);
120 return NormalizePath(name);
121 }
122
ToString(CXString cx_string)123 std::string ToString(CXString cx_string) {
124 std::string string;
125 if (cx_string.data != nullptr) {
126 string = clang_getCString(cx_string);
127 clang_disposeString(cx_string);
128 }
129 return string;
130 }
131
ToString(CXCursorKind kind)132 std::string ToString(CXCursorKind kind) {
133 return ToString(clang_getCursorKindSpelling(kind));
134 }
135
ClangBuiltinTypeName(CXTypeKind kind)136 const char* ClangBuiltinTypeName(CXTypeKind kind) {
137 switch (kind) {
138 // clang-format off
139 case CXType_Bool: return "bool";
140 case CXType_Char_U: return "char";
141 case CXType_UChar: return "unsigned char";
142 case CXType_UShort: return "unsigned short";
143 case CXType_UInt: return "unsigned int";
144 case CXType_ULong: return "unsigned long";
145 case CXType_ULongLong: return "unsigned long long";
146 case CXType_UInt128: return "unsigned __int128";
147 case CXType_Char_S: return "char";
148 case CXType_SChar: return "signed char";
149 case CXType_WChar: return "wchar_t";
150 case CXType_Int: return "int";
151 case CXType_Long: return "long";
152 case CXType_LongLong: return "long long";
153 case CXType_Int128: return "__int128";
154 case CXType_Float: return "float";
155 case CXType_Double: return "double";
156 case CXType_LongDouble: return "long double";
157 case CXType_Float128: return "__float128";
158 #if CINDEX_VERSION_MINOR >= 43
159 case CXType_Half: return "_Float16";
160 #endif
161 case CXType_NullPtr: return "nullptr";
162 default: return "";
163 // clang-format on
164 }
165 }
166