diff --git a/src/language/utils/FunctionSymbolId.hpp b/src/language/utils/FunctionSymbolId.hpp
index d6f0973f7ba0691013982a859369ca6738bbfac6..f96b783fa5bf3853507f4c650e02972faa5d6ebb 100644
--- a/src/language/utils/FunctionSymbolId.hpp
+++ b/src/language/utils/FunctionSymbolId.hpp
@@ -16,14 +16,13 @@ class FunctionSymbolId
   std::shared_ptr<SymbolTable> m_symbol_table = nullptr;
 
  public:
-  PUGS_INLINE uint64_t
+  [[nodiscard]] PUGS_INLINE uint64_t
   id() const noexcept
   {
     return m_function_id;
   }
 
-  PUGS_INLINE
-  const SymbolTable&
+  [[nodiscard]] PUGS_INLINE const SymbolTable&
   symbolTable() const
   {
     Assert(m_symbol_table, "FunctionSymbolId is not initialized properly");
@@ -37,7 +36,14 @@ class FunctionSymbolId
     return os;
   }
 
+  FunctionSymbolId& operator=(const FunctionSymbolId&) = default;
+  FunctionSymbolId& operator=(FunctionSymbolId&&) = default;
+
   FunctionSymbolId() = default;
+
+  FunctionSymbolId(const FunctionSymbolId&) = default;
+  FunctionSymbolId(FunctionSymbolId&&)      = default;
+
   FunctionSymbolId(uint64_t function_id, const std::shared_ptr<SymbolTable>& symbol_table)
     : m_function_id(function_id), m_symbol_table(symbol_table)
   {}
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index e695a79daf8d6818e21c736c8fa3231fb19efcb6..195aa699acf565f8edd2ef5a80a515ede3092348 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -58,6 +58,7 @@ add_executable (unit_tests
   test_FakeProcessor.cpp
   test_ForProcessor.cpp
   test_FunctionProcessor.cpp
+  test_FunctionSymbolId.cpp
   test_FunctionTable.cpp
   test_IfProcessor.cpp
   test_IncDecExpressionProcessor.cpp
diff --git a/tests/test_FunctionSymbolId.cpp b/tests/test_FunctionSymbolId.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3213f86d1962d3d8726e20ce866d83d2b73f69b2
--- /dev/null
+++ b/tests/test_FunctionSymbolId.cpp
@@ -0,0 +1,56 @@
+#include <catch2/catch.hpp>
+
+#include <language/utils/FunctionSymbolId.hpp>
+#include <language/utils/SymbolTable.hpp>
+#include <utils/PugsAssert.hpp>
+
+#include <sstream>
+
+// clazy:excludeall=non-pod-global-static
+
+TEST_CASE("FunctionSymbolId", "[language]")
+{
+  std::shared_ptr<SymbolTable> s = std::make_shared<SymbolTable>();
+  const FunctionSymbolId f{2, s};
+  REQUIRE(f.id() == 2);
+  REQUIRE(&f.symbolTable() == &(*s));
+
+  {
+    FunctionSymbolId g{f};
+    REQUIRE(g.id() == 2);
+  }
+
+  {
+    FunctionSymbolId h{4, s};
+    FunctionSymbolId g{std::move(h)};
+
+    REQUIRE(g.id() == 4);
+  }
+
+  {
+    FunctionSymbolId g;
+    g = f;
+    REQUIRE(g.id() == 2);
+  }
+
+  {
+    FunctionSymbolId g;
+    FunctionSymbolId h{4, s};
+    g = std::move(h);
+    REQUIRE(g.id() == 4);
+  }
+
+  {
+    std::ostringstream sout;
+    sout << f;
+
+    REQUIRE(sout.str() == "2");
+  }
+
+#ifndef NDEBUG
+  {
+    std::shared_ptr<SymbolTable> unset_s;
+    REQUIRE_THROWS_AS((FunctionSymbolId{0, unset_s}.symbolTable()), AssertError);
+  }
+#endif
+}