-
Notifications
You must be signed in to change notification settings - Fork 1.7k
Add a hint: "@internal" annotation #28066
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Comments
According to https://www.dartlang.org/tools/pub/package-layout#implementation-files, everything inside the 'src' directory is already suppose to be treated as internal implementation detail. Would it be sufficient to have a lint rule that flagged uses of code inside 'src' from anywhere outside the defining package? |
Not really, because you actually would want to export public classes. For example: // Assume this is lib/my_package.dart
// OK!
export 'src/internal.dart' show PublicInterfaceIWantToExpose;
// Oops!
export 'src/internal.dart' show ScaryThingIMeantToKeepPrivate; Lots of the SDK/core packages work this way |
The lint rule I was proposing would only create a lint when a library in another package's 'src' directory is being imported. It wouldn't check for uses of re-exported classes. |
The |
I like this annotation. In addition to the original end goal:
the analyzer should warn if an |
As far as your original goal though, you don't need any concept of whether two libraries are part of the same package (A), you just need the public API concept (B). Maybe this can be simply defined: File paths that match
I think this is better than looking at how libraries were imported. I'm pretty sure relative import paths could skirt any enforcement at that level. Import tracking would also require following imports and exports all the way up to an element's definition. |
How do you handle a library in lib exporting a library in lib/src? I'd rather go for defining libraries being in "the same package" as having URIs starting with This is not a language semantics issue, just an optional analyzer hint, so there is no harm in understanding pub packages - it's tooling, just like pub itself. |
But in what sense does a library "have a URI"? For example, if the analyzer is examining /Users/me/code/test/foo_test.dart (with a package root of /Users/me/code), what URI does it "have"? If that file has some imports: import 'test_helper.dart';
import '../lib/src/foo.dart'; What URIs do those "have"? |
Inside Dart, all libraries have one exact URL, all distinct (because if they are the same, it's the same library). This is well defined, and hasn't changed in a long time. You can import the same file twice, using different URLs, and it will be considered two different libraries (introducing different classes and having separate global states), which is why you should avoid doing that. Imports with relative paths are resolved against the library URL. So, import the |
IMO we should remove this. Made sense for Dartium, doesn't make sense anymore. |
@lrhn Thanks this is a good rigid definition. But this means that most packages are written so that none of their libraries are in the same "package." E.g. in the collection package, export "src/algorithms.dart";
export "src/canonicalized_map.dart"; So if I have a file like: import "package:collection/collection.dart"; then there is:
So none of them are in the same package by your proposed definition |
Since import and export resolution is relative to the URI of the importing library, the That's not new, it's how it has always worked. It's also why I always use relative URIs inside the lib directory, but never in or out of it. If you get into a situation where two files in a the same Dart package (in the |
I see, my bad. This looks good then. Probably a valid, concise definition of package that we can work with. Thanks @lrhn ! |
Warning about library imports form other packages |
@zoechi Yeah we might be able to use the linter rule's logic about whether two URIs are from the same package, and whether a URI is private implementation. The linter's rule just asks if the first path segment is "src" though, which doesn't cover relative imports. |
It's worth noting that if we get |
I'm not opposed to the idea, I just don't have cycles for it right now. But I will suggest that "internal" is rather general, and perhaps something more specific (like "notForExport") might be better. Also, is it more common to want classes in |
@bwilkerson asked me to write up a proposal for the semantics of
In addition, Dartdoc should ignore any internal APIs. |
Consider naming it Also consider making it a warning if an instance member or instance member parameter is marked internal if it overrides a non-internal member or parameter (that is, the API feature is already public in the super-class, so marking it internal will not prevent its use, and is likely an error) |
I'd be okay with |
Friendly ping! We're getting more and more users itching to use the Dart Sass AST, and this is still a blocking issue. |
I strongly believe that users should not be required to annotate code in Also, I don't understand the purpose of allowing members of a class to be marked as being private when the class is public. |
I agree that for larger packages there is likely more internal than external code, for smaller packages and utility packages I think that reverses. Even given the noise in the small package case I would prefer to positively annotate the stuff that I intend to be exported than to negatively annotate the stuff that is meant to be private. My reasoning is that if I forget to mark with
+1 - I find it harder to work with classes that have a different interaction within a package than outside of it, library level privacy has always been enough for me. Though the workarounds I've seen people use (like unsafe casts to |
I strongly disagree with this. It's a very common pattern, explicitly encouraged by the pub documentation, for packages intended for user consumption to define essentially all of their code beneath What's more, if we were to define all code defined under
Sass has a number of class hierarchies that we want to both use internally and expose for external consumption: the ASTs for Sass and CSS, and the values that can be assigned to variables and used in CSS declarations. Both of these have some members that we don't want to support externally, such as We could theoretically create separate public interfaces and private implementation classes for all of these, but that's a huge maintenance burden. Doing that for the nine value classes was and continues to be a pain; doing it for the eighty-six AST classes just so we can hide a handful of members is too much. |
I think it's clear at this point we should schedule something (offline) to discuss. I recommend someone from the analyzer team, language team, and potentially @srawlins. Thanks! |
As a major stakeholder for this feature, I'd like to be involved in discussions of it. |
Sure, did not mean to exclude, I just mean the potential implementors need to meet up. |
How would this be breaking? My understanding is we're only talking about static analysis which would be opt-in. I think some of the disagreement in this thread is stemming from the fact that we're talking about different requests. Could we maybe rescope this thread to the original request and file a new issue for adding new privacy modifiers including package private? As a reminder the original request was: export 'package:my_package/src/internal.dart`
// ^ WARNING: Exporting symbol `KeyValuePair`, which is marked `@internal`. If we scope this to warnings for package authors then this is not breaking for any consumer of any package. |
The original proposed specification says that the
These are effectively the semantics I want for package-private members. I'm worried that if we consider these two requests as orthogonal, we'll end up with two annotations with (at least) confusingly similar descriptions. Put another way, both Matan and I want to express the idea "this thing should only be used in this package" to tools. We're interested in different ways this idea could be surfaced to different users, but the idea is the same. |
Re-upping. I'd like a hypothetical
/cc @nex3 @srawlins @davidmorgan for thoughts. Oops, I originally missed #28066 (comment) by @nex3 - I think this covers all the cases I'm concerned about, and would greatly help in creating the API for a large framework like Angular. |
I'd prefer a warning to a hint for external uses of an internal member--I've been in situations before where users within Google have started using private API surfaces that weren't enforced as private and I've had been on the hook to fix their code when the API changed. I'm not sure if there's latitude for the analyzer to decide something's a warning, though, and I could live with a hint. |
I think semantically the team has been resistant to adding warnings or errors that are entirely metadata driven, and I think that is mostly the right tradeoff. In practice users externally have been all over the place, but internally we can easily treat any specific hint as fatal (we do for many, if not all, hints). |
Piling on again, since I just hit this. Here's my "vision" from my closed issue: Used to annotate a declaration in a Tools, such as the analyzer, can provide feedback if
|
I'm super excited about the ability for this annotation to enable cleanup of public, but unexposed members. Right now, there is no easy way to find and delete |
@kevmoo Some of what you specified isn't feasible because it requires global analysis. For example:
Providing feedback like this would require that when we find an annotated declaration we then look at all of the public libraries to see whether any of them contain an export of the annotated declaration. Is that what you were attempting to describe, or do you actually want the opposite (that we provide feedback is we find an export of an annotated declaration)? |
I feel like we're saying the same thing. imagine |
Ok. My confusion came from the way it was worded. The first bullet item clearly means "produce a hint at the point of declaration". The similarity of the sentence structure led me to conclude that the second bullet point meant the same thing. (Probably too much time spent reading the spec. :-)) Producing a hint at the point at which the item is exported is both feasible and sensible. |
To reiterate, the behavior @kevmoo outlines in #28066 (comment) is not sufficient for Sass's needs. We also want the analyzer to provide feedback if a member of a type marked as // package:sass/sass.dart
abstract class CssNode extends AstNode {
@internal
bool get isGroupEnd;
// ...
}
// myapp/sass_processor.dart
import 'package:sass/sass.dart';
void main() {
CssNode node = /* ... */;
print(node.isGroupEnd); // Prints a warning.
} |
The meta package does not yet have an `internal` constant; this CL just adds one to the mock packages for testing. Two checks are implemented: * Hint if an @internal annotation is found on an element which is already part of a package's public API (based on file path). * Hint if an element annotated with @internal is exported from a package's public API. The notion of "public API" is also implemented for each type of Package: BasicPackage, BazelPackage, GnPackage, PackageBuildPackage, and PubPackage. Bug: #28066 Change-Id: Ifc2709028afcd241f59e802f5952539f717704c3 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/163126 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
Bug: #28066 Change-Id: Iee39fee48340fb9311a8f7a07d1ae7aa474a22f4 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/163961 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
…r ctor call Bug: #28066 Change-Id: Ifd7a54e590699de02d0db80d5aa38a3a0b2bce73 Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/164248 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Samuel Rawlins <[email protected]>
Proposed specification:
I'm finding this increasingly necessary when working on large packages.
For example, assume I have the following in
lib/src/internal.dart
:And later in my application I write the following in
lib/my_package.dart
:The text was updated successfully, but these errors were encountered: