Skip to content

Commit 5bc1d41

Browse files
authored
Don't stop the entire linting process on individual file errors (#428)
See: #425 Signed-off-by: Juan Cruz Viotti <[email protected]>
1 parent d8f747d commit 5bc1d41

8 files changed

+321
-144
lines changed

src/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ add_subdirectory(http)
22

33
add_executable(jsonschema_cli
44
main.cc configure.h.in command.h
5-
utils.h utils.cc
5+
utils.h utils.cc error.h
66
command_fmt.cc
77
command_inspect.cc
88
command_bundle.cc

src/command_lint.cc

Lines changed: 44 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <sstream> // std::ostringstream
1212

1313
#include "command.h"
14+
#include "error.h"
1415
#include "utils.h"
1516

1617
template <typename Options, typename Iterator>
@@ -154,14 +155,25 @@ auto sourcemeta::jsonschema::cli::lint(
154155

155156
auto copy = entry.second;
156157

157-
try {
158-
bundle.apply(
159-
copy, sourcemeta::core::schema_official_walker, custom_resolver,
160-
get_lint_callback(errors_array, entry.first, output_json), dialect,
161-
sourcemeta::core::URI::from_path(entry.first).recompose());
162-
} catch (const sourcemeta::core::SchemaUnknownBaseDialectError &) {
163-
throw FileError<sourcemeta::core::SchemaUnknownBaseDialectError>(
164-
entry.first);
158+
const auto wrapper_result = sourcemeta::jsonschema::try_catch([&]() {
159+
try {
160+
bundle.apply(
161+
copy, sourcemeta::core::schema_official_walker, custom_resolver,
162+
get_lint_callback(errors_array, entry.first, output_json),
163+
dialect,
164+
sourcemeta::core::URI::from_path(entry.first).recompose());
165+
return EXIT_SUCCESS;
166+
} catch (const sourcemeta::core::SchemaUnknownBaseDialectError &) {
167+
throw FileError<sourcemeta::core::SchemaUnknownBaseDialectError>(
168+
entry.first);
169+
} catch (const sourcemeta::core::SchemaResolutionError &error) {
170+
throw FileError<sourcemeta::core::SchemaResolutionError>(entry.first,
171+
error);
172+
}
173+
});
174+
175+
if (wrapper_result != EXIT_SUCCESS) {
176+
result = false;
165177
}
166178

167179
if (copy != entry.second) {
@@ -175,18 +187,31 @@ auto sourcemeta::jsonschema::cli::lint(
175187
for_each_json(options.at(""), parse_ignore(options),
176188
parse_extensions(options))) {
177189
log_verbose(options) << "Linting: " << entry.first.string() << "\n";
178-
try {
179-
const auto subresult = bundle.check(
180-
entry.second, sourcemeta::core::schema_official_walker,
181-
custom_resolver,
182-
get_lint_callback(errors_array, entry.first, output_json), dialect,
183-
sourcemeta::core::URI::from_path(entry.first).recompose());
184-
if (!subresult.first) {
185-
result = false;
190+
191+
const auto wrapper_result = sourcemeta::jsonschema::try_catch([&]() {
192+
try {
193+
const auto subresult = bundle.check(
194+
entry.second, sourcemeta::core::schema_official_walker,
195+
custom_resolver,
196+
get_lint_callback(errors_array, entry.first, output_json),
197+
dialect,
198+
sourcemeta::core::URI::from_path(entry.first).recompose());
199+
if (subresult.first) {
200+
return EXIT_SUCCESS;
201+
} else {
202+
return EXIT_FAILURE;
203+
}
204+
} catch (const sourcemeta::core::SchemaUnknownBaseDialectError &) {
205+
throw FileError<sourcemeta::core::SchemaUnknownBaseDialectError>(
206+
entry.first);
207+
} catch (const sourcemeta::core::SchemaResolutionError &error) {
208+
throw FileError<sourcemeta::core::SchemaResolutionError>(entry.first,
209+
error);
186210
}
187-
} catch (const sourcemeta::core::SchemaUnknownBaseDialectError &) {
188-
throw FileError<sourcemeta::core::SchemaUnknownBaseDialectError>(
189-
entry.first);
211+
});
212+
213+
if (wrapper_result != EXIT_SUCCESS) {
214+
result = false;
190215
}
191216
}
192217
}

src/error.h

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#ifndef SOURCEMETA_JSONSCHEMA_CLI_ERROR_H_
2+
#define SOURCEMETA_JSONSCHEMA_CLI_ERROR_H_
3+
4+
#include <functional> // std::function
5+
6+
#include "utils.h"
7+
8+
namespace sourcemeta::jsonschema {
9+
10+
inline auto try_catch(const std::function<int()> &callback) noexcept -> int {
11+
try {
12+
return callback();
13+
} catch (const sourcemeta::core::SchemaReferenceError &error) {
14+
std::cerr << "error: " << error.what() << "\n " << error.id()
15+
<< "\n at schema location \"";
16+
sourcemeta::core::stringify(error.location(), std::cerr);
17+
std::cerr << "\"\n";
18+
return EXIT_FAILURE;
19+
} catch (const sourcemeta::jsonschema::cli::FileError<
20+
sourcemeta::core::SchemaRelativeMetaschemaResolutionError> &error) {
21+
std::cerr << "error: " << error.what() << "\n uri " << error.id() << "\n";
22+
std::cerr << " at "
23+
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
24+
error.path())
25+
.string()
26+
<< "\n";
27+
return EXIT_FAILURE;
28+
} catch (
29+
const sourcemeta::core::SchemaRelativeMetaschemaResolutionError &error) {
30+
std::cerr << "error: " << error.what() << "\n " << error.id() << "\n";
31+
return EXIT_FAILURE;
32+
} catch (const sourcemeta::jsonschema::cli::FileError<
33+
sourcemeta::core::SchemaResolutionError> &error) {
34+
std::cerr << "error: " << error.what() << "\n uri " << error.id() << "\n";
35+
std::cerr << " at "
36+
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
37+
error.path())
38+
.string()
39+
<< "\n";
40+
std::cerr << "\nThis is likely because you forgot to import such schema "
41+
"using --resolve/-r\n";
42+
return EXIT_FAILURE;
43+
} catch (const sourcemeta::core::SchemaResolutionError &error) {
44+
std::cerr << "error: " << error.what() << "\n " << error.id() << "\n";
45+
std::cerr << "\nThis is likely because you forgot to import such schema "
46+
"using --resolve/-r\n";
47+
return EXIT_FAILURE;
48+
} catch (const sourcemeta::core::SchemaUnknownDialectError &error) {
49+
std::cerr << "error: " << error.what() << "\n";
50+
std::cerr
51+
<< "\nThis is likely because you forgot to import such meta-schema "
52+
"using --resolve/-r\n";
53+
return EXIT_FAILURE;
54+
} catch (const sourcemeta::jsonschema::cli::FileError<
55+
sourcemeta::core::SchemaUnknownBaseDialectError> &error) {
56+
std::cerr << "error: " << error.what() << "\n";
57+
std::cerr << " at "
58+
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
59+
error.path())
60+
.string()
61+
<< "\n";
62+
std::cerr << "\nAre you sure the input is a valid JSON Schema and its "
63+
"base dialect is known?\n";
64+
std::cerr << "If the input does not declare the $schema keyword, you might "
65+
"want to\n";
66+
std::cerr
67+
<< "explicitly declare a default dialect using --default-dialect/-d\n";
68+
return EXIT_FAILURE;
69+
} catch (const sourcemeta::core::SchemaUnknownBaseDialectError &error) {
70+
std::cerr << "error: " << error.what() << "\n";
71+
std::cerr << "\nAre you sure the input is a valid JSON Schema and its "
72+
"base dialect is known?\n";
73+
std::cerr << "If the input does not declare the $schema keyword, you might "
74+
"want to\n";
75+
std::cerr
76+
<< "explicitly declare a default dialect using --default-dialect/-d\n";
77+
return EXIT_FAILURE;
78+
} catch (const sourcemeta::core::SchemaError &error) {
79+
std::cerr << "error: " << error.what() << "\n";
80+
return EXIT_FAILURE;
81+
} catch (const sourcemeta::core::SchemaVocabularyError &error) {
82+
std::cerr << "error: " << error.what() << "\n " << error.uri()
83+
<< "\n\nTo request support for it, please open an issue "
84+
"at\nhttps://github.com/sourcemeta/jsonschema\n";
85+
return EXIT_FAILURE;
86+
} catch (const sourcemeta::core::URIParseError &error) {
87+
std::cerr << "error: " << error.what() << " at column " << error.column()
88+
<< "\n";
89+
return EXIT_FAILURE;
90+
} catch (const sourcemeta::core::JSONFileParseError &error) {
91+
std::cerr << "error: " << error.what() << " at line " << error.line()
92+
<< " and column " << error.column() << "\n "
93+
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
94+
error.path())
95+
.string()
96+
<< "\n";
97+
return EXIT_FAILURE;
98+
} catch (const sourcemeta::core::JSONParseError &error) {
99+
std::cerr << "error: " << error.what() << " at line " << error.line()
100+
<< " and column " << error.column() << "\n";
101+
return EXIT_FAILURE;
102+
} catch (const std::filesystem::filesystem_error &error) {
103+
// See https://en.cppreference.com/w/cpp/error/errc
104+
if (error.code() == std::errc::no_such_file_or_directory) {
105+
std::cerr << "error: " << error.code().message() << "\n "
106+
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
107+
error.path1())
108+
.string()
109+
<< "\n";
110+
} else if (error.code() == std::errc::is_a_directory) {
111+
std::cerr << "error: The input was supposed to be a file but it is a "
112+
"directory\n "
113+
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
114+
error.path1())
115+
.string()
116+
<< "\n";
117+
} else {
118+
std::cerr << "error: " << error.what() << "\n";
119+
}
120+
121+
return EXIT_FAILURE;
122+
} catch (const std::runtime_error &error) {
123+
std::cerr << "error: " << error.what() << "\n";
124+
return EXIT_FAILURE;
125+
} catch (const std::exception &error) {
126+
std::cerr << "unexpected error: " << error.what()
127+
<< "\nPlease report it at "
128+
<< "https://github.com/sourcemeta/jsonschema\n";
129+
return EXIT_FAILURE;
130+
}
131+
}
132+
133+
} // namespace sourcemeta::jsonschema
134+
135+
#endif

src/main.cc

Lines changed: 3 additions & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111

1212
#include "command.h"
1313
#include "configure.h"
14+
#include "error.h"
1415
#include "utils.h"
1516

1617
constexpr std::string_view USAGE_DETAILS{R"EOF(
@@ -144,128 +145,11 @@ auto jsonschema_main(const std::string &program, const std::string &command,
144145
}
145146

146147
auto main(int argc, char *argv[]) noexcept -> int {
147-
try {
148+
return sourcemeta::jsonschema::try_catch([argc, &argv]() {
148149
const std::string program{argv[0]};
149150
const std::string command{argc > 1 ? argv[1] : "help"};
150151
const std::vector<std::string> arguments{argv + std::min(2, argc),
151152
argv + argc};
152153
return jsonschema_main(program, command, arguments);
153-
} catch (const sourcemeta::core::SchemaReferenceError &error) {
154-
std::cerr << "error: " << error.what() << "\n " << error.id()
155-
<< "\n at schema location \"";
156-
sourcemeta::core::stringify(error.location(), std::cerr);
157-
std::cerr << "\"\n";
158-
return EXIT_FAILURE;
159-
} catch (const sourcemeta::jsonschema::cli::FileError<
160-
sourcemeta::core::SchemaRelativeMetaschemaResolutionError> &error) {
161-
std::cerr << "error: " << error.what() << "\n uri " << error.id() << "\n";
162-
std::cerr << " at "
163-
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
164-
error.path())
165-
.string()
166-
<< "\n";
167-
return EXIT_FAILURE;
168-
} catch (
169-
const sourcemeta::core::SchemaRelativeMetaschemaResolutionError &error) {
170-
std::cerr << "error: " << error.what() << "\n " << error.id() << "\n";
171-
return EXIT_FAILURE;
172-
} catch (const sourcemeta::jsonschema::cli::FileError<
173-
sourcemeta::core::SchemaResolutionError> &error) {
174-
std::cerr << "error: " << error.what() << "\n uri " << error.id() << "\n";
175-
std::cerr << " at "
176-
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
177-
error.path())
178-
.string()
179-
<< "\n";
180-
std::cerr << "\nThis is likely because you forgot to import such schema "
181-
"using --resolve/-r\n";
182-
return EXIT_FAILURE;
183-
} catch (const sourcemeta::core::SchemaResolutionError &error) {
184-
std::cerr << "error: " << error.what() << "\n " << error.id() << "\n";
185-
std::cerr << "\nThis is likely because you forgot to import such schema "
186-
"using --resolve/-r\n";
187-
return EXIT_FAILURE;
188-
} catch (const sourcemeta::core::SchemaUnknownDialectError &error) {
189-
std::cerr << "error: " << error.what() << "\n";
190-
std::cerr
191-
<< "\nThis is likely because you forgot to import such meta-schema "
192-
"using --resolve/-r\n";
193-
return EXIT_FAILURE;
194-
} catch (const sourcemeta::jsonschema::cli::FileError<
195-
sourcemeta::core::SchemaUnknownBaseDialectError> &error) {
196-
std::cerr << "error: " << error.what() << "\n";
197-
std::cerr << " at "
198-
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
199-
error.path())
200-
.string()
201-
<< "\n";
202-
std::cerr << "\nAre you sure the input is a valid JSON Schema and its "
203-
"base dialect is known?\n";
204-
std::cerr << "If the input does not declare the $schema keyword, you might "
205-
"want to\n";
206-
std::cerr
207-
<< "explicitly declare a default dialect using --default-dialect/-d\n";
208-
return EXIT_FAILURE;
209-
} catch (const sourcemeta::core::SchemaUnknownBaseDialectError &error) {
210-
std::cerr << "error: " << error.what() << "\n";
211-
std::cerr << "\nAre you sure the input is a valid JSON Schema and its "
212-
"base dialect is known?\n";
213-
std::cerr << "If the input does not declare the $schema keyword, you might "
214-
"want to\n";
215-
std::cerr
216-
<< "explicitly declare a default dialect using --default-dialect/-d\n";
217-
return EXIT_FAILURE;
218-
} catch (const sourcemeta::core::SchemaError &error) {
219-
std::cerr << "error: " << error.what() << "\n";
220-
return EXIT_FAILURE;
221-
} catch (const sourcemeta::core::SchemaVocabularyError &error) {
222-
std::cerr << "error: " << error.what() << "\n " << error.uri()
223-
<< "\n\nTo request support for it, please open an issue "
224-
"at\nhttps://github.com/sourcemeta/jsonschema\n";
225-
return EXIT_FAILURE;
226-
} catch (const sourcemeta::core::URIParseError &error) {
227-
std::cerr << "error: " << error.what() << " at column " << error.column()
228-
<< "\n";
229-
return EXIT_FAILURE;
230-
} catch (const sourcemeta::core::JSONFileParseError &error) {
231-
std::cerr << "error: " << error.what() << " at line " << error.line()
232-
<< " and column " << error.column() << "\n "
233-
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
234-
error.path())
235-
.string()
236-
<< "\n";
237-
return EXIT_FAILURE;
238-
} catch (const sourcemeta::core::JSONParseError &error) {
239-
std::cerr << "error: " << error.what() << " at line " << error.line()
240-
<< " and column " << error.column() << "\n";
241-
return EXIT_FAILURE;
242-
} catch (const std::filesystem::filesystem_error &error) {
243-
// See https://en.cppreference.com/w/cpp/error/errc
244-
if (error.code() == std::errc::no_such_file_or_directory) {
245-
std::cerr << "error: " << error.code().message() << "\n "
246-
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
247-
error.path1())
248-
.string()
249-
<< "\n";
250-
} else if (error.code() == std::errc::is_a_directory) {
251-
std::cerr << "error: The input was supposed to be a file but it is a "
252-
"directory\n "
253-
<< sourcemeta::jsonschema::cli::safe_weakly_canonical(
254-
error.path1())
255-
.string()
256-
<< "\n";
257-
} else {
258-
std::cerr << "error: " << error.what() << "\n";
259-
}
260-
261-
return EXIT_FAILURE;
262-
} catch (const std::runtime_error &error) {
263-
std::cerr << "error: " << error.what() << "\n";
264-
return EXIT_FAILURE;
265-
} catch (const std::exception &error) {
266-
std::cerr << "unexpected error: " << error.what()
267-
<< "\nPlease report it at "
268-
<< "https://github.com/sourcemeta/jsonschema\n";
269-
return EXIT_FAILURE;
270-
}
154+
});
271155
}

test/CMakeLists.txt

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -226,19 +226,22 @@ add_jsonschema_test_unix(lint/fail_lint_fix_yml)
226226
add_jsonschema_test_unix(lint/fail_lint_json)
227227
add_jsonschema_test_unix(lint/fail_lint_non_schema)
228228
add_jsonschema_test_unix(lint/fail_lint_non_schema_fix)
229-
add_jsonschema_test_unix(lint/pass_lint_json)
230-
add_jsonschema_test_unix(lint/pass_lint_fix_json)
231-
add_jsonschema_test_unix(lint/pass_lint_ignore_short)
232-
add_jsonschema_test_unix(lint/pass_lint_ignore_long)
233229
add_jsonschema_test_unix(lint/fail_bigint)
230+
add_jsonschema_test_unix(lint/fail_lint_directory_unresolvable)
231+
add_jsonschema_test_unix(lint/fail_lint_directory_unresolvable_fix)
232+
add_jsonschema_test_unix(lint/fail_lint_directory_unresolvable_dialect)
234233
add_jsonschema_test_unix(lint/fail_lint_disable_one)
235234
add_jsonschema_test_unix(lint/fail_lint_disable_one_verbose)
236235
add_jsonschema_test_unix(lint/fail_lint_disable_unknown_verbose)
237236
add_jsonschema_test_unix(lint/fail_lint_disable_many)
238237
add_jsonschema_test_unix(lint/fail_lint_examples)
238+
add_jsonschema_test_unix(lint/fail_lint_default)
239+
add_jsonschema_test_unix(lint/pass_lint_json)
240+
add_jsonschema_test_unix(lint/pass_lint_fix_json)
241+
add_jsonschema_test_unix(lint/pass_lint_ignore_short)
242+
add_jsonschema_test_unix(lint/pass_lint_ignore_long)
239243
add_jsonschema_test_unix(lint/pass_lint_examples_fix)
240244
add_jsonschema_test_unix(lint/pass_lint_examples_no_fix)
241-
add_jsonschema_test_unix(lint/fail_lint_default)
242245
add_jsonschema_test_unix(lint/pass_lint_default_fix)
243246
add_jsonschema_test_unix(lint/pass_lint_default_no_fix)
244247
add_jsonschema_test_unix(lint/pass_lint_anonymous_default_with_ref)

0 commit comments

Comments
 (0)