Coverage Report

Created: 2026-01-05 06:35

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/shaderc/libshaderc_util/src/message.cc
Line
Count
Source
1
// Copyright 2015 The Shaderc Authors. All rights reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     http://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include "libshaderc_util/message.h"
16
17
#include <algorithm>
18
#include <cstring>
19
#include <iostream>
20
#include <iterator>
21
22
namespace shaderc_util {
23
24
namespace {
25
26
// Given a message, deduces and returns its type. If the message type is
27
// recognized, advances *message past the prefix indicating the type. Otherwise,
28
// leaves *message unchanged and returns MessageType::Unknown.
29
673k
MessageType DeduceMessageType(string_piece* message) {
30
673k
  static const char kErrorMessage[] = "ERROR: ";
31
673k
  static const char kWarningMessage[] = "WARNING: ";
32
673k
  static const char kGlobalWarningMessage[] = "Warning, ";
33
34
673k
  if (message->starts_with(kErrorMessage)) {
35
294k
    *message = message->substr(::strlen(kErrorMessage));
36
294k
    return MessageType::Error;
37
379k
  } else if (message->starts_with(kWarningMessage)) {
38
186
    *message = message->substr(::strlen(kWarningMessage));
39
186
    return MessageType::Warning;
40
379k
  } else if (message->starts_with(kGlobalWarningMessage)) {
41
0
    *message = message->substr(::strlen(kGlobalWarningMessage));
42
0
    return MessageType::GlobalWarning;
43
0
  }
44
379k
  return MessageType::Unknown;
45
673k
}
46
47
// Deduces a location specification from the given message. A location
48
// specification is of the form "<source-name>:<line-number>:" and a trailing
49
// space.  If the deduction is successful, returns true and updates source_name
50
// and line_number to the deduced source name and line numer respectively. The
51
// prefix standing for the location specification in message is skipped.
52
// Otherwise, returns false and keeps all parameters untouched.
53
bool DeduceLocationSpec(string_piece* message, string_piece* source_name,
54
294k
                        string_piece* line_number) {
55
294k
  if (!message || message->empty()) {
56
0
    return false;
57
0
  }
58
59
  // When we find a pattern like this:
60
  //     colon
61
  //     digits
62
  //     colon
63
  //     space
64
  // Then deduce that the source_name is the text before the first colon,
65
  // the line number is the digits, and the message is the text after the
66
  // second colon.
67
68
294k
  const size_t size = message->size();
69
294k
  if (size <= 4) {
70
    // A valid message must have a colon, a digit, a colon, and a space.
71
0
    return false;
72
0
  }
73
  // The last possible position of the first colon.
74
294k
  const size_t first_colon_cutoff = size - 4;
75
  // The last possible position of the second colon.
76
294k
  const size_t next_colon_cutoff = size - 2;
77
78
294k
  for (size_t first_colon_pos = message->find_first_of(':'), next_colon_pos = 0;
79
80
       // There is a first colon, and it's not too close to the end
81
295k
       (first_colon_pos != string_piece::npos) &&
82
293k
       (first_colon_pos <= first_colon_cutoff);
83
84
       // Try the next pair of colons.
85
294k
       first_colon_pos = next_colon_pos) {
86
    // We're guaranteed to have at least 3 more characters.
87
    // Guarantee progress toward the end of the string.
88
293k
    next_colon_pos = message->find_first_of(':', first_colon_pos + 1);
89
293k
    if ((next_colon_pos == string_piece::npos) ||
90
293k
        (next_colon_pos > next_colon_cutoff)) {
91
      // No valid solution.
92
424
      return false;
93
424
    }
94
293k
    if (first_colon_pos + 1 == next_colon_pos) {
95
      // There is no room for digits.
96
0
      continue;
97
0
    }
98
293k
    if ((message->data()[next_colon_pos + 1] != ' ')) {
99
      // There is no space character after the second colon.
100
531
      continue;
101
531
    }
102
292k
    if (message->find_first_not_of("0123456789", first_colon_pos + 1) ==
103
292k
        next_colon_pos) {
104
      // We found the first solution.
105
292k
      *source_name = message->substr(0, first_colon_pos);
106
292k
      *line_number = message->substr(first_colon_pos + 1,
107
292k
                                     next_colon_pos - 1 - first_colon_pos);
108
292k
      *message = message->substr(next_colon_pos + 2);
109
292k
      return true;
110
292k
    }
111
292k
  }
112
113
1.58k
  return false;
114
294k
}
115
116
// Returns true if the given message is a summary message.
117
2.00k
bool IsSummaryMessage(const string_piece& message) {
118
2.00k
  const size_t space_loc = message.find_first_of(' ');
119
2.00k
  if (space_loc == string_piece::npos) return false;
120
2.00k
  const string_piece number = message.substr(0, space_loc);
121
2.00k
  const string_piece rest = message.substr(space_loc + 1);
122
2.00k
  if (!std::all_of(number.begin(), number.end(), ::isdigit)) return false;
123
1.58k
  if (!rest.starts_with("compilation errors.")) return false;
124
1.58k
  return true;
125
1.58k
}
126
127
}  // anonymous namespace
128
129
MessageType ParseGlslangOutput(const string_piece& message,
130
                               bool warnings_as_errors, bool suppress_warnings,
131
                               string_piece* source_name,
132
673k
                               string_piece* line_number, string_piece* rest) {
133
673k
  string_piece rest_of_message(message);
134
673k
  source_name->clear();
135
673k
  line_number->clear();
136
673k
  rest->clear();
137
138
  // The glslang warning/error messages are typically of the following form:
139
  //   <message-type> <location-specification> <message-body>
140
  //
141
  // <message-type> can be "WARNING:", "ERROR:", or "Warning, ". "WARNING:"
142
  // means a warning message for a certain line, while "Warning, " means a
143
  // global one.
144
  //
145
  // <location-specification> is of the form:
146
  //   <filename-or-string-number>:<line-number>:
147
  // It doesn't exist if the warning/error message is a global one.
148
  //
149
  // See Glslang's TInfoSink class implementation for details.
150
151
673k
  bool is_error = false;
152
153
  // Handle <message-type>.
154
673k
  switch (DeduceMessageType(&rest_of_message)) {
155
186
    case MessageType::Warning:
156
186
      if (suppress_warnings) return MessageType::Ignored;
157
75
      break;
158
294k
    case MessageType::Error:
159
294k
      is_error = true;
160
294k
      break;
161
0
    case MessageType::GlobalWarning:
162
0
      if (suppress_warnings) return MessageType::Ignored;
163
0
      *rest = rest_of_message;
164
0
      return warnings_as_errors ? MessageType::GlobalError
165
0
                                : MessageType::GlobalWarning;
166
379k
    case MessageType::Unknown:
167
379k
      *rest = rest_of_message;
168
379k
      return MessageType::Unknown;
169
0
    default:
170
0
      break;
171
673k
  }
172
173
294k
  rest_of_message = rest_of_message.strip_whitespace();
174
294k
  if (rest_of_message.empty()) return MessageType::Unknown;
175
176
  // Now we have stripped the <message-type>. Try to see if we can find
177
  // a <location-specification>.
178
294k
  if (DeduceLocationSpec(&rest_of_message, source_name, line_number)) {
179
292k
    *rest = rest_of_message;
180
292k
    return (is_error || warnings_as_errors) ? MessageType::Error
181
292k
                                            : MessageType::Warning;
182
292k
  } else {
183
    // No <location-specification>. This is a global warning/error message.
184
    // A special kind of global message is summary message, which should
185
    // start with a number.
186
2.00k
    *rest = rest_of_message;
187
2.00k
    if (IsSummaryMessage(rest_of_message)) {
188
1.58k
      return (is_error || warnings_as_errors) ? MessageType::ErrorSummary
189
1.58k
                                              : MessageType::WarningSummary;
190
1.58k
    }
191
428
    return (is_error || warnings_as_errors) ? MessageType::GlobalError
192
428
                                            : MessageType::GlobalWarning;
193
2.00k
  }
194
0
  return MessageType::Unknown;
195
294k
}
196
197
bool PrintFilteredErrors(const string_piece& file_name,
198
                         std::ostream* error_stream, bool warnings_as_errors,
199
                         bool suppress_warnings, const char* error_list,
200
2.25k
                         size_t* total_warnings, size_t* total_errors) {
201
2.25k
  const char* ignored_error_strings[] = {
202
2.25k
      "Warning, version 310 is not yet complete; most version-specific "
203
2.25k
      "features are present, but some are missing.",
204
2.25k
      "Warning, version 400 is not yet complete; most version-specific "
205
2.25k
      "features are present, but some are missing.",
206
2.25k
      "Warning, version 410 is not yet complete; most version-specific "
207
2.25k
      "features are present, but some are missing.",
208
2.25k
      "Warning, version 420 is not yet complete; most version-specific "
209
2.25k
      "features are present, but some are missing.",
210
2.25k
      "Warning, version 430 is not yet complete; most version-specific "
211
2.25k
      "features are present, but some are missing.",
212
2.25k
      "Warning, version 440 is not yet complete; most version-specific "
213
2.25k
      "features are present, but some are missing.",
214
2.25k
      "Warning, version 450 is not yet complete; most version-specific "
215
2.25k
      "features are present, but some are missing.",
216
2.25k
      "Linked vertex stage:", "Linked fragment stage:",
217
2.25k
      "Linked tessellation control stage:",
218
2.25k
      "Linked tessellation evaluation stage:", "Linked geometry stage:",
219
2.25k
      "Linked compute stage:", ""};
220
2.25k
  size_t existing_total_errors = *total_errors;
221
2.25k
  string_piece error_messages(error_list);
222
675k
  for (const string_piece& message : error_messages.get_fields('\n')) {
223
675k
    if (std::find(std::begin(ignored_error_strings),
224
675k
                  std::end(ignored_error_strings),
225
675k
                  message) == std::end(ignored_error_strings)) {
226
673k
      string_piece source_name;
227
673k
      string_piece line_number;
228
673k
      string_piece rest;
229
673k
      const MessageType type =
230
673k
          ParseGlslangOutput(message, warnings_as_errors, suppress_warnings,
231
673k
                             &source_name, &line_number, &rest);
232
673k
      string_piece name = file_name;
233
673k
      if (!source_name.empty()) {
234
        // -1 is the string number for the preamble injected by us.
235
292k
        name = source_name == "-1" ? "<command line>" : source_name;
236
292k
      }
237
673k
      switch (type) {
238
292k
        case MessageType::Error:
239
292k
        case MessageType::Warning:
240
292k
          assert(!name.empty() && !line_number.empty() && !rest.empty());
241
292k
          *error_stream << name << ":" << line_number << ": "
242
292k
                        << (type == MessageType::Error ? "error: "
243
292k
                                                       : "warning: ")
244
292k
                        << rest.strip_whitespace() << std::endl;
245
292k
          *total_errors += type == MessageType::Error;
246
292k
          *total_warnings += type == MessageType::Warning;
247
292k
          break;
248
1.58k
        case MessageType::ErrorSummary:
249
1.58k
        case MessageType::WarningSummary:
250
1.58k
          break;
251
428
        case MessageType::GlobalError:
252
428
        case MessageType::GlobalWarning:
253
428
          assert(!rest.empty());
254
428
          *total_errors += type == MessageType::GlobalError;
255
428
          *total_warnings += type == MessageType::GlobalWarning;
256
428
          *error_stream << name << ": "
257
428
                        << (type == MessageType::GlobalError ? "error"
258
428
                                                             : "warning")
259
428
                        << ": " << rest.strip_whitespace() << std::endl;
260
428
          break;
261
379k
        case MessageType::Unknown:
262
379k
          *error_stream << name << ":";
263
379k
          *error_stream << " " << message << std::endl;
264
379k
          break;
265
111
        case MessageType::Ignored:
266
111
          break;
267
673k
      }
268
673k
    }
269
675k
  }
270
2.25k
  return (existing_total_errors == *total_errors);
271
2.25k
}
272
273
// Outputs the number of warnings and errors if there are any.
274
void OutputMessages(std::ostream* error_stream, size_t total_warnings,
275
0
                    size_t total_errors) {
276
0
  if (total_warnings > 0 || total_errors > 0) {
277
0
    if (total_warnings > 0 && total_errors > 0) {
278
0
      *error_stream << total_warnings << " warning"
279
0
                    << (total_warnings > 1 ? "s" : "") << " and "
280
0
                    << total_errors << " error" << (total_errors > 1 ? "s" : "")
281
0
                    << " generated." << std::endl;
282
0
    } else if (total_warnings > 0) {
283
0
      *error_stream << total_warnings << " warning"
284
0
                    << (total_warnings > 1 ? "s" : "") << " generated."
285
0
                    << std::endl;
286
0
    } else if (total_errors > 0) {
287
0
      *error_stream << total_errors << " error" << (total_errors > 1 ? "s" : "")
288
0
                    << " generated." << std::endl;
289
0
    }
290
0
  }
291
0
}
292
293
}  // namespace glslc