Skip to content

B3: the shape checks Part A could not remove - #17

Merged
milyin merged 1 commit into
docs/validation-umbrellafrom
step/b3-shape-checks
Aug 6, 2026
Merged

B3: the shape checks Part A could not remove#17
milyin merged 1 commit into
docs/validation-umbrellafrom
step/b3-shape-checks

Conversation

@milyin

@milyin milyin commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Step B3 of #6. Stacked on #16. Last of the checks.

Part A (#7#11) made most bad shapes unbuildable. Three survive, because each
depends on a relation between fields or on where a declaration sits rather
than on one field's type — neither of which a type in the model can capture.

Check Example that used to render
property-without-type-or-value val x — no type, no value, no accessors
enum-entry-missing-arguments enum class K(val code: Int) { A(1), B }
function-without-body fun run() at top level

The bodiless-function rule needs context

The walker tracks what encloses each declaration:

Container Bodiless function
interface / sealed interface allowed — abstract by position, no keyword
abstract / sealed class allowed, but the member must say abstract
top level, concrete class, companion object never — a body is required

A companion is concrete, which is easy to overlook: an interface's companion
object holds real code, so its members need bodies even though the interface's
own members do not. Tested.

external has been a body kind since A5 (#11) rather than a modifier, so an
external fun is never flagged here — it has a body kind, just not a body.

The property check is deliberately narrow

Only the all three absent case is flagged. A type alone is an abstract
property; a value alone infers its type; accessors alone is the third legal
shape. Flagging any of those would be a false positive, so the check stays on
the one case that is unambiguously wrong. There is a test asserting all four
legal shapes pass.

Verification

All 80 pre-existing tests pass. Six added, including the negative cases: an enum
with no constructor needing no entry arguments, and the four legal property
shapes.

Two existing tests built bodiless functions incidentally while asserting
something else (import collection, identifier checks); they now supply bodies.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds the remaining B3 validation checks that Part A couldn’t make unrepresentable, by validating declaration shapes that depend on cross-field relationships or declaration context (container).

Changes:

  • Add shape validation for: bare properties (val x), enum entries missing constructor arguments, and bodiless functions in disallowed containers.
  • Track declaration container context (file / interface / abstract class / concrete) to validate bodiless-function legality.
  • Extend and adjust tests to cover the new checks and update incidental bodiless functions to include bodies.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/validate.rs Introduces B3 shape checks (property, enum entry, function body) with container-aware traversal.
src/tests.rs Adds targeted tests for the new checks and updates existing tests to satisfy the new function-body rule.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/validate.rs
Comment on lines +556 to +571
if let KtClassKind::Enum { ctor, entries } = &c.kind {
if !ctor.is_empty() {
for e in entries.iter().filter(|e| e.args.is_none()) {
d.push(
Check::EnumEntryMissingArguments,
&inner,
format!(
"entry `{}` passes no arguments, but the enum declares {} \
constructor parameter(s)",
e.name,
ctor.len()
),
);
}
}
}
Comment thread src/validate.rs
Comment on lines +529 to +533
Container::Abstract => f
.modifiers
.iter()
.any(|m| m.split(' ').any(|w| w == "abstract")),
Container::File | Container::Concrete => false,
Three shapes are still buildable after the structural work, because each
depends on a relation between fields or on where a declaration sits
rather than on one field's type:

  * `val x` with no type, no value and no accessors
  * an enum entry passing no arguments to a constructor the enum declares
  * a function with no body somewhere that does not mean "abstract"

The last one needs context, so the walker tracks its container: an
interface member is abstract by position and needs no keyword; a member
of an abstract or sealed class may be abstract but has to say so; at top
level, inside a concrete class, or inside a companion — which is
concrete — a body is always required. `external` is a body kind since
A5, so it is never flagged.

The property check is deliberately narrow. Only the all-three-absent
case is unambiguously wrong: a type alone is an abstract property and a
value alone infers its type, so flagging either would be a false
positive. Tested.

Two existing tests built bodiless functions incidentally while asserting
something else; they now supply bodies.
@milyin
milyin force-pushed the step/b3-shape-checks branch from 133b37d to 42419dc Compare August 6, 2026 11:40
@milyin
milyin force-pushed the step/b2-scopes-and-namespaces branch from 4405e14 to 093b90d Compare August 6, 2026 11:40
@milyin
milyin changed the base branch from step/b2-scopes-and-namespaces to docs/validation-umbrella August 6, 2026 11:41
@milyin
milyin merged commit ffa5abc into docs/validation-umbrella Aug 6, 2026
1 of 4 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants