rspec/rules/S112/cfamily/rule.adoc
Marco Borgeaud 76791bbfe8
Remove link to CERT Java from CFamily (#3296)
Remove irrelevant links from C/C++ descriptions. No effort was made to
replace them when there wasn't already a link to CERT C. This will be
done as part of a separate effort, one day.
2023-10-18 10:49:02 +00:00

125 lines
3.9 KiB
Plaintext

This rule raises an issue when a generic exception (such as ``++std::exception++``, ``++std::logic_error++`` or ``++std::runtime_error++``) is thrown.
== Why is this an issue?
Throwing generic exceptions such as ``++std::exception++``, ``++std::logic_error++`` and ``++std::runtime_error++`` will have a negative impact on any code trying to catch these exceptions.
From a consumer perspective, it is generally a best practice to only catch exceptions you intend to handle. Other exceptions should ideally not be caught and let propagate up the stack trace so that they can be dealt with appropriately. When a generic exception is thrown, it forces consumers to catch exceptions they do not intend to handle, which they then have to re-throw.
Besides, when working with a generic type of exception, the only way to distinguish between multiple exceptions is to check their message, which is error-prone and difficult to maintain. Legitimate exceptions may be unintentionally silenced and errors may be hidden.
For instance, in the following code, the fact that `checkState` throws a generic exception leads us to catch a permission error that shouldn't have been caught:
[source,cpp]
----
void openFile(File& file) {
if (!has_permissions(file)) {
throw std::invalid_argument("Couldn't open file");
}
// ...
}
void checkState(File const& file) {
if (!file.is_valid()) {
throw std::exception(); // Noncompliant
}
// ...
}
void test(File file) {
try {
openFile(file);
checkState(false);
closeFile(file);
} catch (std::exception& s) {
// If we don't have the correct permissions to open, the
// invalid_argument exception will be caught and we will try closing a
// file that was never opened
closeFile(file);
}
}
----
Therefore, throwing the most specific exception possible is recommended so consumers can handle it intentionally.
== How to fix it
To fix this issue, make sure to throw specific exceptions that are relevant to the context in which they arise. It is recommended to either:
* Throw a subtype of ``++std::exception++`` when one matches. For instance, ``++std::invalid_argument++`` could be raised when an unexpected argument is provided to a function.
* Define a custom exception type that derives from ``++std::exception++`` or one of its subclasses.
=== Code examples
==== Noncompliant code example
[source,cpp,diff-id=1,diff-type=noncompliant]
----
#include <stdexcept>
void checkState(S state) {
if (!state.is_valid()) {
throw std::exception("State is invalid"); // Noncompliant: this will be difficult for consumers to handle
}
// ...
}
----
==== Compliant solution
The solution below involves throwing ``++std::invalid_argument++``, which is an appropriate exception type for this situation:
[source,cpp,diff-id=1,diff-type=compliant]
----
#include <stdexcept>
void checkState(S state) {
if (!state.is_valid()) {
throw std::invalid_argument("State is invalid"); // Compliant
}
// ...
}
----
Alternatively, one can define a custom ``++invalid_state++`` exception that can be caught specifically:
[source,cpp,diff-id=1,diff-type=compliant]
----
struct invalid_state : public std::exception {};
void checkState(S state) {
if (!state.is_valid()) {
throw invalide_state(); // Compliant
}
// ...
}
----
== Resources
=== Standards
* CWE - https://cwe.mitre.org/data/definitions/397[397 Declaration of Throws for Generic Exception]
=== External coding guidelines
* {cpp} Core Guidelines - https://github.com/isocpp/CppCoreGuidelines/blob/036324/CppCoreGuidelines.md#Re-exception-types[E.14: Use purpose-designed user-defined types as exceptions (not built-in types)]
=== Related rules
* S1181 - Generic exceptions should not be caught
ifdef::env-github,rspecator-view[]
'''
== Implementation Specification
(visible only on this page)
include::../message.adoc[]
'''
endif::env-github,rspecator-view[]