dart-lang / linter

Linter for Dart.
https://dart.dev/tools/linter-rules
BSD 3-Clause "New" or "Revised" License
635 stars 170 forks source link

Lint for undocumented exceptions. #2246

Open bsutton opened 4 years ago

bsutton commented 4 years ago

The dart language supports exceptions but not checked exceptions.

The lack of check exceptions means that developer must rely on documentation to determine what exceptions a method will throw.

If you look at a significant portions of code published on pub.dev, developers are simply not documenting the exceptions that are thrown. Documenting exceptions is particularly error prone as it requires the developer to explore each called api to determine the set of exceptions the api throws.

Of course given that most api's are not documenting their exceptions the result is that a developer must examine thousands of lines of code to determine what exceptions are being thrown. This of course results in the developer not documenting their own api as its simply too much effort and worse they simply are not doing the necessary work to handle exceptions appropriately. The lack of documentation then cascades up into the next level of code and so on.

The end result is that much of the code published on pub.dev is of questionable quality.

This problem could largely be resolved by providing a lint rule for pedantic that warns if an api throws an exception.

By adding this link to pedantic we would see a very rapid improvement on the quality of code on pub.dev for the following reasons. 1) as a user of a pub.dev package I can see what exceptions are being thrown and handle them correctly. 2) as a publisher on pub.dev its now easy to document my exceptions as the linter generates a list of thrown exceptions. 3) if I make a code change which changes the set of thrown exceptions the linter will notify my to fix the doco. 4) ide's can be modified to provide a 'quick fix' for this new lint rule which will make keeping doco up to date simple. 5) with exceptions now documented developers will be force to actually consider what exceptions are going to be thrown and take appropriate action rather than just ignoring them as happens now. 6) when third party projects change the set of thrown exceptions (as happened recently with firebase) I get a lint rather than having to wait for a runtime exception.

Essentially we end up with a cascade affect and everyone benefits.

Here is an example of how I would see the workflow.

/// Throws NotARealException
void dowork()
{
   actuallyDoTheWork();
}

void actuallyDoTheWork()
{
   throws PosixException();
}

Linter:

The method dowork throws an non-existant exception NotARealException. Please remove.
The method actuallyDoTheWork throws PosixException. Please document the throws statement.

Once I correct the above lints I should then see:

void dowork()
{
   actuallyDoTheWork();
}

/// Throws PosixException when something goes wrong.
void actuallyDoTheWork()
{
   throws PosixException();
}

Lint error:

The method dowork throws PosixException. Please document the throws statement.

I see two alternatives for how the linter inspects the code.

1) the linter just looks at the body of a method for any direct exceptions and the documentation of all invoked methods (as per the above example) 2) the linter looks at the body of the method and the body of any direct api calls.

The 2nd alternative would see a much faster improvement in code on pub.dev as it would: 1) allow users of a library to at least see any 1st level exceptions third party packages throw 2) would encourage developers to raise issues against the third party project to improve their exception documentation.

The result is that the lint would essentially cascade both 'up' into the developers own code and 'down' into third party projects resulting in an improvement in quality of all dart code.

This lint looks (in my ignorant opinion) fairly easy to implement.

The benefits to the community however will be huge.

bwilkerson commented 4 years ago

Thanks for the suggestion.

If I remember correctly, the original rationale for not supporting checked exceptions was a belief that having any unchecked exceptions effectively negates the value of supporting checked exceptions, and the assumption that unchecked exceptions were unavoidable. I tend to agree, but I mention it not to start a discussion about the value of checked exceptions but because I believe a similar principle is at work here and should be considered.

If we did implement a lint for this, then the lint would only be effective if all of the packages that a given package depends on have also enabled the lint. Packages whose authors did not choose to enable the lint have effectively turned all exceptions into unchecked exceptions. The fact that it requires wide-spread adoption of the lint to give the lint value really does significantly diminish the value of having the lint.

I suspect that you realize that and that that's why you suggested adding the lint to pedantic: as a forcing function for adoption. Unfortunately, adding it to pedantic isn't a trivial process. The pedantic rule set is the set of lints that are used by internal developers at Google. In order to add a lint to that set we'd first need to convince internal developers to adopt the lint, and that doesn't seem like a likely event. So, I don't think we can force adoption that way. Adoption would need to happen more organically.

In the absence of widespread demand for the lint, I don't see much value in adding it. I'll leave this issue open for now so that community members can voice their opinions; if there's enough demand then we'd certainly consider adding it.

bsutton commented 4 years ago

My first statement would be that, regardless of the checked vs unchecked debate, documentation on what exceptions an api throws would help improve code quality and make the life of many developers easier.

`If we did implement a lint for this, then the lint would only be effective if all of the packages that a given package depends'

I wouldn't agree with this statement.

Whilst the mechanism would certainly work better if dependencies implemented it, the lint would provide intrinsic value without requiring other packages to use it.

If we could implement my preferred recommendation (look inside the first level of called code) then we get a certain level of documentation being 'pulled' from the dependencies even if the package doesn't implement the lint.

As a package developer who wants to provide quality documentation the lint would make the process of documenting exceptions easier. I would suggest that the core reason exceptions are not currently documented is because it's hard to do so. This lint would essentially automate the hardest piece of documenting exceptions (discovering what exceptions are thrown).

Whilst the inclusion in pedantic would be ideal I understand that it might be difficult to get the google team to adopt it internally.

This however doesn't undermine the intrinsic value of the lint and I would expect over time that anyone interested in generating quality code would adopt the lint.

Discovering what exceptions are thrown is a constant friction point when coding in dart so I believe that developers would be happy to adopt it as it solves a daily problem with their own libraries.

We could also encourage the adoptions by adding it to effective dart.

The core tenant in the lint system is that it should improve code quality, I would suggest that this lint will have a significant impact on code quality.

I would ask that you reconsider this lint.

On Wed, 23 Sep 2020 at 01:31, Brian Wilkerson notifications@github.com wrote:

Thanks for the suggestion.

If I remember correctly, the original rationale for not supporting checked exceptions was a belief that having any unchecked exceptions effectively negates the value of supporting checked exceptions, and the assumption that unchecked exceptions were unavoidable. I tend to agree, but I mention it not to start a discussion about the value of checked exceptions but because I believe a similar principle is at work here and should be considered.

If we did implement a lint for this, then the lint would only be effective if all of the packages that a given package depends on have also enabled the lint. Packages whose authors did not choose to enable the lint have effectively turned all exceptions into unchecked exceptions. The fact that it requires wide-spread adoption of the lint to give the lint value really does significantly diminish the value of having the lint.

I suspect that you realize that and that that's why you suggested adding the lint to pedantic: as a forcing function for adoption. Unfortunately, adding it to pedantic isn't a trivial process. The pedantic rule set is the set of lints that are used by internal developers at Google. In order to add a lint to that set we'd first need to convince internal developers to adopt the lint, and that doesn't seem like a likely event. So, I don't think we can force adoption that way. Adoption would need to happen more organically.

In the absence of widespread demand for the lint, I don't see much value in adding it. I'll leave this issue open for now so that community members can voice their opinions; if there's enough demand then we'd certainly consider adding it.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/dart-lang/linter/issues/2246#issuecomment-696798219, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAG32OEZHTQFAJSC3DR3BGTSHC7OVANCNFSM4RVC6GPQ .

pq commented 4 years ago

Thanks for the thoughtful conversation!

I see where Brian is going with the potential limitations if such an analysis were not universally enforced but I'm pretty compelled by @bsutton's observations and the general motivation. If nothing else, it seems like this could improve package documentation.

I'm in favor.

👍

lrhn commented 4 years ago

If the analyzer can actually analyze which methods throw which exceptions, then it would be possible to only warn about undocumented exceptions on API boundaries.

I wouldn't worry about code that is internal to a package, only documentation on API boundaries is really important. Otherwise we force people to add documentation to internal implementation details, where the author is the only one using it anyway. That rabbit hole is what makes people dislike Java's checked exceptions (or to catch and rethrow as unchecked to make it through your helper functions, then maybe recatch and rethrow as checked again at the API boundary). Alternatively, have a second lint for internal consistency as well, for people who want to document all their internal functions.

The big problem here is obviously correctly detecting which exceptions can be thrown. Static functions are probably reasonably easy, but virtual methods are harder. With the third item above, it might be sufficient to look at the actual interface documentation. It's too much of a false-positive generator to claim that an interface method throws an exception just because one subclass somewhere does, that one class might just be for testing and won't ever affect the real program.

This can work for async functions too, but it's going to be hard to analyze direct uses of Future.error.

It would require analyzing the entire program, to follow call chains to the end, not just looking at signatures and documentation of what you are calling. If that's too much, then it's true that it only works for exceptions that are already documented as throwing.

bsutton commented 4 years ago

I would want to document internal method but can understand that might not be everyone's preference (but I still get to think they are wrong:)

OlegAlexander commented 4 years ago

Perhaps the lint for internal functions can be optional for those who want it. But the lint for public APIs should arguably be on by default.

natebosch commented 3 years ago

Overall I do expect this would be infeasible to handle for a sufficient number of cases as to make it trustworthy.

Adding more in depth structure to DartDoc would be a departure from our current idioms and user expectations. This is still pretty readable, but I would strongly oppose any solutions which lead us towards @throws or something similar. https://dart.dev/guides/language/effective-dart/documentation#do-use-prose-to-explain-parameters-return-values-and-exceptions

The transitive behavior here has potential implications for evolving packages in the ecosystem - the dependencies present when we analyze a package to produce the lints may not match the dependencies present for a downstream users. This pushes towards a place where we need to model any change in exception as a major version bump (which one could certainly argue is a good thing). As a consequence we could have higher ecosystem churn - if one of my dependencies changes a thrown exception, even if that doesn't impact me directly, I can't have a wide constraint on that dependency, because my documentation is forcibly tied to a single major version of my dependency's documentation.

If we do decide to pursue this, we should treat subtypes of Error as implementation details. It shouldn't matter what error I throw, and callers should never catch it. I would be strongly opposed to a model where change from one error to another needs to be a major version bump.

bsutton commented 3 years ago

@natebosch thats for the response

If a method is documented as /// Throws FooException if the Bar is not initialized;, but I know I'm initializing Bar before the call, how do I prove to the analyzer that I don't need to document FooException as an exception my method might throw?

It would seem that this scenario could be handled via the existing 'ignore lint' mechanism. I don't know if you can pass an argument to the ignore mechanism but this sort of concept might work:

void a(int b)
{
    if (b == null) throw Bad;
}

void c()
{
   // ignore:  undocumented_throws Bad
    a(1);
}

As to the prose I supposed the following might work:

/// If you don't pass [b] then we will throw [Bad].
void a(int b)
{
    if (b == null) throw Bad;
}

Mentioning the exception by name in square brackets could be considered sufficient for the linter to be happy.

The transitive behavior here has potential implications for evolving packages in the ecosystem

I agree that this is needs some thought.

any change in exception as a major version bump (which one could certainly argue is a good thing).

This very issue is what brought me here. Firebase made a breaking change as to what exceptions were being thrown and I had no idea that I had a problem. Fortunately the code didn't make it into production.

As a consequence we could have higher ecosystem churn - if one of my dependencies changes a thrown exception, even if that doesn't impact me directly,

This is really no different to a change to an api that you aren't using.

I doubt that exceptions change any more frequently than apis (in fact I suspect they change less often) as such I wouldn't expect this to create any great amount of churn and the churn it does create will be valid and necessary.

we should treat subtypes of Error as implementation details.

I think I agree with this. If users felt otherwise it could be implemented as an alternate lint.

bsutton commented 3 years ago

I think we should also allow documentation to target a base exception class.


class BaseException implements Exception {}

class DerivedException extends BaseException {}

/// There is no escaping the fact that we throw [BaseException]
void main()
{
throws DerivedException();
}

I would consider this lazy documentation but we probably need to cater for everyone.

We could have a lint that controls whether this is allowed or explicit declarations of each exception is required.

Explicit declarations of every exception can become very noisy.

safield commented 3 years ago

This is a pain point for me. Not sure what the best solution is for dart, but I personally am missing my checked exceptions I am used to.

rubenferreira97 commented 3 years ago

After some Flutter and Dart experimenting I can feel empathy with this issue, caused mostly by poor documentation. I miss checked exceptions, however I know they don't belong to dart.

Given that I would like to ask if tracing exceptions by analyzing the throw keyword is feasible in Dart Lint or Dart itself.

Example:

We know that functionThatMightThrow throws SomeException

// Linter (Information): This function throws the following exceptions: SomeException.
void functionThatMightThrow()
{
   if(someErrorOccurred) throws SomeException;
}

We know that functionThatThrows throws SomeException and SomeException2 (since this function itself do not catch SomeException)

// Linter (Information): This function throws the following exceptions: SomeException, SomeException2.
// Linter (Warning): Unhandled exceptions on line functionThatMightThrow: SomeException (Will rethrow)
void functionThatThrows()
{
   functionThatMightThrow(); 
   throws SomeException2;
}
//Linter (Warning): Unhandled exceptions on line functionThatThrows: SomeException, SomeException2 (Will rethrow)
void doStuff()
{
   functionThatThrows();
}

This might be an optional rule. Since Dart seems to differentiate between Exceptions and Errors maybe take that into account and only check for throws Exceptions . Exceptions normally need to be catch as they represent run-time problems.

This could be a warning or just an informative list of exceptions that each function throws, like mouse hovering a function and giving the list.

I don't know if this could be ever be achieved however I think that this issue really needs some care or else what will happen is: programmers will not handle exceptions for a foolish reason, they don't know which exceptions to catch. Even if we might not know why those exceptions are being throw, since that's the documentation role, at least we are aware that the program might throw X, Y, Z exception.

LukasPoque commented 3 years ago

Any updates on this topic? Would love to see something like this as lint rule.

Levi-Lesches commented 3 years ago

I think with the rules from @lrhn's comment and the ability to use an // ignore comment, this lint would be perfect.

xuanswe commented 2 years ago

Problem

Expectations

Dart tool should answer these 2 questions with the least and acceptable effort in both create-site and use-site. Ideally, I would like to not having any changes at create-site and only minimal changes per project at use-site. Performance should be considered too in case of deep transitive dependencies (both project internal and external).

Solutions?

Honestly, I don't have any solution in mind yet. I also see that all proposed solutions above don't satisfy my expectations. For now, I just want to share my summary about the original problem and continue to think about ideas to satisfy the expectations.

subzero911 commented 2 years ago

It's better to use annotations for that. Annotations are more native Dart way to add some metadata for linter, than comments.

@throws(BadRequestException(), FatalError(), SomethingWentWrongExeption())
abstract class TimeApiService {
  Future<TimeResponse> getTime();
}
bsutton commented 2 years ago

@subzero911 The advantage of doing it in the comments is that a lot of libraries already document the exceptions in the comments so we get to leverage that rather than having to duplicate the existing comment.

Example from Version:

  /// Returns the primary version out of [versions].
  ///
  /// This is the highest-numbered stable (non-prerelease) version. If there
  /// are no stable versions, it's just the highest-numbered version.
  ///
  /// If [versions] is empty, throws a [StateError].
xuanswe commented 2 years ago

Regardless using annotation or documentation, we are converting all runtime exceptions to the problem of checked exceptions, which force developers to add something that should be done by compiler or language tools. It only changes from method declaration (in Java) to other places (annotation or documentation). Doesn't make sense to me :-).

To me, compiler or language tools have the responsibility to tell me:

All these jobs should not be on developers' shoulders :-)

rubenferreira97 commented 2 years ago

It seems you are mixing lint errors with compiler errors. The developer does not need to add something in order to compile. The only way to tell the developer that he might be forgetting something, well it's telling him "Hey this is a lint error, not a obligatory one, it might be worth to have a look".

Regarding your last sentence, developers should (and IMO must) have documentation on their shoulders. Developers also should check for errors if they are calling another person's code with a respective API. You shouldn't skip their instructions (documentation).

xuanswe commented 2 years ago

@rubenferreira97 sorry for confusion, I think I need to clarify that I am talking only on the use-site (including transitive call chain).

I totally agree that, we need a rule to show message (of course, configurable levels: ignore/info/warn/error) to add documentation of possible exceptions and removing impossible exceptions. But only at the method, where the throws keyword is found directly inside it.

I disagree to force documentation anywhere, which indirectly throws an exception. Because if a method throws an exception indirectly, this method is considered use-site. The use-site developer doesn't have responsibility to document the exception. He has the right to know about it but ignore it. "Ignore" means "rethrow automatically" like what we are doing now.

xuanswe commented 2 years ago

Now, even if we have that rule for the method, that directly throws an exception, we should never believe that the documentation about exceptions provided by a package is the source of truth. The main reason is that, the package could ignore the rule or even doing things wrongly (you know human make mistakes).

If documentation is not the source of truth, dart needs to gather this information using the source code as the truth and merge the description of the valid exception found in the package documentation. It will lead to performance problem. I am not sure if we can have any solution for the performance issue here. Do you think if indexing could solve this issue? Dart should only re-index for the part, which is detected there are changes. The indexing parts should be prioritized with an optimal ordering, ex. internal methods before methods on dependencies, the deeper dependency should be indexed after the nearer one. The indexing of any part could be available immediately even the whole indexing is not yet completed.

For the packages on pub.dev, the index file could be generated by pub.dev, so localhost can ignore indexing for the parts found from a trusted place like pub.dev.

Levi-Lesches commented 2 years ago

Now, even if we have that rule for the method, that directly throws an exception, we should never believe that the documentation about exceptions provided by a package is the source of truth.

I disagree with that. Two points often brought up around this topic are:

Both of these are perfectly valid since while the compiler may know what could happen, the programmer (usually) knows what will happen, and why it's happening, and what the real behavior should look like. If you want to ignore that, that's fine, but I wouldn't say that's the popular opinion. After all, Java is one of the only mainstream languages with checked exceptions and the rest of them seem to work just fine.


I disagree to force documentation anywhere, which indirectly throws an exception. Because if a method throws an exception indirectly, this method is considered use-site.

Maybe I'm not following here, but this is where you kind of lose me. Say I have this:

E a<E>(List<E> values, int index) => values[index];
int b(int index) => a([1, 2 ,3], index);
void main() => b(4);

As you can see, this will throw an IndexError at runtime. Question is, where does the documentation go? At first, b was a call site so it's exempt from documenting it. But when you look at it from main's perspective, it's using b which can potentially be unsafe, but b has no documentation. Assume each function is in a separate package with different authors -- who sees what warnings? Here's at least how I'd like to see it:

Now, main will see that it needs to call b with a number smaller than 3, whichever way b decides to handle it. That's the goal, isn't it?

xuanswe commented 2 years ago

Basically, I see that we all don't have controversial on direct exceptions. The controversial is on transitive exceptions.

For transitive exceptions, I think this way:

So the final result is that, dart will show the truth (real call chain from source code) plus the additional documentation found from the call chain.

2 reasons why I couldn't consider documentation is the source of truth:

Could you please prove that I am wrong with the 2 reasons above? I am happy if someone can prove that, then the community could go on the easier path.

Java initially wants to solve this issue with checked exceptions by forcing declaring or handling "transitive" checked exceptions. And it doesn't work well, that's why modern languages learned the lesson and avoid the similar problem. Moving the place of the rule from method declaration to documentation/annotation for transitive exceptions is not the solution.

lrhn commented 2 years ago

To me, it's very important to distinguish errors and exceptions. Errors are like Java's RuntimeException and are unchecked. Checking them programmatically is a complete waste of time, because they won't happen in well written code (and non-well written code will, well, throw to signal that, and take potentially down the entire program, and that's fine).

And, also, all code can throw errors, it just might be StackOverflowError or OutOfMemoryError. Or it may be an IndexError if you use an unvalidated index into a list. The mistake was doing that to begin with, not checking against length first. It wasn't a mistake to not catch that error.

We don't generally document errors being thrown, we instead document restrictions that must be checked to avoid that error, like "the index must be non-negative and less than length". That's what the caller must ensure. It's an error if they forget.

Exceptions are where the caller is expected to catch and handle the exception. I want exceptions to be documented, at least on the public API of a library/package. What it does internally is an implementation detail, but you can obviously document there too for your own convenience.

The way we've chosen to document exceptions in the platform libraries (decided that around the 1.0 release, some code will predate that) is to have a DartDoc paragraph starting with "Throws":

///
/// Throws [SomeException] ... if something, when something, unless something,
/// meaning something. Maybe suggested responses.

(And one of the reasons we try to avoid saying "Throws [ArgumentError] if argument is bad.", is to save that syntax for exceptions. That's why errors are just written as "Argument must not be bad". Also because we don't consider changing which error is thrown a breaking change.)

Levi-Lesches commented 2 years ago

Yep, I like that there's that distinction. My hope is that such a lint will make people think not just about what errors they haven't documented, but also about how they can handle it themselves. I tried to show it a bit in my example too, where for each step I either documented it or added in if statements to catch possible errors and document that behavior instead.

Ap3lsin4k commented 1 year ago

Adding to @lrhn comment:

Language Exception Terminology Terminology for Uncaught Errors
Dart/Python Exception Error
Java Checked exceptions Unchecked errors
Go Error Panic
Rust Result Panic

I wanted to express my support for the idea of implementing a lint for undocumented exceptions. Properly documenting exceptions in code is crucial for improving code clarity, and maintainability, and reducing potential issues during development.

By enforcing the documentation of exceptions and drawing inspiration from other languages' approaches, we can improve code quality, promote explicit error handling, and enhance the overall reliability of Dart projects.


As for exceptions that never occur, I would use a "panic-like" behavior: terminating the program, or logging an unexpected occurrence. This approach emphasizes the confidence that the exception will not be thrown and helps identify unexpected scenarios during development.

I would like to provide an extreme approach to answer @natebosch's question:

... I know I'm initializing Bar before the cal ....

void c() {
  try {
    a(1);
 } catch (Bad) {
    // Perform panic-like action here, such as terminating the program
    log("this is not expected to happen") 
    exit(1);
  }
}

By doing so, other developers can understand that this particular situation is not expected to happen. That way you don't need to ignore the linter.


In Dart, it can be challenging to determine all possible exceptions that a function might raise without thoroughly reading its documentation or analyzing its implementation.

Having IDE support or a compiler that can automatically identify and suggest exception handling for functions would indeed be helpful. However, traversing all inner calls of all functions to determine potential exceptions can be a computationally expensive process, especially in large codebases.

One possible solution to address this issue is by standardizing exception documentation and making it more explicit in function signatures. By providing clear and standardized documentation about the exceptions that a function may throw, IDEs could potentially offer autocomplete or suggestions for exception handling, enhancing developer productivity and reducing the likelihood of missed exceptions.

Drawing from the experiences of languages like Go and Rust, where the exact exception type is not as critical as ensuring explicit error handling, it may be beneficial to focus on encapsulating error messages within exceptions. This approach allows for flexibility in modifying the error message without breaking client code, as long as the exception type remains the same. In contrast, requiring the specification of exact error types, as seen in Java, can sometimes lead to verbosity and increased complexity.

By adopting practices that prioritize standardized exception documentation and promoting explicit error handling, we can improve the developer experience, code maintainability, and overall code quality.

Thank you for considering this suggestion.

rrousselGit commented 11 months ago

When considering the annotation idea, instead of an annotation, what about a generic typedef?

// The typedef is No-op, and useful only for the analyzer
typedef Throws<ResultT, ErrorT> = ResultT;

Then instead of:

int fn() {
  throw FormatException();
}

We'd write:

Throws<int, FormatException>  fn() {
  throw FormatException();
}

This doesn't impact the function signature.
It is slightly worse in terms of syntax, but has the benefit of working with function variables:

Throws<int, Error> Function() callback;

As for when throwing multiple types, we can use records:

Throws<int, (FormatException, UnsupportedError)> Function() callback;

This even supports more advances cases, such as a function returning a function:

Throws<Throws<void, FormatException> Function(), StateError> callback;

callback = () {
  if (something) throw StateError();

  return () {
    if (something) throw FormatException();
  };
}
bsutton commented 11 months ago

@rrousselGit my inclination is not to go down this path.

We still need to document why the exceptions are being throw so this proposal just ends up in documenting the exceptions twice.

rrousselGit commented 11 months ago

Making sure exceptions are explicitly thrown or handled is the responsibility of the type system though, not comments.
That's why lots of folks use the Result pattern.

Mirko607 commented 3 weeks ago

I would love to see some results rising from all the discussions. Maybe we don't need the full fledged solution that hits it all. Maybe we can start rather simple with a lint that is limited to only scan the body of a function for throw statements and advise the programmer to document the exception.

bsutton commented 3 weeks ago

Sounds like a good starting point.

On Mon, 21 Oct 2024, 9:40 pm MisterMirko, @.***> wrote:

I would love to see some results rising from all the discussions. Maybe we don't need the full fledged solution that hits it all. Maybe we can start rather simple with a lint that is limited to only scan the body of a function for throw statements and advise the programmer to document the exception.

— Reply to this email directly, view it on GitHub https://github.com/dart-lang/linter/issues/2246#issuecomment-2426292063, or unsubscribe https://github.com/notifications/unsubscribe-auth/AAG32OGYXY3O6OBPWG2MJETZ4TKY5AVCNFSM6AAAAABQJ3JACWVHI2DSMVQWIX3LMV43OSLTON2WKQ3PNVWWK3TUHMZDIMRWGI4TEMBWGM . You are receiving this because you were mentioned.Message ID: @.***>