Main Content

MISRA C++:2023 Rule 18.5.1

A noexcept function should not attempt to propagate an exception to the calling function

Since R2024b

Description

Rule Definition

A noexcept function should not attempt to propagate an exception to the calling function.

Rationale

You can specify that a callable entity does not raise an exception by specifying it as noexcept, or noexcept(true), or noexcept(<true condition>). The compiler expects that a noexcept function does not exit with an exception. Based on this assumption, the compiler omits the exception handing process for noexcept functions. When a noexcept function exits with an exception, the exception becomes unhandled.

If a noexcept function exits with an exception, the compiler invokes std::terminate() implicitly. The function std::terminate() terminates the program execution in an implementation-defined manner. That is, the exact process of program termination depends on the particular set of software and hardware that you use. For instance, std:terminate() might invoke std::abort() to abnormally abort the execution without unwinding the stack, leading to resource leak and security vulnerabilities.

Specify functions as noexcept or noexcept(true) only when you know the functions raise no exceptions. If you cannot determine the exception specification of a function, specify it by using noexcept(false). Destructors of objects are implicitly noexcept(<true>). Do not raise exceptions in destructors.

Polyspace Implementation

Polyspace® reports a violation of this rule if either of these conditions are true:

  • A destructor of an object exits with an unhandled exception.

  • A noexcept, noexcept(true), or noexcept(<true condition>) callable entity exits with an exception.

When a noexcept callable entity invokes other callable entities, Polyspace reports violations of this rule only if the called entities are explicitly specified as noexcept(<false>). For example:

  • Functions — Polyspace reports a violation if a noexcept function calls a function that is explicitly specified as noexcept(<false>). Some standard library functions, such as the constructor of std::string, use pointers to functions to perform memory allocation, which might raise exceptions. Because these functions are not specified as noexcept(<false>), Polyspace does not flag a function that calls these standard library functions.

  • External function — Polyspace reports a violation if a noexcept function calls an external function that is explicitly specified as noexcept(<false>).

  • Virtual function — Polyspace reports a violation when a noexcept function calls a virtual function that is explicitly specified as noexcept(<false>) in a derived class.

  • Pointers to function — These callable entities cannot be specified as noexcept(<false>). Polyspace does not report a violation if a noexcept function invokes a pointer to function.

When analyzing whether a function raises unhandled exceptions, Polyspace ignores:

  • Exceptions raised in atexit() operations.

  • Dynamic context when checking for exceptions. For instance, a function might raise unhandled exceptions that arise only in certain dynamic contexts. Polyspace flags such a function even if the exception might not be raised.

Troubleshooting

If you expect a rule violation but Polyspace does not report it, see Diagnose Why Coding Standard Violations Do Not Appear as Expected.

Examples

expand all

This example shows how Polyspace flags noexcept functions that might raise unhandled exceptions. Consider this code containing several noexcept functions. These functions invoke other callable entities like functions, external functions, and virtual functions.

#include <stdexcept>
#include <typeinfo>
void LibraryFunc(); 
void LibraryFunc_noexcept_false() noexcept(false);  
void LibraryFunc_noexcept_true() noexcept(true);    



void SpecFalseCT() noexcept  // Noncompliant
{
	try {
		LibraryFunc_noexcept_false();
	} catch (int &e) {
		LibraryFunc_noexcept_false();  
	} catch (std::exception &e) {
	} catch (...) {
	}
}

class A {
public:
	virtual void f() {}              
};

class B : A {
public:
	virtual void f() noexcept {}     
};

class C : B {
public:
	virtual void f() noexcept {}     
};

class D : A {
public:
	virtual void f() noexcept(false) { throw(2);}
};

void A1(A &a) noexcept {          // Noncompliant
	a.f();
}

void D2(D &d) noexcept {          //Compliant
	try {
		d.f();
	} catch (int i) {
	} catch (...) {
	}
}

void B2(B *b) noexcept {          // Compliant
	b->f();
}
template <class T>
T f_tp(T a) noexcept(sizeof(T)<=4)    // Noncompliant
{
	if (sizeof(T) >4 ) {
		throw std::runtime_error("invalid case");
	}
	return a;
}
void instantiate(void)
{
	f_tp<char>(1);
}
void f() noexcept {               //Noncompliant
	throw std::runtime_error("dead code");
}

void g() noexcept {               // Compliant
	f();
}  

  • Polyspace flags the declaration of the function template f_tp because:

    • The condition sizeof(T)<=4 evaluates to true for char so the template becomes a noexcept(true) function.

    • Polyspace analyzes the noexcept(true) instance of the template statically. Polyspace deduces that the template might raise an exception because of the throw statement, even though the condition sizeof(T)>4 is false. That is, Polyspace flags the template even though the throw statement is never reached.

    Polyspace ignores function templates that are not instantiated.

  • Polyspace flags the noexcept function SpecFaleCT() because this function calls the noexcept(false) external function LibraryFunc_noexcept_false() without encapsulating it in a try-catch block. Any exceptions raised by this call to the external function might raise an unhandled exception.

  • Polyspace flags the declaration of the noexcept function A1() because this function might call the noexcept(false) function D.f() when the input parameter a is of class D. Depending on the class of the input parameter, the noexcept polymorphic function A1() might raise an unhandled exception.

  • Polyspace flags the function f() because it is a noexcept function that uses throw to raise an unhandled exception. Polyspace does not flag the noexcept function g() even though it calls f() because f() is specified as noexcept.

  • Polyspace does not flag the noexcept function D2() even though it calls the noexcept(false) function D.f() because D2() handles the exceptions that might arise by using a catch(...) block.

In this example, the destructor of class myClass is implicitly noexcept(<true>). It contains an explicit throw statement without encasing it in a try-catch block. Polyspace flags the throw statement.

#include <exception>
#include <stdexcept>
class myClass {
	//...
	~myClass()  { 
		//...
        if(cond)
            throw std::logic_error("Error"); //Noncompliant
	}
	private:
    volatile int cond;
};

Check Information

Group: Exception Handling
Category: Advisory

Version History

Introduced in R2024b