Skip to content

fix(getter): self- and super-references split three ways across languages #1380

Description

@dekobon

Summary

The workspace splits three ways on whether a self-reference (this
/ self) and a super-reference (super / base) are Halstead
operators or operands. Java, C# and Kotlin call them operators; every
other language that classifies them at all calls them operands. The
same source translated between two languages therefore scores different
n1 / N1 / n2 / N2, and the derived volume / difficulty / effort
/ time / bugs and all three maintainability-index variants move with it.

Found while resolving #1361, which added the missing Cpp / Mozcpp
This arm and had to pick a side. It picked operand, on the
majority plus a structural argument (see below); this issue is the
remaining disagreement, deliberately left unsettled there rather than
widened into a twelve-language rewrite.

Measured

Every row below was measured with bca ops --no-config at the pinned
grammars, not read off the source.

Language self-reference as super-reference as site
Java This operator Super operator src/getter/java.rs
C# This operator Base operator src/getter/csharp.rs
Kotlin This operator Super operator src/getter/kotlin.rs
JavaScript This operand Super operand src/getter.rs (shared macro)
MozJS This operand Super operand src/getter.rs (shared macro)
TypeScript This operand Super operand src/getter.rs (shared macro)
TSX This operand Super operand src/getter.rs (shared macro)
Ruby Zelf operand Super operand src/getter/ruby.rs
Rust Zelf operand src/getter/rust.rs
C++ This operand (#1361) src/getter/cpp.rs
Mozcpp This operand (#1361) src/getter/mozcpp.rs
PHP $this (variable_name) operand grammar, not an arm
Python self (identifier) operand grammar, not an arm
Groovy this (identifier) operand grammar, not an arm
ObjC self (identifier) operand grammar, not an arm
Lua self (identifier) operand grammar, not an arm

Four of the operand rows (Python, Groovy, ObjC, Lua) are operands by
grammar accident
— those grammars spell the keyword as a plain
identifier (kind_id 1), so no dispatch arm is involved and no
deliberate decision was ever made. PHP is a fourth: $this is a
variable_name, while PHP's Zelf arm — which is an operator —
covers the unrelated self:: class reference, not the instance.

So the live disagreement is Java / C# / Kotlin (three deliberate
operator arms) against the JS family plus Ruby, Rust and C++ (seven
deliberate operand arms). Counting the grammar-accident rows too, the
split is eleven operand against three operator.

Which way is right

#1361's argument for operand, restated so this issue can be decided
without re-deriving it:

  • Structure. A member access is <receiver> <op> <field>. p.x
    bills operands p / x around the . operator. Classifying the
    receiver as an operator makes this.x a binary operator with one
    operand, and scores the identical AST differently based only on how
    the receiver is spelled. In Java today, this.x and p.x are
    (n1, n2) (2, 1) and (1, 2) respectively.
  • Role. A self-reference denotes a value and stands wherever a
    variable stands (return this;, f(this), super.g()); the . /
    -> acting on it is already counted.
  • Provenance. The three operator arms each swept the keyword in
    under a // Operator: … keywords heading — classification by lexical
    class rather than by Halstead role. That is the same style that
    produced Per-language Halstead operator/operand classification inconsistencies #695's accessor-keyword split, where get / set were
    operands in the JS family and operators in C#. The JS-family operand
    arm, by contrast, reasons about it explicitly: its MetaProperty
    note calls this "one atomic operand".

Proposed resolution

Move This / Super out of the operator arm and into the operand arm
in java.rs, csharp.rs (This / Base) and kotlin.rs, leaving
PHP's Zelf / Parent alone — self:: / parent:: are class
references in scope-resolution position, a genuinely different
construct from $this.

Per grammar-dispatch section 1, enumerate the numeric-suffix aliases in
all three generated enums before writing the arms, and per section 5
confirm no wrapper already bills the same text (super_interfaces,
base_list, super_expression).

Expected impact

n1 / N1 fall by one occurrence per self/super reference and n2 /
N2 rise by the same, in Java, C# and Kotlin. Snapshot churn is
limited to whatever those languages hold in the integration corpora,
which per the project's own corpus notes is small next to the C-family
share.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions