yql_issue.cpp 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341
  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,
  181. bool colorize) const
  182. {
  183. using namespace NColorizer;
  184. TMap<ui32, TStringBuf> lines;
  185. ProgramLinesWithErrors(programText, Issues_, lines);
  186. for (const TIssue& topIssue: Issues_) {
  187. WalkThroughIssues(topIssue, false, [&](const TIssue& issue, ui16 level) {
  188. auto shift = level * 4;
  189. Indent(out, shift);
  190. if (colorize) {
  191. out << DarkGray();
  192. }
  193. out << programFilename;
  194. if (colorize) {
  195. out << Old();
  196. }
  197. out << ':';
  198. if (colorize) {
  199. out << Purple();
  200. }
  201. out << issue.Range();
  202. if (colorize) {
  203. out << Old();
  204. }
  205. auto severityName = SeverityToString(issue.GetSeverity());
  206. if (colorize) {
  207. if (issue.GetSeverity() == TSeverityIds::S_INFO) {
  208. out << LightGreen();
  209. } else if (issue.GetSeverity() == TSeverityIds::S_WARNING) {
  210. out << Yellow();
  211. } else {
  212. out << LightRed();
  213. }
  214. }
  215. out << ": "<< severityName << ": " << issue.GetMessage();
  216. if (colorize) {
  217. out << Old();
  218. }
  219. out << '\n';
  220. Indent(out, shift);
  221. if (issue.Position.HasValue()) {
  222. out << '\t' << lines[issue.Position.Row] << '\n';
  223. out << '\t';
  224. if (issue.Position.Column > 0) {
  225. Indent(out, issue.Position.Column - 1);
  226. }
  227. out << '^';
  228. }
  229. out << Endl;
  230. });
  231. }
  232. }
  233. TIssue ExceptionToIssue(const std::exception& e, const TPosition& pos) {
  234. TStringBuf messageBuf = e.what();
  235. auto parsedPos = TryParseTerminationMessage(messageBuf);
  236. auto issue = TIssue(parsedPos.GetOrElse(pos), messageBuf);
  237. const TErrorException* errorException = dynamic_cast<const TErrorException*>(&e);
  238. if (errorException) {
  239. issue.SetCode(errorException->GetCode(), ESeverity::TSeverityIds_ESeverityId_S_ERROR);
  240. } else {
  241. issue.SetCode(UNEXPECTED_ERROR, ESeverity::TSeverityIds_ESeverityId_S_FATAL);
  242. }
  243. return issue;
  244. }
  245. static constexpr TStringBuf TerminationMessageMarker = "Terminate was called, reason(";
  246. TMaybe<TPosition> TryParseTerminationMessage(TStringBuf& message) {
  247. size_t len = 0;
  248. size_t startPos = message.find(TerminationMessageMarker);
  249. size_t endPos = 0;
  250. if (startPos != TString::npos) {
  251. endPos = message.find(')', startPos + TerminationMessageMarker.size());
  252. if (endPos != TString::npos) {
  253. TStringBuf lenText = message.Tail(startPos + TerminationMessageMarker.size())
  254. .Trunc(endPos - startPos - TerminationMessageMarker.size());
  255. try {
  256. len = FromString<size_t>(lenText);
  257. } catch (const TFromStringException&) {
  258. len = 0;
  259. }
  260. }
  261. }
  262. if (len) {
  263. message = message.Tail(endPos + 3).Trunc(len);
  264. auto s = message;
  265. TMaybe<TStringBuf> file;
  266. TMaybe<TStringBuf> row;
  267. TMaybe<TStringBuf> column;
  268. GetNext(s, ':', file);
  269. GetNext(s, ':', row);
  270. GetNext(s, ':', column);
  271. ui32 rowValue, columnValue;
  272. if (file && row && column && TryFromString(*row, rowValue) && TryFromString(*column, columnValue)) {
  273. message = StripStringLeft(s);
  274. return TPosition(columnValue, rowValue, TString(*file));
  275. }
  276. }
  277. return Nothing();
  278. }
  279. } // namspace NYql
  280. template <>
  281. void Out<NYql::TPosition>(IOutputStream& out, const NYql::TPosition& pos) {
  282. out << (pos.File ? pos.File : "<main>");
  283. if (pos) {
  284. out << ":" << pos.Row << ':' << pos.Column;
  285. }
  286. }
  287. template<>
  288. void Out<NYql::TRange>(IOutputStream & out, const NYql::TRange & range) {
  289. if (range.IsRange()) {
  290. out << '[' << range.Position << '-' << range.EndPosition << ']';
  291. } else {
  292. out << range.Position;
  293. }
  294. }
  295. template <>
  296. void Out<NYql::TIssue>(IOutputStream& out, const NYql::TIssue& error) {
  297. error.PrintTo(out);
  298. }
  299. template <>
  300. void Out<NYql::TIssues>(IOutputStream& out, const NYql::TIssues& error) {
  301. error.PrintTo(out);
  302. }