diff options
Diffstat (limited to 'gcc/d/dmd/mustuse.d')
-rw-r--r-- | gcc/d/dmd/mustuse.d | 244 |
1 files changed, 244 insertions, 0 deletions
diff --git a/gcc/d/dmd/mustuse.d b/gcc/d/dmd/mustuse.d new file mode 100644 index 00000000000..4eb422805db --- /dev/null +++ b/gcc/d/dmd/mustuse.d @@ -0,0 +1,244 @@ +/** + * Compile-time checks associated with the @mustuse attribute. + * + * Copyright: Copyright (C) 2022 by The D Language Foundation, All Rights Reserved + * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0) + * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/mustuse.d, _mustuse.d) + * Documentation: https://dlang.org/phobos/dmd_mustuse.html + * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mustuse.d + */ + +module dmd.mustuse; + +import dmd.dscope; +import dmd.dsymbol; +import dmd.expression; +import dmd.globals; +import dmd.identifier; + +// Used in isIncrementOrDecrement +private static const StringExp plusPlus, minusMinus; + +// Loc.initial cannot be used in static initializers, so +// these need a static constructor. +static this() +{ + plusPlus = new StringExp(Loc.initial, "++"); + minusMinus = new StringExp(Loc.initial, "--"); +} + +/** + * Check whether discarding an expression would violate the requirements of + * @mustuse. If so, emit an error. + * + * Params: + * e = the expression to check + * sc = scope in which `e` was semantically analyzed + * + * Returns: true on error, false on success. + */ +bool checkMustUse(Expression e, Scope* sc) +{ + import dmd.id : Id; + + assert(e.type); + if (auto sym = e.type.toDsymbol(sc)) + { + auto sd = sym.isStructDeclaration(); + // isStructDeclaration returns non-null for both structs and unions + if (sd && hasMustUseAttribute(sd, sc) && !isAssignment(e) && !isIncrementOrDecrement(e)) + { + e.error("ignored value of `@%s` type `%s`; prepend a `cast(void)` if intentional", + Id.udaMustUse.toChars(), e.type.toPrettyChars(true)); + return true; + } + } + return false; +} + +/** + * Called from a symbol's semantic to check for reserved usage of @mustuse. + * + * If such usage is found, emits an errror. + * + * Params: + * sym = symbol to check + */ +void checkMustUseReserved(Dsymbol sym) +{ + import dmd.attrib : foreachUdaNoSemantic; + import dmd.errors : error; + import dmd.id : Id; + + // Can't use foreachUda (and by extension hasMustUseAttribute) while + // semantic analysis of `sym` is still in progress + foreachUdaNoSemantic(sym, (exp) { + if (isMustUseAttribute(exp)) + { + if (sym.isFuncDeclaration()) + { + error(sym.loc, "`@%s` on functions is reserved for future use", + Id.udaMustUse.toChars()); + sym.errors = true; + } + else if (sym.isClassDeclaration() || sym.isEnumDeclaration()) + { + error(sym.loc, "`@%s` on `%s` types is reserved for future use", + Id.udaMustUse.toChars(), sym.kind()); + sym.errors = true; + } + } + return 0; // continue + }); +} + +/** + * Returns: true if the given expression is an assignment, either simple (a = b) + * or compound (a += b, etc). + */ +private bool isAssignment(Expression e) +{ + if (e.isAssignExp || e.isBinAssignExp) + return true; + if (auto ce = e.isCallExp()) + { + if (auto fd = ce.f) + { + auto id = fd.ident; + if (id && isAssignmentOpId(id)) + return true; + } + } + return false; +} + +/** + * Returns: true if id is the identifier of an assignment operator overload. + */ +private bool isAssignmentOpId(Identifier id) +{ + import dmd.id : Id; + + return id == Id.assign + || id == Id.addass + || id == Id.subass + || id == Id.mulass + || id == Id.divass + || id == Id.modass + || id == Id.andass + || id == Id.orass + || id == Id.xorass + || id == Id.shlass + || id == Id.shrass + || id == Id.ushrass + || id == Id.catass + || id == Id.indexass + || id == Id.slice + || id == Id.sliceass + || id == Id.opOpAssign + || id == Id.opIndexOpAssign + || id == Id.opSliceOpAssign + || id == Id.powass; +} + +/** + * Returns: true if the given expression is an increment (++) or decrement (--). + */ +private bool isIncrementOrDecrement(Expression e) +{ + import dmd.dtemplate : isExpression; + import dmd.globals : Loc; + import dmd.id : Id; + import dmd.tokens : EXP; + + if (e.op == EXP.plusPlus + || e.op == EXP.minusMinus + || e.op == EXP.prePlusPlus + || e.op == EXP.preMinusMinus) + return true; + if (auto call = e.isCallExp()) + { + // Check for overloaded preincrement + // e.g., a.opUnary!"++" + if (auto fd = call.f) + { + if (fd.ident == Id.opUnary && fd.parent) + { + if (auto ti = fd.parent.isTemplateInstance()) + { + auto tiargs = ti.tiargs; + if (tiargs && tiargs.length >= 1) + { + if (auto argExp = (*tiargs)[0].isExpression()) + { + auto op = argExp.isStringExp(); + if (op && (op.compare(plusPlus) == 0 || op.compare(minusMinus) == 0)) + return true; + } + } + } + } + } + } + else if (auto comma = e.isCommaExp()) + { + // Check for overloaded postincrement + // e.g., (auto tmp = a, ++a, tmp) + if (comma.e1) + { + if (auto left = comma.e1.isCommaExp()) + { + if (auto middle = left.e2) + { + if (middle && isIncrementOrDecrement(middle)) + return true; + } + } + } + } + return false; +} + +/** + * Returns: true if the given symbol has the @mustuse attribute. + */ +private bool hasMustUseAttribute(Dsymbol sym, Scope* sc) +{ + import dmd.attrib : foreachUda; + + bool result = false; + + foreachUda(sym, sc, (Expression uda) { + if (isMustUseAttribute(uda)) + { + result = true; + return 1; // break + } + return 0; // continue + }); + + return result; +} + +/** + * Returns: true if the given expression is core.attribute.mustuse. + */ +private bool isMustUseAttribute(Expression e) +{ + import dmd.attrib : isCoreUda; + import dmd.id : Id; + + // Logic based on dmd.objc.Supported.declaredAsOptionalCount + auto typeExp = e.isTypeExp; + if (!typeExp) + return false; + + auto typeEnum = typeExp.type.isTypeEnum(); + if (!typeEnum) + return false; + + if (isCoreUda(typeEnum.sym, Id.udaMustUse)) + return true; + + return false; +} |