aboutsummaryrefslogtreecommitdiffhomepage
path: root/scripts/linters/contiguous_use_block.rb
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-06-28 17:45:06 +0900
committernsfisis <nsfisis@gmail.com>2026-06-28 17:45:26 +0900
commit53f1fb395f33e0fb8db9aebd09ea9082f650f9f1 (patch)
treef9e8bf0e9d4b1e98cce383574fb6a13b684fff08 /scripts/linters/contiguous_use_block.rb
parent212f5cd75b1403ee75ffa44d7ebdb181174340c0 (diff)
downloadphp-shirabe-53f1fb395f33e0fb8db9aebd09ea9082f650f9f1.tar.gz
php-shirabe-53f1fb395f33e0fb8db9aebd09ea9082f650f9f1.tar.zst
php-shirabe-53f1fb395f33e0fb8db9aebd09ea9082f650f9f1.zip
refactor: add linter
Diffstat (limited to 'scripts/linters/contiguous_use_block.rb')
-rw-r--r--scripts/linters/contiguous_use_block.rb82
1 files changed, 82 insertions, 0 deletions
diff --git a/scripts/linters/contiguous_use_block.rb b/scripts/linters/contiguous_use_block.rb
new file mode 100644
index 0000000..b535a03
--- /dev/null
+++ b/scripts/linters/contiguous_use_block.rb
@@ -0,0 +1,82 @@
+def contiguous_use_block(root_dir, excludes = [])
+ pattern = root_dir.join('crates', '**', '*.rs').to_s
+ errors = Dir.glob(pattern).sort.flat_map do |path|
+ relative = Pathname.new(path).relative_path_from(root_dir).to_s
+ next [] if excludes.include?(relative)
+
+ find_split_use_block(path, relative)
+ end
+
+ return true if errors.empty?
+
+ puts 'Found blank lines splitting the leading `use` block into sections.'
+ puts 'All `use` statements at the top of the file must be contiguous (no blank lines between them):'
+ errors.each do |err|
+ puts " #{err}"
+ end
+ false
+end
+
+USE_START_RE = /\A(?:pub(?:\([^)]*\))?\s+)?use\b/
+
+def find_split_use_block(path, relative)
+ lines = File.readlines(path)
+ errors = []
+
+ i = skip_preamble(lines)
+ return [] if i.nil?
+
+ loop do
+ i = consume_use_statement(lines, i)
+ break if i >= lines.length
+
+ blanks = []
+ j = i
+ while j < lines.length
+ stripped = lines[j].strip
+ if stripped.empty?
+ blanks << j
+ j += 1
+ elsif stripped.start_with?('//') || stripped.start_with?('#[')
+ j += 1
+ else
+ break
+ end
+ end
+
+ if j < lines.length && lines[j].strip =~ USE_START_RE
+ blanks.each do |bi|
+ errors << "#{relative}:#{bi + 1}: blank line splits the leading `use` block"
+ end
+ i = j
+ else
+ break
+ end
+ end
+
+ errors
+end
+
+def skip_preamble(lines)
+ lines.each_with_index do |raw, idx|
+ stripped = raw.strip
+ return idx if stripped =~ USE_START_RE
+ next if stripped.empty? || stripped.start_with?('//') || stripped.start_with?('#![') || stripped.start_with?('#[')
+
+ return nil
+ end
+ nil
+end
+
+def consume_use_statement(lines, start_idx)
+ brace_depth = 0
+ i = start_idx
+ while i < lines.length
+ line = lines[i]
+ brace_depth += line.count('{') - line.count('}')
+ done = brace_depth <= 0 && line.rstrip.end_with?(';')
+ i += 1
+ return i if done
+ end
+ i
+end