Here is a pure XPath 3.1 solution:
The function $allProps() below, returns a sequence of strings that are the names of all the properties of an element whose id is equal to the $id parameter passed to the function.
In this sample expression the function $allProps() is called 3 times -- once for each "elem" element and the returned properties are delimited by a NL character:
let $root := /,
      $allProps-inner := function($id as xs:integer, $self as function(*)) as xs:string*
{
  let $elem := $root/*/elem[xs:integer(@id )eq $id],
        $ownProperties := $elem/property/@name[not(. eq 'inherits')]/string(),
        $ParentId := $elem/property[@name eq 'inherits']/@value
    return
      (
        $ownProperties, 
        if(empty($ParentId)) then ()
        else
           $self($ParentId, $self)
         )
 },
$allProps :=  function($id as xs:integer) as xs:string*
{ $allProps-inner($id, $allProps-inner ) }
return
  (
    $allProps(1), '
',
    $allProps(2), '
',
    $allProps(3), '
'
)
XSLT 3.0 - based verification:
<xsl:stylesheet version="3.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
  xmlns:xs="http://www.w3.org/2001/XMLSchema">
  <xsl:output omit-xml-declaration="yes" indent="yes"/>
  <xsl:template match="/">
    <xsl:value-of select=
      "let $root := /,
           $allProps-inner := function($id as xs:integer, $self as function(*)) as xs:string*
          {
           let $elem := $root/*/elem[xs:integer(@id )eq $id],
           $ownProperties := $elem/property/@name[not(. eq 'inherits')]/string(),
           $ParentId := $elem/property[@name eq 'inherits']/@value
           return
           (
           $ownProperties, 
           if(empty($ParentId)) then ()
           else
             $self($ParentId, $self)
           )
          },
          $allProps :=  function($id as xs:integer) as xs:string*
          { $allProps-inner($id, $allProps-inner ) }
      return
         (
          $allProps(1), '
',
          $allProps(2), '
',
          $allProps(3), '
'
         )
      "/>
  </xsl:template>
</xsl:stylesheet>
When this transformation is applied on the provided XML document:
<elems>
  <elem id="1">
    <property name="a" value="alpha"/>
  </elem>
  <elem id="2">
    <property name="inherits" value="1"/>
    <property name="b" value="bravo"/>
  </elem>
  <elem id="3">
    <property name="inherits" value="2"/>
    <property name="c" value="charlie"/>
  </elem>
</elems>
the wanted, correct result is produced:
 a 
 b a 
 c b a 
Finally, we reach naturally the solution of the original question:
So a query for elements with property c would return 3, and its
  reverse would return 1 and 2. A query for elements with property b
  would return 2 and 3 and its reverse would return 1. Finally, a call
  for elements with property a would return 1, 2 and 3, and it's reverse
  would not return anything.
How do I do that?
let $root := /,
      $allProps-inner := function($id as xs:integer, $self as function(*)) as xs:string*
{
  let $elem := $root/*/elem[xs:integer(@id )eq $id],
        $ownProperties := $elem/property/@name[not(. eq 'inherits')]/string(),
        $ParentId := $elem/property[@name eq 'inherits']/@value
    return
      (
        $ownProperties, 
        if(empty($ParentId)) then ()
        else
           $self($ParentId, $self)
         )
 },
$allProps :=  function($id as xs:integer) as xs:string*
{ $allProps-inner($id, $allProps-inner ) }
return
  (
    for $name in ('a', 'b', 'c')
      return
         ( $root/*/elem[$name = $allProps(@id) ]/@id, '
' )
)
When this XPath expression is evaluated (just replace the XPath expression in the transformation with this one), then the result when output is the wanted, correct one:
 1 2 3 
 2 3 
 3