Skip to content

Typed rules - #5025

Open
vidit-od wants to merge 19 commits into
haskell:masterfrom
vidit-od:Typed-Rules
Open

Typed rules#5025
vidit-od wants to merge 19 commits into
haskell:masterfrom
vidit-od:Typed-Rules

Conversation

@vidit-od

@vidit-od vidit-od commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

Introduces Hierarchical Typed Rules

Comment thread ghcide/src/Development/IDE/Core/Actions.hs Outdated
Comment thread ghcide/src/Development/IDE/Core/RuleInput.hs Outdated
Comment thread ghcide/src/Development/IDE/Core/Rules.hs Outdated
Comment thread ghcide/src/Development/IDE/Core/Rules.hs Outdated

@wz1000 wz1000 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Broadly looks good, some inputs need to be tightened up

Comment thread ghcide/src/Development/IDE/Core/RuleInput.hs Outdated
Comment thread ghcide/src/Development/IDE/Core/Actions.hs Outdated
Comment thread ghcide/src/Development/IDE/Core/Actions.hs Outdated
Comment thread ghcide/src/Development/IDE/Core/Rules.hs Outdated
Comment thread ghcide/src/Development/IDE/Core/RuleTypes.hs Outdated
vidit-od added 9 commits July 31, 2026 08:36
Store SomeInput in Shake keys and route rule lookup helpers through typed RuleInput values instead of raw normalized file paths. This also updates tracing and no-file handling to work with the typed key representation.
Thread SomeFileInput, SomeHaskellInput, and ProjectHaskellInput through file store, HIE indexing, and module rules. Add a generic input reclassification helper so call sites can move between compatible typed rule inputs without bespoke wrappers.
Thread typed rule inputs through core Shake helpers, file-store state, dependency graph lookups, and LSP request handlers.

Convert raw normalized paths at API boundaries so project, Haskell, and general file rules receive the appropriate input type.
Builds Ghcide, removes helpers that are not used or should not be used
Carry ProjectHaskellInput through dependency information, import lookup, reverse dependency traversal, HIE reads, and eval/reference paths instead of round-tripping through NormalizedFilePath.

This keeps non-project Haskell inputs out of project-only rules while preserving the typed inputs needed by downstream Shake rules.
Track project Haskell inputs in session state, known targets, VFS-backed services, and caches.

Migrate hover, notes, eval, and semantic token rules to use their typed inputs directly.

@fendor fendor left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Thanks, this looks very nice! This is a nitpick review, so it is the intention to be done after the review is addressed. I have reviewed the ghcide changes, will review plugins soon.

Further, we agreed the following documentation is still missing and will be added:

    * What are the existing rules?
    * What is RuleInput?
    * Why do we have RuleInput
    * What is the design of RuleInput
        Note [Hierarchical Inputs]
        Note [RuleInput]
        
        Reference these notes from various type classes and RuleInput type
    * Helper functions need documentation

Comment thread ghcide/session-loader/Development/IDE/Session/Diagnostics.hs Outdated
Comment thread ghcide/session-loader/Development/IDE/Session/Ghc.hs Outdated
Comment thread ghcide/session-loader/Development/IDE/Session/Ghc.hs Outdated
Comment thread ghcide/src/Development/IDE/Import/DependencyInformation.hs Outdated
Comment thread ghcide/src/Development/IDE/Import/FindImports.hs
Comment thread ghcide/src/Development/IDE/Spans/Pragmas.hs Outdated
Comment thread hls-plugin-api/src/Ide/Plugin/Error.hs Outdated
Comment thread hls-plugin-api/src/Ide/Plugin/Error.hs
Comment thread hls-plugin-api/src/Ide/Plugin/Error.hs
toPriority :: PluginError -> Priority
toPriority (PluginInternalError _) = Error
toPriority (PluginInvalidParams _) = Warning
toPriority (PluginUnsupportedUriType _) = Warning

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We might want to demote this to Debug. IIRC, a warning is displayed in the editor, but these errors will be perfectly sensible in the future, right?

@wz1000 opinions?

@vidit-od
vidit-od force-pushed the Typed-Rules branch 3 times, most recently from 0e49c0f to 330adaf Compare August 4, 2026 05:45
@vidit-od
vidit-od force-pushed the Typed-Rules branch 4 times, most recently from 144b524 to ec6e677 Compare August 4, 2026 20:50

@fendor fendor left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Some more comments :)

Comment thread ghcide/src/Development/IDE/Core/RuleInput.hs
Comment thread ghcide/src/Development/IDE/Core/RuleInput.hs
Comment thread ghcide/src/Development/IDE/Core/RuleInput.hs
Comment thread ghcide/src/Development/IDE/Core/RuleInput.hs
Comment on lines +104 to +107
Within this hierarchy, a parent accepts all descendants. Upcasting wraps a child
in each parent constructor, then uses 'toInput' for 'SomeInput'; for example,
'ProjectHaskellInput' -> 'SomeHaskellInput' -> 'SomeFileInput' -> 'SomeInput'.
'fromInput' performs the checked downcast and succeeds only within its branch.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What does a parent accepts all descendants mean?

Comment thread ghcide/src/Development/IDE/Core/RuleInput.hs
Comment thread ghcide/src/Development/IDE/Core/RuleInput.hs
Comment thread ghcide/src/Development/IDE/Core/RuleInput.hs
Comment thread ghcide/src/Development/IDE/Core/RuleInput.hs
Comment thread ghcide/src/Development/IDE/Core/RuleInput.hs
case file of
SomeProjectHaskellInput projectFile ->
setFileModified (cmapWithPrio LogFileStore recorder) (VFSModified vfs) ide False projectFile action
_ -> setSomethingModified (VFSModified vfs) ide (fromNormalizedFilePath (inputFilePath file) ++ " (modified)") action

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

We shouldn't do anything here, rather log this case.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

@wz1000 What should we do in this case? Call setSomethingModified for completeness? We need to add it to the set of files of interest, but it might not need to record that it isn't the first time this non project haskell file was opened, right? 🤔

case file of
SomeProjectHaskellInput projectFile ->
setFileModified (cmapWithPrio LogFileStore recorder) (VFSModified vfs) ide False projectFile action
_ -> setSomethingModified (VFSModified vfs) ide (fromNormalizedFilePath (inputFilePath file) ++ " (modified)") action

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Should we set something modified here? What does this do?

case file of
SomeProjectHaskellInput projectFile ->
setFileModified (cmapWithPrio LogFileStore recorder) (VFSModified vfs) ide True projectFile action
_ -> setSomethingModified (VFSModified vfs) ide (fromNormalizedFilePath (inputFilePath file) ++ " (modified)") action

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Same here

Comment thread ghcide/src/Development/IDE/Core/Rules.hs
Comment thread ghcide/src/Development/IDE/Core/Shake.hs
Comment thread ghcide/src/Development/IDE/Core/Shake.hs
Comment on lines +1296 to +1298
let mbFile = case inputFingerprint input of
InputFile file -> Just file
_ -> Nothing

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Move this into inProgress notification change

-- * creating a dependency: If everything depends on GetModificationTime, we lose early cutoff
-- * creating bogus "file does not exists" diagnostics
| otherwise -> useWithoutDependency (GetModificationTime_ False) (toSomeFileInput file)
_ -> pure Nothing

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Please list out all the possible cases here. I think that's better, even though the rhs is going to be identical for each

Comment on lines +87 to +88
-- GHC import paths are untyped and may include generated dependency roots;
-- retain only project Haskell inputs.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Which generated dependency roots? Where would this come from?

resolveCompletion ide _pid comp@CompletionItem{_detail,_documentation,_data_} uri (CompletionResolveData _ needType (NameDetails mod occ)) =
do
file <- getNormalizedFilePathE uri
projectInput <- withExceptT (const PluginStaleResolve) $ classifyAsHaskell uri

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Wrong classifier if completions only work with ProjectHaskell files, right?

Comment on lines -178 to +198
parseActions :: CI String -> [NormalizedFilePath] -> Action (Either Text [Bool])
parseActions "typecheck" fps = Right . fmap isJust <$> uses TypeCheck fps
parseActions "getLocatedImports" fps = Right . fmap isJust <$> uses GetLocatedImports fps
parseActions "getmodsummary" fps = Right . fmap isJust <$> uses GetModSummary fps
parseActions "getmodsummarywithouttimestamps" fps = Right . fmap isJust <$> uses GetModSummaryWithoutTimestamps fps
parseActions "getparsedmodule" fps = Right . fmap isJust <$> uses GetParsedModule fps
parseActions "ghcsession" fps = Right . fmap isJust <$> uses GhcSession fps
parseActions "ghcsessiondeps" fps = Right . fmap isJust <$> uses GhcSessionDeps fps
parseActions "gethieast" fps = Right . fmap isJust <$> uses GetHieAst fps
parseActions "getFileContents" fps = Right . fmap isJust <$> uses GetFileContents fps
parseActions other _ = return $ Left $ "Cannot parse ide rule: " <> pack (original other)
parseActions :: CI String -> [SomeFileInput] -> Action (Either Text [Bool])
parseActions action fps
| action == fromString "typecheck"
, Just pFiles <- traverse projectFile fps =
fmap (Right . map isJust) (uses TypeCheck pFiles)
where
projectFile (SomeFileHaskellInput (SomeProjectHaskellInput pFile)) = Just pFile
projectFile _ = Nothing
parseActions action fps = sequence <$> traverse (parseAction action) fps

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

What is going on here?

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.

3 participants