1 /*
2  * Error.hpp
3  *
4  * Copyright (C) 2021 by RStudio, PBC
5  *
6  * Unless you have received this program directly from RStudio pursuant to the terms of a commercial license agreement
7  * with RStudio, then this program is licensed to you under the following terms:
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
10  * documentation files (the "Software"), to deal in the Software without restriction, including without limitation the
11  * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
12  * permit persons to whom the Software is furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in all copies or substantial portions of the
15  * Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18  * WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
19  * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
20  * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  *
22  */
23 
24 #ifndef SHARED_CORE_ERROR_HPP
25 #define SHARED_CORE_ERROR_HPP
26 
27 #include <string>
28 #include <system_error>
29 #include <vector>
30 
31 #include <boost/current_function.hpp>
32 #include <boost/system/error_code.hpp>
33 
34 #include "Logger.hpp"
35 #include "PImpl.hpp"
36 
37 namespace rstudio {
38 namespace core {
39 
40 class FilePath;
41 class Error;
42 class Success;
43 
44 /**
45  * @brief A class which can be derived from in order to prevent child classes from being derived from further.
46  */
47 class ErrorLock
48 {
49    friend class Error;
50    friend class Success;
51 
52    /**
53     * @brief Virtual Destructor.
54     */
55    virtual ~ErrorLock() = default;
56 
57 private:
58    /**
59     * @brief Private constructor to prevent further derivation of Error and Success.
60     */
61    ErrorLock() = default;
62 
63    /**
64     * @brief Private copy constructor to prevent further derivation of Error and Success.
65     */
66    ErrorLock(const ErrorLock&) = default;
67 };
68 
69 /**
70  * @brief Class which represents the location of an error.
71  */
72 class ErrorLocation
73 {
74 public:
75    /**
76     * @brief Default constructor.
77     */
78    ErrorLocation();
79 
80    /**
81     * @brief Copy constructor.
82     *
83     * @param in_other   The error location to move to this.
84     */
85    ErrorLocation(const ErrorLocation& in_other);
86 
87    /**
88     * @brief Move constructor.
89     *
90     * @param in_other   The error location to move to this.
91     */
92    ErrorLocation(ErrorLocation&& in_other) noexcept;
93 
94    /**
95     * @brief Constructor.
96     *
97     * @param in_function    The function in which the error occurred.
98     * @param in_file        The file in which the error occurred.
99     * @param in_line        The line at which the error occurred.
100     */
101    ErrorLocation(const char* in_function, const char* in_file, long in_line);
102 
103    /**
104     * @brief Assignment operator.
105     *
106     * @param in_other    The location to copy to this location.
107     *
108     * @return A reference to this location.
109     */
110    ErrorLocation& operator=(const ErrorLocation& in_other);
111 
112    /**
113     * @brief Equality comparison operator.
114     *
115     * @param in_location    The location to compare this location with.
116     *
117     * @return True if in_location is the same as this location; false otherwise.
118     */
119    bool operator==(const ErrorLocation& in_location) const;
120 
121    /**
122     * @brief Formats the error location as a string.
123     *
124     * @return The error location formatted as a string.
125     */
126    std::string asString() const;
127 
128    /**
129     * @brief Gets the file where the error occurred.
130     *
131     * @return The file where the error occurred.
132     */
133    const std::string& getFile() const;
134 
135    /**
136     * @brief Gets the function where the error occurred.
137     *
138     * @return The function where the error occurred.
139     */
140    const std::string& getFunction() const;
141 
142    /**
143     * @brief Gets the line where the error occurred.
144     *
145     * @return The line where the error occurred.
146     */
147    long getLine() const;
148 
149    /**
150     * @brief Checks whether the location is set.
151     *
152     * @return True if a location has been set; false otherwise.
153     */
154    bool hasLocation() const;
155 
156 private:
157    // The private implementation of ErrorLocation.
158    PRIVATE_IMPL(m_impl);
159 };
160 
161 /**
162  * @brief Convenience typedef for error properties.
163  */
164 typedef std::vector<std::pair<std::string, std::string> > ErrorProperties;
165 
166 
167 /**
168  * @brief Class which represents an error.
169  *
170  * This class should not be derived from since it is returned by value throughout the SDK. Instead, create helper
171  * functions for each "subclass" of Error that would be desired.
172  */
173 class Error : public virtual ErrorLock
174 {
175 public:
176    /**
177     * @brief Constructor.
178     */
179    Error();
180 
181    /**
182     * @brief Copy constructor.
183     *
184     * @param in_other   The error to copy.
185     */
186    Error(const Error& in_other);
187 
188    /**
189     * @brief Constructor.
190     *
191     * @param in_ec              The boost error code to convert from.
192     * @param in_location        The location of the error.
193     */
194    Error(const boost::system::error_code& in_ec, const ErrorLocation& in_location);
195 
196    /**
197     * @brief Constructor.
198     *
199     * @param in_ec              The boost error code to convert from.
200     * @param in_cause           The error which caused this error.
201     * @param in_location        The location of the error.
202     */
203    Error(const boost::system::error_code& in_ec, const Error& in_cause, const ErrorLocation& in_location);
204 
205    /**
206     * @brief Constructor.
207     *
208     * @param in_ec              The boost error code to convert from.
209     * @param in_message         The detailed error message. (e.g. "The JobNetworkRequest is not supported by this
210     *                           plugin.")
211     * @param in_location        The location of the error.
212     */
213    Error(const boost::system::error_code& in_ec, std::string in_message, const ErrorLocation& in_location);
214 
215    /**
216     * @brief Constructor.
217     *
218     * @param in_ec              The boost error code to convert from.
219     * @param in_message         The detailed error message. (e.g. "The JobNetworkRequest is not supported by this
220     *                           plugin.")
221     * @param in_cause           The error which caused this error.
222     * @param in_location        The location of the error.
223     */
224    Error(const boost::system::error_code& in_ec,
225          std::string in_message,
226          const Error& in_cause,
227          const ErrorLocation& in_location);
228 
229    /**
230     * @brief Constructor.
231     *
232     * @param in_ec              The boost error condition to convert from.
233     * @param in_location        The location of the error.
234     */
235    Error(const boost::system::error_condition& in_ec, const ErrorLocation& in_location);
236 
237    /**
238     * @brief Constructor.
239     *
240     * @param in_ec              The boost error condition to convert from.
241     * @param in_cause           The error which caused this error.
242     * @param in_location        The location of the error.
243     */
244    Error(const boost::system::error_condition& in_ec, const Error& in_cause, const ErrorLocation& in_location);
245 
246    /**
247     * @brief Constructor.
248     *
249     * @param in_ec              The boost error condition to convert from.
250     * @param in_message         The detailed error message. (e.g. "The JobNetworkRequest is not supported by this
251     *                           plugin.")
252     * @param in_location        The location of the error.
253     */
254    Error(const boost::system::error_condition& in_ec, std::string in_message, const ErrorLocation& in_location);
255 
256    /**
257     * @brief Constructor.
258     *
259     * @param in_ec              The boost error condition to convert from.
260     * @param in_message         The detailed error message. (e.g. "The JobNetworkRequest is not supported by this
261     *                           plugin.")
262     * @param in_cause           The error which caused this error.
263     * @param in_location        The location of the error.
264     */
265    Error(const boost::system::error_condition& in_ec,
266          std::string in_message,
267          const Error& in_cause,
268          const ErrorLocation& in_location);
269 
270    /**
271     * @brief Constructor.
272     *
273     * @param in_name            A contextual or categorical name for the error. (e.g. "RequestNotSupported")
274     * @param in_code            The non-zero error code. Note that an error code of zero indicates success. (e.g. 1)
275     * @param in_location        The location of the error.
276     */
277    Error(std::string in_name, int in_code, const ErrorLocation& in_location);
278 
279    /**
280     * @brief Constructor.
281     *
282     * @param in_name            A contextual or categorical name for the error. (e.g. "RequestNotSupported")
283     * @param in_code            The non-zero error code. Note that an error code of zero indicates success. (e.g. 1)
284     * @param in_cause           The error which caused this error.
285     * @param in_location        The location of the error.
286     */
287    Error(std::string in_name, int in_code, const Error& in_cause, const ErrorLocation& in_location);
288 
289 
290    /**
291     * @brief Constructor.
292     *
293     * @param in_name            A contextual or categorical name for the error. (e.g. "RequestNotSupported")
294     * @param in_code            The non-zero error code. Note that an error code of zero indicates success. (e.g. 1)
295     * @param in_message         The detailed error message. (e.g. "The JobNetworkRequest is not supported by this
296     *                           plugin.")
297     * @param in_location        The location of the error.
298     */
299    Error(std::string in_name, int in_code, std::string in_message, const ErrorLocation& in_location);
300 
301    /**
302     * @brief Constructor.
303     *
304     * @param in_name            A contextual or categorical name for the error. (e.g. "RequestNotSupported")
305     * @param in_code            The non-zero error code. Note that an error code of zero indicates success. (e.g. 1)
306     * @param in_message         The detailed error message. (e.g. "The JobNetworkRequest is not supported by this
307     *                           plugin.")
308     * @param in_cause           The error which caused this error.
309     * @param in_location        The location of the error.
310     */
311    Error(std::string in_name,
312          int in_code,
313          std::string in_message,
314          const Error& in_cause,
315          const ErrorLocation& in_location);
316 
317    /**
318    * @brief Non-virtual destructor because only Success inherits Error and it will keep Error lightweight.
319    */
320    ~Error() override = default;
321 
322    /**
323     * @brief Overloaded operator bool to allow Errors to be treated as boolean values.
324     *
325     * @return True if there is an error; false otherwise.
326     */
327    explicit operator bool() const;
328 
329    /**
330     * @brief Overloaded operator ! to allow Errors to be treated as boolean values.
331     *
332     * @return True if there is not an error; false otherwise.
333     */
334    bool operator!() const;
335 
336    /**
337     * @brief Equality operator. Two errors are equal if their codes and names are the same.
338     *
339     * @param in_other   The error to compare with this error.
340     *
341     * @return True if in_other is equal to this error; false otherwise.
342     */
343    bool operator==(const Error& in_other) const;
344 
345    /**
346     * @brief Equality operator. Two errors are equal if their codes and names are the same.
347     *
348     * @param in_ec   The boost error code to compare with this error.
349     *
350     * @return True if in_ec has the same error code and category name as this error; false otherwise.
351     */
352    bool operator==(const boost::system::error_code& in_ec) const;
353 
354    /**
355     * @brief Inequality operator. Two errors are equal if their codes and names are the same.
356     *
357     * @param in_other   The error to compare with this error.
358     *
359     * @return True if in_other is not equal to this error; false otherwise.
360     */
361    bool operator!=(const Error& in_other) const;
362 
363    /**
364     * @brief Inequality operator. Two errors are equal if their codes and names are the same.
365     *
366     * @param in_ec   The boost error code to compare with this error.
367     *
368     * @return True if !(this == in_ec) would return true; false otherwise.
369     */
370    bool operator!=(const boost::system::error_code& in_ec) const;
371 
372    /**
373     * @brief Add or updates a property of this error. If any properties with the specified name exist, they will all be
374     *        updated.
375     *
376     * @param in_name        The name of the property to add or update.
377     * @param in_value       The new value of the property.
378     */
379    void addOrUpdateProperty(const std::string& in_name, const std::string& in_value);
380 
381    /**
382     * @brief Add or updates a property of this error. If any properties with the specified name exist, they will all be
383     *        updated.
384     *
385     * @param in_name        The name of the property to add or update.
386     * @param in_value       The new value of the property.
387     */
388    void addOrUpdateProperty(const std::string& in_name, const FilePath& in_value);
389 
390    /**
391     * @brief Add or updates a property of this error. If any properties with the specified name exist, they will all be
392     *        updated.
393     *
394     * @param in_name        The name of the property to add or update.
395     * @param in_value       The new value of the property.
396     */
397    void addOrUpdateProperty(const std::string& in_name, int in_value);
398 
399    /**
400     * @brief Adds a property of this error. If a property with the same name already exists, a duplicate will be added.
401     *
402     * @param in_name        The name of the property to add or update.
403     * @param in_value       The new value of the property.
404     */
405    void addProperty(const std::string& in_name, const std::string& in_value);
406 
407    /**
408     * @brief Adds a property of this error. If a property with the same name already exists, a duplicate will be added.
409     *
410     * @param in_name        The name of the property to add or update.
411     * @param in_value       The new value of the property.
412     */
413    void addProperty(const std::string& in_name, const FilePath& in_value);
414 
415    /**
416     * @brief Adds a property of this error. If a property with the same name already exists, a duplicate will be added.
417     *
418     * @param in_name        The name of the property to add or update.
419     * @param in_value       The new value of the property.
420     */
421    void addProperty(const std::string& in_name, int in_value);
422 
423    /**
424     * @brief Formats the error as a string.
425     *
426     * @return The error formatted as a string.
427     */
428    std::string asString() const;
429 
430    /**
431     * @brief Checks whether this error was caused by a separate error.
432     *
433     * @return True if the error has an associated cause.
434     */
435    bool hasCause() const;
436 
437    /**
438     * @brief Gets the error which caused this error.
439     *
440     * @return The error which caused this error.
441     */
442    const Error& getCause() const;
443 
444    /**
445     * @brief Gets the error code.
446     *
447     * @return The error code.
448     */
449    int getCode() const;
450 
451    /**
452     * @brief Gets the location where the error occurred.
453     *
454     * @return The location where the error occurred.
455     */
456    const ErrorLocation& getLocation() const;
457 
458    /**
459     * @brief Gets the error message.
460     *
461     * @return The error message.
462     */
463    const std::string& getMessage() const;
464 
465    /**
466     * @brief Gets the name of the error.
467     *
468     * @return The name of the error.
469     */
470    const std::string& getName() const;
471 
472    /**
473     * @brief Gets the custom properties of the error.
474     *
475     * @return The custom properties of this error.
476     */
477    const ErrorProperties& getProperties() const;
478 
479    /**
480     * @brief Gets a custom property of this error.
481     *
482     * @param name   The name of the property to retrieve.
483     *
484     * @return The value of the specified property, if it exists; empty string otherwise.
485     */
486    std::string getProperty(const std::string& name) const;
487 
488    /**
489     * @brief Gets the cause of the error.
490     *
491     * @return The cause of the error.
492     */
493    std::string getSummary() const;
494 
495    /**
496     * @brief Gets whether this error was expected or not.
497     *
498     * @return True if this error was expected; false otherwise.
499     */
500    bool isExpected() const;
501 
502    /**
503     * @brief Sets the property that indicates that this error was expected.
504     *        Errors are unexpected by default; only unexpected errors will be logged.
505     *        Expected errors can be marked as such to suppress logging of those errors.
506     */
507    void setExpected();
508 
509 private:
510    /**
511     * @brief Helper method to copy the error object on write.
512     */
513    void copyOnWrite();
514 
515    /**
516     * @brief Helper method that checks whether this error object represents an error or not (i.e. a non-zero error
517     *        code).
518     *
519     * @return True if the error code is non-zero; false otherwise.
520     */
521    bool isError() const;
522 
523    // The private implementation of Error.
524    PRIVATE_IMPL_SHARED(m_impl);
525 
526    /**
527     * @brief Gets a reference to the private implementation member.
528     *
529     * @return A reference to the private implementation member.
530     */
531    Impl& impl() const;
532 };
533 
534 /**
535  * @brief Class which represents a successful operation (i.e. no error).
536  */
537 class Success : public Error
538 {
539 public:
540    /**
541     * @brief Constructor.
542     */
Success()543    Success() : Error() {}
544 };
545 
546 /**
547  * @brief Output stream operator. Writes the specified error to the provided output stream.
548  *
549  * @param io_ostream     The output stream to which to write the error.
550  * @param in_error       The error to write to the output stream.
551  *
552  * @return A reference to the provided output stream, for chaining writes.
553  */
554 std::ostream& operator<<(std::ostream& io_ostream, const Error& in_error);
555 
556 #ifdef _WIN32
557 
558 // Use this macro instead of systemError(::GetLastError(), ERROR_LOCATION) otherwise
559 // the ERROR_LOCATION macro may evaluate first and reset the Win32 error code to
560 // zero (no error), causing the wrong value to be passed to systemError. This is currently
561 // the case on debug builds using MSVC.
562 #define LAST_SYSTEM_ERROR() []() {auto lastErr = ::GetLastError(); return systemError(lastErr, ERROR_LOCATION);}()
563 
564 #endif // _WIN32
565 
566 /**
567  * @brief Function which creates a system error.
568  *
569  * @param in_code            The error code. (e.g. 1)
570  * @param in_location        The location of the error.
571  *
572  * @return A system error.
573  */
574 Error systemError(int in_code, const ErrorLocation& in_location);
575 
576 /**
577  * @brief Function which creates a system error.
578  *
579  * @param in_code            The std system error code.
580  * @param in_location        The location of the error.
581  *
582  * @return A system error.
583  */
584 Error systemError(const std::error_code& in_code, const ErrorLocation& in_location);
585 
586 /**
587  * @brief Function which creates a system error.
588  *
589  * @param in_error           The system error which occurred.
590  * @param in_location        The location of the error.
591  *
592  * @return A system error.
593  */
594 Error systemError(const std::system_error& in_error, const ErrorLocation& in_location);
595 
596 /**
597  * @brief Function which creates a system error.
598  *
599  * @param in_code            The error code. (e.g. 1)
600  * @param in_cause           The error which caused this error.
601  * @param in_location        The location of the error.
602  *
603  * @return A system error.
604  */
605 Error systemError(int in_code, const Error& in_cause, const ErrorLocation& in_location);
606 
607 /**
608  * @brief Function which creates a system error.
609  *
610  * @param in_code            The std system error code.
611  * @param in_cause           The error which caused this error.
612  * @param in_location        The location of the error.
613  *
614  * @return A system error.
615  */
616 Error systemError(const std::error_code& in_code, const Error& in_cause, const ErrorLocation& in_location);
617 
618 /**
619  * @brief Function which creates a system error.
620  *
621  * @param in_error           The system error which occurred.
622  * @param in_cause           The error which caused this error.
623  * @param in_location        The location of the error.
624  *
625  * @return A system error.
626  */
627 Error systemError(const std::system_error& in_error, const Error& in_cause, const ErrorLocation& in_location);
628 
629 /**
630  * @brief Function which creates a system error.
631  *
632  * @param in_code            The error code. (e.g. 1)
633  * @param in_description     A detailed description of the error. (e.g. "Failed to open socket while attempting to
634  *                           connect to Kubernetes.")
635  * @param in_location        The location of the error.
636  *
637  * @return A system error.
638  */
639 Error systemError(int in_code, const std::string& in_description, const ErrorLocation& in_location);
640 
641 /**
642  * @brief Function which creates a system error.
643  *
644  * @param in_code            The std system error code.
645  * @param in_description     A detailed description of the error. (e.g. "Failed to open socket while attempting to
646  *                           connect to Kubernetes.")
647  * @param in_location        The location of the error.
648  *
649  * @return A system error.
650  */
651 Error systemError(const std::error_code& in_code, const std::string& in_description, const ErrorLocation& in_location);
652 
653 /**
654  * @brief Function which creates a system error.
655  *
656  * @param in_error           The system error which occurred.
657  * @param in_description     A detailed description of the error. (e.g. "Failed to open socket while attempting to
658  *                           connect to Kubernetes.")
659  * @param in_location        The location of the error.
660  *
661  * @return A system error.
662  */
663 Error systemError(
664    const std::system_error& in_error,
665    const std::string& in_description,
666    const ErrorLocation& in_location);
667 
668 /**
669  * @brief Function which creates a system error.
670  *
671  * @param in_code            The error code. (e.g. 1)
672  * @param in_description     A detailed description of the error. (e.g. "Failed to open socket while attempting to
673  *                           connect to Kubernetes.")
674  * @param in_cause           The error which caused this error.
675  * @param in_location        The location of the error.
676  *
677  * @return A system error.
678  */
679 Error systemError(
680    int in_code,
681    const std::string& in_description,
682    const Error& in_cause,
683    const ErrorLocation& in_location);
684 
685 /**
686  * @brief Function which creates a system error.
687  *
688  * @param in_code            The std system error code.
689  * @param in_description     A detailed description of the error. (e.g. "Failed to open socket while attempting to
690  *                           connect to Kubernetes.")
691  * @param in_cause           The error which caused this error.
692  * @param in_location        The location of the error.
693  *
694  * @return A system error.
695  */
696 Error systemError(
697    const std::error_code& in_code,
698    const std::string& in_description,
699    const Error& in_cause,
700    const ErrorLocation& in_location);
701 
702 /**
703  * @brief Function which creates a system error.
704  *
705  * @param in_error           The system error which occurred.
706  * @param in_description     A detailed description of the error. (e.g. "Failed to open socket while attempting to
707  *                           connect to Kubernetes.")
708  * @param in_cause           The error which caused this error.
709  * @param in_location        The location of the error.
710  *
711  * @return A system error.
712  */
713 Error systemError(
714    const std::system_error& in_error,
715    const std::string& in_description,
716    const Error& in_cause,
717    const ErrorLocation& in_location);
718 
719 /**
720  * @brief Function which creates a system error associated with a particular system call.
721  *
722  * @param in_function        The name of the system call associated with this error.
723  * @param in_code            The error code -- typically errno, or an error number returned by the call itself.
724  * @param in_location        The location of the error.
725  *
726  * @return A system error.
727  */
728 Error systemCallError(
729    const std::string& in_function,
730    int in_code,
731    const ErrorLocation& in_location);
732 
733 /**
734  * @brief Function which creates a system error associated with a particular system call.
735  *
736  * @param in_function        The name of the system call associated with this error.
737  * @param in_code            The error code -- typically errno, or an error number returned by the call itself.
738  * @param in_message         The error message. Overrides the default error message associated with the error code.
739  * @param in_location        The location of the error.
740  *
741  * @return A system error.
742  */
743 Error systemCallError(
744    const std::string& in_function,
745    int in_code,
746    const std::string& in_message,
747    const ErrorLocation& in_location);
748 
749 /**
750  * @brief Function which creates an unknown error. This should be used only when a specific error code cannot be
751  *        determined.
752  *
753  * @param in_message         The detailed error message. (e.g. "Failed to open socket while attempting to connect to
754  *                           Kubernetes.")
755  * @param in_cause           The error which caused this error.
756  * @param in_location        The location of the error.
757  *
758  * @return An unknown error.
759  */
760 Error unknownError(const std::string& in_message, const ErrorLocation& in_location);
761 
762 /**
763  * @brief Function which creates an unknown error. This should be used only when a specific error code cannot be
764  *        determined.
765  *
766  * @param in_message         The detailed error message. (e.g. "Failed to open socket while attempting to connect to
767  *                           Kubernetes.")
768  * @param in_cause           The error which caused this error.
769  * @param in_location        The location of the error.
770  *
771  * @return An unknown error.
772  */
773 Error unknownError(const std::string& in_message, const Error& in_cause, const ErrorLocation& in_location);
774 
775 // return a printable error message from an error (depending on the error this
776 // might require consulting the message, category, or name)
777 std::string errorDescription(const Error& error);
778 std::string errorMessage(const core::Error& error);
779 
780 // return the error message associated with a particular system error code
781 std::string systemErrorMessage(int code);
782 
783 } // namespace core
784 } // namespace rstudio
785 
786 #ifdef STRIPPED_FILENAME
787 #define ERROR_LOCATION rstudio::core::ErrorLocation( \
788       BOOST_CURRENT_FUNCTION, STRIPPED_FILENAME, __LINE__)
789 #else
790 #define ERROR_LOCATION rstudio::core::ErrorLocation( \
791       BOOST_CURRENT_FUNCTION, __FILE__, __LINE__)
792 #endif
793 
794 #define CATCH_UNEXPECTED_EXCEPTION                                                  \
795    catch(const std::exception& e)                                                   \
796    {                                                                                \
797       rstudio::core::log::logErrorMessage(std::string("Unexpected exception: ") +   \
798                         e.what(), ERROR_LOCATION);                                  \
799    }                                                                                \
800    catch(...)                                                                       \
801    {                                                                                \
802       rstudio::core::log::logErrorMessage("Unknown exception", ERROR_LOCATION);     \
803    }
804 
805 #endif
806