Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions src/layer_static_lint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,18 @@ Salsa.@derived function derived_static_lint_diagnostics_for_root(rt, root)
workspace_packages = lint_result.workspace_packages
env = derived_environment(rt, project_uri)

# Names the project declares as dependencies. An unresolved import of one of
# these is a dependency whose symbols could not be indexed/cached (it would
# otherwise be in the env and resolve), as opposed to a name that simply
# isn't in the environment (typo / missing dependency).
project = derived_project(rt, project_uri)
declared_deps = project === nothing ? Set{String}() :
Set{String}(Iterators.flatten((
keys(project.regular_packages),
keys(project.stdlib_packages),
keys(project.deved_packages),
)))

for uri in derived_include_closure(rt, root)
current_res = get!(res, uri, Set{Diagnostic}())

Expand All @@ -209,12 +221,13 @@ Salsa.@derived function derived_static_lint_diagnostics_for_root(rt, root)
push!(current_res, Diagnostic(rng, :warning, "Missing reference: $(err[2].val)", nothing, Symbol[], "StaticLint.jl"))
elseif StaticLint.haserror(err[2], meta_dict) && StaticLint.errorof(err[2], meta_dict) === StaticLint.UnresolvedImport
name = CSTParser.str_value(err[2])
msg = if StaticLint.is_in_wildcard_import(err[2])
"Failed to resolve `$name`. Missing-reference checks are disabled in this scope and all nested scopes."
else
"Failed to resolve `$name`. Anything imported through this statement is assumed to exist and will not be checked."
end
push!(current_res, Diagnostic(rng, :warning, msg, nothing, Symbol[], "StaticLint.jl"))
cause = name in declared_deps ?
"`$name` is a declared dependency but its symbols could not be indexed." :
"Failed to resolve `$name`."
consequence = StaticLint.is_in_wildcard_import(err[2]) ?
"Missing-reference checks are disabled in this scope and all nested scopes." :
"Anything imported through this statement is assumed to exist and will not be checked."
push!(current_res, Diagnostic(rng, :warning, "$cause $consequence", nothing, Symbol[], "StaticLint.jl"))
elseif StaticLint.haserror(err[2], meta_dict) && StaticLint.errorof(err[2], meta_dict) isa StaticLint.LintCodes
code = StaticLint.errorof(err[2], meta_dict)
description = get(StaticLint.LintCodeDescriptions, code, "")
Expand Down
45 changes: 45 additions & 0 deletions test/test_diagnostics.jl
Original file line number Diff line number Diff line change
Expand Up @@ -1428,6 +1428,51 @@ end
@test any(d -> startswith(d.message, "Failed to resolve `ActuallyUnresolved`"), diags)
end

@testitem "unresolved import: declared-but-uncacheable dep vs unknown name" begin
using JuliaWorkspaces.URIs2: URI

# `DeclaredButUncached` is listed in the manifest (so it's a declared
# dependency) but no symbols are cached for it, so it never enters the env.
# `TotallyUnknownPkg` is not declared anywhere.
project_toml = """
name = "UncachedDep"
uuid = "aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeee41"
version = "0.1.0"

[deps]
DeclaredButUncached = "12345678-1234-1234-1234-123456789abc"
"""
manifest_toml = """
julia_version = "1.11.0"
manifest_format = "2.0"
project_hash = "abc123"

[[deps.DeclaredButUncached]]
git-tree-sha1 = "0000000000000000000000000000000000000000"
uuid = "12345678-1234-1234-1234-123456789abc"
version = "1.0.0"
"""
source = """
module UncachedDep
import DeclaredButUncached: foo
using TotallyUnknownPkg
end
"""

jw = JuliaWorkspace()
add_file!(jw, TextFile(URI("file:///uncacheddep/Project.toml"), SourceText(project_toml, "toml")))
add_file!(jw, TextFile(URI("file:///uncacheddep/Manifest.toml"), SourceText(manifest_toml, "toml")))
add_file!(jw, TextFile(URI("file:///uncacheddep/src/UncachedDep.jl"), SourceText(source, "julia")))
JuliaWorkspaces.set_input_env_ready!(jw.runtime, true)

diags = get_diagnostic(jw, URI("file:///uncacheddep/src/UncachedDep.jl"))

# Declared dependency: message attributes the failure to indexing/caching
@test any(d -> d.message == "`DeclaredButUncached` is a declared dependency but its symbols could not be indexed. Anything imported through this statement is assumed to exist and will not be checked.", diags)
# Undeclared name: keeps the generic "Failed to resolve" wording
@test any(d -> d.message == "Failed to resolve `TotallyUnknownPkg`. Missing-reference checks are disabled in this scope and all nested scopes.", diags)
end

@testitem "missing-refs: default is all (getfield refs checked)" begin
using JuliaWorkspaces.URIs2: URI

Expand Down
Loading