1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2  * vim: set ts=8 sts=2 et sw=2 tw=80:
3  * This Source Code Form is subject to the terms of the Mozilla Public
4  * License, v. 2.0. If a copy of the MPL was not distributed with this
5  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 
7 #include "vm/ErrorReporting.h"
8 
9 #include <stdarg.h>
10 #include <utility>
11 
12 #include "jsexn.h"
13 #include "jsfriendapi.h"
14 
15 #include "js/Printf.h"    // JS_vsmprintf
16 #include "js/Warnings.h"  // JS::WarningReporter
17 #include "vm/GlobalObject.h"
18 #include "vm/JSContext.h"
19 
20 #include "vm/JSContext-inl.h"
21 
22 using namespace js;
23 
24 using JS::HandleObject;
25 using JS::HandleValue;
26 using JS::UniqueTwoByteChars;
27 
CallWarningReporter(JSContext * cx,JSErrorReport * reportp)28 void js::CallWarningReporter(JSContext* cx, JSErrorReport* reportp) {
29   MOZ_ASSERT(reportp->isWarning());
30 
31   if (JS::WarningReporter warningReporter = cx->runtime()->warningReporter) {
32     warningReporter(cx, reportp);
33   }
34 }
35 
throwError(JSContext * cx)36 void js::CompileError::throwError(JSContext* cx) {
37   if (isWarning()) {
38     CallWarningReporter(cx, this);
39     return;
40   }
41 
42   // If there's a runtime exception type associated with this error
43   // number, set that as the pending exception.  For errors occuring at
44   // compile time, this is very likely to be a JSEXN_SYNTAXERR.
45   ErrorToException(cx, this, nullptr, nullptr);
46 }
47 
operator ()(JSContext * cx)48 bool js::ReportExceptionClosure::operator()(JSContext* cx) {
49   cx->setPendingExceptionAndCaptureStack(exn_);
50   return false;
51 }
52 
ReportCompileWarning(JSContext * cx,ErrorMetadata && metadata,UniquePtr<JSErrorNotes> notes,unsigned errorNumber,va_list * args)53 bool js::ReportCompileWarning(JSContext* cx, ErrorMetadata&& metadata,
54                               UniquePtr<JSErrorNotes> notes,
55                               unsigned errorNumber, va_list* args) {
56   // On the main thread, report the error immediately. When compiling off
57   // thread, save the error so that the thread finishing the parse can report
58   // it later.
59   CompileError tempErr;
60   CompileError* err = &tempErr;
61   if (cx->isHelperThreadContext() && !cx->addPendingCompileError(&err)) {
62     return false;
63   }
64 
65   err->notes = std::move(notes);
66   err->isWarning_ = true;
67   err->errorNumber = errorNumber;
68 
69   err->filename = metadata.filename;
70   err->lineno = metadata.lineNumber;
71   err->column = metadata.columnNumber;
72   err->isMuted = metadata.isMuted;
73 
74   if (UniqueTwoByteChars lineOfContext = std::move(metadata.lineOfContext)) {
75     err->initOwnedLinebuf(lineOfContext.release(), metadata.lineLength,
76                           metadata.tokenOffset);
77   }
78 
79   if (!ExpandErrorArgumentsVA(cx, GetErrorMessage, nullptr, errorNumber,
80                               ArgumentsAreLatin1, err, *args)) {
81     return false;
82   }
83 
84   if (!cx->isHelperThreadContext()) {
85     err->throwError(cx);
86   }
87 
88   return true;
89 }
90 
ReportCompileErrorImpl(JSContext * cx,js::ErrorMetadata && metadata,js::UniquePtr<JSErrorNotes> notes,unsigned errorNumber,va_list * args,ErrorArgumentsType argumentsType)91 static void ReportCompileErrorImpl(JSContext* cx, js::ErrorMetadata&& metadata,
92                                    js::UniquePtr<JSErrorNotes> notes,
93                                    unsigned errorNumber, va_list* args,
94                                    ErrorArgumentsType argumentsType) {
95   // On the main thread, report the error immediately. When compiling off
96   // thread, save the error so that the thread finishing the parse can report
97   // it later.
98   js::CompileError tempErr;
99   js::CompileError* err = &tempErr;
100   if (cx->isHelperThreadContext() && !cx->addPendingCompileError(&err)) {
101     return;
102   }
103 
104   err->notes = std::move(notes);
105   err->isWarning_ = false;
106   err->errorNumber = errorNumber;
107 
108   err->filename = metadata.filename;
109   err->lineno = metadata.lineNumber;
110   err->column = metadata.columnNumber;
111   err->isMuted = metadata.isMuted;
112 
113   if (UniqueTwoByteChars lineOfContext = std::move(metadata.lineOfContext)) {
114     err->initOwnedLinebuf(lineOfContext.release(), metadata.lineLength,
115                           metadata.tokenOffset);
116   }
117 
118   if (!js::ExpandErrorArgumentsVA(cx, js::GetErrorMessage, nullptr, errorNumber,
119                                   argumentsType, err, *args)) {
120     return;
121   }
122 
123   if (!cx->isHelperThreadContext()) {
124     err->throwError(cx);
125   }
126 }
127 
ReportCompileErrorLatin1(JSContext * cx,ErrorMetadata && metadata,UniquePtr<JSErrorNotes> notes,unsigned errorNumber,va_list * args)128 void js::ReportCompileErrorLatin1(JSContext* cx, ErrorMetadata&& metadata,
129                                   UniquePtr<JSErrorNotes> notes,
130                                   unsigned errorNumber, va_list* args) {
131   ReportCompileErrorImpl(cx, std::move(metadata), std::move(notes), errorNumber,
132                          args, ArgumentsAreLatin1);
133 }
134 
ReportCompileErrorUTF8(JSContext * cx,ErrorMetadata && metadata,UniquePtr<JSErrorNotes> notes,unsigned errorNumber,va_list * args)135 void js::ReportCompileErrorUTF8(JSContext* cx, ErrorMetadata&& metadata,
136                                 UniquePtr<JSErrorNotes> notes,
137                                 unsigned errorNumber, va_list* args) {
138   ReportCompileErrorImpl(cx, std::move(metadata), std::move(notes), errorNumber,
139                          args, ArgumentsAreUTF8);
140 }
141 
ReportErrorToGlobal(JSContext * cx,Handle<GlobalObject * > global,HandleValue error)142 void js::ReportErrorToGlobal(JSContext* cx, Handle<GlobalObject*> global,
143                              HandleValue error) {
144   MOZ_ASSERT(!cx->isExceptionPending());
145 #ifdef DEBUG
146   // No assertSameCompartment version that doesn't take JSContext...
147   if (error.isObject()) {
148     AssertSameCompartment(global, &error.toObject());
149   }
150 #endif  // DEBUG
151   js::ReportExceptionClosure report(error);
152   PrepareScriptEnvironmentAndInvoke(cx, global, report);
153 }
154 
ReportError(JSContext * cx,JSErrorReport * reportp,JSErrorCallback callback,void * userRef)155 static void ReportError(JSContext* cx, JSErrorReport* reportp,
156                         JSErrorCallback callback, void* userRef) {
157   if (reportp->isWarning()) {
158     CallWarningReporter(cx, reportp);
159     return;
160   }
161 
162   // Check the error report, and set a JavaScript-catchable exception
163   // if the error is defined to have an associated exception.
164   ErrorToException(cx, reportp, callback, userRef);
165 }
166 
167 /*
168  * The given JSErrorReport object have been zeroed and must not outlive
169  * cx->fp() (otherwise owned fields may become invalid).
170  */
PopulateReportBlame(JSContext * cx,JSErrorReport * report)171 static void PopulateReportBlame(JSContext* cx, JSErrorReport* report) {
172   JS::Realm* realm = cx->realm();
173   if (!realm) {
174     return;
175   }
176 
177   /*
178    * Walk stack until we find a frame that is associated with a non-builtin
179    * rather than a builtin frame and which we're allowed to know about.
180    */
181   NonBuiltinFrameIter iter(cx, realm->principals());
182   if (iter.done()) {
183     return;
184   }
185 
186   report->filename = iter.filename();
187   if (iter.hasScript()) {
188     report->sourceId = iter.script()->scriptSource()->id();
189   }
190   uint32_t column;
191   report->lineno = iter.computeLine(&column);
192   report->column = FixupColumnForDisplay(column);
193   report->isMuted = iter.mutedErrors();
194 }
195 
196 class MOZ_RAII AutoMessageArgs {
197   size_t totalLength_;
198   /* only {0} thru {9} supported */
199   mozilla::Array<const char*, JS::MaxNumErrorArguments> args_;
200   mozilla::Array<size_t, JS::MaxNumErrorArguments> lengths_;
201   uint16_t count_;
202   bool allocatedElements_ : 1;
203 
204  public:
AutoMessageArgs()205   AutoMessageArgs() : totalLength_(0), count_(0), allocatedElements_(false) {
206     PodArrayZero(args_);
207   }
208 
~AutoMessageArgs()209   ~AutoMessageArgs() {
210     /* free the arguments only if we allocated them */
211     if (allocatedElements_) {
212       uint16_t i = 0;
213       while (i < count_) {
214         if (args_[i]) {
215           js_free((void*)args_[i]);
216         }
217         i++;
218       }
219     }
220   }
221 
args(size_t i) const222   const char* args(size_t i) const {
223     MOZ_ASSERT(i < count_);
224     return args_[i];
225   }
226 
totalLength() const227   size_t totalLength() const { return totalLength_; }
228 
lengths(size_t i) const229   size_t lengths(size_t i) const {
230     MOZ_ASSERT(i < count_);
231     return lengths_[i];
232   }
233 
count() const234   uint16_t count() const { return count_; }
235 
236   /* Gather the arguments into an array, and accumulate their sizes.
237    *
238    * We could template on the type of argsArg, but we're already trusting people
239    * to do the right thing with varargs, so might as well trust them on this
240    * part too.  Upstream consumers do assert that it's the right thing.  Also,
241    * if argsArg were strongly typed we'd still need casting below for this to
242    * compile, because typeArg is not known at compile-time here.
243    */
init(JSContext * cx,void * argsArg,uint16_t countArg,ErrorArgumentsType typeArg,va_list ap)244   bool init(JSContext* cx, void* argsArg, uint16_t countArg,
245             ErrorArgumentsType typeArg, va_list ap) {
246     MOZ_ASSERT(countArg > 0);
247 
248     count_ = countArg;
249 
250     for (uint16_t i = 0; i < count_; i++) {
251       switch (typeArg) {
252         case ArgumentsAreASCII:
253         case ArgumentsAreUTF8: {
254           const char* c = argsArg ? static_cast<const char**>(argsArg)[i]
255                                   : va_arg(ap, const char*);
256           args_[i] = c;
257           MOZ_ASSERT_IF(typeArg == ArgumentsAreASCII,
258                         JS::StringIsASCII(args_[i]));
259           lengths_[i] = strlen(args_[i]);
260           break;
261         }
262         case ArgumentsAreLatin1: {
263           MOZ_ASSERT(!argsArg);
264           const Latin1Char* latin1 = va_arg(ap, Latin1Char*);
265           size_t len = strlen(reinterpret_cast<const char*>(latin1));
266           mozilla::Range<const Latin1Char> range(latin1, len);
267           char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str();
268           if (!utf8) {
269             return false;
270           }
271 
272           args_[i] = utf8;
273           lengths_[i] = strlen(utf8);
274           allocatedElements_ = true;
275           break;
276         }
277         case ArgumentsAreUnicode: {
278           const char16_t* uc = argsArg
279                                    ? static_cast<const char16_t**>(argsArg)[i]
280                                    : va_arg(ap, const char16_t*);
281           size_t len = js_strlen(uc);
282           mozilla::Range<const char16_t> range(uc, len);
283           char* utf8 = JS::CharsToNewUTF8CharsZ(cx, range).c_str();
284           if (!utf8) {
285             return false;
286           }
287 
288           args_[i] = utf8;
289           lengths_[i] = strlen(utf8);
290           allocatedElements_ = true;
291           break;
292         }
293       }
294       totalLength_ += lengths_[i];
295     }
296     return true;
297   }
298 };
299 
300 /*
301  * The arguments from ap need to be packaged up into an array and stored
302  * into the report struct.
303  *
304  * The format string addressed by the error number may contain operands
305  * identified by the format {N}, where N is a decimal digit. Each of these
306  * is to be replaced by the Nth argument from the va_list. The complete
307  * message is placed into reportp->message_.
308  *
309  * Returns true if the expansion succeeds (can fail if out of memory).
310  *
311  * messageArgs is a `const char**` or a `const char16_t**` but templating on
312  * that is not worth it here because AutoMessageArgs takes a void* anyway, and
313  * using void* here simplifies our callers a bit.
314  */
315 template <typename T>
ExpandErrorArgumentsHelper(JSContext * cx,JSErrorCallback callback,void * userRef,const unsigned errorNumber,void * messageArgs,ErrorArgumentsType argumentsType,T * reportp,va_list ap)316 static bool ExpandErrorArgumentsHelper(JSContext* cx, JSErrorCallback callback,
317                                        void* userRef,
318                                        const unsigned errorNumber,
319                                        void* messageArgs,
320                                        ErrorArgumentsType argumentsType,
321                                        T* reportp, va_list ap) {
322   const JSErrorFormatString* efs;
323 
324   if (!callback) {
325     callback = GetErrorMessage;
326   }
327 
328   {
329     gc::AutoSuppressGC suppressGC(cx);
330     efs = callback(userRef, errorNumber);
331   }
332 
333   if (efs) {
334     if constexpr (std::is_same_v<T, JSErrorReport>) {
335       reportp->exnType = efs->exnType;
336     }
337 
338     MOZ_ASSERT(reportp->errorNumber == errorNumber);
339     reportp->errorMessageName = efs->name;
340 
341     MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII,
342                   JS::StringIsASCII(efs->format));
343 
344     uint16_t argCount = efs->argCount;
345     MOZ_RELEASE_ASSERT(argCount <= JS::MaxNumErrorArguments);
346     if (argCount > 0) {
347       /*
348        * Parse the error format, substituting the argument X
349        * for {X} in the format.
350        */
351       if (efs->format) {
352         const char* fmt;
353         char* out;
354 #ifdef DEBUG
355         int expandedArgs = 0;
356 #endif
357         size_t expandedLength;
358         size_t len = strlen(efs->format);
359 
360         AutoMessageArgs args;
361         if (!args.init(cx, messageArgs, argCount, argumentsType, ap)) {
362           return false;
363         }
364 
365         expandedLength = len - (3 * args.count()) /* exclude the {n} */
366                          + args.totalLength();
367 
368         /*
369          * Note - the above calculation assumes that each argument
370          * is used once and only once in the expansion !!!
371          */
372         char* utf8 = out = cx->pod_malloc<char>(expandedLength + 1);
373         if (!out) {
374           return false;
375         }
376 
377         fmt = efs->format;
378         while (*fmt) {
379           if (*fmt == '{') {
380             if (mozilla::IsAsciiDigit(fmt[1])) {
381               int d = AsciiDigitToNumber(fmt[1]);
382               MOZ_RELEASE_ASSERT(d < args.count());
383               strncpy(out, args.args(d), args.lengths(d));
384               out += args.lengths(d);
385               fmt += 3;
386 #ifdef DEBUG
387               expandedArgs++;
388 #endif
389               continue;
390             }
391           }
392           *out++ = *fmt++;
393         }
394         MOZ_ASSERT(expandedArgs == args.count());
395         *out = 0;
396 
397         reportp->initOwnedMessage(utf8);
398       }
399     } else {
400       /* Non-null messageArgs should have at least one non-null arg. */
401       MOZ_ASSERT(!messageArgs);
402       /*
403        * Zero arguments: the format string (if it exists) is the
404        * entire message.
405        */
406       if (efs->format) {
407         reportp->initBorrowedMessage(efs->format);
408       }
409     }
410   }
411   if (!reportp->message()) {
412     /* where's the right place for this ??? */
413     const char* defaultErrorMessage =
414         "No error message available for error number %d";
415     size_t nbytes = strlen(defaultErrorMessage) + 16;
416     char* message = cx->pod_malloc<char>(nbytes);
417     if (!message) {
418       return false;
419     }
420     snprintf(message, nbytes, defaultErrorMessage, errorNumber);
421     reportp->initOwnedMessage(message);
422   }
423   return true;
424 }
425 
ExpandErrorArgumentsVA(JSContext * cx,JSErrorCallback callback,void * userRef,const unsigned errorNumber,const char16_t ** messageArgs,ErrorArgumentsType argumentsType,JSErrorReport * reportp,va_list ap)426 bool js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
427                                 void* userRef, const unsigned errorNumber,
428                                 const char16_t** messageArgs,
429                                 ErrorArgumentsType argumentsType,
430                                 JSErrorReport* reportp, va_list ap) {
431   MOZ_ASSERT(argumentsType == ArgumentsAreUnicode);
432   return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber,
433                                     messageArgs, argumentsType, reportp, ap);
434 }
435 
ExpandErrorArgumentsVA(JSContext * cx,JSErrorCallback callback,void * userRef,const unsigned errorNumber,const char ** messageArgs,ErrorArgumentsType argumentsType,JSErrorReport * reportp,va_list ap)436 bool js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
437                                 void* userRef, const unsigned errorNumber,
438                                 const char** messageArgs,
439                                 ErrorArgumentsType argumentsType,
440                                 JSErrorReport* reportp, va_list ap) {
441   MOZ_ASSERT(argumentsType != ArgumentsAreUnicode);
442   return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber,
443                                     messageArgs, argumentsType, reportp, ap);
444 }
445 
ExpandErrorArgumentsVA(JSContext * cx,JSErrorCallback callback,void * userRef,const unsigned errorNumber,ErrorArgumentsType argumentsType,JSErrorReport * reportp,va_list ap)446 bool js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
447                                 void* userRef, const unsigned errorNumber,
448                                 ErrorArgumentsType argumentsType,
449                                 JSErrorReport* reportp, va_list ap) {
450   return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber, nullptr,
451                                     argumentsType, reportp, ap);
452 }
453 
ExpandErrorArgumentsVA(JSContext * cx,JSErrorCallback callback,void * userRef,const unsigned errorNumber,const char16_t ** messageArgs,ErrorArgumentsType argumentsType,JSErrorNotes::Note * notep,va_list ap)454 bool js::ExpandErrorArgumentsVA(JSContext* cx, JSErrorCallback callback,
455                                 void* userRef, const unsigned errorNumber,
456                                 const char16_t** messageArgs,
457                                 ErrorArgumentsType argumentsType,
458                                 JSErrorNotes::Note* notep, va_list ap) {
459   return ExpandErrorArgumentsHelper(cx, callback, userRef, errorNumber,
460                                     messageArgs, argumentsType, notep, ap);
461 }
462 
ReportErrorNumberVA(JSContext * cx,IsWarning isWarning,JSErrorCallback callback,void * userRef,const unsigned errorNumber,ErrorArgumentsType argumentsType,va_list ap)463 bool js::ReportErrorNumberVA(JSContext* cx, IsWarning isWarning,
464                              JSErrorCallback callback, void* userRef,
465                              const unsigned errorNumber,
466                              ErrorArgumentsType argumentsType, va_list ap) {
467   JSErrorReport report;
468   report.isWarning_ = isWarning == IsWarning::Yes;
469   report.errorNumber = errorNumber;
470   PopulateReportBlame(cx, &report);
471 
472   if (!ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber, argumentsType,
473                               &report, ap)) {
474     return false;
475   }
476 
477   ReportError(cx, &report, callback, userRef);
478 
479   return report.isWarning();
480 }
481 
482 template <typename CharT>
ExpandErrorArguments(JSContext * cx,JSErrorCallback callback,void * userRef,const unsigned errorNumber,const CharT ** messageArgs,js::ErrorArgumentsType argumentsType,JSErrorReport * reportp,...)483 static bool ExpandErrorArguments(JSContext* cx, JSErrorCallback callback,
484                                  void* userRef, const unsigned errorNumber,
485                                  const CharT** messageArgs,
486                                  js::ErrorArgumentsType argumentsType,
487                                  JSErrorReport* reportp, ...) {
488   va_list ap;
489   va_start(ap, reportp);
490   bool expanded =
491       js::ExpandErrorArgumentsVA(cx, callback, userRef, errorNumber,
492                                  messageArgs, argumentsType, reportp, ap);
493   va_end(ap);
494   return expanded;
495 }
496 
497 template <js::ErrorArgumentsType argType, typename CharT>
ReportErrorNumberArray(JSContext * cx,IsWarning isWarning,JSErrorCallback callback,void * userRef,const unsigned errorNumber,const CharT ** args)498 static bool ReportErrorNumberArray(JSContext* cx, IsWarning isWarning,
499                                    JSErrorCallback callback, void* userRef,
500                                    const unsigned errorNumber,
501                                    const CharT** args) {
502   static_assert(
503       (argType == ArgumentsAreUnicode && std::is_same_v<CharT, char16_t>) ||
504           (argType != ArgumentsAreUnicode && std::is_same_v<CharT, char>),
505       "Mismatch between character type and argument type");
506 
507   JSErrorReport report;
508   report.isWarning_ = isWarning == IsWarning::Yes;
509   report.errorNumber = errorNumber;
510   PopulateReportBlame(cx, &report);
511 
512   if (!ExpandErrorArguments(cx, callback, userRef, errorNumber, args, argType,
513                             &report)) {
514     return false;
515   }
516 
517   ReportError(cx, &report, callback, userRef);
518 
519   return report.isWarning();
520 }
521 
ReportErrorNumberUCArray(JSContext * cx,IsWarning isWarning,JSErrorCallback callback,void * userRef,const unsigned errorNumber,const char16_t ** args)522 bool js::ReportErrorNumberUCArray(JSContext* cx, IsWarning isWarning,
523                                   JSErrorCallback callback, void* userRef,
524                                   const unsigned errorNumber,
525                                   const char16_t** args) {
526   return ReportErrorNumberArray<ArgumentsAreUnicode>(
527       cx, isWarning, callback, userRef, errorNumber, args);
528 }
529 
ReportErrorNumberUTF8Array(JSContext * cx,IsWarning isWarning,JSErrorCallback callback,void * userRef,const unsigned errorNumber,const char ** args)530 bool js::ReportErrorNumberUTF8Array(JSContext* cx, IsWarning isWarning,
531                                     JSErrorCallback callback, void* userRef,
532                                     const unsigned errorNumber,
533                                     const char** args) {
534   return ReportErrorNumberArray<ArgumentsAreUTF8>(cx, isWarning, callback,
535                                                   userRef, errorNumber, args);
536 }
537 
ReportErrorVA(JSContext * cx,IsWarning isWarning,const char * format,js::ErrorArgumentsType argumentsType,va_list ap)538 bool js::ReportErrorVA(JSContext* cx, IsWarning isWarning, const char* format,
539                        js::ErrorArgumentsType argumentsType, va_list ap) {
540   JSErrorReport report;
541 
542   UniqueChars message(JS_vsmprintf(format, ap));
543   if (!message) {
544     ReportOutOfMemory(cx);
545     return false;
546   }
547 
548   MOZ_ASSERT_IF(argumentsType == ArgumentsAreASCII,
549                 JS::StringIsASCII(message.get()));
550 
551   report.isWarning_ = isWarning == IsWarning::Yes;
552   report.errorNumber = JSMSG_USER_DEFINED_ERROR;
553   if (argumentsType == ArgumentsAreASCII || argumentsType == ArgumentsAreUTF8) {
554     report.initOwnedMessage(message.release());
555   } else {
556     MOZ_ASSERT(argumentsType == ArgumentsAreLatin1);
557     Latin1Chars latin1(message.get(), strlen(message.get()));
558     UTF8CharsZ utf8(JS::CharsToNewUTF8CharsZ(cx, latin1));
559     if (!utf8) {
560       return false;
561     }
562     report.initOwnedMessage(reinterpret_cast<const char*>(utf8.get()));
563   }
564   PopulateReportBlame(cx, &report);
565 
566   ReportError(cx, &report, nullptr, nullptr);
567 
568   return report.isWarning();
569 }
570