yql_issue.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310
  1. #include "yql_issue.h"
  2. #include "yql_issue_id.h"
  3. #include <yql/essentials/utils/utf8.h>
  4. #include <library/cpp/colorizer/output.h>
  5. #include <util/charset/utf8.h>
  6. #include <util/string/ascii.h>
  7. #include <util/string/split.h>
  8. #include <util/string/strip.h>
  9. #include <util/string/subst.h>
  10. #include <util/system/compiler.h>
  11. #include <util/generic/map.h>
  12. #include <util/generic/stack.h>
  13. #include <cstdlib>
  14. namespace NYql {
  15. void SanitizeNonAscii(TString& s) {
  16. if (!NYql::IsUtf8(s)) {
  17. TString escaped;
  18. escaped.reserve(s.size());
  19. const unsigned char* i = reinterpret_cast<const unsigned char*>(s.data());
  20. const unsigned char* end = i + s.size();
  21. while (i < end) {
  22. wchar32 rune;
  23. size_t runeLen;
  24. const RECODE_RESULT result = SafeReadUTF8Char(rune, runeLen, i, end);
  25. if (result == RECODE_OK) {
  26. escaped.insert(escaped.end(), reinterpret_cast<const char*>(i), reinterpret_cast<const char*>(i + runeLen));
  27. i += runeLen;
  28. } else {
  29. escaped.push_back('?');
  30. ++i;
  31. }
  32. }
  33. s = escaped;
  34. }
  35. }
  36. TTextWalker& TTextWalker::Advance(char c) {
  37. if (c == '\n') {
  38. HaveCr = false;
  39. ++LfCount;
  40. return *this;
  41. }
  42. if (c == '\r' && !HaveCr) {
  43. HaveCr = true;
  44. return *this;
  45. }
  46. ui32 charDistance = 1;
  47. if (Utf8Aware && IsUtf8Intermediate(c)) {
  48. charDistance = 0;
  49. }
  50. // either not '\r' or second '\r'
  51. if (LfCount) {
  52. Position.Row += LfCount;
  53. Position.Column = charDistance;
  54. LfCount = 0;
  55. } else {
  56. Position.Column += charDistance + (HaveCr && c != '\r');
  57. }
  58. HaveCr = (c == '\r');
  59. return *this;
  60. }
  61. void TIssue::PrintTo(IOutputStream& out, bool oneLine) const {
  62. out << Range() << ": " << SeverityToString(GetSeverity()) << ": ";
  63. if (oneLine) {
  64. TString message = StripString(Message);
  65. SubstGlobal(message, '\n', ' ');
  66. out << message;
  67. } else {
  68. out << Message;
  69. }
  70. if (GetCode()) {
  71. out << ", code: " << GetCode();
  72. }
  73. }
  74. void WalkThroughIssues(const TIssue& topIssue, bool leafOnly, std::function<void(const TIssue&, ui16 level)> fn, std::function<void(const TIssue&, ui16 level)> afterChildrenFn) {
  75. enum class EFnType {
  76. Main,
  77. AfterChildren,
  78. };
  79. const bool hasAfterChildrenFn = bool(afterChildrenFn);
  80. TStack<std::tuple<ui16, const TIssue*, EFnType>> issuesStack;
  81. if (hasAfterChildrenFn) {
  82. issuesStack.push(std::make_tuple(0, &topIssue, EFnType::AfterChildren));
  83. }
  84. issuesStack.push(std::make_tuple(0, &topIssue, EFnType::Main));
  85. while (!issuesStack.empty()) {
  86. auto level = std::get<0>(issuesStack.top());
  87. const auto& curIssue = *std::get<1>(issuesStack.top());
  88. const EFnType fnType = std::get<2>(issuesStack.top());
  89. issuesStack.pop();
  90. if (!leafOnly || curIssue.GetSubIssues().empty()) {
  91. if (fnType == EFnType::Main) {
  92. fn(curIssue, level);
  93. } else {
  94. afterChildrenFn(curIssue, level);
  95. }
  96. }
  97. if (fnType == EFnType::Main) {
  98. level++;
  99. const auto& subIssues = curIssue.GetSubIssues();
  100. for (int i = subIssues.size() - 1; i >= 0; i--) {
  101. if (hasAfterChildrenFn) {
  102. issuesStack.push(std::make_tuple(level, subIssues[i].Get(), EFnType::AfterChildren));
  103. }
  104. issuesStack.push(std::make_tuple(level, subIssues[i].Get(), EFnType::Main));
  105. }
  106. }
  107. }
  108. }
  109. namespace {
  110. Y_NO_INLINE void Indent(IOutputStream& out, ui32 indentation) {
  111. char* whitespaces = reinterpret_cast<char*>(alloca(indentation));
  112. memset(whitespaces, ' ', indentation);
  113. out.Write(whitespaces, indentation);
  114. }
  115. void ProgramLinesWithErrors(
  116. const TString& programText,
  117. const TVector<TIssue>& errors,
  118. TMap<ui32, TStringBuf>& lines)
  119. {
  120. TVector<ui32> rows;
  121. for (const auto& topIssue: errors) {
  122. WalkThroughIssues(topIssue, false, [&](const TIssue& issue, ui16 /*level*/) {
  123. for (ui32 row = issue.Position.Row; row <= issue.EndPosition.Row; row++) {
  124. rows.push_back(row);
  125. }
  126. });
  127. }
  128. std::sort(rows.begin(), rows.end());
  129. auto prog = StringSplitter(programText).Split('\n');
  130. auto progIt = prog.begin(), progEnd = prog.end();
  131. ui32 progRow = 1;
  132. for (ui32 row: rows) {
  133. while (progRow < row && progIt != progEnd) {
  134. ++progRow;
  135. ++progIt;
  136. }
  137. if (progIt != progEnd) {
  138. lines[row] = progIt->Token();
  139. }
  140. }
  141. }
  142. } // namspace
  143. void TIssues::PrintTo(IOutputStream& out, bool oneLine) const
  144. {
  145. if (oneLine) {
  146. bool printWithSpace = false;
  147. if (Issues_.size() > 1) {
  148. printWithSpace = true;
  149. out << "[";
  150. }
  151. for (const auto& topIssue: Issues_) {
  152. WalkThroughIssues(topIssue, false, [&](const TIssue& issue, ui16 level) {
  153. if (level > 0) {
  154. out << " subissue: { ";
  155. } else {
  156. out << (printWithSpace ? " { " : "{ ");
  157. }
  158. issue.PrintTo(out, true);
  159. },
  160. [&](const TIssue&, ui16) {
  161. out << " }";
  162. });
  163. }
  164. if (Issues_.size() > 1) {
  165. out << " ]";
  166. }
  167. } else {
  168. for (const auto& topIssue: Issues_) {
  169. WalkThroughIssues(topIssue, false, [&](const TIssue& issue, ui16 level) {
  170. auto shift = level * 4;
  171. Indent(out, shift);
  172. out << issue << Endl;
  173. });
  174. }
  175. }
  176. }
  177. void TIssues::PrintWithProgramTo(
  178. IOutputStream& out,
  179. const TString& programFilename,
  180. const TString& programText) const
  181. {
  182. using namespace NColorizer;
  183. TMap<ui32, TStringBuf> lines;
  184. ProgramLinesWithErrors(programText, Issues_, lines);
  185. for (const TIssue& topIssue: Issues_) {
  186. WalkThroughIssues(topIssue, false, [&](const TIssue& issue, ui16 level) {
  187. auto shift = level * 4;
  188. Indent(out, shift);
  189. out << DarkGray() << programFilename << Old() << ':';
  190. out << Purple() << issue.Range() << Old();
  191. auto color = (issue.GetSeverity() == TSeverityIds::S_WARNING) ? Yellow() : LightRed();
  192. auto severityName = SeverityToString(issue.GetSeverity());
  193. out << color << ": "<< severityName << ": " << issue.GetMessage() << Old() << '\n';
  194. Indent(out, shift);
  195. if (issue.Position.HasValue()) {
  196. out << '\t' << lines[issue.Position.Row] << '\n';
  197. out << '\t';
  198. if (issue.Position.Column > 0) {
  199. Indent(out, issue.Position.Column - 1);
  200. }
  201. out << '^';
  202. }
  203. out << Endl;
  204. });
  205. }
  206. }
  207. TIssue ExceptionToIssue(const std::exception& e, const TPosition& pos) {
  208. TStringBuf messageBuf = e.what();
  209. auto parsedPos = TryParseTerminationMessage(messageBuf);
  210. auto issue = TIssue(parsedPos.GetOrElse(pos), messageBuf);
  211. const TErrorException* errorException = dynamic_cast<const TErrorException*>(&e);
  212. if (errorException) {
  213. issue.SetCode(errorException->GetCode(), ESeverity::TSeverityIds_ESeverityId_S_ERROR);
  214. } else {
  215. issue.SetCode(UNEXPECTED_ERROR, ESeverity::TSeverityIds_ESeverityId_S_FATAL);
  216. }
  217. return issue;
  218. }
  219. static constexpr TStringBuf TerminationMessageMarker = "Terminate was called, reason(";
  220. TMaybe<TPosition> TryParseTerminationMessage(TStringBuf& message) {
  221. size_t len = 0;
  222. size_t startPos = message.find(TerminationMessageMarker);
  223. size_t endPos = 0;
  224. if (startPos != TString::npos) {
  225. endPos = message.find(')', startPos + TerminationMessageMarker.size());
  226. if (endPos != TString::npos) {
  227. TStringBuf lenText = message.Tail(startPos + TerminationMessageMarker.size())
  228. .Trunc(endPos - startPos - TerminationMessageMarker.size());
  229. try {
  230. len = FromString<size_t>(lenText);
  231. } catch (const TFromStringException&) {
  232. len = 0;
  233. }
  234. }
  235. }
  236. if (len) {
  237. message = message.Tail(endPos + 3).Trunc(len);
  238. auto s = message;
  239. TMaybe<TStringBuf> file;
  240. TMaybe<TStringBuf> row;
  241. TMaybe<TStringBuf> column;
  242. GetNext(s, ':', file);
  243. GetNext(s, ':', row);
  244. GetNext(s, ':', column);
  245. ui32 rowValue, columnValue;
  246. if (file && row && column && TryFromString(*row, rowValue) && TryFromString(*column, columnValue)) {
  247. message = StripStringLeft(s);
  248. return TPosition(columnValue, rowValue, TString(*file));
  249. }
  250. }
  251. return Nothing();
  252. }
  253. } // namspace NYql
  254. template <>
  255. void Out<NYql::TPosition>(IOutputStream& out, const NYql::TPosition& pos) {
  256. out << (pos.File ? pos.File : "<main>");
  257. if (pos) {
  258. out << ":" << pos.Row << ':' << pos.Column;
  259. }
  260. }
  261. template<>
  262. void Out<NYql::TRange>(IOutputStream & out, const NYql::TRange & range) {
  263. if (range.IsRange()) {
  264. out << '[' << range.Position << '-' << range.EndPosition << ']';
  265. } else {
  266. out << range.Position;
  267. }
  268. }
  269. template <>
  270. void Out<NYql::TIssue>(IOutputStream& out, const NYql::TIssue& error) {
  271. error.PrintTo(out);
  272. }
  273. template <>
  274. void Out<NYql::TIssues>(IOutputStream& out, const NYql::TIssues& error) {
  275. error.PrintTo(out);
  276. }