Skip to content

Commit c67a421

Browse files
committed
refactor: split error message from source location in reports
fix #672
1 parent 5982cc7 commit c67a421

File tree

6 files changed

+109
-67
lines changed

6 files changed

+109
-67
lines changed

include/mrdocs/Support/Error.hpp

Lines changed: 65 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2775,7 +2775,62 @@ void
27752775
print(
27762776
Level level,
27772777
std::string const& text,
2778-
source_location const* loc = nullptr);
2778+
source_location const* loc = nullptr,
2779+
Error const* e = nullptr);
2780+
2781+
namespace detail {
2782+
template<class Arg0, class... Args>
2783+
requires (!std::same_as<std::decay_t<Arg0>, Error>)
2784+
void
2785+
log_impl(
2786+
Level level,
2787+
Located<std::string_view> fs,
2788+
Arg0&& arg0,
2789+
Args&&... args)
2790+
{
2791+
std::string str = fmt::vformat(
2792+
fs.value,
2793+
fmt::make_format_args(arg0, args...));
2794+
return print(
2795+
level,
2796+
str,
2797+
&fs.where);
2798+
}
2799+
2800+
template<class... Args>
2801+
void
2802+
log_impl(
2803+
Level level,
2804+
Located<std::string_view> fs,
2805+
Error const& e,
2806+
Args&&... args)
2807+
{
2808+
// When the message is an error, we send split
2809+
// the information relevant to the user from
2810+
// the information relevant for bug tracking
2811+
// so that users can understand the message.
2812+
std::string str = fmt::vformat(
2813+
fs.value,
2814+
fmt::make_format_args(e.reason(), args...));
2815+
return print(
2816+
level,
2817+
str,
2818+
&fs.where,
2819+
&e);
2820+
}
2821+
2822+
inline
2823+
void
2824+
log_impl(
2825+
Level level,
2826+
Located<std::string_view> fs)
2827+
{
2828+
return print(
2829+
level,
2830+
{},
2831+
&fs.where);
2832+
}
2833+
}
27792834

27802835
/** Format a message to the console.
27812836
@@ -2791,17 +2846,15 @@ print(
27912846
*/
27922847
template<class... Args>
27932848
void
2794-
format(
2849+
log(
27952850
Level level,
27962851
Located<std::string_view> fs,
27972852
Args&&... args)
27982853
{
2799-
return print(
2854+
return detail::log_impl(
28002855
level,
2801-
fmt::vformat(
2802-
fs.value,
2803-
fmt::make_format_args(args...)),
2804-
&fs.where);
2856+
fs,
2857+
std::forward<Args>(args)...);
28052858
}
28062859

28072860
/** Report a message to the console.
@@ -2812,12 +2865,7 @@ debug(
28122865
Located<std::string_view> format,
28132866
Args&&... args)
28142867
{
2815-
return print(
2816-
Level::debug,
2817-
fmt::vformat(
2818-
format.value,
2819-
fmt::make_format_args(args...)),
2820-
&format.where);
2868+
return log(Level::debug, format, std::forward<Args>(args)...);
28212869
}
28222870

28232871
/** Report a message to the console.
@@ -2828,12 +2876,7 @@ info(
28282876
Located<std::string_view> format,
28292877
Args&&... args)
28302878
{
2831-
return print(
2832-
Level::info,
2833-
fmt::vformat(
2834-
format.value,
2835-
fmt::make_format_args(args...)),
2836-
&format.where);
2879+
return log(Level::info, format, std::forward<Args>(args)...);
28372880
}
28382881

28392882
/** Report a message to the console.
@@ -2844,12 +2887,7 @@ warn(
28442887
Located<std::string_view> format,
28452888
Args&&... args)
28462889
{
2847-
return print(
2848-
Level::warn,
2849-
fmt::vformat(
2850-
format.value,
2851-
fmt::make_format_args(args...)),
2852-
&format.where);
2890+
return log(Level::warn, format, std::forward<Args>(args)...);
28532891
}
28542892

28552893
/** Report a message to the console.
@@ -2860,12 +2898,7 @@ error(
28602898
Located<std::string_view> format,
28612899
Args&&... args)
28622900
{
2863-
return print(
2864-
Level::error,
2865-
fmt::vformat(
2866-
format.value,
2867-
fmt::make_format_args(args...)),
2868-
&format.where);
2901+
return log(Level::error, format, std::forward<Args>(args)...);
28692902
}
28702903

28712904
/** Report a message to the console.
@@ -2876,12 +2909,7 @@ fatal(
28762909
Located<std::string_view> format,
28772910
Args&&... args)
28782911
{
2879-
return print(
2880-
Level::fatal,
2881-
fmt::vformat(
2882-
format.value,
2883-
fmt::make_format_args(args...)),
2884-
&format.where);
2912+
return log(Level::fatal, format, std::forward<Args>(args)...);
28852913
}
28862914

28872915
} // report

src/lib/Lib/CorpusImpl.cpp

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -187,9 +187,8 @@ build(
187187
taskGroup.async(
188188
[&, idx = ++index, path = std::move(file)]()
189189
{
190-
report::format(reportLevel,
190+
report::log(reportLevel,
191191
"[{}/{}] \"{}\"", idx, files.size(), path);
192-
193192
processFile(path);
194193
});
195194
}
@@ -217,7 +216,7 @@ build(
217216
return Unexpected(results.error());
218217
corpus->info_ = std::move(results.value());
219218

220-
report::format(reportLevel,
219+
report::log(reportLevel,
221220
"Extracted {} declarations in {}",
222221
corpus->info_.size(),
223222
format_duration(clock_type::now() - start_time));

src/lib/Support/Error.cpp

Lines changed: 22 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
#include "lib/Support/Error.hpp"
1212
#include <mrdocs/Support/Path.hpp>
13+
#include <mrdocs/Version.hpp>
1314
#include <llvm/Support/Mutex.h>
1415
#include <llvm/Support/raw_ostream.h>
1516
#include <llvm/Support/Signals.h>
@@ -201,13 +202,14 @@ void
201202
print(
202203
Level level,
203204
std::string const& text,
204-
source_location const* loc)
205+
source_location const* loc,
206+
Error const* e)
205207
{
206208
call_impl(level,
207209
[&](llvm::raw_ostream& os)
208210
{
209211
os << text;
210-
}, loc);
212+
}, loc, e);
211213
}
212214

213215
//------------------------------------------------
@@ -230,7 +232,8 @@ void
230232
call_impl(
231233
Level level,
232234
std::function<void(llvm::raw_ostream&)> f,
233-
source_location const* loc)
235+
source_location const* loc,
236+
Error const* e)
234237
{
235238
std::string s;
236239
if(level >= level_)
@@ -242,11 +245,22 @@ call_impl(
242245
level == Level::error ||
243246
level == Level::fatal))
244247
{
245-
os << "\n" <<
246-
fmt::format(
247-
" Reported at {}({})",
248-
::SourceFileNames::getFileName(loc->file_name()),
249-
loc->line());
248+
os << "\n\n";
249+
os << "An issue occurred during execution.\n";
250+
os << "If you believe this is a bug, please report it at https://github.com/cppalliance/mrdocs/issues\n"
251+
"with the following details:\n";
252+
os << fmt::format(" MrDocs Version: {} (Build: {})\n", project_version, project_version_build);
253+
if (e)
254+
{
255+
os << fmt::format(
256+
" Error Location: `{}` at line {}\n",
257+
::SourceFileNames::getFileName(e->location().file_name()),
258+
e->location().line());
259+
}
260+
os << fmt::format(
261+
" Reported From: `{}` at line {}",
262+
::SourceFileNames::getFileName(loc->file_name()),
263+
loc->line());
250264
// VFALCO attach a stack trace for Level::fatal
251265
}
252266
os << '\n';

src/lib/Support/Error.hpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,8 @@ void
119119
call_impl(
120120
Level level,
121121
std::function<void(llvm::raw_ostream&)> f,
122-
source_location const* loc);
122+
source_location const* loc,
123+
Error const* e = nullptr);
123124

124125
/** Formatted reporting to a live stream.
125126

src/tool/ToolMain.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ mrdocs_main(int argc, char const** argv)
135135
auto exp = DoGenerateAction(configPath, dirs, argv);
136136
if (!exp)
137137
{
138-
report::error("Generating reference failed: {}", exp.error().message());
138+
report::error("Generating reference failed: {}", exp.error());
139139
}
140140
if (report::results.errorCount > 0 ||
141141
report::results.fatalCount > 0)

util/generate-config-info.py

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -105,14 +105,14 @@ def validate_and_normalize_option(option):
105105
if 'command-line-only' not in option:
106106
option['command-line-only'] = False
107107
if 'brief' not in option:
108-
raise ValueError(f'Option {option["name"]} must have a "brief" description')
108+
raise ValueError(f'Option "{option["name"]}" must have a "brief" description')
109109
if 'details' not in option:
110110
option['details'] = ''
111111
if 'type' not in option:
112112
option['type'] = 'string'
113113
if not is_valid_option_type(option['type']):
114114
raise ValueError(
115-
f'Option {option["name"]} has an invalid type {option["type"]}: It should be one of {get_valid_option_values()}')
115+
f'Option "{option["name"]}" has an invalid type {option["type"]}: It should be one of {get_valid_option_values()}')
116116
if option['type'] == 'enum':
117117
if 'values' not in option:
118118
raise ValueError(f'Option "{option["name"]}" is of type enum and must have "values"')
@@ -541,7 +541,7 @@ def option_validation_snippet(option):
541541
validation_contents += f'// s.{camel_name} is required and has no default value\n'
542542
validation_contents += f'if (s.{camel_name}.empty())\n'
543543
validation_contents += f'{{\n'
544-
validation_contents += f' return Unexpected(formatError("{option["name"]} option is required"));'
544+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option is required"));'
545545
validation_contents += f'}}\n'
546546
validation_contents += f'else\n'
547547
validation_contents += f'{{\n'
@@ -592,30 +592,30 @@ def option_validation_snippet(option):
592592
if option['must-exist']:
593593
validation_contents += f'if (!s.{camel_name}.empty() && !files::exists(s.{camel_name}))\n'
594594
validation_contents += f'{{\n'
595-
validation_contents += f' return Unexpected(formatError("{option["name"]} option: path does not exist: {{}}", s.{camel_name}));\n'
595+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option: path does not exist: {{}}", s.{camel_name}));\n'
596596
validation_contents += f'}}\n'
597597
if option['type'] == 'file-path':
598598
validation_contents += f'if (files::isDirectory(s.{camel_name}))\n'
599599
validation_contents += f'{{\n'
600-
validation_contents += f' return Unexpected(formatError("{option["name"]} option: path should be a regular file: {{}}", s.{camel_name}));\n'
600+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option: path should be a regular file: {{}}", s.{camel_name}));\n'
601601
validation_contents += f'}}\n'
602602
if option['type'] == 'dir-path':
603603
validation_contents += f'if (!files::isDirectory(s.{camel_name}))\n'
604604
validation_contents += f'{{\n'
605-
validation_contents += f' return Unexpected(formatError("{option["name"]} option: path should be a directory: {{}}", s.{camel_name}));\n'
605+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option: path should be a directory: {{}}", s.{camel_name}));\n'
606606
validation_contents += f'}}\n'
607607
elif option['type'] in ['file-path', 'dir-path']:
608608
validation_contents += f'if (files::exists(s.{camel_name}))\n'
609609
validation_contents += f'{{\n'
610610
if option['type'] == 'file-path':
611611
validation_contents += f'if (files::isDirectory(s.{camel_name}))\n'
612612
validation_contents += f'{{\n'
613-
validation_contents += f' return Unexpected(formatError("{option["name"]} option: path should be a regular file: {{}}", s.{camel_name}));\n'
613+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option: path should be a regular file: {{}}", s.{camel_name}));\n'
614614
validation_contents += f'}}\n'
615615
if option['type'] == 'dir-path':
616616
validation_contents += f'if (!files::isDirectory(s.{camel_name}))\n'
617617
validation_contents += f'{{\n'
618-
validation_contents += f' return Unexpected(formatError("{option["name"]} option: path should be a directory: {{}}", s.{camel_name}));\n'
618+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option: path should be a directory: {{}}", s.{camel_name}));\n'
619619
validation_contents += f'}}\n'
620620
validation_contents += f'}}\n'
621621

@@ -625,7 +625,7 @@ def option_validation_snippet(option):
625625
validation_contents += f'// s.{camel_name} paths are required and have no default value\n'
626626
validation_contents += f'if (s.{camel_name}.empty())\n'
627627
validation_contents += f'{{\n'
628-
validation_contents += f' return Unexpected(formatError("{option["name"]} option is required"));'
628+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option is required"));'
629629
validation_contents += f'}}\n'
630630
validation_contents += f'else\n'
631631
validation_contents += f'{{\n'
@@ -694,7 +694,7 @@ def option_validation_snippet(option):
694694
if option['must-exist']:
695695
validation_contents += f' if (!files::exists(p))\n'
696696
validation_contents += f' {{\n'
697-
validation_contents += f' return Unexpected(formatError("{option["name"]} option: path does not exist: {{}}", p));\n'
697+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option: path does not exist: {{}}", p));\n'
698698
validation_contents += f' }}\n'
699699
if option['command-line-sink'] and 'filename-mapping' in option:
700700
validation_contents += f' auto f = files::getFileName(p);\n'
@@ -724,7 +724,7 @@ def option_validation_snippet(option):
724724
validation_contents += f'// s.{camel_name} is required with no default value.'
725725
validation_contents += f'if (s.{camel_name}.empty())\n'
726726
validation_contents += f'{{\n'
727-
validation_contents += f' return Unexpected(formatError("{option["name"]} option is required"));'
727+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option is required"));'
728728
validation_contents += f'}}\n'
729729
else:
730730
validation_contents += f'// s.{camel_name} is not required and has no default value\n'
@@ -745,7 +745,7 @@ def option_validation_snippet(option):
745745
validation_contents += f'// s.{camel_name} is required with no default value.'
746746
validation_contents += f'if (s.{camel_name}.empty())\n'
747747
validation_contents += f'{{\n'
748-
validation_contents += f' return Unexpected(formatError("{option["name"]} option is required"));'
748+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option is required"));'
749749
validation_contents += f'}}\n'
750750
else:
751751
validation_contents += f'// s.{camel_name} is not required and has no default value\n'
@@ -766,12 +766,12 @@ def option_validation_snippet(option):
766766
if 'min-value' in option:
767767
validation_contents += f'if (std::cmp_less(s.{camel_name}, {option["min-value"]}))\n'
768768
validation_contents += f'{{\n'
769-
validation_contents += f' return Unexpected(formatError("{option["name"]} option: value is less than {option["min-value"]}: {{}}", s.{camel_name}));\n'
769+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option: value is less than {option["min-value"]}: {{}}", s.{camel_name}));\n'
770770
validation_contents += f'}}\n'
771771
if 'max-value' in option:
772772
validation_contents += f'if (std::cmp_greater(s.{camel_name}, {option["max-value"]}))\n'
773773
validation_contents += f'{{\n'
774-
validation_contents += f' return Unexpected(formatError("{option["name"]} option: value is greater than {option["max-value"]}: {{}}", s.{camel_name}));\n'
774+
validation_contents += f' return Unexpected(formatError("`{option["name"]}` option: value is greater than {option["max-value"]}: {{}}", s.{camel_name}));\n'
775775
validation_contents += f'}}\n'
776776

777777
contents += validation_contents
@@ -830,7 +830,7 @@ def generate_public_settings_cpp(config):
830830
for option in category['options']:
831831
if not option["command-line-only"]:
832832
contents += f' // {option["brief"]}\n'
833-
contents += f' io.mapOptional("{option["name"]}", s.{to_camel_case(option["name"])});\n'
833+
contents += f' io.mapOptional("`{option["name"]}`", s.{to_camel_case(option["name"])});\n'
834834
contents += ' }\n'
835835
contents += '};\n\n'
836836

@@ -1060,7 +1060,7 @@ def generate_public_toolargs_cpp(config):
10601060
option_contents += f' }}\n'
10611061
option_contents += f' else\n'
10621062
option_contents += f' {{\n'
1063-
option_contents += f' return Unexpected(formatError("{option["name"]} option: invalid value: {{}}", this->{camel_name}));\n'
1063+
option_contents += f' return Unexpected(formatError("`{option["name"]}` option: invalid value: {{}}", this->{camel_name}));\n'
10641064
option_contents += f' }}\n'
10651065
else:
10661066
option_contents += f' s.{camel_name} = this->{camel_name};\n'
@@ -1149,7 +1149,7 @@ def to_cpp_default_value(option):
11491149
if option_default.startswith('<'):
11501150
closing_bracket = option_default.find('>')
11511151
if closing_bracket == -1:
1152-
raise ValueError(f'Invalid default value {option_default} for option {option["name"]}')
1152+
raise ValueError(f'Invalid default value {option_default} for option `{option["name"]}`')
11531153
reference_path = option_default[1:closing_bracket]
11541154
if reference_path == 'config-dir':
11551155
option_default = '.' + option_default[closing_bracket + 1:]

0 commit comments

Comments
 (0)