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 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438
|
%-----------------------------------------------------------------------------%
% Copyright (C) 1997-1999 The University of Melbourne.
% This file may only be copied under the terms of the GNU General
% Public License - see the file COPYING in the Mercury distribution.
%-----------------------------------------------------------------------------%
%
% error_util.m
% Main author: zs.
%
% This module contains code that can be helpful in the formatting of
% error messages.
%
%-----------------------------------------------------------------------------%
:- module error_util.
:- interface.
:- import_module hlds_module, hlds_pred, prog_data.
:- import_module assoc_list, io, list, std_util.
% Given a context, a starting indentation level and a list of words,
% print an error message that looks like this:
%
% module.m:10: first line of error message blah blah blah
% module.m:10: second line of error message blah blah blah
% module.m:10: third line of error message blah blah blah
%
% The words will be packed into lines as tightly as possible,
% with spaces between each pair of words, subject to the constraints
% that every line starts with a context, followed by Indent+1 spaces
% on the first line and Indent+3 spaces on later lines, and that every
% line contains at most 79 characters (unless a long single word
% forces the line over this limit).
%
% The caller supplies the list of words to be printed in the form
% of a list of error message components. Each component may specify
% a string to printed exactly as it is, or it may specify a string
% containing a list of words, which may be broken at white space.
:- type format_component
---> fixed(string) % This string should appear in the output
% in one piece, as it is.
; words(string) % This string contains words separated by
% white space. The words should appear in
% the output in the given order, but the
% white space may be rearranged and line
% breaks may be inserted.
; nl % Insert a line break if there has been text
% output since the last line break.
.
% Convert a list of strings into a list of format_components,
% suitable for displaying as an error message.
:- pred error_util__list_to_pieces(list(string)::in,
list(format_component)::out) is det.
% Display the given error message.
:- pred write_error_pieces(prog_context::in, int::in,
list(format_component)::in, io__state::di, io__state::uo) is det.
% Report a warning, and set the exit status to error if the
% --halt-at-warn option is set. This predicate does the same thing as
% prog_io_util__report_warning, except that it does a nicer job of
% displaying the warning message.
:- pred report_warning(prog_context::in, int::in, list(format_component)::in,
io__state::di, io__state::uo) is det.
% Predicates to convert a predicate names to strings.
:- pred error_util__describe_one_pred_name(module_info::in, pred_id::in,
string::out) is det.
:- pred error_util__describe_one_proc_name(module_info::in, pred_proc_id::in,
string::out) is det.
:- pred error_util__describe_several_proc_names(module_info::in,
list(pred_proc_id)::in, list(format_component)::out) is det.
:- pred error_util__describe_one_call_site(module_info::in,
pair(pred_proc_id, prog_context)::in, string::out) is det.
:- pred error_util__describe_several_call_sites(module_info::in,
assoc_list(pred_proc_id, prog_context)::in,
list(format_component)::out) is det.
% report_error_num_args(MaybePredOrFunc, Arity, CorrectArities).
%
% Write
% "wrong number of arguments (<Arity>; should be <CorrectArities>)",
% adjusting `Arity' and `CorrectArities' if `MaybePredOrFunc' is
% `yes(function)'.
:- pred report_error_num_args(maybe(pred_or_func), int, list(int),
io__state, io__state).
:- mode report_error_num_args(in, in, in, di, uo) is det.
:- implementation.
:- import_module prog_out, globals, options.
:- import_module bool, io, list, term, char, string, int.
error_util__list_to_pieces([], []).
error_util__list_to_pieces([Elem], [words(Elem)]).
error_util__list_to_pieces([Elem1, Elem2],
[fixed(Elem1), words("and"), fixed(Elem2)]).
error_util__list_to_pieces([Elem1, Elem2, Elem3 | Elems], Pieces) :-
string__append(Elem1, ",", Piece1),
error_util__list_to_pieces([Elem2, Elem3 | Elems], Pieces1),
Pieces = [fixed(Piece1) | Pieces1].
report_warning(Context, Indent, Components) -->
globals__io_lookup_bool_option(halt_at_warn, HaltAtWarn),
( { HaltAtWarn = yes } ->
io__set_exit_status(1)
;
[]
),
write_error_pieces(Context, Indent, Components).
write_error_pieces(Context, Indent, Components) -->
{
% The fixed characters at the start of the line are:
% filename
% :
% line number (min 3 chars)
% :
% space
% indent
term__context_file(Context, FileName),
term__context_line(Context, LineNumber),
string__length(FileName, FileNameLength),
string__int_to_string(LineNumber, LineNumberStr),
string__length(LineNumberStr, LineNumberStrLength0),
( LineNumberStrLength0 < 3 ->
LineNumberStrLength = 3
;
LineNumberStrLength = LineNumberStrLength0
),
Remain is 79 - (FileNameLength + 1 +
LineNumberStrLength + 2 + Indent),
convert_components_to_word_list(Components, [], [], Words),
group_words(yes, Words, Remain, Lines)
},
write_lines(Lines, Context, Indent).
:- pred write_lines(list(list(string))::in, prog_context::in, int::in,
io__state::di, io__state::uo) is det.
write_lines([], _, _) --> [].
write_lines([Line | Lines], Context, Indent) -->
prog_out__write_context(Context),
{ string__pad_left("", ' ', Indent, IndentStr) },
io__write_string(IndentStr),
write_line(Line),
{ Indent2 is Indent + 2 },
write_nonfirst_lines(Lines, Context, Indent2).
:- pred write_nonfirst_lines(list(list(string))::in, prog_context::in, int::in,
io__state::di, io__state::uo) is det.
write_nonfirst_lines([], _, _) --> [].
write_nonfirst_lines([Line | Lines], Context, Indent) -->
prog_out__write_context(Context),
{ string__pad_left("", ' ', Indent, IndentStr) },
io__write_string(IndentStr),
write_line(Line),
write_nonfirst_lines(Lines, Context, Indent).
:- pred write_line(list(string)::in, io__state::di, io__state::uo) is det.
write_line([]) --> [].
write_line([Word | Words]) -->
io__write_string(Word),
write_line_rest(Words),
io__write_char('\n').
:- pred write_line_rest(list(string)::in, io__state::di, io__state::uo) is det.
write_line_rest([]) --> [].
write_line_rest([Word | Words]) -->
io__write_char(' '),
io__write_string(Word),
write_line_rest(Words).
%----------------------------------------------------------------------------%
:- pred convert_components_to_word_list(list(format_component)::in,
list(string)::in, list(list(string))::in,
list(list(string))::out) is det.
convert_components_to_word_list([], Words0, Paras0, Paras) :-
list__reverse(Words0, Words),
list__reverse([Words | Paras0], Paras).
convert_components_to_word_list([Component | Components], Words0,
Paras0, Paras) :-
(
Component = fixed(Word),
Words1 = [Word | Words0],
Paras1 = Paras0
;
Component = words(WordsStr),
break_into_words(WordsStr, Words0, Words1),
Paras1 = Paras0
;
Component = nl,
list__reverse(Words0, Words),
Paras1 = [Words | Paras0],
Words1 = []
),
convert_components_to_word_list(Components, Words1, Paras1, Paras).
:- pred break_into_words(string::in, list(string)::in,
list(string)::out) is det.
break_into_words(String, Words0, Words) :-
break_into_words_from(String, 0, Words0, Words).
:- pred break_into_words_from(string::in, int::in,
list(string)::in, list(string)::out) is det.
break_into_words_from(String, Cur, Words0, Words) :-
( find_word_start(String, Cur, Start) ->
find_word_end(String, Start, End),
Length is End - Start + 1,
string__substring(String, Start, Length, Word),
Next is End + 1,
break_into_words_from(String, Next, [Word | Words0], Words)
;
Words = Words0
).
:- pred find_word_start(string::in, int::in, int::out) is semidet.
find_word_start(String, Cur, WordStart) :-
string__index(String, Cur, Char),
( char__is_whitespace(Char) ->
Next is Cur + 1,
find_word_start(String, Next, WordStart)
;
WordStart = Cur
).
:- pred find_word_end(string::in, int::in, int::out) is det.
find_word_end(String, Cur, WordEnd) :-
Next is Cur + 1,
( string__index(String, Next, Char) ->
( char__is_whitespace(Char) ->
WordEnd = Cur
;
find_word_end(String, Next, WordEnd)
)
;
WordEnd = Cur
).
%----------------------------------------------------------------------------%
% Groups the given words into lines. The first line can have up to Max
% characters on it; the later lines (if any) up to Max-2 characters.
% The given list of words must be nonempty, since we always return
% at least one line.
:- pred group_words(bool::in, list(list(string))::in, int::in,
list(list(string))::out) is det.
group_words(IsFirst, Paras, Max, Lines) :-
(
Paras = [],
Lines = []
;
Paras = [FirstPara | LaterParas],
(
FirstPara = [],
group_words(IsFirst, LaterParas, Max, Lines)
;
FirstPara = [FirstWord | LaterWords],
get_line_of_words(FirstWord, LaterWords,
Max, Line, RestWords),
( IsFirst = yes ->
Max2 is Max - 2
;
Max2 = Max
),
group_nonfirst_line_words(RestWords, Max2, RestLines1),
Lines1 = [Line | RestLines1],
group_words(no, LaterParas, Max2, RestLines),
list__append(Lines1, RestLines, Lines)
)
).
:- pred group_nonfirst_line_words(list(string)::in, int::in,
list(list(string))::out) is det.
group_nonfirst_line_words(Words, Max, Lines) :-
(
Words = [],
Lines = []
;
Words = [FirstWord | LaterWords],
get_line_of_words(FirstWord, LaterWords, Max, Line, RestWords),
group_nonfirst_line_words(RestWords, Max, RestLines),
Lines = [Line | RestLines]
).
:- pred get_line_of_words(string::in, list(string)::in, int::in,
list(string)::out, list(string)::out) is det.
get_line_of_words(FirstWord, LaterWords, MaxLen, Line, RestWords) :-
string__length(FirstWord, FirstWordLen),
get_later_words(LaterWords, FirstWordLen, MaxLen, [FirstWord],
Line, RestWords).
:- pred get_later_words(list(string)::in, int::in, int::in,
list(string)::in, list(string)::out, list(string)::out) is det.
get_later_words([], _, _, Line, Line, []).
get_later_words([Word | Words], OldLen, MaxLen, Line0, Line, RestWords) :-
string__length(Word, WordLen),
NewLen is OldLen + 1 + WordLen,
( NewLen =< MaxLen ->
list__append(Line0, [Word], Line1),
get_later_words(Words, NewLen, MaxLen,
Line1, Line, RestWords)
;
Line = Line0,
RestWords = [Word | Words]
).
%-----------------------------------------------------------------------------%
% The code of this predicate duplicates the functionality of
% hlds_out__write_pred_id. Changes here should be made there as well.
error_util__describe_one_pred_name(Module, PredId, Piece) :-
module_info_pred_info(Module, PredId, PredInfo),
pred_info_module(PredInfo, ModuleName),
prog_out__sym_name_to_string(ModuleName, ModuleNameString),
pred_info_name(PredInfo, PredName),
pred_info_arity(PredInfo, Arity),
pred_info_get_is_pred_or_func(PredInfo, PredOrFunc),
(
PredOrFunc = predicate,
PredOrFuncPart = "predicate",
OrigArity = Arity
;
PredOrFunc = function,
PredOrFuncPart = "function",
OrigArity is Arity - 1
),
(
pred_info_get_goal_type(PredInfo, assertion)
->
Piece = "promise"
;
string__int_to_string(OrigArity, ArityPart),
string__append_list([
PredOrFuncPart,
" `",
ModuleNameString,
":",
PredName,
"/",
ArityPart,
"'"], Piece)
).
error_util__describe_one_proc_name(Module, proc(PredId, ProcId), Piece) :-
error_util__describe_one_pred_name(Module, PredId, PredPiece),
proc_id_to_int(ProcId, ProcIdInt),
string__int_to_string(ProcIdInt, ProcIdPart),
string__append_list([
PredPiece,
" mode ",
ProcIdPart
], Piece).
error_util__describe_several_proc_names(Module, PPIds, Pieces) :-
list__map(error_util__describe_one_proc_name(Module), PPIds, Pieces0),
error_util__list_to_pieces(Pieces0, Pieces).
error_util__describe_one_call_site(Module, PPId - Context, Piece) :-
error_util__describe_one_proc_name(Module, PPId, ProcName),
term__context_file(Context, FileName),
term__context_line(Context, LineNumber),
string__int_to_string(LineNumber, LineNumberPart),
string__append_list([
ProcName,
" at ",
FileName,
":",
LineNumberPart
], Piece).
error_util__describe_several_call_sites(Module, Sites, Pieces) :-
list__map(error_util__describe_one_call_site(Module), Sites, Pieces0),
error_util__list_to_pieces(Pieces0, Pieces).
%-----------------------------------------------------------------------------%
report_error_num_args(MaybePredOrFunc, Arity0, Arities0) -->
% Adjust arities for functions.
{ MaybePredOrFunc = yes(function) ->
adjust_func_arity(function, Arity, Arity0),
list__map(
(pred(OtherArity0::in, OtherArity::out) is det :-
adjust_func_arity(function,
OtherArity, OtherArity0)
),
Arities0, Arities)
;
Arity = Arity0,
Arities = Arities0
},
io__write_string("wrong number of arguments ("),
io__write_int(Arity),
io__write_string("; should be "),
report_error_right_num_args(Arities),
io__write_string(")").
:- pred report_error_right_num_args(list(int), io__state, io__state).
:- mode report_error_right_num_args(in, di, uo) is det.
report_error_right_num_args([]) --> [].
report_error_right_num_args([Arity | Arities]) -->
io__write_int(Arity),
( { Arities = [] } ->
[]
; { Arities = [_] } ->
io__write_string(" or ")
;
io__write_string(", ")
),
report_error_right_num_args(Arities).
|