2 * Enforce visibility contrains such as `public` and `private`.
4 * Specification: $(LINK2 https://dlang.org/spec/attribute.html#visibility_attributes, Visibility Attributes)
6 * Copyright: Copyright (C) 1999-2021 by The D Language Foundation, All Rights Reserved
7 * Authors: $(LINK2 http://www.digitalmars.com, Walter Bright)
8 * License: $(LINK2 http://www.boost.org/LICENSE_1_0.txt, Boost License 1.0)
9 * Source: $(LINK2 https://github.com/dlang/dmd/blob/master/src/dmd/access.d, _access.d)
10 * Documentation: https://dlang.org/phobos/dmd_access.html
11 * Coverage: https://codecov.io/gh/dlang/dmd/src/master/src/dmd/access.d
19 import dmd
.declaration
;
25 import dmd
.expression
;
31 private enum LOG
= false;
34 /*******************************
35 * Do access check for member of this class, this class being the
36 * type of the 'this' pointer used to access smember.
37 * Returns true if the member is not accessible.
39 bool checkAccess(AggregateDeclaration ad
, Loc loc
, Scope
* sc
, Dsymbol smember
)
43 printf("AggregateDeclaration::checkAccess() for %s.%s in function %s() in scope %s\n", ad
.toChars(), smember
.toChars(), f ? f
.toChars() : null, cdscope ? cdscope
.toChars() : null);
46 const p
= smember
.toParent();
47 if (p
&& p
.isTemplateInstance())
49 return false; // for backward compatibility
52 if (!symbolIsVisible(sc
, smember
))
54 // when in @safe code or with -preview=dip1000
55 if (sc
.flags
& SCOPE
.onlysafeaccess
)
57 // if there is a func. ask for it's opinion of safety, and if it considers the access @safe accept it.
58 if (sc
.func
&& !sc
.func
.setUnsafe())
62 ad
.error(loc
, "%s `%s` is not accessible%s", smember
.kind(), smember
.toChars(), (sc
.flags
& SCOPE
.onlysafeaccess
) ?
" from `@safe` code".ptr
: "".ptr
);
63 //printf("smember = %s %s, vis = %d, semanticRun = %d\n",
64 // smember.kind(), smember.toPrettyChars(), smember.visible() smember.semanticRun);
70 /****************************************
71 * Determine if scope sc has package level access to s.
73 private bool hasPackageAccess(Scope
* sc
, Dsymbol s
)
75 return hasPackageAccess(sc
._module
, s
);
78 private bool hasPackageAccess(Module mod
, Dsymbol s
)
82 printf("hasPackageAccess(s = '%s', mod = '%s', s.visibility.pkg = '%s')\n", s
.toChars(), mod
.toChars(), s
.visible().pkg ? s
.visible().pkg
.toChars() : "NULL");
86 pkg
= s
.visible().pkg
;
89 // no explicit package for visibility, inferring most qualified one
90 for (; s
; s
= s
.parent
)
92 if (auto m
= s
.isModule())
94 DsymbolTable dst
= Package
.resolve(m
.md ? m
.md
.packages
: null, null, null);
96 Dsymbol s2
= dst
.lookup(m
.ident
);
98 Package p
= s2
.isPackage();
99 if (p
&& p
.isPackageMod())
105 else if ((pkg
= s
.isPackage()) !is null)
112 printf("\tsymbol access binds to package '%s'\n", pkg
.toChars());
116 if (pkg
== mod
.parent
)
120 printf("\tsc is in permitted package for s\n");
124 if (pkg
.isPackageMod() == mod
)
128 printf("\ts is in same package.d module as sc\n");
132 Dsymbol ancestor
= mod
.parent
;
133 for (; ancestor
; ancestor
= ancestor
.parent
)
139 printf("\tsc is in permitted ancestor package for s\n");
147 printf("\tno package access\n");
152 /****************************************
153 * Determine if scope sc has protected level access to cd.
155 private bool hasProtectedAccess(Scope
*sc
, Dsymbol s
)
157 if (auto cd
= s
.isClassMember()) // also includes interfaces
159 for (auto scx
= sc
; scx
; scx
= scx
.enclosing
)
163 auto cd2
= scx
.scopesym
.isClassDeclaration();
164 if (cd2
&& cd
.isBaseOf(cd2
, null))
168 return sc
._module
== s
.getAccessModule();
171 /****************************************
172 * Check access to d for expression e.d
173 * Returns true if the declaration is not accessible.
175 bool checkAccess(Loc loc
, Scope
* sc
, Expression e
, Dsymbol d
)
177 if (sc
.flags
& SCOPE
.noaccesscheck
)
183 printf("checkAccess(%s . %s)\n", e
.toChars(), d
.toChars());
184 printf("\te.type = %s\n", e
.type
.toChars());
188 printf("checkAccess(%s)\n", d
.toPrettyChars());
191 if (d
.isUnitTestDeclaration())
193 // Unittests are always accessible.
200 if (auto tc
= e
.type
.isTypeClass())
203 ClassDeclaration cd
= tc
.sym
;
204 if (e
.op
== EXP
.super_
)
206 if (ClassDeclaration cd2
= sc
.func
.toParent().isClassDeclaration())
209 return checkAccess(cd
, loc
, sc
, d
);
211 else if (auto ts
= e
.type
.isTypeStruct())
214 StructDeclaration cd
= ts
.sym
;
215 return checkAccess(cd
, loc
, sc
, d
);
220 /****************************************
221 * Check access to package/module `p` from scope `sc`.
224 * sc = scope from which to access to a fully qualified package name
225 * p = the package/module to check access for
226 * Returns: true if the package is not accessible.
228 * Because a global symbol table tree is used for imported packages/modules,
229 * access to them needs to be checked based on the imports in the scope chain
230 * (see https://issues.dlang.org/show_bug.cgi?id=313).
233 bool checkAccess(Scope
* sc
, Package p
)
237 for (; sc
; sc
= sc
.enclosing
)
239 if (sc
.scopesym
&& sc
.scopesym
.isPackageAccessible(p
, Visibility(Visibility
.Kind
.private_
)))
247 * Check whether symbols `s` is visible in `mod`.
250 * mod = lookup origin
251 * s = symbol to check for visibility
252 * Returns: true if s is visible in mod
254 bool symbolIsVisible(Module mod
, Dsymbol s
)
256 // should sort overloads by ascending visibility instead of iterating here
257 s
= mostVisibleOverload(s
);
258 final switch (s
.visible().kind
)
260 case Visibility
.Kind
.undefined
: return true;
261 case Visibility
.Kind
.none
: return false; // no access
262 case Visibility
.Kind
.private_
: return s
.getAccessModule() == mod
;
263 case Visibility
.Kind
.package_
: return s
.getAccessModule() == mod ||
hasPackageAccess(mod
, s
);
264 case Visibility
.Kind
.protected_
: return s
.getAccessModule() == mod
;
265 case Visibility
.Kind
.public_
, Visibility
.Kind
.export_
: return true;
270 * Same as above, but determines the lookup module from symbols `origin`.
272 bool symbolIsVisible(Dsymbol origin
, Dsymbol s
)
274 return symbolIsVisible(origin
.getAccessModule(), s
);
278 * Same as above but also checks for protected symbols visible from scope `sc`.
279 * Used for qualified name lookup.
283 * s = symbol to check for visibility
284 * Returns: true if s is visible by origin
286 bool symbolIsVisible(Scope
*sc
, Dsymbol s
)
288 s
= mostVisibleOverload(s
);
289 return checkSymbolAccess(sc
, s
);
293 * Check if a symbol is visible from a given scope without taking
294 * into account the most visible overload.
298 * s = symbol to check for visibility
299 * Returns: true if s is visible by origin
301 bool checkSymbolAccess(Scope
*sc
, Dsymbol s
)
303 final switch (s
.visible().kind
)
305 case Visibility
.Kind
.undefined
: return true;
306 case Visibility
.Kind
.none
: return false; // no access
307 case Visibility
.Kind
.private_
: return sc
._module
== s
.getAccessModule();
308 case Visibility
.Kind
.package_
: return sc
._module
== s
.getAccessModule() ||
hasPackageAccess(sc
._module
, s
);
309 case Visibility
.Kind
.protected_
: return hasProtectedAccess(sc
, s
);
310 case Visibility
.Kind
.public_
, Visibility
.Kind
.export_
: return true;
315 * Use the most visible overload to check visibility. Later perform an access
316 * check on the resolved overload. This function is similar to overloadApply,
317 * but doesn't recurse nor resolve aliases because visibility is an
318 * attribute of the alias not the aliasee.
320 public Dsymbol
mostVisibleOverload(Dsymbol s
, Module mod
= null)
322 if (!s
.isOverloadable())
325 Dsymbol next
, fstart
= s
, mostVisible
= s
;
329 // private void func(int) {}
330 if (auto fd
= s
.isFuncDeclaration())
332 // template temp(T) {}
333 // private template temp(T:int) {}
334 else if (auto td
= s
.isTemplateDeclaration())
336 // alias common = mod1.func1;
337 // alias common = mod2.func2;
338 else if (auto fa
= s
.isFuncAliasDeclaration())
340 // alias common = mod1.templ1;
341 // alias common = mod2.templ2;
342 else if (auto od
= s
.isOverDeclaration())
345 // private void name(int) {}
346 else if (auto ad
= s
.isAliasDeclaration())
348 assert(ad
.isOverloadable || ad
.type
&& ad
.type
.ty
== Terror
,
349 "Non overloadable Aliasee in overload list");
350 // Yet unresolved aliases store overloads in overnext.
351 if (ad
.semanticRun
< PASS
.semanticdone
)
355 /* This is a bit messy due to the complicated implementation of
356 * alias. Aliases aren't overloadable themselves, but if their
357 * Aliasee is overloadable they can be converted to an overloadable
360 * This is done by replacing the Aliasee w/ FuncAliasDeclaration
361 * (for functions) or OverDeclaration (for templates) which are
362 * simply overloadable aliases w/ weird names.
364 * Usually aliases should not be resolved for visibility checking
365 * b/c public aliases to private symbols are public. But for the
366 * overloadable alias situation, the Alias (_ad_) has been moved
367 * into its own Aliasee, leaving a shell that we peel away here.
369 auto aliasee
= ad
.toAlias();
370 if (aliasee
.isFuncAliasDeclaration || aliasee
.isOverDeclaration
)
374 /* A simple alias can be at the end of a function or template overload chain.
375 * It can't have further overloads b/c it would have been
376 * converted to an overloadable alias.
378 assert(ad
.overnext
is null, "Unresolved overload of alias");
382 // handled by dmd.func.overloadApply for unknown reason
383 assert(next
!is ad
); // should not alias itself
384 assert(next
!is fstart
); // should not alias the overload list itself
390 * Return the "effective" visibility attribute of a symbol when accessed in a module.
391 * The effective visibility attribute is the same as the regular visibility attribute,
392 * except package() is "private" if the module is outside the package;
393 * otherwise, "public".
395 static Visibility
visibilitySeenFromModule(Dsymbol d
, Module mod
= null)
397 Visibility vis
= d
.visible();
398 if (mod
&& vis
.kind
== Visibility
.Kind
.package_
)
400 return hasPackageAccess(mod
, d
) ?
Visibility(Visibility
.Kind
.public_
) : Visibility(Visibility
.Kind
.private_
);
406 visibilitySeenFromModule(mostVisible
, mod
) < visibilitySeenFromModule(next
, mod
))