diff --git a/composer.json b/composer.json
index 070cd9d..049bc80 100644
--- a/composer.json
+++ b/composer.json
@@ -28,7 +28,7 @@
         "squizlabs/php_codesniffer": "^3.5",
         "symfony/css-selector": "^4.4|^5.0",
         "symfony/dom-crawler": "^4.4|^5.0",
-        "vimeo/psalm": "^3.12|^4.0"
+        "vimeo/psalm": "^4.30|^5.22"
     },
     "autoload": {
         "psr-4": {
diff --git a/psalm.xml b/psalm.xml
index 832ed33..c687ab6 100644
--- a/psalm.xml
+++ b/psalm.xml
@@ -5,6 +5,8 @@
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xmlns="https://getpsalm.org/schema/config"
     xsi:schemaLocation="https://getpsalm.org/schema/config vendor/vimeo/psalm/config.xsd"
+	findUnusedBaselineEntry="true"
+	findUnusedCode="false"
 >
     <projectFiles>
         <directory name="src/" />
diff --git a/src/analyzer/Model/TombstoneIndex.php b/src/analyzer/Model/TombstoneIndex.php
index c80e7d1..a878dbe 100644
--- a/src/analyzer/Model/TombstoneIndex.php
+++ b/src/analyzer/Model/TombstoneIndex.php
@@ -7,6 +7,9 @@
 use Scheb\Tombstone\Core\Model\FilePathInterface;
 use Scheb\Tombstone\Core\Model\Tombstone;
 
+/**
+ * @template-implements \IteratorAggregate<array-key, Tombstone>
+ */
 class TombstoneIndex implements \Countable, \IteratorAggregate
 {
     /**
diff --git a/src/analyzer/Model/VampireIndex.php b/src/analyzer/Model/VampireIndex.php
index c0c8041..0542a89 100644
--- a/src/analyzer/Model/VampireIndex.php
+++ b/src/analyzer/Model/VampireIndex.php
@@ -6,6 +6,9 @@
 
 use Scheb\Tombstone\Core\Model\Vampire;
 
+/**
+ * @template-implements \IteratorAggregate<array-key, Vampire>
+ */
 class VampireIndex implements \Countable, \IteratorAggregate
 {
     /**
diff --git a/src/analyzer/Report/Checkstyle/CheckstyleReportGenerator.php b/src/analyzer/Report/Checkstyle/CheckstyleReportGenerator.php
index 6334aea..37ad3c7 100644
--- a/src/analyzer/Report/Checkstyle/CheckstyleReportGenerator.php
+++ b/src/analyzer/Report/Checkstyle/CheckstyleReportGenerator.php
@@ -82,7 +82,7 @@ private function getCalledBy(Tombstone $tombstone): string
         }
 
         $invoker = array_shift($vampires)->getInvoker();
-        $calledBy = sprintf(' by "%s"', $invoker ?: 'global scope');
+        $calledBy = sprintf(' by "%s"', null !== $invoker ? $invoker : 'global scope');
 
         $numAdditionalVampires = $numVampires - 1;
         if ($numAdditionalVampires > 0) {
diff --git a/src/analyzer/Report/Console/ConsoleReportGenerator.php b/src/analyzer/Report/Console/ConsoleReportGenerator.php
index 2947eae..796603a 100644
--- a/src/analyzer/Report/Console/ConsoleReportGenerator.php
+++ b/src/analyzer/Report/Console/ConsoleReportGenerator.php
@@ -94,8 +94,9 @@ private function displayTombstones(array $result): void
             $this->newLine();
             $this->printTombstone($tombstone, 'RIP');
             $date = $tombstone->getTombstoneDate();
-            if ($date) {
-                if ($age = TimePeriodFormatter::formatAge($date)) {
+            if (null !== $date) {
+                $age = TimePeriodFormatter::formatAge($date);
+                if (null !== $age) {
                     $this->output->writeln(sprintf('    was not called for %s', $age));
                 } else {
                     $this->output->writeln(sprintf('    was not called since %s', $date));
@@ -109,10 +110,10 @@ private function printTombstone(Tombstone $tombstone, string $prefix): void
         $this->output->writeln(sprintf('  [%s] <info>%s</info>', $prefix, (string) $tombstone));
         $this->output->writeln(sprintf('    in <comment>line %s</comment>', $tombstone->getLine()));
         $method = $tombstone->getMethod();
-        if ($method) {
+        if (null !== $method) {
             $this->output->writeln(sprintf('    in method <comment>%s</comment>', $method));
         } else {
-            $this->output->writeln(sprintf('    in global scope'));
+            $this->output->writeln('    in global scope');
         }
     }
 
diff --git a/src/analyzer/Report/Html/Renderer/DashboardRenderer.php b/src/analyzer/Report/Html/Renderer/DashboardRenderer.php
index f7568db..e47f8c7 100644
--- a/src/analyzer/Report/Html/Renderer/DashboardRenderer.php
+++ b/src/analyzer/Report/Html/Renderer/DashboardRenderer.php
@@ -131,7 +131,8 @@ private function getDeadSince(Tombstone $tombstone): string
             return 'since unknown';
         }
 
-        if ($age = TimePeriodFormatter::formatAge($date)) {
+        $age = TimePeriodFormatter::formatAge($date);
+        if (null !== $age) {
             return 'for '.$age;
         }
 
@@ -167,7 +168,7 @@ private function renderInvokers(Tombstone $tombstone): string
         $invokersString = '';
         foreach ($invokers as $invoker) {
             $this->invokerTemplate->setVar([
-                'invoker' => $invoker ? htmlspecialchars($invoker) : 'global scope',
+                'invoker' => null !== $invoker ? htmlspecialchars($invoker) : 'global scope',
             ]);
             $invokersString .= $this->invokerTemplate->render();
         }
@@ -218,7 +219,8 @@ private function renderDeletedTombstones(AnalyzerFileResult $fileResult): string
     private function getLastCalled(Vampire $vampire): string
     {
         $invocationDate = $vampire->getInvocationDate();
-        if ($age = TimePeriodFormatter::formatAge($invocationDate)) {
+        $age = TimePeriodFormatter::formatAge($invocationDate);
+        if (null !== $age) {
             return $age;
         }
 
@@ -228,7 +230,7 @@ private function getLastCalled(Vampire $vampire): string
     private function getTombstoneScope(Tombstone $tombstone): string
     {
         $method = $tombstone->getMethod();
-        if ($method) {
+        if (null !== $method) {
             return sprintf('method <samp>%s</samp>', htmlspecialchars($method));
         }
 
diff --git a/src/analyzer/Report/Html/Renderer/PhpFileFormatter.php b/src/analyzer/Report/Html/Renderer/PhpFileFormatter.php
index dd93fd7..a51d38e 100644
--- a/src/analyzer/Report/Html/Renderer/PhpFileFormatter.php
+++ b/src/analyzer/Report/Html/Renderer/PhpFileFormatter.php
@@ -49,8 +49,9 @@ private function formatTokens(array $tokens): \Traversable
         $stringFlag = false;
         $result = '';
         foreach ($tokens as $j => $token) {
+            $previousToken = $j > 0 ? $tokens[$j - 1] : null;
             if (\is_string($token)) {
-                if ('"' === $token && '\\' !== $tokens[$j - 1]) {
+                if ('"' === $token && '\\' !== $previousToken) {
                     $result .= $this->highlighter->formatString($token);
                     $stringFlag = !$stringFlag;
                 } else {
diff --git a/src/analyzer/Report/TimePeriodFormatter.php b/src/analyzer/Report/TimePeriodFormatter.php
index ef8a8a2..144fe9c 100644
--- a/src/analyzer/Report/TimePeriodFormatter.php
+++ b/src/analyzer/Report/TimePeriodFormatter.php
@@ -11,7 +11,7 @@ class TimePeriodFormatter
     public static function formatAge(string $date): ?string
     {
         $tombstoneDate = strtotime($date);
-        if (!$tombstoneDate) {
+        if (false === $tombstoneDate) {
             return null;
         }
 
diff --git a/src/analyzer/Stock/TombstoneNodeVisitor.php b/src/analyzer/Stock/TombstoneNodeVisitor.php
index d99725c..9151ea9 100644
--- a/src/analyzer/Stock/TombstoneNodeVisitor.php
+++ b/src/analyzer/Stock/TombstoneNodeVisitor.php
@@ -42,7 +42,7 @@ public function __construct(TombstoneExtractor $tombstoneCallback, array $tombst
         $this->tombstoneFunctionNames = $tombstoneFunctionNames;
     }
 
-    public function enterNode(Node $node)
+    public function enterNode(Node $node): void
     {
         parent::enterNode($node);
         if ($node instanceof Class_) {
@@ -114,7 +114,7 @@ private function visitStaticCallNode(StaticCall $node): void
         }
     }
 
-    public function leaveNode(Node $node)
+    public function leaveNode(Node $node): void
     {
         parent::leaveNode($node);
         if ($node instanceof ClassMethod || $node instanceof Function_) {
diff --git a/src/core/Model/RootPath.php b/src/core/Model/RootPath.php
index 671c447..a9b970b 100644
--- a/src/core/Model/RootPath.php
+++ b/src/core/Model/RootPath.php
@@ -69,11 +69,11 @@ private function createRelativePath(string $path): RelativeFilePath
     {
         if ('' !== $path && '.' === $path[0]) {
             if ('.' === $path) {
-                $path = ''; // Path is equal root path
-            } elseif ('.' === $path[0]) {
-                // Remove leading "./"
-                $path = preg_replace('#^(\\./)+#', '', $path);
+                // Path is equal root path
+                return new RelativeFilePath('', $this);
             }
+            // Remove leading "./"
+            $path = preg_replace('#^(\\./)+#', '', $path);
         }
 
         return new RelativeFilePath($path, $this);
diff --git a/src/core/Model/StackTrace.php b/src/core/Model/StackTrace.php
index 1fccafe..0033c8c 100644
--- a/src/core/Model/StackTrace.php
+++ b/src/core/Model/StackTrace.php
@@ -4,7 +4,7 @@
 
 namespace Scheb\Tombstone\Core\Model;
 
-class StackTrace implements \Countable, \IteratorAggregate, \ArrayAccess
+class StackTrace implements \Countable, StackTraceInterface
 {
     /**
      * @var StackTraceFrame[]
@@ -32,7 +32,7 @@ public function count(): int
     }
 
     /**
-     * @return \Traversable<int, StackTraceFrame>
+     * @return \Traversable<array-key, StackTraceFrame>
      */
     public function getIterator(): \Traversable
     {
diff --git a/src/core/Model/StackTraceInterface.php b/src/core/Model/StackTraceInterface.php
new file mode 100644
index 0000000..2eee7c4
--- /dev/null
+++ b/src/core/Model/StackTraceInterface.php
@@ -0,0 +1,14 @@
+<?php
+
+declare(strict_types=1);
+
+namespace Scheb\Tombstone\Core\Model;
+
+/**
+ * @extends \IteratorAggregate<array-key, StackTraceFrame>
+ * @extends \ArrayAccess<array-key, StackTraceFrame>
+ */
+interface StackTraceInterface extends \IteratorAggregate, \ArrayAccess
+{
+    public function getHash(): int;
+}
diff --git a/src/logger/Handler/PsrLoggerHandler.php b/src/logger/Handler/PsrLoggerHandler.php
index fedf610..b0ec904 100644
--- a/src/logger/Handler/PsrLoggerHandler.php
+++ b/src/logger/Handler/PsrLoggerHandler.php
@@ -14,8 +14,14 @@ class PsrLoggerHandler extends AbstractHandler
      */
     private $logger;
 
+    /**
+     * @var string|int|mixed
+     */
     private $level;
 
+    /**
+     * @param string|int|mixed $level
+     */
     public function __construct(LoggerInterface $logger, $level)
     {
         $this->logger = $logger;
diff --git a/src/logger/Handler/StreamHandler.php b/src/logger/Handler/StreamHandler.php
index 9a34cac..14c3fbf 100644
--- a/src/logger/Handler/StreamHandler.php
+++ b/src/logger/Handler/StreamHandler.php
@@ -71,7 +71,7 @@ public function __construct($stream, ?int $filePermission = null, $useLocking =
 
     public function close(): void
     {
-        if ($this->url && \is_resource($this->stream)) {
+        if (null !== $this->url && \is_resource($this->stream)) {
             fclose($this->stream);
         }
         $this->stream = null;