Skip to content

Commit b302545

Browse files
authored
Merge pull request prettier#817 from prettier/better-errors
Fix for prettier#814
2 parents 885f408 + c5036cc commit b302545

9 files changed

Lines changed: 74 additions & 59 deletions

File tree

β€ŽCHANGELOG.mdβ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) a
1010

1111
- [#812](https://github.com/prettier/plugin-ruby/issues/812) - valscion, kddeisz - Splats and blocks within args that have comments attached should place their respective operators in the right place.
1212
- [#816](https://github.com/prettier/plugin-ruby/pull/816) - valscion - Document RuboCop's Style/Lambda should be disabled
13+
- [#814](https://github.com/prettier/plugin-ruby/issues/814) - jscheid, kddeisz - Provide better errors when the source location of the error is known.
1314

1415
## [1.5.2] - 2021-02-03
1516

β€Žsrc/haml/parser.rbβ€Ž

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,6 @@ module Prettier
131131
class HAMLParser
132132
def self.parse(source)
133133
Haml::Parser.new({}).call(source).as_json
134-
rescue StandardError
135-
false
136134
end
137135
end
138136
end

β€Žsrc/parser/parseSync.jsβ€Ž

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -5,26 +5,21 @@ const requestParse = require("./requestParse");
55
// synchronous and Node.js does not offer a mechanism for synchronous socket
66
// requests.
77
function parseSync(parser, source) {
8-
const response = requestParse(parser, source);
8+
const { stdout, stderr, status } = requestParse(parser, source);
99

10-
if (
11-
response.stdout.length === 0 ||
12-
(response.status !== null && response.status !== 0)
13-
) {
14-
console.error("Could not parse response from server");
15-
console.error(response);
16-
17-
throw new Error(response.stderr || "An unknown error occurred");
10+
if (stdout.length === 0 || (status !== null && status !== 0)) {
11+
throw new Error(stderr || "An unknown error occurred");
1812
}
1913

20-
const parsed = JSON.parse(response.stdout);
14+
const parsed = JSON.parse(stdout);
2115

2216
if (parsed.error) {
23-
throw new Error(
24-
`@prettier/plugin-ruby encountered an error when attempting to parse \
25-
the ruby source. This usually means there was a syntax error in the \
26-
file in question. You can verify by running \`ruby -i [path/to/file]\`.`
27-
);
17+
const error = new Error(parsed.error);
18+
if (parsed.loc) {
19+
error.loc = parsed.loc;
20+
}
21+
22+
throw error;
2823
}
2924

3025
return parsed;

β€Žsrc/parser/server.rbβ€Ž

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@
4848
else
4949
socket.write('{ "error": true }')
5050
end
51+
rescue Prettier::Parser::ParserError => error
52+
loc = { start: { line: error.lineno, column: error.column } }
53+
socket.write(JSON.fast_generate(error: error.message, loc: loc))
54+
rescue StandardError => error
55+
socket.write(JSON.fast_generate(error: error.message))
5156
ensure
5257
socket.close
5358
end

β€Žsrc/rbs/parser.rbβ€Ž

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,8 +87,6 @@ module Prettier
8787
class RBSParser
8888
def self.parse(text)
8989
{ declarations: RBS::Parser.parse_signature(text) }
90-
rescue StandardError
91-
false
9290
end
9391
end
9492
end

β€Žsrc/ruby/parser.rbβ€Ž

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1729,6 +1729,28 @@ def on_paren(contents)
17291729
)
17301730
end
17311731

1732+
# A special parser error so that we can get nice syntax displays on the error
1733+
# message when prettier prints out the results.
1734+
class ParserError < StandardError
1735+
attr_reader :lineno, :column
1736+
1737+
def initialize(error, lineno, column)
1738+
super(error)
1739+
@lineno = lineno
1740+
@column = column
1741+
end
1742+
end
1743+
1744+
# If we encounter a parse error, just immediately bail out so that our runner
1745+
# can catch it.
1746+
def on_parse_error(error, *)
1747+
raise ParserError.new(error, lineno, column)
1748+
end
1749+
alias on_alias_error on_parse_error
1750+
alias on_assign_error on_parse_error
1751+
alias on_class_name_error on_parse_error
1752+
alias on_param_error on_parse_error
1753+
17321754
# The program node is the very top of the AST. Here we'll attach all of
17331755
# the comments that we've gathered up over the course of parsing the
17341756
# source string. We'll also attach on the __END__ content if there was

β€Žtest/js/files.test.jsβ€Ž

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,20 @@
1-
describe("files", () => {
2-
test("handles full files that match", () =>
3-
expect("files/Gemfile").toInferParser());
1+
const path = require("path");
2+
const { getFileInfo } = require("prettier");
3+
4+
const plugin = require("../../src/plugin");
5+
6+
function getInferredParser(filename) {
7+
const filepath = path.join(__dirname, filename);
48

5-
test("handles shebangs that match", () =>
6-
expect("files/shebang").toInferParser());
9+
return getFileInfo(filepath, { plugins: [plugin] }).then(
10+
({ inferredParser }) => inferredParser
11+
);
12+
}
13+
14+
describe("files", () => {
15+
const cases = ["files/Gemfile", "files/shebang", "files/test.rake"];
716

8-
test("handles extensions that match", () =>
9-
expect("files/test.rake").toInferParser());
17+
test.each(cases)("infers Ruby parser from %s", (filename) =>
18+
expect(getInferredParser(filename)).resolves.toBe("ruby")
19+
);
1020
});

β€Žtest/js/ruby/errors.test.jsβ€Ž

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,25 @@
1+
const prettier = require("prettier");
12
const { print } = require("../../../src/ruby/printer");
23

34
describe("errors", () => {
4-
test("invalid ruby", () => expect("<>").toFailFormat());
5+
const cases = [
6+
"alias $a $1",
7+
"self = 1",
8+
"$` = 1",
9+
"class foo; end",
10+
"def foo(A) end",
11+
"def foo($a) end",
12+
"def foo(@a) end",
13+
"def foo(@@a) end",
14+
"<>"
15+
];
16+
17+
test.each(cases)("fails for %s", (content) => {
18+
const format = () =>
19+
prettier.format(content, { parser: "ruby", plugins: ["."] });
20+
21+
expect(format).toThrow();
22+
});
523

624
test("when encountering an unsupported node type", () => {
725
const path = { getValue: () => ({ type: "unsupported", body: {} }) };

β€Žtest/js/setupTests.jsβ€Ž

Lines changed: 0 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
const net = require("net");
2-
const path = require("path");
32
const prettier = require("prettier");
43

54
// eslint-disable-next-line no-underscore-dangle
@@ -76,36 +75,5 @@ expect.extend({
7675
},
7776
toMatchFormat(before, config = {}) {
7877
return checkFormat(before, before.code || before, config);
79-
},
80-
toFailFormat(before, message) {
81-
let pass = false;
82-
let error = null;
83-
84-
try {
85-
prettier.format(before, { parser: "ruby", plugins: ["."] });
86-
} catch (caught) {
87-
error = caught;
88-
pass = caught.message === message;
89-
}
90-
91-
return {
92-
pass,
93-
message: () => `
94-
Expected format to throw an error for ${before} with ${message},
95-
but got ${error.message} instead
96-
`
97-
};
98-
},
99-
toInferParser(filename) {
100-
const filepath = path.join(__dirname, filename);
101-
const plugin = path.join(__dirname, "..", "..", "src", "plugin");
102-
103-
return prettier
104-
.getFileInfo(filepath, { plugins: [plugin] })
105-
.then((props) => ({
106-
pass: props.inferredParser === "ruby",
107-
message: () =>
108-
`Expected prettier to infer the ruby parser for ${filename}, but got ${props.inferredParser} instead`
109-
}));
11078
}
11179
});

0 commit comments

Comments
 (0)