Skip to content

Config options with multiple overloads lose their description, or gain it only intermittently #173

Description

@bentsherman

Summary

Follow-up to #172, which fixed the type shown for config options with several accepted types. The description for those options has the same order-dependence and is still wrong: it is taken from whichever overload is enumerated first, and only one overload per directive is annotated. Some process directives lose their documentation entirely, and others gain or lose it between runs of the same build.

Mechanism

SpecNode.processScope() (in nf-lang) builds the process scope by walking ProcessDsl$DirectiveDsl.getDeclaredMethods(), keeping the one-parameter overloads. Decompiled, it does:

if( !map.containsKey(method.getName()) )
    map.put(method.getName(), new Option(annotatedDescription(method, ""), types));
map.get(method.getName()).types.add(method.getParameterTypes()[0]);

The type list accumulates across every overload — which is what #172 was able to fix by joining them. The description is only read from the first overload encountered, and later overloads contribute nothing but their type.

Exactly one overload per directive carries @Description:

HAS @Description <- void arch(Map<String, ?>);
no @Description  <- void arch(Map<String, ?>, String);
no @Description  <- void arch(String);

HAS @Description <- void cache(String);
no @Description  <- void cache(Boolean);

So the description survives only when the annotated overload happens to be enumerated first.

Impact

Two distinct symptoms, both from that one line.

1. Descriptions permanently lost. Where the annotated overload is reliably not first, the option never has documentation. arch, disk and publishDir returned an empty description in 6 out of 6 runs. arch is annotated on arch(Map) but arch(String) is enumerated first, so the text is never used.

2. Descriptions appear and disappear between runs. For the rest, enumeration order is not stable, so the same build gives different answers. Over 6 runs of the same language-server-all.jar, requesting completion inside a process { } block:

option description present description empty
accelerator 1 5
cache 1 5
clusterOptions 1 5
pod 1 5
shell 1 5
arch 0 6
disk 0 6
publishDir 0 6

Non-overloaded directives are unaffected — cpus has a single annotated overload and returned its description in 6 of 6 runs.

Hover is affected the same way, since ConfigHoverProvider reads the same option.description().

Note that Class.getDeclaredMethods() order was stable across 5 runs when called in isolation on a bare JVM, so the instability appears to depend on the surrounding load rather than on reflection order alone. The synchronized added to ConfigSpecFactory.defaultScopes() in #172 did not remove it.

Reproduction

Workspace with main.nf (any valid script) and:

process {
    cpus = 4

}

Session: initializeinitializeddidChangeConfigurationdidOpen main.nfdidOpen nextflow.config → completion on the blank line inside process {. Read the documentation field of the returned items and repeat with a fresh server process. Opening the .nf file first matters — config-only sessions were stable in my earlier testing on #172.

Suggested fix

When merging overloads, do not let the first one decide the description — prefer a non-empty one. Mirroring #172's shape, something like keeping the existing description only if the incoming one is empty:

var existing = map.get(name);
var description = annotatedDescription(method, "");
if( existing.description().isEmpty() && !description.isEmpty() )
    // adopt the annotated description

Making the enumeration order deterministic would fix symptom 2 but not symptom 1, since arch, disk and publishDir would still deterministically pick the unannotated overload.

Environment

Found by the JAR-versus-native-image response equivalence check added in #146: with the #172 fix in place the type comparison is now stable, and this is what the diff surfaced next.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions