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
31bool hasPackageAccess(Scope *sc, Dsymbol *s);
32bool hasPackageAccess(Module *mod, Dsymbol *s);
33bool hasPrivateAccess(AggregateDeclaration *ad, Dsymbol *smember);
34bool isFriendOf(AggregateDeclaration *ad, AggregateDeclaration *cd);
35static Dsymbol *mostVisibleOverload(Dsymbol *s);
36
37/****************************************
38 * Return Prot access for Dsymbol smember in this declaration.
39 */
40Prot 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 */
97static 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 */
148bool 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 */
198bool 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 */
216bool hasPackageAccess(Scope *sc, Dsymbol *s)
217{
218    return hasPackageAccess(sc->_module, s);
219}
220
221bool 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 */
276bool 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 */
295bool 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 */
334bool 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 */
389bool 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 */
414bool 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 */
442bool 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 */
456bool 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 */
486static 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