"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "src/dmd/chkformat.d" between
dmd-2.095.0.tar.gz and dmd-2.095.1.tar.gz

About: DMD (Digital Mars D) is the D reference compiler. D is an object-oriented, imperative, multi-paradigm system programming language.

chkformat.d  (dmd-2.095.0):chkformat.d  (dmd-2.095.1)
skipping to change at line 16 skipping to change at line 16
* License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
* Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/chkform at.d, _chkformat.d) * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/chkform at.d, _chkformat.d)
* Documentation: https://dlang.org/phobos/dmd_chkformat.html * Documentation: https://dlang.org/phobos/dmd_chkformat.html
* Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/chkformat.d * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/chkformat.d
*/ */
module dmd.chkformat; module dmd.chkformat;
//import core.stdc.stdio : printf, scanf; //import core.stdc.stdio : printf, scanf;
import core.stdc.ctype : isdigit; import core.stdc.ctype : isdigit;
import dmd.cond;
import dmd.errors; import dmd.errors;
import dmd.expression; import dmd.expression;
import dmd.globals; import dmd.globals;
import dmd.identifier;
import dmd.mtype; import dmd.mtype;
import dmd.target; import dmd.target;
/****************************************** /******************************************
* Check that arguments to a printf format string are compatible * Check that arguments to a printf format string are compatible
* with that string. Issue errors for incompatibilities. * with that string. Issue errors for incompatibilities.
* *
* Follows the C99 specification for printf. * Follows the C99 specification for printf.
* *
* Takes a generous, rather than strict, view of compatiblity. * Takes a generous, rather than strict, view of compatiblity.
skipping to change at line 61 skipping to change at line 63
* *
* Returns: * Returns:
* `true` if errors occurred * `true` if errors occurred
* References: * References:
* C99 7.19.6.1 * C99 7.19.6.1
* http://www.cplusplus.com/reference/cstdio/printf/ * http://www.cplusplus.com/reference/cstdio/printf/
*/ */
bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre ssion[] args, bool isVa_list) bool checkPrintfFormat(ref const Loc loc, scope const char[] format, scope Expre ssion[] args, bool isVa_list)
{ {
//printf("checkPrintFormat('%.*s')\n", cast(int)format.length, format.ptr); //printf("checkPrintFormat('%.*s')\n", cast(int)format.length, format.ptr);
size_t n = 0; size_t n, gnu_m_count; // index in args / number of Format.GNU_m
for (size_t i = 0; i < format.length;) for (size_t i = 0; i < format.length;)
{ {
if (format[i] != '%') if (format[i] != '%')
{ {
++i; ++i;
continue; continue;
} }
bool widthStar; bool widthStar;
bool precisionStar; bool precisionStar;
size_t j = i; size_t j = i;
skipping to change at line 87 skipping to change at line 89
continue; // "%%", no arguments continue; // "%%", no arguments
if (isVa_list) if (isVa_list)
{ {
// format check only // format check only
if (fmt == Format.error) if (fmt == Format.error)
deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast( int)slice.length, slice.ptr); deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast( int)slice.length, slice.ptr);
continue; continue;
} }
Expression getNextArg() if (fmt == Format.GNU_m)
++gnu_m_count;
Expression getNextArg(ref bool skip)
{ {
if (n == args.length) if (n == args.length)
{ {
deprecation(loc, "more format specifiers than %d arguments", cas if (args.length < (n + 1) - gnu_m_count)
t(int)n); deprecation(loc, "more format specifiers than %zd arguments"
, n);
else
skip = true;
return null; return null;
} }
return args[n++]; return args[n++];
} }
void errorMsg(const char* prefix, const char[] specifier, Expression arg , const char* texpect, Type tactual) void errorMsg(const char* prefix, const char[] specifier, Expression arg , const char* texpect, Type tactual)
{ {
deprecation(arg.loc, "%sargument `%s` for format specification `\"%. *s\"` must be `%s`, not `%s`", deprecation(arg.loc, "%sargument `%s` for format specification `\"%. *s\"` must be `%s`, not `%s`",
prefix ? prefix : "", arg.toChars(), cast(int)slice.length, sl ice.ptr, texpect, tactual.toChars()); prefix ? prefix : "", arg.toChars(), cast(int)slice.length, sl ice.ptr, texpect, tactual.toChars());
} }
if (widthStar) if (widthStar)
{ {
auto e = getNextArg(); bool skip;
auto e = getNextArg(skip);
if (skip)
continue;
if (!e) if (!e)
return true; return true;
auto t = e.type.toBasetype(); auto t = e.type.toBasetype();
if (t.ty != Tint32 && t.ty != Tuns32) if (t.ty != Tint32 && t.ty != Tuns32)
errorMsg("width ", slice, e, "int", t); errorMsg("width ", slice, e, "int", t);
} }
if (precisionStar) if (precisionStar)
{ {
auto e = getNextArg(); bool skip;
auto e = getNextArg(skip);
if (skip)
continue;
if (!e) if (!e)
return true; return true;
auto t = e.type.toBasetype(); auto t = e.type.toBasetype();
if (t.ty != Tint32 && t.ty != Tuns32) if (t.ty != Tint32 && t.ty != Tuns32)
errorMsg("precision ", slice, e, "int", t); errorMsg("precision ", slice, e, "int", t);
} }
auto e = getNextArg(); bool skip;
auto e = getNextArg(skip);
if (skip)
continue;
if (!e) if (!e)
return true; return true;
auto t = e.type.toBasetype(); auto t = e.type.toBasetype();
auto tnext = t.nextOf(); auto tnext = t.nextOf();
const c_longsize = target.c.longsize; const c_longsize = target.c.longsize;
const is64bit = global.params.is64bit; const is64bit = global.params.is64bit;
// Types which are promoted to int are allowed. // Types which are promoted to int are allowed.
// Spec: C99 6.5.2.2.7 // Spec: C99 6.5.2.2.7
final switch (fmt) final switch (fmt)
skipping to change at line 181 skipping to change at line 198
case Format.zd: // size_t case Format.zd: // size_t
if (!(t.isintegral() && t.size() == (is64bit ? 8 : 4))) if (!(t.isintegral() && t.size() == (is64bit ? 8 : 4)))
errorMsg(null, slice, e, "size_t", t); errorMsg(null, slice, e, "size_t", t);
break; break;
case Format.td: // ptrdiff_t case Format.td: // ptrdiff_t
if (!(t.isintegral() && t.size() == (is64bit ? 8 : 4))) if (!(t.isintegral() && t.size() == (is64bit ? 8 : 4)))
errorMsg(null, slice, e, "ptrdiff_t", t); errorMsg(null, slice, e, "ptrdiff_t", t);
break; break;
case Format.GNU_a: // Format.GNU_a is only for scanf
case Format.lg: case Format.lg:
case Format.g: // double case Format.g: // double
if (t.ty != Tfloat64 && t.ty != Timaginary64) if (t.ty != Tfloat64 && t.ty != Timaginary64)
errorMsg(null, slice, e, "double", t); errorMsg(null, slice, e, "double", t);
break; break;
case Format.Lg: // long double case Format.Lg: // long double
if (t.ty != Tfloat80 && t.ty != Timaginary80) if (t.ty != Tfloat80 && t.ty != Timaginary80)
errorMsg(null, slice, e, "real", t); errorMsg(null, slice, e, "real", t);
break; break;
skipping to change at line 262 skipping to change at line 280
case Format.ls: // pointer to wchar_t string case Format.ls: // pointer to wchar_t string
const twchar_t = global.params.targetOS == TargetOS.Windows ? Tw char : Tdchar; const twchar_t = global.params.targetOS == TargetOS.Windows ? Tw char : Tdchar;
if (!(t.ty == Tpointer && tnext.ty == twchar_t)) if (!(t.ty == Tpointer && tnext.ty == twchar_t))
errorMsg(null, slice, e, "wchar_t*", t); errorMsg(null, slice, e, "wchar_t*", t);
break; break;
case Format.error: case Format.error:
deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast( int)slice.length, slice.ptr); deprecation(loc, "format specifier `\"%.*s\"` is invalid", cast( int)slice.length, slice.ptr);
break; break;
case Format.GNU_m:
break; // not assert(0) because it may go through it if there a
re extra arguments
case Format.percent: case Format.percent:
assert(0); assert(0);
} }
} }
return false; return false;
} }
/****************************************** /******************************************
* Check that arguments to a scanf format string are compatible * Check that arguments to a scanf format string are compatible
* with that string. Issue errors for incompatibilities. * with that string. Issue errors for incompatibilities.
skipping to change at line 441 skipping to change at line 462
case Format.ju: // pointer to uintmax_t case Format.ju: // pointer to uintmax_t
if (!(t.ty == Tpointer && tnext.ty == (is64bit ? Tuns64 : Tuns32 ))) if (!(t.ty == Tpointer && tnext.ty == (is64bit ? Tuns64 : Tuns32 )))
errorMsg(null, slice, e, "ulong*", t); errorMsg(null, slice, e, "ulong*", t);
break; break;
case Format.g: // pointer to float case Format.g: // pointer to float
if (!(t.ty == Tpointer && tnext.ty == Tfloat32)) if (!(t.ty == Tpointer && tnext.ty == Tfloat32))
errorMsg(null, slice, e, "float*", t); errorMsg(null, slice, e, "float*", t);
break; break;
case Format.lg: // pointer to double case Format.lg: // pointer to double
if (!(t.ty == Tpointer && tnext.ty == Tfloat64)) if (!(t.ty == Tpointer && tnext.ty == Tfloat64))
errorMsg(null, slice, e, "double*", t); errorMsg(null, slice, e, "double*", t);
break; break;
case Format.Lg: // pointer to long double case Format.Lg: // pointer to long double
if (!(t.ty == Tpointer && tnext.ty == Tfloat80)) if (!(t.ty == Tpointer && tnext.ty == Tfloat80))
errorMsg(null, slice, e, "real*", t); errorMsg(null, slice, e, "real*", t);
break; break;
case Format.GNU_a:
case Format.GNU_m:
case Format.c: case Format.c:
case Format.s: // pointer to char string case Format.s: // pointer to char string
if (!(t.ty == Tpointer && (tnext.ty == Tchar || tnext.ty == Tint 8 || tnext.ty == Tuns8))) if (!(t.ty == Tpointer && (tnext.ty == Tchar || tnext.ty == Tint 8 || tnext.ty == Tuns8)))
errorMsg(null, slice, e, "char*", t); errorMsg(null, slice, e, "char*", t);
break; break;
case Format.lc: case Format.lc:
case Format.ls: // pointer to wchar_t string case Format.ls: // pointer to wchar_t string
const twchar_t = global.params.targetOS == TargetOS.Windows ? Tw char : Tdchar; const twchar_t = global.params.targetOS == TargetOS.Windows ? Tw char : Tdchar;
if (!(t.ty == Tpointer && tnext.ty == twchar_t)) if (!(t.ty == Tpointer && tnext.ty == twchar_t))
skipping to change at line 495 skipping to change at line 520
* *
* Params: * Params:
* format = format string * format = format string
* idx = index of `%` of start of format specifier, * idx = index of `%` of start of format specifier,
* which gets updated to index past the end of it, * which gets updated to index past the end of it,
* even if `Format.error` is returned * even if `Format.error` is returned
* asterisk = set if there is a `*` sub-specifier * asterisk = set if there is a `*` sub-specifier
* Returns: * Returns:
* Format * Format
*/ */
pure nothrow @safe
Format parseScanfFormatSpecifier(scope const char[] format, ref size_t idx, Format parseScanfFormatSpecifier(scope const char[] format, ref size_t idx,
out bool asterisk) out bool asterisk) nothrow pure @safe
{ {
auto i = idx; auto i = idx;
assert(format[i] == '%'); assert(format[i] == '%');
const length = format.length; const length = format.length;
Format error() Format error()
{ {
idx = i; idx = i;
return Format.error; return Format.error;
} }
skipping to change at line 585 skipping to change at line 609
* Params: * Params:
* format = format string * format = format string
* idx = index of `%` of start of format specifier, * idx = index of `%` of start of format specifier,
* which gets updated to index past the end of it, * which gets updated to index past the end of it,
* even if `Format.error` is returned * even if `Format.error` is returned
* widthStar = set if * for width * widthStar = set if * for width
* precisionStar = set if * for precision * precisionStar = set if * for precision
* Returns: * Returns:
* Format * Format
*/ */
pure nothrow @safe
Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx, Format parsePrintfFormatSpecifier(scope const char[] format, ref size_t idx,
out bool widthStar, out bool precisionStar) out bool widthStar, out bool precisionStar) nothrow pure @safe
{ {
auto i = idx; auto i = idx;
assert(format[i] == '%'); assert(format[i] == '%');
const length = format.length; const length = format.length;
bool hash; bool hash;
bool zero; bool zero;
bool flags; bool flags;
bool width; bool width;
bool precision; bool precision;
skipping to change at line 769 skipping to change at line 792
lc, // wint_t (printf) lc, // wint_t (printf)
p, // pointer p, // pointer
n, // pointer to int n, // pointer to int
hhn, // pointer to signed char hhn, // pointer to signed char
hn, // pointer to short hn, // pointer to short
ln, // pointer to long int ln, // pointer to long int
lln, // pointer to long long int lln, // pointer to long long int
jn, // pointer to intmax_t jn, // pointer to intmax_t
zn, // pointer to size_t zn, // pointer to size_t
tn, // pointer to ptrdiff_t tn, // pointer to ptrdiff_t
GNU_a, // GNU ext. : address to a string with no maximum size (scanf)
GNU_m, // GNU ext. : string corresponding to the error code in errno (p
rintf) / length modifier (scanf)
percent, // %% (i.e. no argument) percent, // %% (i.e. no argument)
error, // invalid format specification error, // invalid format specification
} }
/************************************** /**************************************
* Parse the *length specifier* and the *specifier* of the following form: * Parse the *length specifier* and the *specifier* of the following form:
* `[length]specifier` * `[length]specifier`
* *
* Params: * Params:
* format = format string * format = format string
* idx = index of of start of format specifier, * idx = index of of start of format specifier,
* which gets updated to index past the end of it, * which gets updated to index past the end of it,
* even if `Format.error` is returned * even if `Format.error` is returned
* genSpecifier = Generic specifier. For instance, it will be set to `d` if the * genSpecifier = Generic specifier. For instance, it will be set to `d` if the
* format is `hdd`. * format is `hdd`.
* Returns: * Returns:
* Format * Format
*/ */
pure @safe nothrow
Format parseGenericFormatSpecifier(scope const char[] format, Format parseGenericFormatSpecifier(scope const char[] format,
ref size_t idx, out char genSpecifier) ref size_t idx, out char genSpecifier, bool useGNUExts =
findCondition(global.versionids, Identifier.idPool("CRuntime_Glibc"))) nothr
ow pure @trusted
{ {
const length = format.length; const length = format.length;
/* Read the `length modifier` /* Read the `length modifier`
*/ */
const lm = format[idx]; const lm = format[idx];
bool lm1; // if jztL bool lm1; // if jztL
bool lm2; // if `hh` or `ll` bool lm2; // if `hh` or `ll`
if (lm == 'j' || if (lm == 'j' ||
lm == 'z' || lm == 'z' ||
skipping to change at line 861 skipping to change at line 886
specifier = lm == 'h' && lm2 ? Format.hhu : specifier = lm == 'h' && lm2 ? Format.hhu :
lm == 'h' ? Format.hu : lm == 'h' ? Format.hu :
lm == 'l' && lm2 ? Format.llu : lm == 'l' && lm2 ? Format.llu :
lm == 'l' ? Format.lu : lm == 'l' ? Format.lu :
lm == 'j' ? Format.ju : lm == 'j' ? Format.ju :
lm == 'z' ? Format.zd : lm == 'z' ? Format.zd :
lm == 't' ? Format.td : lm == 't' ? Format.td :
Format.u; Format.u;
break; break;
case 'a':
if (useGNUExts)
{
// https://www.gnu.org/software/libc/manual/html_node/Dynamic-St
ring-Input.html
specifier = Format.GNU_a;
break;
}
goto case;
case 'f': case 'f':
case 'F': case 'F':
case 'e': case 'e':
case 'E': case 'E':
case 'g': case 'g':
case 'G': case 'G':
case 'a':
case 'A': case 'A':
if (lm == 'L') if (lm == 'L')
specifier = Format.Lg; specifier = Format.Lg;
else if (lm1 || lm2 || lm == 'h') else if (lm1 || lm2 || lm == 'h')
specifier = Format.error; specifier = Format.error;
else else
specifier = lm == 'l' ? Format.lg : Format.g; specifier = lm == 'l' ? Format.lg : Format.g;
break; break;
case 'c': case 'c':
skipping to change at line 912 skipping to change at line 945
specifier = lm == 'l' && lm2 ? Format.lln : specifier = lm == 'l' && lm2 ? Format.lln :
lm == 'l' ? Format.ln : lm == 'l' ? Format.ln :
lm == 'h' && lm2 ? Format.hhn : lm == 'h' && lm2 ? Format.hhn :
lm == 'h' ? Format.hn : lm == 'h' ? Format.hn :
lm == 'j' ? Format.jn : lm == 'j' ? Format.jn :
lm == 'z' ? Format.zn : lm == 'z' ? Format.zn :
lm == 't' ? Format.tn : lm == 't' ? Format.tn :
Format.n; Format.n;
break; break;
case 'm':
if (useGNUExts)
{
// http://www.gnu.org/software/libc/manual/html_node/Other-Outpu
t-Conversions.html
specifier = Format.GNU_m;
break;
}
goto default;
default: default:
specifier = Format.error; specifier = Format.error;
break; break;
} }
++idx; ++idx;
return specifier; // success return specifier; // success
} }
unittest unittest
skipping to change at line 1077 skipping to change at line 1119
idx = 0; idx = 0;
assert(parsePrintfFormatSpecifier("%F", idx, widthStar, precisionStar) == F ormat.g); assert(parsePrintfFormatSpecifier("%F", idx, widthStar, precisionStar) == F ormat.g);
assert(idx == 2); assert(idx == 2);
idx = 0; idx = 0;
assert(parsePrintfFormatSpecifier("%G", idx, widthStar, precisionStar) == F ormat.g); assert(parsePrintfFormatSpecifier("%G", idx, widthStar, precisionStar) == F ormat.g);
assert(idx == 2); assert(idx == 2);
idx = 0; idx = 0;
assert(parsePrintfFormatSpecifier("%a", idx, widthStar, precisionStar) == F Format g = parsePrintfFormatSpecifier("%a", idx, widthStar, precisionStar);
ormat.g); assert(g == Format.g || g == Format.GNU_a);
assert(idx == 2); assert(idx == 2);
idx = 0; idx = 0;
assert(parsePrintfFormatSpecifier("%A", idx, widthStar, precisionStar) == F ormat.g); assert(parsePrintfFormatSpecifier("%A", idx, widthStar, precisionStar) == F ormat.g);
assert(idx == 2); assert(idx == 2);
idx = 0; idx = 0;
assert(parsePrintfFormatSpecifier("%lg", idx, widthStar, precisionStar) == Format.lg); assert(parsePrintfFormatSpecifier("%lg", idx, widthStar, precisionStar) == Format.lg);
assert(idx == 3); assert(idx == 3);
skipping to change at line 1247 skipping to change at line 1290
idx = 0; idx = 0;
assert(parseScanfFormatSpecifier("%f", idx, asterisk) == Format.g); assert(parseScanfFormatSpecifier("%f", idx, asterisk) == Format.g);
assert(idx == 2); assert(idx == 2);
idx = 0; idx = 0;
assert(parseScanfFormatSpecifier("%e", idx, asterisk) == Format.g); assert(parseScanfFormatSpecifier("%e", idx, asterisk) == Format.g);
assert(idx == 2); assert(idx == 2);
idx = 0; idx = 0;
assert(parseScanfFormatSpecifier("%a", idx, asterisk) == Format.g); g = parseScanfFormatSpecifier("%a", idx, asterisk);
assert(g == Format.g || g == Format.GNU_a);
assert(idx == 2); assert(idx == 2);
idx = 0; idx = 0;
assert(parseScanfFormatSpecifier("%c", idx, asterisk) == Format.c); assert(parseScanfFormatSpecifier("%c", idx, asterisk) == Format.c);
assert(idx == 2); assert(idx == 2);
// asterisk // asterisk
idx = 0; idx = 0;
assert(parseScanfFormatSpecifier("%*d", idx, asterisk) == Format.d); assert(parseScanfFormatSpecifier("%*d", idx, asterisk) == Format.d);
assert(idx == 3); assert(idx == 3);
 End of changes. 25 change blocks. 
17 lines changed or deleted 65 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)