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