diff --git a/apps/els_lsp/priv/code_navigation/src/diagnostics_xref.erl b/apps/els_lsp/priv/code_navigation/src/diagnostics_xref.erl index 1e716ea2..49eed564 100644 --- a/apps/els_lsp/priv/code_navigation/src/diagnostics_xref.erl +++ b/apps/els_lsp/priv/code_navigation/src/diagnostics_xref.erl @@ -12,3 +12,6 @@ existing() -> dynamic_call(Foo, Bar) -> Foo:bar(), foo:Bar(). + +not_exported() -> + lists:map_1(foo, [1,2,3]). diff --git a/apps/els_lsp/src/els_crossref_diagnostics.erl b/apps/els_lsp/src/els_crossref_diagnostics.erl index 5e6b03f5..dc1d60c3 100644 --- a/apps/els_lsp/src/els_crossref_diagnostics.erl +++ b/apps/els_lsp/src/els_crossref_diagnostics.erl @@ -18,6 +18,8 @@ source/0 ]). +-type missing_reason() :: module | function | export. + %%============================================================================== %% Includes %%============================================================================== @@ -114,19 +116,23 @@ make_diagnostic({missing, Kind}, #{id := Id} = POI) -> make_diagnostic(true, _) -> []. --spec range(module | function, els_poi:poi()) -> els_poi:poi_range(). +-spec range(missing_reason(), els_poi:poi()) -> els_poi:poi_range(). range(module, #{data := #{mod_range := Range}}) -> Range; range(function, #{data := #{name_range := Range}}) -> Range; +range(export, #{data := #{name_range := Range}}) -> + Range; range(_, #{range := Range}) -> Range. --spec error_msg(module | function, els_poi:poi_id()) -> binary(). +-spec error_msg(missing_reason(), els_poi:poi_id()) -> binary(). error_msg(module, {M, _F, _A}) -> els_utils:to_binary(io_lib:format("Cannot find module ~p", [M])); error_msg(function, Id) -> - els_utils:to_binary(io_lib:format("Cannot find definition for function ~s", [id_str(Id)])). + els_utils:to_binary(io_lib:format("Cannot find definition for function ~s", [id_str(Id)])); +error_msg(export, Id) -> + els_utils:to_binary(io_lib:format("Function ~s is not exported.", [id_str(Id)])). -spec id_str(els_poi:poi_id()) -> string(). id_str(Id) -> @@ -136,7 +142,7 @@ id_str(Id) -> end. -spec has_definition(els_poi:poi(), els_dt_document:item(), _) -> - true | {missing, function | module}. + true | {missing, missing_reason()}. has_definition(#{data := #{imported := true}}, _Document, _Opts) -> %% Call to a bif true; @@ -177,7 +183,9 @@ has_definition( case function_lookup(MFA) of true -> true; - false -> + {missing, export} -> + true; + {missing, function} -> case els_code_navigation:goto_definition(Uri, POI) of {ok, _Defs} -> true; @@ -189,12 +197,14 @@ has_definition(#{id := {M, _F, _A} = MFA} = POI, _Document, _Opts) -> case function_lookup(MFA) of true -> true; - false -> + {missing, export} -> + {missing, export}; + {missing, function} -> case els_utils:find_module(M) of {ok, Uri} -> case els_code_navigation:goto_definition(Uri, POI) of {ok, _Defs} -> - true; + function_lookup(MFA); {error, _Error} -> {missing, function} end; @@ -205,13 +215,15 @@ has_definition(#{id := {M, _F, _A} = MFA} = POI, _Document, _Opts) -> has_definition(_POI, #{uri := _Uri}, _Opts) -> true. --spec function_lookup(mfa()) -> boolean(). +-spec function_lookup(mfa()) -> true | {missing, missing_reason()}. function_lookup(MFA) -> - case els_db:lookup(els_dt_functions:name(), MFA) of + case els_dt_functions:lookup(MFA) of {ok, []} -> - false; - {ok, _} -> - true + {missing, function}; + {ok, [#{is_exported := true}]} -> + true; + {ok, [#{is_exported := false}]} -> + {missing, export} end. -spec lager_definition(atom(), integer()) -> boolean(). diff --git a/apps/els_lsp/src/els_dt_functions.erl b/apps/els_lsp/src/els_dt_functions.erl index 8b7b955e..5446be45 100644 --- a/apps/els_lsp/src/els_dt_functions.erl +++ b/apps/els_lsp/src/els_dt_functions.erl @@ -37,13 +37,15 @@ -record(els_dt_functions, { mfa :: mfa() | '_' | {atom(), '_', '_'}, - version :: version() | '_' + version :: version() | '_', + is_exported :: boolean() | '_' }). -type els_dt_functions() :: #els_dt_functions{}. -type version() :: null | integer(). -type item() :: #{ mfa := mfa(), - version := version() + version := version(), + is_exported := boolean() }. -export_type([item/0]). @@ -65,21 +67,25 @@ opts() -> -spec from_item(item()) -> els_dt_functions(). from_item(#{ mfa := MFA, - version := Version + version := Version, + is_exported := IsExported }) -> #els_dt_functions{ mfa = MFA, - version = Version + version = Version, + is_exported = IsExported }. -spec to_item(els_dt_functions()) -> item(). to_item(#els_dt_functions{ mfa = MFA, - version = Version + version = Version, + is_exported = IsExported }) -> #{ mfa => MFA, - version => Version + version => Version, + is_exported => IsExported }. -spec insert(item()) -> ok | {error, any()}. @@ -96,8 +102,7 @@ versioned_insert(#{mfa := MFA, version := Version} = Map) -> els_db:conditional_write(name(), MFA, Record, Condition). -spec lookup(mfa()) -> {ok, [item()]}. -lookup({M, _F, _A} = MFA) -> - {ok, _Uris} = els_utils:find_modules(M), +lookup(MFA) -> {ok, Items} = els_db:lookup(name(), MFA), {ok, [to_item(Item) || Item <- Items]}. diff --git a/apps/els_lsp/src/els_indexing.erl b/apps/els_lsp/src/els_indexing.erl index 99eca4ee..773737d1 100644 --- a/apps/els_lsp/src/els_indexing.erl +++ b/apps/els_lsp/src/els_indexing.erl @@ -137,14 +137,17 @@ index_signature(M, Text, #{id := {F, A}, range := Range, data := #{args := Args} -spec index_functions(atom(), uri(), [els_poi:poi()], version()) -> ok. index_functions(M, Uri, POIs, Version) -> ok = els_dt_functions:versioned_delete_by_uri(Uri, Version), - [index_function(M, POI, Version) || #{kind := function} = POI <- POIs], + Exports = [{F, A} || #{id := {F, A}, kind := export_entry} <- POIs], + [index_function(M, POI, Exports, Version) || #{kind := function} = POI <- POIs], ok. --spec index_function(atom(), els_poi:poi(), version()) -> ok. -index_function(M, #{id := {F, A}}, Version) -> +-spec index_function(atom(), els_poi:poi(), els_poi:poi_id(), version()) -> ok. +index_function(M, #{id := {F, A}}, Exports, Version) -> + IsExported = lists:member({F, A}, Exports), els_dt_functions:versioned_insert(#{ mfa => {M, F, A}, - version => Version + version => Version, + is_exported => IsExported }). -spec index_references(atom(), uri(), [els_poi:poi()], version()) -> ok. diff --git a/apps/els_lsp/test/els_diagnostics_SUITE.erl b/apps/els_lsp/test/els_diagnostics_SUITE.erl index 64f44925..011b5cc8 100644 --- a/apps/els_lsp/test/els_diagnostics_SUITE.erl +++ b/apps/els_lsp/test/els_diagnostics_SUITE.erl @@ -786,6 +786,10 @@ crossref(_Config) -> #{ message => <<"Cannot find definition for function lists:map/3">>, range => {{5, 8}, {5, 11}} + }, + #{ + message => <<"Function lists:map_1/2 is not exported.">>, + range => {{16, 10}, {16, 15}} } ], Warnings = [], @@ -802,6 +806,10 @@ crossref_compiler_enabled(_Config) -> #{ message => <<"Cannot find definition for function lists:map/3">>, range => {{5, 8}, {5, 11}} + }, + #{ + message => <<"Function lists:map_1/2 is not exported.">>, + range => {{16, 10}, {16, 15}} } ], Warnings = [],