Skip to content

Warn on unrecognized config keys with a "did you mean" suggestion#3966

Open
markselby9 wants to merge 1 commit into
facebook:mainfrom
markselby9:feat/3943-config-unknown-keys
Open

Warn on unrecognized config keys with a "did you mean" suggestion#3966
markselby9 wants to merge 1 commit into
facebook:mainfrom
markselby9:feat/3943-config-unknown-keys

Conversation

@markselby9

Copy link
Copy Markdown
Contributor

Summary

Closes #3943.

When a pyrefly.toml / [tool.pyrefly] contains a key Pyrefly doesn't recognize, serde's #[serde(flatten)] catch-all silently swept it into extras, and from_file surfaced it only as a single lumped line:

Extra keys found in config: check-unannotated-def, made-up-key

That tells you something is wrong but not what you meant. This PR replaces it with one warning per unrecognized key and a "did you mean" suggestion when a known option is close:

Unknown config key `check-unannotated-def`. Did you mean `check-unannotated-defs`?
Unknown config key `made-up-key`

(As the issue notes, unknown error kinds already fail to parse — this brings unknown config keys closer to that behavior, while still only warning so users can intentionally share a config across tools.)

Details

  • Scoped candidates. Top-level keys are matched against the project-wide options plus ConfigBase settings; [[sub-config]] keys are matched against matches + ConfigBase only — so a sub-config typo never suggests a project-only key like project-includes.
  • Shared ranking. The suggestion reuses the edit-distance ranking already used for identifier typos in pyrefly_util::suggest. I generalized that module's internals into one private closest<T> and exposed a best_suggestion_str for plain strings; best_suggestion (used by 6 call sites) now delegates to it with unchanged behavior.
  • Migration aliases (e.g. snake_case python_version) are accepted by serde, so they never reach extras and are never flagged.

Test plan

  • New tests in crates/pyrefly_config/src/config.rs:
    • unknown key with a close match → warns + suggests
    • unknown key with no close match → warns, no suggestion
    • unknown key inside a [[sub-config]] → warns with the sub-config context + suggestion
    • canonical keys and a snake_case migration alias → no warnings
    • test_known_config_keys_cover_serialized_fields: a sync guard so newly-added (serialized) config fields must be added to the candidate lists
  • cargo test -p pyrefly_config — 224 passed.
  • cargo test -p pyrefly_util — 71 passed (guards the shared helper refactor).
  • Suggestion-exercising modules in the main crate unaffected: test::attributes (187), test::typed_dict (161), test::scope (96), test::enums (61), test::calls (47) — all green.
  • python3 test.py --no-test --no-tensor-shapes --no-conformance --no-jsonschema — clean.

A config key Pyrefly doesn't recognize — a typo like `check-unannotated-def`,
or a key copied from another tool — is swept into serde's `flatten` catch-all
and was surfaced only as a single lumped "Extra keys found" line, giving no
hint about what the user likely meant.

Emit one warning per unrecognized key and, when a known option is within a
small edit distance, suggest it (e.g. "Did you mean `check-unannotated-defs`?").
Top-level and `[[sub-config]]` keys are matched against the options valid in
their own scope, so a sub-config typo never suggests a project-only key.

The suggestion reuses the edit-distance ranking already used for identifier
typos in `pyrefly_util::suggest`, generalized to plain strings so the config
and identifier paths share one implementation.

Closes facebook#3943
@markselby9 markselby9 force-pushed the feat/3943-config-unknown-keys branch from c1e7715 to a94469b Compare June 29, 2026 04:10
@github-actions github-actions Bot added size/l and removed size/l labels Jun 29, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Feature request: Warn on unrecognized config keys

1 participant