summaryrefslogtreecommitdiff
path: root/gcc/d/dmd/mustuse.d
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/d/dmd/mustuse.d')
-rw-r--r--gcc/d/dmd/mustuse.d244
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;
+}