This solution is both: slightly shorter and, more importantly, more efficient due to the use of keys:
<xsl:stylesheet version="1.0"
xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
<xsl:output omit-xml-declaration="yes" indent="yes"/>
<xsl:key name="kPhraseByMorse"
match="*[not(self::silence) and not(self::message)]"
use="generate-id(preceding-sibling::silence[1])"/>
<xsl:template match="/">
<complexMessage>
<word>
<xsl:call-template name="makeCode"/>
</word>
<xsl:apply-templates select="*/silence"/>
</complexMessage>
</xsl:template>
<xsl:template match="silence">
<word>
<xsl:call-template name="makeCode">
<xsl:with-param name="pId" select="generate-id()"/>
</xsl:call-template>
</word>
</xsl:template>
<xsl:template name="makeCode">
<xsl:param name="pId"/>
<xsl:attribute name="code">
<xsl:apply-templates select="key('kPhraseByMorse', $pId)"/>
</xsl:attribute>
</xsl:template>
<xsl:template match="dot">.</xsl:template>
<xsl:template match="line">-</xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided source XML, the correct result is produced:
<complexMessage>
<word code="-"/>
<word code=".--"/>
<word code="."/>
<word code="-..."/>
</complexMessage>