rubydex 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +9 -14
- data/THIRD_PARTY_LICENSES.html +13 -24
- data/ext/rubydex/definition.c +34 -0
- data/ext/rubydex/definition.h +3 -0
- data/ext/rubydex/diagnostic.c +2 -49
- data/ext/rubydex/extconf.rb +4 -2
- data/ext/rubydex/graph.c +59 -11
- data/lib/rubydex/cli/command/dead_code.rb +59 -0
- data/lib/rubydex/cli/command/lint/explain.rb +8 -6
- data/lib/rubydex/cli/command/lint.rb +10 -4
- data/lib/rubydex/linter/rule_loader.rb +1 -1
- data/lib/rubydex/mcp_server/tools/codebase_stats_tool.rb +1 -1
- data/lib/rubydex/mcp_server/tools/find_constant_references_tool.rb +4 -4
- data/lib/rubydex/mcp_server/tools/find_dead_code_candidates_tool.rb +33 -0
- data/lib/rubydex/mcp_server/tools/get_declaration_tool.rb +2 -2
- data/lib/rubydex/mcp_server/tools/get_descendants_tool.rb +4 -4
- data/lib/rubydex/mcp_server/tools/get_file_declarations_tool.rb +2 -2
- data/lib/rubydex/mcp_server/tools/search_declarations_tool.rb +6 -6
- data/lib/rubydex/mcp_server.rb +24 -12
- data/lib/rubydex/rules/dynamic_ancestor.rb +29 -0
- data/lib/rubydex/rules/dynamic_constant_reference.rb +25 -0
- data/lib/rubydex/rules/dynamic_singleton_definition.rb +30 -0
- data/lib/rubydex/rules/invalid_constant_visibility.rb +28 -0
- data/lib/rubydex/rules/invalid_method_visibility.rb +28 -0
- data/lib/rubydex/rules/parse_error.rb +25 -0
- data/lib/rubydex/rules/parse_warning.rb +28 -0
- data/lib/rubydex/rules/top_level_mixin_self.rb +27 -0
- data/lib/rubydex/rules/undefined_constant_visibility_target.rb +27 -0
- data/lib/rubydex/rules/undefined_method_visibility_target.rb +27 -0
- data/lib/rubydex/skill.rb +1 -0
- data/lib/rubydex/version.rb +1 -1
- data/lib/rubydex.rb +2 -0
- data/lib/rubydex_linter/rules/rule_structure.rb +19 -4
- data/rbi/rubydex.rbi +32 -130
- data/rust/Cargo.lock +17 -11
- data/rust/Cargo.toml +3 -0
- data/rust/rubydex/Cargo.toml +6 -2
- data/rust/rubydex/src/bin/generate_ruby_rules.rs +94 -0
- data/rust/rubydex/src/compile_assertions.rs +2 -0
- data/rust/rubydex/src/config.rs +1 -1
- data/rust/rubydex/src/diagnostic.rs +142 -51
- data/rust/rubydex/src/indexing/rbs_indexer.rs +4 -4
- data/rust/rubydex/src/indexing/ruby_indexer.rs +26 -18
- data/rust/rubydex/src/indexing/ruby_indexer_tests.rs +42 -2
- data/rust/rubydex/src/indexing.rs +6 -6
- data/rust/rubydex/src/listing.rs +12 -12
- data/rust/rubydex/src/model/built_in.rs +1 -1
- data/rust/rubydex/src/model/comment.rs +4 -4
- data/rust/rubydex/src/model/declaration.rs +25 -17
- data/rust/rubydex/src/model/document.rs +11 -11
- data/rust/rubydex/src/model/graph.rs +3 -3
- data/rust/rubydex/src/offset.rs +1 -13
- data/rust/rubydex/src/operation/ruby_builder.rs +29 -19
- data/rust/rubydex/src/query.rs +193 -2
- data/rust/rubydex/src/test_utils/graph_test.rs +2 -2
- data/rust/rubydex/src/test_utils/local_graph_test.rs +13 -6
- data/rust/rubydex-sys/Cargo.toml +2 -2
- data/rust/rubydex-sys/src/definition_api.rs +69 -2
- data/rust/rubydex-sys/src/diagnostic_api.rs +0 -35
- data/rust/rubydex-sys/src/graph_api.rs +193 -45
- data/rust/rubydex-sys/src/name_api.rs +219 -48
- metadata +191 -180
data/rust/rubydex/src/query.rs
CHANGED
|
@@ -5,12 +5,12 @@ use std::thread;
|
|
|
5
5
|
|
|
6
6
|
use url::Url;
|
|
7
7
|
|
|
8
|
-
use crate::model::built_in::OBJECT_ID;
|
|
8
|
+
use crate::model::built_in::{BUILT_IN_URI_ID, OBJECT_ID};
|
|
9
9
|
use crate::model::declaration::{Ancestor, Declaration, Namespace};
|
|
10
10
|
use crate::model::definitions::{Definition, Parameter};
|
|
11
11
|
use crate::model::graph::Graph;
|
|
12
12
|
use crate::model::identity_maps::IdentityHashSet;
|
|
13
|
-
use crate::model::ids::{DeclarationId, DefinitionId, NameId, StringId, UriId};
|
|
13
|
+
use crate::model::ids::{ConstantReferenceId, DeclarationId, DefinitionId, NameId, StringId, UriId};
|
|
14
14
|
use crate::model::keywords::{self, Keyword};
|
|
15
15
|
use crate::model::name::NameRef;
|
|
16
16
|
use crate::model::visibility::Visibility;
|
|
@@ -833,6 +833,64 @@ pub fn follow_method_alias(graph: &Graph, alias_id: DefinitionId) -> Result<Decl
|
|
|
833
833
|
}
|
|
834
834
|
}
|
|
835
835
|
|
|
836
|
+
/// Returns `true` when any definition of `declaration` was seeded by rubydex rather than read from indexed source.
|
|
837
|
+
///
|
|
838
|
+
/// Dead code candidates are reported at declaration granularity, so a seeded definition disqualifies the declaration
|
|
839
|
+
/// even when user code also reopens it.
|
|
840
|
+
fn is_built_in(graph: &Graph, declaration: &Declaration) -> bool {
|
|
841
|
+
declaration.definitions().iter().any(|definition_id| {
|
|
842
|
+
graph
|
|
843
|
+
.definitions()
|
|
844
|
+
.get(definition_id)
|
|
845
|
+
.is_some_and(|definition| definition.uri_id() == &*BUILT_IN_URI_ID)
|
|
846
|
+
})
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/// Returns a list of declarations that may be unused in the codebase, which means
|
|
850
|
+
/// that the analysis could not find any references to them. This misses
|
|
851
|
+
/// meta-programming or untyped code, which is why the term candidates is used.
|
|
852
|
+
///
|
|
853
|
+
/// Currently, only supports constants.
|
|
854
|
+
///
|
|
855
|
+
/// # Panics
|
|
856
|
+
///
|
|
857
|
+
/// Will panic if any of the threads panic
|
|
858
|
+
pub fn dead_code_candidates(graph: &Graph) -> Vec<DeclarationId> {
|
|
859
|
+
let num_threads = thread::available_parallelism().map_or(4, std::num::NonZero::get);
|
|
860
|
+
let declarations = graph.declarations();
|
|
861
|
+
|
|
862
|
+
let ids: Vec<DeclarationId> = declarations.keys().copied().collect();
|
|
863
|
+
let chunk_size = ids.len().div_ceil(num_threads);
|
|
864
|
+
|
|
865
|
+
if chunk_size == 0 {
|
|
866
|
+
return Vec::new();
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
thread::scope(|s| {
|
|
870
|
+
let handles: Vec<_> = ids
|
|
871
|
+
.chunks(chunk_size)
|
|
872
|
+
.map(|chunk| {
|
|
873
|
+
s.spawn(|| {
|
|
874
|
+
chunk
|
|
875
|
+
.iter()
|
|
876
|
+
.filter(|id| {
|
|
877
|
+
let decl = declarations.get(id).unwrap();
|
|
878
|
+
decl.constant_references()
|
|
879
|
+
.is_some_and(<[ConstantReferenceId]>::is_empty)
|
|
880
|
+
&& !matches!(decl, Declaration::Namespace(Namespace::SingletonClass(_)))
|
|
881
|
+
&& !decl.has_no_definitions()
|
|
882
|
+
&& !is_built_in(graph, decl)
|
|
883
|
+
})
|
|
884
|
+
.copied()
|
|
885
|
+
.collect::<Vec<_>>()
|
|
886
|
+
})
|
|
887
|
+
})
|
|
888
|
+
.collect();
|
|
889
|
+
|
|
890
|
+
handles.into_iter().flat_map(|h| h.join().unwrap()).collect()
|
|
891
|
+
})
|
|
892
|
+
}
|
|
893
|
+
|
|
836
894
|
#[cfg(test)]
|
|
837
895
|
mod tests {
|
|
838
896
|
use std::str::FromStr;
|
|
@@ -920,6 +978,21 @@ mod tests {
|
|
|
920
978
|
};
|
|
921
979
|
}
|
|
922
980
|
|
|
981
|
+
macro_rules! assert_dead_code_candidates {
|
|
982
|
+
($context:expr, [$($expected:expr),* $(,)?]) => {
|
|
983
|
+
let mut actual: Vec<String> = dead_code_candidates($context.graph())
|
|
984
|
+
.iter()
|
|
985
|
+
.map(|id| $context.graph().declarations().get(id).unwrap().name().to_string())
|
|
986
|
+
.collect();
|
|
987
|
+
actual.sort();
|
|
988
|
+
|
|
989
|
+
let mut expected: Vec<String> = vec![$(String::from($expected)),*];
|
|
990
|
+
expected.sort();
|
|
991
|
+
|
|
992
|
+
assert_eq!(expected, actual);
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
|
|
923
996
|
#[test]
|
|
924
997
|
fn fuzzy_search_returns_partial_matches() {
|
|
925
998
|
let mut context = GraphTest::new();
|
|
@@ -3982,4 +4055,122 @@ mod tests {
|
|
|
3982
4055
|
Err(FindMemberError::DeclarationNotFound),
|
|
3983
4056
|
);
|
|
3984
4057
|
}
|
|
4058
|
+
|
|
4059
|
+
#[test]
|
|
4060
|
+
fn dead_code_candidates_returns_unreferenced_constants() {
|
|
4061
|
+
let mut context = GraphTest::new();
|
|
4062
|
+
context.index_uri(
|
|
4063
|
+
"file:///foo.rb",
|
|
4064
|
+
r"
|
|
4065
|
+
class UnusedClass; end
|
|
4066
|
+
module UnusedModule; end
|
|
4067
|
+
UNUSED_CONSTANT = 1
|
|
4068
|
+
",
|
|
4069
|
+
);
|
|
4070
|
+
context.resolve();
|
|
4071
|
+
|
|
4072
|
+
assert_dead_code_candidates!(context, ["UnusedClass", "UnusedModule", "UNUSED_CONSTANT"]);
|
|
4073
|
+
}
|
|
4074
|
+
|
|
4075
|
+
#[test]
|
|
4076
|
+
fn dead_code_candidates_excludes_referenced_constants() {
|
|
4077
|
+
let mut context = GraphTest::new();
|
|
4078
|
+
context.index_uri(
|
|
4079
|
+
"file:///foo.rb",
|
|
4080
|
+
r"
|
|
4081
|
+
class UsedClass; end
|
|
4082
|
+
USED_CONSTANT = 1
|
|
4083
|
+
",
|
|
4084
|
+
);
|
|
4085
|
+
context.index_uri(
|
|
4086
|
+
"file:///bar.rb",
|
|
4087
|
+
r"
|
|
4088
|
+
UsedClass
|
|
4089
|
+
USED_CONSTANT
|
|
4090
|
+
",
|
|
4091
|
+
);
|
|
4092
|
+
context.resolve();
|
|
4093
|
+
|
|
4094
|
+
assert_dead_code_candidates!(context, []);
|
|
4095
|
+
}
|
|
4096
|
+
|
|
4097
|
+
#[test]
|
|
4098
|
+
fn dead_code_candidates_excludes_methods_and_variables() {
|
|
4099
|
+
let mut context = GraphTest::new();
|
|
4100
|
+
context.index_uri(
|
|
4101
|
+
"file:///foo.rb",
|
|
4102
|
+
r"
|
|
4103
|
+
$global_var = 1
|
|
4104
|
+
|
|
4105
|
+
class Holder
|
|
4106
|
+
@@class_var = 1
|
|
4107
|
+
|
|
4108
|
+
def initialize
|
|
4109
|
+
@instance_var = 1
|
|
4110
|
+
end
|
|
4111
|
+
|
|
4112
|
+
def never_called; end
|
|
4113
|
+
end
|
|
4114
|
+
",
|
|
4115
|
+
);
|
|
4116
|
+
context.resolve();
|
|
4117
|
+
|
|
4118
|
+
assert_dead_code_candidates!(context, ["Holder"]);
|
|
4119
|
+
}
|
|
4120
|
+
|
|
4121
|
+
#[test]
|
|
4122
|
+
fn dead_code_candidates_counts_references_rather_than_reachability() {
|
|
4123
|
+
let mut context = GraphTest::new();
|
|
4124
|
+
context.index_uri(
|
|
4125
|
+
"file:///foo.rb",
|
|
4126
|
+
r"
|
|
4127
|
+
class Target; end
|
|
4128
|
+
AliasName = Target
|
|
4129
|
+
",
|
|
4130
|
+
);
|
|
4131
|
+
context.resolve();
|
|
4132
|
+
|
|
4133
|
+
// `AliasName` is itself dead, but it still references `Target`, giving `Target` a non-zero count.
|
|
4134
|
+
// A single pass only peels the outermost layer of a dead subgraph.
|
|
4135
|
+
assert_dead_code_candidates!(context, ["AliasName"]);
|
|
4136
|
+
}
|
|
4137
|
+
|
|
4138
|
+
#[test]
|
|
4139
|
+
fn dead_code_candidates_excludes_built_ins_and_definitionless_declarations() {
|
|
4140
|
+
let mut context = GraphTest::new();
|
|
4141
|
+
context.index_uri(
|
|
4142
|
+
"file:///foo.rb",
|
|
4143
|
+
r"
|
|
4144
|
+
class Unused
|
|
4145
|
+
def self.class_method; end
|
|
4146
|
+
end
|
|
4147
|
+
|
|
4148
|
+
class Class
|
|
4149
|
+
end
|
|
4150
|
+
",
|
|
4151
|
+
);
|
|
4152
|
+
context.resolve();
|
|
4153
|
+
|
|
4154
|
+
// A reopened built-in remains excluded regardless of definition order.
|
|
4155
|
+
assert_dead_code_candidates!(context, ["Unused"]);
|
|
4156
|
+
}
|
|
4157
|
+
|
|
4158
|
+
#[test]
|
|
4159
|
+
fn dead_code_candidates_excludes_singleton_classes() {
|
|
4160
|
+
let mut context = GraphTest::new();
|
|
4161
|
+
context.index_uri(
|
|
4162
|
+
"file:///foo.rb",
|
|
4163
|
+
r"
|
|
4164
|
+
class Unused; end
|
|
4165
|
+
class Attached; end
|
|
4166
|
+
|
|
4167
|
+
class << Attached
|
|
4168
|
+
def never_called; end
|
|
4169
|
+
end
|
|
4170
|
+
",
|
|
4171
|
+
);
|
|
4172
|
+
context.resolve();
|
|
4173
|
+
|
|
4174
|
+
assert_dead_code_candidates!(context, ["Unused"]);
|
|
4175
|
+
}
|
|
3985
4176
|
}
|
|
@@ -44,14 +44,14 @@ impl GraphTest {
|
|
|
44
44
|
/// Indexes a Ruby source
|
|
45
45
|
pub fn index_uri(&mut self, uri: &str, source: &str) {
|
|
46
46
|
let source = normalize_indentation(source);
|
|
47
|
-
let local_graph = indexing::build_local_graph(uri.
|
|
47
|
+
let local_graph = indexing::build_local_graph(uri.into(), &source, &LanguageId::Ruby, self.backend);
|
|
48
48
|
self.graph.consume_document_changes(local_graph);
|
|
49
49
|
}
|
|
50
50
|
|
|
51
51
|
/// Indexes an RBS source
|
|
52
52
|
pub fn index_rbs_uri(&mut self, uri: &str, source: &str) {
|
|
53
53
|
let source = normalize_indentation(source);
|
|
54
|
-
indexing::index_source(&mut self.graph, uri, &source, &LanguageId::Rbs);
|
|
54
|
+
indexing::index_source(&mut self.graph, uri.into(), &source, &LanguageId::Rbs);
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
pub fn delete_uri(&mut self, uri: &str) {
|
|
@@ -24,22 +24,29 @@ impl LocalGraphTest {
|
|
|
24
24
|
|
|
25
25
|
#[must_use]
|
|
26
26
|
pub fn new_with_backend(uri: &str, source: &str, backend: IndexerBackend) -> Self {
|
|
27
|
-
let uri = uri.to_string();
|
|
28
27
|
let source = normalize_indentation(source);
|
|
29
|
-
let graph = build_local_graph(uri.
|
|
30
|
-
|
|
28
|
+
let graph = build_local_graph(uri.into(), &source, &LanguageId::Ruby, backend);
|
|
29
|
+
|
|
30
|
+
Self {
|
|
31
|
+
uri: uri.to_string(),
|
|
32
|
+
source,
|
|
33
|
+
graph,
|
|
34
|
+
}
|
|
31
35
|
}
|
|
32
36
|
|
|
33
37
|
#[must_use]
|
|
34
38
|
pub fn new_rbs(uri: &str, source: &str) -> Self {
|
|
35
|
-
let uri = uri.to_string();
|
|
36
39
|
let source = normalize_indentation(source);
|
|
37
40
|
|
|
38
|
-
let mut indexer = RBSIndexer::new(uri.
|
|
41
|
+
let mut indexer = RBSIndexer::new(uri.into(), &source);
|
|
39
42
|
indexer.index();
|
|
40
43
|
let graph = indexer.local_graph();
|
|
41
44
|
|
|
42
|
-
Self {
|
|
45
|
+
Self {
|
|
46
|
+
uri: uri.to_string(),
|
|
47
|
+
source,
|
|
48
|
+
graph,
|
|
49
|
+
}
|
|
43
50
|
}
|
|
44
51
|
|
|
45
52
|
#[must_use]
|
data/rust/rubydex-sys/Cargo.toml
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[package]
|
|
2
2
|
name = "rubydex-sys"
|
|
3
|
-
version =
|
|
3
|
+
version.workspace = true
|
|
4
4
|
edition = "2024"
|
|
5
5
|
rust-version = "1.89.0"
|
|
6
6
|
license = "MIT"
|
|
@@ -15,7 +15,7 @@ categories = ["development-tools", "external-ffi-bindings"]
|
|
|
15
15
|
crate-type = ["cdylib", "staticlib"]
|
|
16
16
|
|
|
17
17
|
[dependencies]
|
|
18
|
-
rubydex = { version = "0.2
|
|
18
|
+
rubydex = { version = "=0.4.2", path = "../rubydex" }
|
|
19
19
|
libc = "0.2.174"
|
|
20
20
|
url = "2.5.4"
|
|
21
21
|
line-index = "0.1.2"
|
|
@@ -6,7 +6,9 @@ use crate::location_api::{Location, create_location_for_uri_and_offset};
|
|
|
6
6
|
use crate::reference_api::CConstantReference;
|
|
7
7
|
use libc::c_char;
|
|
8
8
|
use rubydex::model::definitions::{Definition, Mixin};
|
|
9
|
-
use rubydex::model::
|
|
9
|
+
use rubydex::model::graph::Graph;
|
|
10
|
+
use rubydex::model::ids::{DefinitionId, NameId};
|
|
11
|
+
use rubydex::model::name::ParentScope;
|
|
10
12
|
use rubydex::query::AliasResolutionError;
|
|
11
13
|
use std::ffi::CString;
|
|
12
14
|
use std::ptr;
|
|
@@ -110,6 +112,71 @@ pub unsafe extern "C" fn rdx_definition_name(pointer: GraphPointer, definition_i
|
|
|
110
112
|
})
|
|
111
113
|
}
|
|
112
114
|
|
|
115
|
+
fn raw_name_for_name(graph: &Graph, name_id: NameId) -> String {
|
|
116
|
+
let mut raw_name = String::new();
|
|
117
|
+
append_raw_name(graph, name_id, &mut raw_name);
|
|
118
|
+
raw_name
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
fn append_raw_name(graph: &Graph, name_id: NameId, raw_name: &mut String) {
|
|
122
|
+
let name = graph
|
|
123
|
+
.names()
|
|
124
|
+
.get(&name_id)
|
|
125
|
+
.expect("name should exist while building raw_name");
|
|
126
|
+
let simple_name = graph
|
|
127
|
+
.strings()
|
|
128
|
+
.get(name.str())
|
|
129
|
+
.expect("string should exist while building raw_name")
|
|
130
|
+
.as_str();
|
|
131
|
+
|
|
132
|
+
match name.parent_scope() {
|
|
133
|
+
ParentScope::None => {}
|
|
134
|
+
ParentScope::TopLevel => raw_name.push_str("::"),
|
|
135
|
+
ParentScope::Some(parent_id) | ParentScope::Attached(parent_id) => {
|
|
136
|
+
append_raw_name(graph, *parent_id, raw_name);
|
|
137
|
+
raw_name.push_str("::");
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
raw_name.push_str(simple_name);
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
fn raw_name_id(definition: &Definition) -> Option<&NameId> {
|
|
145
|
+
match definition {
|
|
146
|
+
Definition::Class(_) | Definition::Module(_) | Definition::Constant(_) | Definition::ConstantAlias(_) => {
|
|
147
|
+
definition.name_id()
|
|
148
|
+
}
|
|
149
|
+
_ => None,
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/// Returns the UTF-8 raw name string for a class, module, constant, or constant-alias definition id.
|
|
154
|
+
///
|
|
155
|
+
/// The raw name is reconstructed from the definition's `Name`, preserving explicit parent scopes like `Foo::Bar` and
|
|
156
|
+
/// rooted paths like `::Bar`. Returns null if the definition cannot be found or does not support raw names. Caller must
|
|
157
|
+
/// free a non-null result with `free_c_string`.
|
|
158
|
+
///
|
|
159
|
+
/// # Panics
|
|
160
|
+
///
|
|
161
|
+
/// Panics if the graph contains an invalid name or string reference, or if the name contains an embedded null byte.
|
|
162
|
+
///
|
|
163
|
+
/// # Safety
|
|
164
|
+
///
|
|
165
|
+
/// Assumes pointer is valid.
|
|
166
|
+
///
|
|
167
|
+
#[unsafe(no_mangle)]
|
|
168
|
+
pub unsafe extern "C" fn rdx_definition_raw_name(pointer: GraphPointer, definition_id: u64) -> *const c_char {
|
|
169
|
+
with_graph(pointer, |graph| {
|
|
170
|
+
let def_id = DefinitionId::new(definition_id);
|
|
171
|
+
let Some(name_id) = graph.definitions().get(&def_id).and_then(raw_name_id) else {
|
|
172
|
+
return ptr::null();
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
let name = raw_name_for_name(graph, *name_id);
|
|
176
|
+
CString::new(name).unwrap().into_raw().cast_const()
|
|
177
|
+
})
|
|
178
|
+
}
|
|
179
|
+
|
|
113
180
|
/// Shared iterator over definition (id, kind) pairs
|
|
114
181
|
#[derive(Debug)]
|
|
115
182
|
pub struct DefinitionsIter {
|
|
@@ -181,7 +248,7 @@ pub unsafe extern "C" fn rdx_definition_comments(pointer: GraphPointer, definiti
|
|
|
181
248
|
.comments()
|
|
182
249
|
.iter()
|
|
183
250
|
.map(|c| CommentEntry {
|
|
184
|
-
string: CString::new(c.string()
|
|
251
|
+
string: CString::new(c.string()).unwrap().into_raw().cast_const(),
|
|
185
252
|
location: create_location_for_uri_and_offset(graph, document, c.offset()),
|
|
186
253
|
})
|
|
187
254
|
.collect::<Vec<CommentEntry>>()
|
|
@@ -32,7 +32,6 @@ impl From<Severity> for DiagnosticSeverity {
|
|
|
32
32
|
pub struct CRule {
|
|
33
33
|
pub name: *const c_char,
|
|
34
34
|
pub name_length: usize,
|
|
35
|
-
pub default_severity: DiagnosticSeverity,
|
|
36
35
|
}
|
|
37
36
|
|
|
38
37
|
impl From<Rule> for CRule {
|
|
@@ -42,44 +41,10 @@ impl From<Rule> for CRule {
|
|
|
42
41
|
Self {
|
|
43
42
|
name: name.as_ptr().cast::<c_char>(),
|
|
44
43
|
name_length: name.len(),
|
|
45
|
-
default_severity: DiagnosticSeverity::from(rule.default_severity()),
|
|
46
44
|
}
|
|
47
45
|
}
|
|
48
46
|
}
|
|
49
47
|
|
|
50
|
-
#[repr(C)]
|
|
51
|
-
pub struct CRuleArray {
|
|
52
|
-
pub items: *mut CRule,
|
|
53
|
-
pub len: usize,
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
/// Returns every rule the graph can report. Caller must free it with `rdx_rules_free`.
|
|
57
|
-
#[unsafe(no_mangle)]
|
|
58
|
-
pub extern "C" fn rdx_rules() -> CRuleArray {
|
|
59
|
-
let items = Rule::all().iter().copied().map(CRule::from).collect::<Box<[CRule]>>();
|
|
60
|
-
|
|
61
|
-
CRuleArray {
|
|
62
|
-
len: items.len(),
|
|
63
|
-
items: Box::into_raw(items).cast::<CRule>(),
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/// Frees an array previously returned by `rdx_rules`.
|
|
68
|
-
///
|
|
69
|
-
/// # Safety
|
|
70
|
-
///
|
|
71
|
-
/// - `rules` must have been returned by `rdx_rules` and must not be used afterwards.
|
|
72
|
-
#[unsafe(no_mangle)]
|
|
73
|
-
pub unsafe extern "C" fn rdx_rules_free(rules: CRuleArray) {
|
|
74
|
-
if rules.items.is_null() {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
unsafe {
|
|
79
|
-
let _ = Box::from_raw(ptr::slice_from_raw_parts_mut(rules.items, rules.len));
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
48
|
/// C-compatible struct representing a diagnostic entry.
|
|
84
49
|
#[repr(C)]
|
|
85
50
|
pub struct DiagnosticEntry {
|