summaryrefslogtreecommitdiffstats
path: root/tools/lint/eslint/eslint-plugin-spidermonkey-js/lib/processors/self-hosted.js
blob: 8a607ffbe5c2f32fef96bc74e4205f0936b8e4cb (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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
/**
 * @fileoverview Remove macros from SpiderMonkey's self-hosted JS.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

"use strict";

const path = require("path");
const fs = require("fs");

const selfHostedRegex = /js\/src\/builtin\/.*?\.js$/;
const macroRegex = /\s*\#(if|ifdef|else|elif|endif|include|define|undef).*/;

function isSelfHostedFile(filename) {
  if (path.win32) {
    filename = filename.split(path.sep).join("/");
  }
  return selfHostedRegex.test(filename);
}

function tryReadFile(filePath) {
  if (!path.isAbsolute(filePath)) {
    return "";
  }
  if (!fs.existsSync(filePath)) {
    // Safely handle the case when the file wasn't found, because throwing
    // errors can lead to confusing result when used with ESLint.
    return "";
  }
  return fs.readFileSync(filePath, "utf-8");
}

// Adjust the range of fixes to match the original source code.
function createFix(lines, message) {
  let { line, column, fix } = message;

  // Line and column are 1-based. Make sure we got a valid input.
  if (line <= 0 || column <= 0) {
    return null;
  }

  // Reject to create a fix when the line is out of range for some reason.
  if (line > lines.length) {
    return null;
  }

  // Find the absolute start position of the line in the original file.
  let startOfLine = 0;
  for (let i = 0; i < line - 1; ++i) {
    // Add the length of the line, including its line separator.
    startOfLine += lines[i].length + "\n".length;
  }

  // Add the 1-based column to the start of line to get the start position.
  let start = startOfLine + (column - 1);

  // Add the fix range to get the end position.
  let end = start + (fix.range[1] - fix.range[0]);

  // And finally return the new fix object.
  return { text: fix.text, range: [start, end] };
}

module.exports = {
  preprocess(text, filename) {
    if (!isSelfHostedFile(filename)) {
      return [text];
    }

    let lines = text.split(/\n/);
    for (let i = 0; i < lines.length; i++) {
      if (!macroRegex.test(lines[i])) {
        // No macro here, nothing to do.
        continue;
      }

      for (; i < lines.length; i++) {
        // The macro isn't correctly indented, so we need to instruct
        // prettier to ignore them.
        lines[i] = "// prettier-ignore -- " + lines[i];

        // If the line ends with a backslash (\), the next line
        // is also part of part of the macro.
        if (!lines[i].endsWith("\\")) {
          break;
        }
      }
    }

    return [lines.join("\n")];
  },

  postprocess(messages, filename) {
    // Don't attempt to create fixes for any non-selfhosted files.
    if (!isSelfHostedFile(filename)) {
      return [].concat(...messages);
    }

    let lines = null;

    let result = [];
    for (let message of messages.flat()) {
      if (message.fix) {
        if (lines === null) {
          lines = tryReadFile(filename).split(/\n/);
        }

        let fix = createFix(lines, message);
        if (fix) {
          message.fix = fix;
        } else {
          // We couldn't create a fix, so we better remove the passed in fix,
          // because its range points to the preprocessor output, but the post-
          // processor must translate it into a range of the original source.
          delete message.fix;
        }
      }

      result.push(message);
    }
    return result;
  },

  supportsAutofix: true,
};