xref: /netbsd-src/external/gpl3/gcc/dist/gcc/d/dmd/mustuse.d (revision b1e838363e3c6fc78a55519254d99869742dd33c)
1 /**
2  * Compile-time checks associated with the @mustuse attribute.
3  *
4  * Copyright: Copyright (C) 2022 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  */
10 
11 module dmd.mustuse;
12 
13 import dmd.dscope;
14 import dmd.dsymbol;
15 import dmd.expression;
16 import dmd.globals;
17 import dmd.identifier;
18 
19 // Used in isIncrementOrDecrement
20 private static const StringExp plusPlus, minusMinus;
21 
22 // Loc.initial cannot be used in static initializers, so
23 // these need a static constructor.
this()24 static this()
25 {
26     plusPlus = new StringExp(Loc.initial, "++");
27     minusMinus = new StringExp(Loc.initial, "--");
28 }
29 
30 /**
31  * Check whether discarding an expression would violate the requirements of
32  * @mustuse. If so, emit an error.
33  *
34  * Params:
35  *   e = the expression to check
36  *   sc = scope in which `e` was semantically analyzed
37  *
38  * Returns: true on error, false on success.
39  */
checkMustUse(Expression e,Scope * sc)40 bool checkMustUse(Expression e, Scope* sc)
41 {
42     import dmd.id : Id;
43 
44     assert(e.type);
45     if (auto sym = e.type.toDsymbol(sc))
46     {
47         auto sd = sym.isStructDeclaration();
48         // isStructDeclaration returns non-null for both structs and unions
49         if (sd && hasMustUseAttribute(sd, sc) && !isAssignment(e) && !isIncrementOrDecrement(e))
50         {
51             e.error("ignored value of `@%s` type `%s`; prepend a `cast(void)` if intentional",
52                 Id.udaMustUse.toChars(), e.type.toPrettyChars(true));
53             return true;
54         }
55     }
56     return false;
57 }
58 
59 /**
60  * Called from a symbol's semantic to check for reserved usage of @mustuse.
61  *
62  * If such usage is found, emits an errror.
63  *
64  * Params:
65  *   sym = symbol to check
66  */
checkMustUseReserved(Dsymbol sym)67 void checkMustUseReserved(Dsymbol sym)
68 {
69     import dmd.attrib : foreachUdaNoSemantic;
70     import dmd.errors : error;
71     import dmd.id : Id;
72 
73     // Can't use foreachUda (and by extension hasMustUseAttribute) while
74     // semantic analysis of `sym` is still in progress
75     foreachUdaNoSemantic(sym, (exp) {
76         if (isMustUseAttribute(exp))
77         {
78             if (sym.isFuncDeclaration())
79             {
80                 error(sym.loc, "`@%s` on functions is reserved for future use",
81                     Id.udaMustUse.toChars());
82                 sym.errors = true;
83             }
84             else if (sym.isClassDeclaration() || sym.isEnumDeclaration())
85             {
86                 error(sym.loc, "`@%s` on `%s` types is reserved for future use",
87                     Id.udaMustUse.toChars(), sym.kind());
88                 sym.errors = true;
89             }
90         }
91         return 0; // continue
92     });
93 }
94 
95 /**
96  * Returns: true if the given expression is an assignment, either simple (a = b)
97  * or compound (a += b, etc).
98  */
isAssignment(Expression e)99 private bool isAssignment(Expression e)
100 {
101     if (e.isAssignExp || e.isBinAssignExp)
102         return true;
103     if (auto ce = e.isCallExp())
104     {
105         if (auto fd = ce.f)
106         {
107             auto id = fd.ident;
108             if (id && isAssignmentOpId(id))
109                 return true;
110         }
111     }
112     return false;
113 }
114 
115 /**
116  * Returns: true if id is the identifier of an assignment operator overload.
117  */
isAssignmentOpId(Identifier id)118 private bool isAssignmentOpId(Identifier id)
119 {
120     import dmd.id : Id;
121 
122     return id == Id.assign
123         || id == Id.addass
124         || id == Id.subass
125         || id == Id.mulass
126         || id == Id.divass
127         || id == Id.modass
128         || id == Id.andass
129         || id == Id.orass
130         || id == Id.xorass
131         || id == Id.shlass
132         || id == Id.shrass
133         || id == Id.ushrass
134         || id == Id.catass
135         || id == Id.indexass
136         || id == Id.slice
137         || id == Id.sliceass
138         || id == Id.opOpAssign
139         || id == Id.opIndexOpAssign
140         || id == Id.opSliceOpAssign
141         || id == Id.powass;
142 }
143 
144 /**
145  * Returns: true if the given expression is an increment (++) or decrement (--).
146  */
isIncrementOrDecrement(Expression e)147 private bool isIncrementOrDecrement(Expression e)
148 {
149     import dmd.dtemplate : isExpression;
150     import dmd.globals : Loc;
151     import dmd.id : Id;
152     import dmd.tokens : EXP;
153 
154     if (e.op == EXP.plusPlus
155         || e.op == EXP.minusMinus
156         || e.op == EXP.prePlusPlus
157         || e.op == EXP.preMinusMinus)
158         return true;
159     if (auto call = e.isCallExp())
160     {
161         // Check for overloaded preincrement
162         // e.g., a.opUnary!"++"
163         if (auto fd = call.f)
164         {
165             if (fd.ident == Id.opUnary && fd.parent)
166             {
167                 if (auto ti = fd.parent.isTemplateInstance())
168                 {
169                     auto tiargs = ti.tiargs;
170                     if (tiargs && tiargs.length >= 1)
171                     {
172                         if (auto argExp = (*tiargs)[0].isExpression())
173                         {
174                             auto op = argExp.isStringExp();
175                             if (op && (op.compare(plusPlus) == 0 || op.compare(minusMinus) == 0))
176                                 return true;
177                         }
178                     }
179                 }
180             }
181         }
182     }
183     else if (auto comma = e.isCommaExp())
184     {
185         // Check for overloaded postincrement
186         // e.g., (auto tmp = a, ++a, tmp)
187         if (comma.e1)
188         {
189             if (auto left = comma.e1.isCommaExp())
190             {
191                 if (auto middle = left.e2)
192                 {
193                     if (middle && isIncrementOrDecrement(middle))
194                         return true;
195                 }
196             }
197         }
198     }
199     return false;
200 }
201 
202 /**
203  * Returns: true if the given symbol has the @mustuse attribute.
204  */
hasMustUseAttribute(Dsymbol sym,Scope * sc)205 private bool hasMustUseAttribute(Dsymbol sym, Scope* sc)
206 {
207     import dmd.attrib : foreachUda;
208 
209     bool result = false;
210 
211     foreachUda(sym, sc, (Expression uda) {
212         if (isMustUseAttribute(uda))
213         {
214             result = true;
215             return 1; // break
216         }
217         return 0; // continue
218     });
219 
220     return result;
221 }
222 
223 /**
224  * Returns: true if the given expression is core.attribute.mustuse.
225  */
isMustUseAttribute(Expression e)226 private bool isMustUseAttribute(Expression e)
227 {
228     import dmd.attrib : isCoreUda;
229     import dmd.id : Id;
230 
231     // Logic based on dmd.objc.Supported.declaredAsOptionalCount
232     auto typeExp = e.isTypeExp;
233     if (!typeExp)
234         return false;
235 
236     auto typeEnum = typeExp.type.isTypeEnum();
237     if (!typeEnum)
238         return false;
239 
240     if (isCoreUda(typeEnum.sym, Id.udaMustUse))
241         return true;
242 
243     return false;
244 }
245