1 /* Compiler implementation of the D programming language
2 * Copyright (C) 1999-2019 by The D Language Foundation, All Rights Reserved
3 * written by Walter Bright
4 * http://www.digitalmars.com
5 * Distributed under the Boost Software License, Version 1.0.
6 * http://www.boost.org/LICENSE_1_0.txt
7 * https://github.com/D-Programming-Language/dmd/blob/master/src/access.c
8 */
9
10 #include "root/dsystem.h"
11 #include "root/root.h"
12 #include "root/rmem.h"
13
14 #include "errors.h"
15 #include "enum.h"
16 #include "aggregate.h"
17 #include "init.h"
18 #include "attrib.h"
19 #include "scope.h"
20 #include "id.h"
21 #include "mtype.h"
22 #include "declaration.h"
23 #include "aggregate.h"
24 #include "expression.h"
25 #include "module.h"
26 #include "template.h"
27
28 /* Code to do access checks
29 */
30
31 bool hasPackageAccess(Scope *sc, Dsymbol *s);
32 bool hasPackageAccess(Module *mod, Dsymbol *s);
33 bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember);
34 bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd);
35 static Dsymbol *mostVisibleOverload(Dsymbol *s);
36
37 /****************************************
38 * Return Prot access for Dsymbol smember in this declaration.
39 */
getAccess(AggregateDeclaration * ad,Dsymbol * smember)40 Prot getAccess(AggregateDeclaration *ad, Dsymbol *smember)
41 {
42 Prot access_ret = Prot(PROTnone);
43
44 assert(ad->isStructDeclaration() || ad->isClassDeclaration());
45 if (smember->toParent() == ad)
46 {
47 access_ret = smember->prot();
48 }
49 else if (smember->isDeclaration()->isStatic())
50 {
51 access_ret = smember->prot();
52 }
53 if (ClassDeclaration *cd = ad->isClassDeclaration())
54 {
55 for (size_t i = 0; i < cd->baseclasses->dim; i++)
56 {
57 BaseClass *b = (*cd->baseclasses)[i];
58
59 Prot access = getAccess(b->sym, smember);
60 switch (access.kind)
61 {
62 case PROTnone:
63 break;
64
65 case PROTprivate:
66 access_ret = Prot(PROTnone); // private members of base class not accessible
67 break;
68
69 case PROTpackage:
70 case PROTprotected:
71 case PROTpublic:
72 case PROTexport:
73 // If access is to be tightened
74 if (PROTpublic < access.kind)
75 access = Prot(PROTpublic);
76
77 // Pick path with loosest access
78 if (access_ret.isMoreRestrictiveThan(access))
79 access_ret = access;
80 break;
81
82 default:
83 assert(0);
84 }
85 }
86 }
87
88 return access_ret;
89 }
90
91 /********************************************************
92 * Helper function for checkAccess()
93 * Returns:
94 * false is not accessible
95 * true is accessible
96 */
isAccessible(Dsymbol * smember,Dsymbol * sfunc,AggregateDeclaration * dthis,AggregateDeclaration * cdscope)97 static bool isAccessible(
98 Dsymbol *smember,
99 Dsymbol *sfunc,
100 AggregateDeclaration *dthis,
101 AggregateDeclaration *cdscope)
102 {
103 assert(dthis);
104
105 if (hasPrivateAccess(dthis, sfunc) ||
106 isFriendOf(dthis, cdscope))
107 {
108 if (smember->toParent() == dthis)
109 return true;
110
111 if (ClassDeclaration *cdthis = dthis->isClassDeclaration())
112 {
113 for (size_t i = 0; i < cdthis->baseclasses->dim; i++)
114 {
115 BaseClass *b = (*cdthis->baseclasses)[i];
116 Prot access = getAccess(b->sym, smember);
117 if (access.kind >= PROTprotected ||
118 isAccessible(smember, sfunc, b->sym, cdscope))
119 {
120 return true;
121 }
122 }
123 }
124 }
125 else
126 {
127 if (smember->toParent() != dthis)
128 {
129 if (ClassDeclaration *cdthis = dthis->isClassDeclaration())
130 {
131 for (size_t i = 0; i < cdthis->baseclasses->dim; i++)
132 {
133 BaseClass *b = (*cdthis->baseclasses)[i];
134 if (isAccessible(smember, sfunc, b->sym, cdscope))
135 return true;
136 }
137 }
138 }
139 }
140 return false;
141 }
142
143 /*******************************
144 * Do access check for member of this class, this class being the
145 * type of the 'this' pointer used to access smember.
146 * Returns true if the member is not accessible.
147 */
checkAccess(AggregateDeclaration * ad,Loc loc,Scope * sc,Dsymbol * smember)148 bool checkAccess(AggregateDeclaration *ad, Loc loc, Scope *sc, Dsymbol *smember)
149 {
150 FuncDeclaration *f = sc->func;
151 AggregateDeclaration *cdscope = sc->getStructClassScope();
152
153 Dsymbol *smemberparent = smember->toParent();
154 if (!smemberparent || !smemberparent->isAggregateDeclaration())
155 {
156 return false; // then it is accessible
157 }
158
159 // BUG: should enable this check
160 //assert(smember->parent->isBaseOf(this, NULL));
161
162 bool result;
163 Prot access;
164 if (smemberparent == ad)
165 {
166 access = smember->prot();
167 result = access.kind >= PROTpublic ||
168 hasPrivateAccess(ad, f) ||
169 isFriendOf(ad, cdscope) ||
170 (access.kind == PROTpackage && hasPackageAccess(sc, smember)) ||
171 ad->getAccessModule() == sc->_module;
172 }
173 else if ((access = getAccess(ad, smember)).kind >= PROTpublic)
174 {
175 result = true;
176 }
177 else if (access.kind == PROTpackage && hasPackageAccess(sc, ad))
178 {
179 result = true;
180 }
181 else
182 {
183 result = isAccessible(smember, f, ad, cdscope);
184 }
185 if (!result)
186 {
187 ad->error(loc, "member %s is not accessible", smember->toChars());
188 //printf("smember = %s %s, prot = %d, semanticRun = %d\n",
189 // smember->kind(), smember->toPrettyChars(), smember->prot(), smember->semanticRun);
190 return true;
191 }
192 return false;
193 }
194
195 /****************************************
196 * Determine if this is the same or friend of cd.
197 */
isFriendOf(AggregateDeclaration * ad,AggregateDeclaration * cd)198 bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd)
199 {
200 if (ad == cd)
201 return true;
202
203 // Friends if both are in the same module
204 //if (toParent() == cd->toParent())
205 if (cd && ad->getAccessModule() == cd->getAccessModule())
206 {
207 return true;
208 }
209
210 return false;
211 }
212
213 /****************************************
214 * Determine if scope sc has package level access to s.
215 */
hasPackageAccess(Scope * sc,Dsymbol * s)216 bool hasPackageAccess(Scope *sc, Dsymbol *s)
217 {
218 return hasPackageAccess(sc->_module, s);
219 }
220
hasPackageAccess(Module * mod,Dsymbol * s)221 bool hasPackageAccess(Module *mod, Dsymbol *s)
222 {
223 Package *pkg = NULL;
224
225 if (s->prot().pkg)
226 pkg = s->prot().pkg;
227 else
228 {
229 // no explicit package for protection, inferring most qualified one
230 for (; s; s = s->parent)
231 {
232 if (Module *m = s->isModule())
233 {
234 DsymbolTable *dst = Package::resolve(m->md ? m->md->packages : NULL, NULL, NULL);
235 assert(dst);
236 Dsymbol *s2 = dst->lookup(m->ident);
237 assert(s2);
238 Package *p = s2->isPackage();
239 if (p && p->isPackageMod())
240 {
241 pkg = p;
242 break;
243 }
244 }
245 else if ((pkg = s->isPackage()) != NULL)
246 break;
247 }
248 }
249
250 if (pkg)
251 {
252 if (pkg == mod->parent)
253 {
254 return true;
255 }
256 if (pkg->isPackageMod() == mod)
257 {
258 return true;
259 }
260 Dsymbol* ancestor = mod->parent;
261 for (; ancestor; ancestor = ancestor->parent)
262 {
263 if (ancestor == pkg)
264 {
265 return true;
266 }
267 }
268 }
269
270 return false;
271 }
272
273 /****************************************
274 * Determine if scope sc has protected level access to cd.
275 */
hasProtectedAccess(Scope * sc,Dsymbol * s)276 bool hasProtectedAccess(Scope *sc, Dsymbol *s)
277 {
278 if (ClassDeclaration *cd = s->isClassMember()) // also includes interfaces
279 {
280 for (Scope *scx = sc; scx; scx = scx->enclosing)
281 {
282 if (!scx->scopesym)
283 continue;
284 ClassDeclaration *cd2 = scx->scopesym->isClassDeclaration();
285 if (cd2 && cd->isBaseOf(cd2, NULL))
286 return true;
287 }
288 }
289 return sc->_module == s->getAccessModule();
290 }
291
292 /**********************************
293 * Determine if smember has access to private members of this declaration.
294 */
hasPrivateAccess(AggregateDeclaration * ad,Dsymbol * smember)295 bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember)
296 {
297 if (smember)
298 {
299 AggregateDeclaration *cd = NULL;
300 Dsymbol *smemberparent = smember->toParent();
301 if (smemberparent)
302 cd = smemberparent->isAggregateDeclaration();
303
304 if (ad == cd) // smember is a member of this class
305 {
306 return true; // so we get private access
307 }
308
309 // If both are members of the same module, grant access
310 while (1)
311 {
312 Dsymbol *sp = smember->toParent();
313 if (sp->isFuncDeclaration() && smember->isFuncDeclaration())
314 smember = sp;
315 else
316 break;
317 }
318 if (!cd && ad->toParent() == smember->toParent())
319 {
320 return true;
321 }
322 if (!cd && ad->getAccessModule() == smember->getAccessModule())
323 {
324 return true;
325 }
326 }
327 return false;
328 }
329
330 /****************************************
331 * Check access to d for expression e.d
332 * Returns true if the declaration is not accessible.
333 */
checkAccess(Loc loc,Scope * sc,Expression * e,Declaration * d)334 bool checkAccess(Loc loc, Scope *sc, Expression *e, Declaration *d)
335 {
336 if (sc->flags & SCOPEnoaccesscheck)
337 return false;
338
339 if (d->isUnitTestDeclaration())
340 {
341 // Unittests are always accessible.
342 return false;
343 }
344 if (!e)
345 {
346 if ((d->prot().kind == PROTprivate && d->getAccessModule() != sc->_module) ||
347 (d->prot().kind == PROTpackage && !hasPackageAccess(sc, d)))
348 {
349 error(loc, "%s %s is not accessible from module %s",
350 d->kind(), d->toPrettyChars(), sc->_module->toChars());
351 return true;
352 }
353 }
354 else if (e->type->ty == Tclass)
355 {
356 // Do access check
357 ClassDeclaration *cd = (ClassDeclaration *)(((TypeClass *)e->type)->sym);
358 if (e->op == TOKsuper)
359 {
360 ClassDeclaration *cd2 = sc->func->toParent()->isClassDeclaration();
361 if (cd2)
362 cd = cd2;
363 }
364 return checkAccess(cd, loc, sc, d);
365 }
366 else if (e->type->ty == Tstruct)
367 {
368 // Do access check
369 StructDeclaration *cd = (StructDeclaration *)(((TypeStruct *)e->type)->sym);
370 return checkAccess(cd, loc, sc, d);
371 }
372 return false;
373 }
374
375 /****************************************
376 * Check access to package/module `p` from scope `sc`.
377 *
378 * Params:
379 * loc = source location for issued error message
380 * sc = scope from which to access to a fully qualified package name
381 * p = the package/module to check access for
382 * Returns: true if the package is not accessible.
383 *
384 * Because a global symbol table tree is used for imported packages/modules,
385 * access to them needs to be checked based on the imports in the scope chain
386 * (see Bugzilla 313).
387 *
388 */
checkAccess(Loc loc,Scope * sc,Package * p)389 bool checkAccess(Loc loc, Scope *sc, Package *p)
390 {
391 if (sc->_module == p)
392 return false;
393 for (; sc; sc = sc->enclosing)
394 {
395 if (sc->scopesym && sc->scopesym->isPackageAccessible(p, Prot(PROTprivate)))
396 return false;
397 }
398 const char *name = p->toPrettyChars();
399 if (p->isPkgMod == PKGmodule || p->isModule())
400 deprecation(loc, "%s %s is not accessible here, perhaps add 'static import %s;'", p->kind(), name, name);
401 else
402 deprecation(loc, "%s %s is not accessible here", p->kind(), name);
403 return true;
404 }
405
406 /**
407 * Check whether symbols `s` is visible in `mod`.
408 *
409 * Params:
410 * mod = lookup origin
411 * s = symbol to check for visibility
412 * Returns: true if s is visible in mod
413 */
symbolIsVisible(Module * mod,Dsymbol * s)414 bool symbolIsVisible(Module *mod, Dsymbol *s)
415 {
416 // should sort overloads by ascending protection instead of iterating here
417 s = mostVisibleOverload(s);
418
419 switch (s->prot().kind)
420 {
421 case PROTundefined:
422 return true;
423 case PROTnone:
424 return false; // no access
425 case PROTprivate:
426 return s->getAccessModule() == mod;
427 case PROTpackage:
428 return s->getAccessModule() == mod || hasPackageAccess(mod, s);
429 case PROTprotected:
430 return s->getAccessModule() == mod;
431 case PROTpublic:
432 case PROTexport:
433 return true;
434 default:
435 assert(0);
436 }
437 }
438
439 /**
440 * Same as above, but determines the lookup module from symbols `origin`.
441 */
symbolIsVisible(Dsymbol * origin,Dsymbol * s)442 bool symbolIsVisible(Dsymbol *origin, Dsymbol *s)
443 {
444 return symbolIsVisible(origin->getAccessModule(), s);
445 }
446
447 /**
448 * Same as above but also checks for protected symbols visible from scope `sc`.
449 * Used for qualified name lookup.
450 *
451 * Params:
452 * sc = lookup scope
453 * s = symbol to check for visibility
454 * Returns: true if s is visible by origin
455 */
symbolIsVisible(Scope * sc,Dsymbol * s)456 bool symbolIsVisible(Scope *sc, Dsymbol *s)
457 {
458 s = mostVisibleOverload(s);
459
460 switch (s->prot().kind)
461 {
462 case PROTundefined:
463 return true;
464 case PROTnone:
465 return false; // no access
466 case PROTprivate:
467 return sc->_module == s->getAccessModule();
468 case PROTpackage:
469 return sc->_module == s->getAccessModule() || hasPackageAccess(sc->_module, s);
470 case PROTprotected:
471 return hasProtectedAccess(sc, s);
472 case PROTpublic:
473 case PROTexport:
474 return true;
475 default:
476 assert(0);
477 }
478 }
479
480 /**
481 * Use the most visible overload to check visibility. Later perform an access
482 * check on the resolved overload. This function is similar to overloadApply,
483 * but doesn't recurse nor resolve aliases because protection/visibility is an
484 * attribute of the alias not the aliasee.
485 */
mostVisibleOverload(Dsymbol * s)486 static Dsymbol *mostVisibleOverload(Dsymbol *s)
487 {
488 if (!s->isOverloadable())
489 return s;
490
491 Dsymbol *next = NULL;
492 Dsymbol *fstart = s;
493 Dsymbol *mostVisible = s;
494 for (; s; s = next)
495 {
496 // void func() {}
497 // private void func(int) {}
498 if (FuncDeclaration *fd = s->isFuncDeclaration())
499 next = fd->overnext;
500 // template temp(T) {}
501 // private template temp(T:int) {}
502 else if (TemplateDeclaration *td = s->isTemplateDeclaration())
503 next = td->overnext;
504 // alias common = mod1.func1;
505 // alias common = mod2.func2;
506 else if (FuncAliasDeclaration *fa = s->isFuncAliasDeclaration())
507 next = fa->overnext;
508 // alias common = mod1.templ1;
509 // alias common = mod2.templ2;
510 else if (OverDeclaration *od = s->isOverDeclaration())
511 next = od->overnext;
512 // alias name = sym;
513 // private void name(int) {}
514 else if (AliasDeclaration *ad = s->isAliasDeclaration())
515 {
516 if (!ad->isOverloadable())
517 {
518 //printf("Non overloadable Aliasee in overload list\n");
519 assert(0);
520 }
521 // Yet unresolved aliases store overloads in overnext.
522 if (ad->semanticRun < PASSsemanticdone)
523 next = ad->overnext;
524 else
525 {
526 /* This is a bit messy due to the complicated implementation of
527 * alias. Aliases aren't overloadable themselves, but if their
528 * Aliasee is overloadable they can be converted to an overloadable
529 * alias.
530 *
531 * This is done by replacing the Aliasee w/ FuncAliasDeclaration
532 * (for functions) or OverDeclaration (for templates) which are
533 * simply overloadable aliases w/ weird names.
534 *
535 * Usually aliases should not be resolved for visibility checking
536 * b/c public aliases to private symbols are public. But for the
537 * overloadable alias situation, the Alias (_ad_) has been moved
538 * into it's own Aliasee, leaving a shell that we peel away here.
539 */
540 Dsymbol *aliasee = ad->toAlias();
541 if (aliasee->isFuncAliasDeclaration() || aliasee->isOverDeclaration())
542 next = aliasee;
543 else
544 {
545 /* A simple alias can be at the end of a function or template overload chain.
546 * It can't have further overloads b/c it would have been
547 * converted to an overloadable alias.
548 */
549 if (ad->overnext)
550 {
551 //printf("Unresolved overload of alias\n");
552 assert(0);
553 }
554 break;
555 }
556 }
557
558 // handled by overloadApply for unknown reason
559 assert(next != ad); // should not alias itself
560 assert(next != fstart); // should not alias the overload list itself
561 }
562 else
563 break;
564
565 if (next && mostVisible->prot().isMoreRestrictiveThan(next->prot()))
566 mostVisible = next;
567 }
568 return mostVisible;
569 }
570