aboutsummaryrefslogtreecommitdiffhomepage
path: root/scripts/linters/contiguous_use_block.rb
blob: bce9e32ba89a2e9383865789d58c16ea611a7f50 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
def contiguous_use_block(root_dir)
  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
    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