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