luaur_analysis/functions/
lint_comments.rs1use crate::functions::emit_warning::emit_warning;
2use crate::functions::fuzzy_match::fuzzy_match;
3use crate::records::lint_context::LintContext;
4use core::ffi::c_char;
5use luaur_ast::records::hot_comment::HotComment;
6use luaur_config::enums::code::Code;
7use luaur_config::records::lint_warning::LintWarning;
8
9pub fn lint_comments(context: &mut LintContext, hotcomments: &[HotComment]) {
10 let mut seen_mode = false;
11
12 for hc in hotcomments {
13 if hc.content.is_empty()
15 || hc.content.as_bytes().first() == Some(&b' ')
16 || hc.content.as_bytes().first() == Some(&b'\t')
17 {
18 continue;
19 }
20
21 if !hc.header {
22 emit_warning(
23 context,
24 Code::Code_CommentDirective,
25 hc.location,
26 format_args!("Comment directive is ignored because it is placed after the first non-comment token")
27 );
28 } else {
29 let space_pos = hc.content.find(|c| c == ' ' || c == '\t');
30
31 let first = if let Some(pos) = space_pos {
32 &hc.content[..pos]
33 } else {
34 &hc.content[..]
35 };
36
37 if first == "nolint" {
38 let notspace_pos = if let Some(pos) = space_pos {
39 hc.content[pos..]
40 .find(|c| c != ' ' && c != '\t')
41 .map(|p| pos + p)
42 } else {
43 None
44 };
45
46 if space_pos.is_none() || notspace_pos.is_none() {
47 } else if LintWarning::parse_name(&hc.content[notspace_pos.unwrap()..])
49 == Code::Code_Unknown
50 {
51 let rule = &hc.content[notspace_pos.unwrap()..];
52
53 let suggestion_ptr =
55 fuzzy_match(rule, K_WARNING_NAMES.as_ptr(), K_WARNING_NAMES.len());
56 if !suggestion_ptr.is_null() {
57 let suggestion =
58 unsafe { core::ffi::CStr::from_ptr(suggestion_ptr).to_string_lossy() };
59 emit_warning(
60 context,
61 Code::Code_CommentDirective,
62 hc.location,
63 format_args!("nolint directive refers to unknown lint rule '{}'; did you mean '{}'?", rule, suggestion)
64 );
65 } else {
66 emit_warning(
67 context,
68 Code::Code_CommentDirective,
69 hc.location,
70 format_args!("nolint directive refers to unknown lint rule '{}'", rule),
71 );
72 }
73 }
74 } else if first == "nocheck" || first == "nonstrict" || first == "strict" {
75 if space_pos.is_some() {
76 emit_warning(
77 context,
78 Code::Code_CommentDirective,
79 hc.location,
80 format_args!("Comment directive with the type checking mode has extra symbols at the end of the line")
81 );
82 } else if seen_mode {
83 emit_warning(
84 context,
85 Code::Code_CommentDirective,
86 hc.location,
87 format_args!(
88 "Comment directive with the type checking mode has already been used"
89 ),
90 );
91 } else {
92 seen_mode = true;
93 }
94 } else if first == "optimize" {
95 let notspace_pos = if let Some(pos) = space_pos {
96 hc.content[pos..]
97 .find(|c| c != ' ' && c != '\t')
98 .map(|p| pos + p)
99 } else {
100 None
101 };
102
103 if space_pos.is_none() || notspace_pos.is_none() {
104 emit_warning(
105 context,
106 Code::Code_CommentDirective,
107 hc.location,
108 format_args!("optimize directive requires an optimization level"),
109 );
110 } else {
111 let level = &hc.content[notspace_pos.unwrap()..];
112
113 if level != "0" && level != "1" && level != "2" {
114 emit_warning(
115 context,
116 Code::Code_CommentDirective,
117 hc.location,
118 format_args!("optimize directive uses unknown optimization level '{}', 0..2 expected", level)
119 );
120 }
121 }
122 } else if first == "native" {
123 if space_pos.is_some() {
124 emit_warning(
125 context,
126 Code::Code_CommentDirective,
127 hc.location,
128 format_args!("native directive has extra symbols at the end of the line"),
129 );
130 }
131 } else {
132 const K_HOT_COMMENTS: [*const c_char; 6] = [
133 c"nolint".as_ptr(),
134 c"nocheck".as_ptr(),
135 c"nonstrict".as_ptr(),
136 c"strict".as_ptr(),
137 c"optimize".as_ptr(),
138 c"native".as_ptr(),
139 ];
140
141 let suggestion_ptr =
142 fuzzy_match(first, K_HOT_COMMENTS.as_ptr(), K_HOT_COMMENTS.len());
143 if !suggestion_ptr.is_null() {
144 let suggestion =
145 unsafe { core::ffi::CStr::from_ptr(suggestion_ptr).to_string_lossy() };
146 emit_warning(
147 context,
148 Code::Code_CommentDirective,
149 hc.location,
150 format_args!(
151 "Unknown comment directive '{}'; did you mean '{}'?",
152 first, suggestion
153 ),
154 );
155 } else {
156 emit_warning(
157 context,
158 Code::Code_CommentDirective,
159 hc.location,
160 format_args!("Unknown comment directive '{}'", first),
161 );
162 }
163 }
164 }
165 }
166}
167
168const K_WARNING_NAMES: [*const c_char; 29] = [
171 c"UnknownGlobal".as_ptr(),
172 c"DeprecatedGlobal".as_ptr(),
173 c"GlobalUsedAsLocal".as_ptr(),
174 c"LocalShadow".as_ptr(),
175 c"SameLineStatement".as_ptr(),
176 c"MultiLineStatement".as_ptr(),
177 c"LocalUnused".as_ptr(),
178 c"FunctionUnused".as_ptr(),
179 c"ImportUnused".as_ptr(),
180 c"BuiltinGlobalWrite".as_ptr(),
181 c"PlaceholderRead".as_ptr(),
182 c"UnreachableCode".as_ptr(),
183 c"UnknownType".as_ptr(),
184 c"ForRange".as_ptr(),
185 c"UnbalancedAssignment".as_ptr(),
186 c"ImplicitReturn".as_ptr(),
187 c"DuplicateLocal".as_ptr(),
188 c"FormatString".as_ptr(),
189 c"TableLiteral".as_ptr(),
190 c"UninitializedLocal".as_ptr(),
191 c"DuplicateFunction".as_ptr(),
192 c"DeprecatedApi".as_ptr(),
193 c"TableOperations".as_ptr(),
194 c"DuplicateCondition".as_ptr(),
195 c"MisleadingAndOr".as_ptr(),
196 c"CommentDirective".as_ptr(),
197 c"IntegerParsing".as_ptr(),
198 c"ComparisonPrecedence".as_ptr(),
199 c"RedundantNativeAttribute".as_ptr(),
200];