d: Merge upstream dmd, druntime 4c18eed967, phobos d945686a4.
[official-gcc.git] / gcc / d / dmd / mustuse.d
blob1d831bbed6b3582bb41ea0c17dc595912a8c2c39
1 /**
2 * Compile-time checks associated with the @mustuse attribute.
4 * Copyright: Copyright (C) 2022-2023 by The D Language Foundation, All Rights Reserved
5 * License: $(LINK2 https://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
6 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/mustuse.d, _mustuse.d)
7 * Documentation: https://dlang.org/phobos/dmd_mustuse.html
8 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/mustuse.d
9 */
11 module dmd.mustuse;
13 import dmd.dscope;
14 import dmd.dsymbol;
15 import dmd.errors;
16 import dmd.expression;
17 import dmd.globals;
18 import dmd.identifier;
19 import dmd.location;
21 // Used in isIncrementOrDecrement
22 private const StringExp plusPlus, minusMinus;
24 // Loc.initial cannot be used in static initializers, so
25 // these need a static constructor.
26 shared static this()
28 plusPlus = new StringExp(Loc.initial, "++");
29 minusMinus = new StringExp(Loc.initial, "--");
32 /**
33 * Check whether discarding an expression would violate the requirements of
34 * @mustuse. If so, emit an error.
36 * Params:
37 * e = the expression to check
38 * sc = scope in which `e` was semantically analyzed
40 * Returns: true on error, false on success.
42 bool checkMustUse(Expression e, Scope* sc)
44 import dmd.id : Id;
46 assert(e.type);
47 if (auto sym = e.type.toDsymbol(sc))
49 auto sd = sym.isStructDeclaration();
50 // isStructDeclaration returns non-null for both structs and unions
51 if (sd && hasMustUseAttribute(sd, sc) && !isAssignment(e) && !isIncrementOrDecrement(e))
53 error(e.loc, "ignored value of `@%s` type `%s`; prepend a `cast(void)` if intentional",
54 Id.udaMustUse.toChars(), e.type.toPrettyChars(true));
55 return true;
58 return false;
61 /**
62 * Called from a symbol's semantic to check for reserved usage of @mustuse.
64 * If such usage is found, emits an errror.
66 * Params:
67 * sym = symbol to check
69 void checkMustUseReserved(Dsymbol sym)
71 import dmd.attrib : foreachUdaNoSemantic;
72 import dmd.errors : error;
73 import dmd.id : Id;
75 // Can't use foreachUda (and by extension hasMustUseAttribute) while
76 // semantic analysis of `sym` is still in progress
77 foreachUdaNoSemantic(sym, (exp) {
78 if (isMustUseAttribute(exp))
80 if (sym.isFuncDeclaration())
82 error(sym.loc, "`@%s` on functions is reserved for future use",
83 Id.udaMustUse.toChars());
84 sym.errors = true;
86 else if (sym.isClassDeclaration() || sym.isEnumDeclaration())
88 error(sym.loc, "`@%s` on `%s` types is reserved for future use",
89 Id.udaMustUse.toChars(), sym.kind());
90 sym.errors = true;
93 return 0; // continue
94 });
97 /**
98 * Returns: true if the given expression is an assignment, either simple (a = b)
99 * or compound (a += b, etc).
101 private bool isAssignment(Expression e)
103 if (e.isAssignExp || e.isBinAssignExp || e.isConstructExp || e.isBlitExp)
104 return true;
105 if (auto ce = e.isCallExp())
107 if (auto fd = ce.f)
109 auto id = fd.ident;
110 if (id && isAssignmentOpId(id))
111 return true;
114 return false;
118 * Returns: true if id is the identifier of an assignment operator overload.
120 private bool isAssignmentOpId(Identifier id)
122 import dmd.id : Id;
124 return id == Id.assign
125 || id == Id.addass
126 || id == Id.subass
127 || id == Id.mulass
128 || id == Id.divass
129 || id == Id.modass
130 || id == Id.andass
131 || id == Id.orass
132 || id == Id.xorass
133 || id == Id.shlass
134 || id == Id.shrass
135 || id == Id.ushrass
136 || id == Id.catass
137 || id == Id.indexass
138 || id == Id.slice
139 || id == Id.sliceass
140 || id == Id.opOpAssign
141 || id == Id.opIndexOpAssign
142 || id == Id.opSliceOpAssign
143 || id == Id.powass;
147 * Returns: true if the given expression is an increment (++) or decrement (--).
149 private bool isIncrementOrDecrement(Expression e)
151 import dmd.dtemplate : isExpression;
152 import dmd.location;
153 import dmd.id : Id;
154 import dmd.tokens : EXP;
156 if (e.op == EXP.plusPlus
157 || e.op == EXP.minusMinus
158 || e.op == EXP.prePlusPlus
159 || e.op == EXP.preMinusMinus)
160 return true;
161 if (auto call = e.isCallExp())
163 // Check for overloaded preincrement
164 // e.g., a.opUnary!"++"
165 if (auto fd = call.f)
167 if (fd.ident == Id.opUnary && fd.parent)
169 if (auto ti = fd.parent.isTemplateInstance())
171 auto tiargs = ti.tiargs;
172 if (tiargs && tiargs.length >= 1)
174 if (auto argExp = (*tiargs)[0].isExpression())
176 auto op = argExp.isStringExp();
177 if (op && (op.compare(plusPlus) == 0 || op.compare(minusMinus) == 0))
178 return true;
185 else if (auto comma = e.isCommaExp())
187 // Check for overloaded postincrement
188 // e.g., (auto tmp = a, ++a, tmp)
189 if (comma.e1)
191 if (auto left = comma.e1.isCommaExp())
193 if (auto middle = left.e2)
195 if (middle && isIncrementOrDecrement(middle))
196 return true;
201 return false;
205 * Returns: true if the given symbol has the @mustuse attribute.
207 private bool hasMustUseAttribute(Dsymbol sym, Scope* sc)
209 import dmd.attrib : foreachUda;
211 bool result = false;
213 foreachUda(sym, sc, (Expression uda) {
214 if (isMustUseAttribute(uda))
216 result = true;
217 return 1; // break
219 return 0; // continue
222 return result;
226 * Returns: true if the given expression is core.attribute.mustuse.
228 private bool isMustUseAttribute(Expression e)
230 import dmd.attrib : isCoreUda;
231 import dmd.id : Id;
233 // Logic based on dmd.objc.Supported.declaredAsOptionalCount
234 auto typeExp = e.isTypeExp;
235 if (!typeExp)
236 return false;
238 auto typeEnum = typeExp.type.isTypeEnum();
239 if (!typeEnum)
240 return false;
242 if (isCoreUda(typeEnum.sym, Id.udaMustUse))
243 return true;
245 return false;