Skip to content

Commit 0a62533

Browse files
srawlinscommit-bot@chromium.org
authored andcommitted
Check for @alwaysThrows when evaluating for dead code.
The meta package version 1.1.0 includes a new `alwaysThrows`, which developers can use to annotate methods that always throw. This helps to avoid erroneous DEAD_CODE warnings from analyzer. Bug: #31384 Change-Id: I70e2469b4f3a0d2c87064851160b268ea2259807 Reviewed-on: https://dart-review.googlesource.com/26563 Commit-Queue: Samuel Rawlins <[email protected]> Reviewed-by: Brian Wilkerson <[email protected]>
1 parent 031ef98 commit 0a62533

File tree

7 files changed

+142
-1
lines changed

7 files changed

+142
-1
lines changed

pkg/analyzer/lib/dart/element/element.dart

+12
Original file line numberDiff line numberDiff line change
@@ -608,6 +608,12 @@ abstract class Element implements AnalysisTarget, ResolutionTarget {
608608
*/
609609
int get id;
610610

611+
/**
612+
* Return `true` if this element has an annotation of the form
613+
* '@alwaysThrows'.
614+
*/
615+
bool get isAlwaysThrows;
616+
611617
/**
612618
* Return `true` if this element has an annotation of the form '@deprecated'
613619
* or '@Deprecated('..')'.
@@ -808,6 +814,12 @@ abstract class ElementAnnotation
808814
*/
809815
Element get element;
810816

817+
/**
818+
* Return `true` if this annotation marks the associated function as always
819+
* throwing.
820+
*/
821+
bool get isAlwaysThrows;
822+
811823
/**
812824
* Return `true` if this annotation marks the associated element as being
813825
* deprecated.

pkg/analyzer/lib/src/dart/element/element.dart

+19
Original file line numberDiff line numberDiff line change
@@ -2789,6 +2789,12 @@ class DynamicElementImpl extends ElementImpl implements TypeDefiningElement {
27892789
* A concrete implementation of an [ElementAnnotation].
27902790
*/
27912791
class ElementAnnotationImpl implements ElementAnnotation {
2792+
/**
2793+
* The name of the top-level variable used to mark that a function always
2794+
* throws, for dead code purposes.
2795+
*/
2796+
static String _ALWAYS_THROWS_VARIABLE_NAME = "alwaysThrows";
2797+
27922798
/**
27932799
* The name of the top-level variable used to mark a method parameter as
27942800
* covariant.
@@ -2904,6 +2910,12 @@ class ElementAnnotationImpl implements ElementAnnotation {
29042910
@override
29052911
AnalysisContext get context => compilationUnit.library.context;
29062912

2913+
@override
2914+
bool get isAlwaysThrows =>
2915+
element is PropertyAccessorElement &&
2916+
element.name == _ALWAYS_THROWS_VARIABLE_NAME &&
2917+
element.library?.name == _META_LIB_NAME;
2918+
29072919
/**
29082920
* Return `true` if this annotation marks the associated parameter as being
29092921
* covariant, meaning it is allowed to have a narrower type in an override.
@@ -3156,6 +3168,10 @@ abstract class ElementImpl implements Element {
31563168
*/
31573169
String get identifier => name;
31583170

3171+
@override
3172+
bool get isAlwaysThrows =>
3173+
metadata.any((ElementAnnotation annotation) => annotation.isAlwaysThrows);
3174+
31593175
@override
31603176
bool get isDeprecated {
31613177
for (ElementAnnotation annotation in metadata) {
@@ -7415,6 +7431,9 @@ class MultiplyDefinedElementImpl implements MultiplyDefinedElement {
74157431
@override
74167432
Element get enclosingElement => null;
74177433

7434+
@override
7435+
bool get isAlwaysThrows => false;
7436+
74187437
@override
74197438
bool get isDeprecated => false;
74207439

pkg/analyzer/lib/src/dart/element/handle.dart

+3
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,9 @@ abstract class ElementHandle implements Element {
347347
@override
348348
int get hashCode => _location.hashCode;
349349

350+
@override
351+
bool get isAlwaysThrows => actualElement.isAlwaysThrows;
352+
350353
@override
351354
bool get isDeprecated => actualElement.isDeprecated;
352355

pkg/analyzer/lib/src/dart/element/member.dart

+3
Original file line numberDiff line numberDiff line change
@@ -393,6 +393,9 @@ abstract class Member implements Element {
393393
@override
394394
int get id => _baseElement.id;
395395

396+
@override
397+
bool get isAlwaysThrows => _baseElement.isAlwaysThrows;
398+
396399
@override
397400
bool get isDeprecated => _baseElement.isDeprecated;
398401

pkg/analyzer/lib/src/generated/resolver.dart

+4-1
Original file line numberDiff line numberDiff line change
@@ -633,7 +633,6 @@ class BestPracticesVerifier extends RecursiveAstVisitor<Object> {
633633
*
634634
* @param element some element to check for deprecated use of
635635
* @param node the node use for the location of the error
636-
* @return `true` if and only if a hint code is generated on the passed node
637636
* See [HintCode.DEPRECATED_MEMBER_USE].
638637
*/
639638
void _checkForDeprecatedMemberUse(Element element, AstNode node) {
@@ -3269,6 +3268,10 @@ class ExitDetector extends GeneralizingAstVisitor<bool> {
32693268
return false;
32703269
}
32713270
}
3271+
Element element = node.methodName.staticElement;
3272+
if (element != null && element.isAlwaysThrows) {
3273+
return true;
3274+
}
32723275
return _nodeExits(node.argumentList);
32733276
}
32743277

pkg/analyzer/test/generated/hint_code_test.dart

+66
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ class HintCodeTest extends ResolverTestCase {
3131
r'''
3232
library meta;
3333
34+
const _AlwaysThrows alwaysThrows = const _AlwaysThrows();
3435
const _Factory factory = const _Factory();
3536
const Immutable immutable = const Immutable();
3637
const _Literal literal = const _Literal();
@@ -46,6 +47,9 @@ class Immutable {
4647
final String reason;
4748
const Immutable([this.reason]);
4849
}
50+
class _AlwaysThrows {
51+
const _AlwaysThrows();
52+
}
4953
class _Factory {
5054
const _Factory();
5155
}
@@ -719,6 +723,68 @@ f() {
719723
verify([source]);
720724
}
721725

726+
test_deadCode_statementAfterAlwaysThrowsFunction() async {
727+
Source source = addSource(r'''
728+
import 'package:meta/meta.dart';
729+
730+
@alwaysThrows
731+
void a() {
732+
throw 'msg';
733+
}
734+
735+
f() {
736+
var one = 1;
737+
a();
738+
var two = 2;
739+
}''');
740+
await computeAnalysisResult(source);
741+
assertErrors(source, [HintCode.DEAD_CODE]);
742+
verify([source]);
743+
}
744+
745+
test_deadCode_statementAfterAlwaysThrowsMethod() async {
746+
Source source = addSource(r'''
747+
import 'package:meta/meta.dart';
748+
749+
class C {
750+
@alwaysThrows
751+
void a() {
752+
throw 'msg';
753+
}
754+
}
755+
756+
f() {
757+
var one = 1;
758+
new C().a();
759+
var two = 2;
760+
}''');
761+
await computeAnalysisResult(source);
762+
assertErrors(source, [HintCode.DEAD_CODE]);
763+
verify([source]);
764+
}
765+
766+
@failingTest
767+
test_deadCode_statementAfterAlwaysThrowsGetter() async {
768+
Source source = addSource(r'''
769+
import 'package:meta/meta.dart';
770+
771+
class C {
772+
@alwaysThrows
773+
int get a {
774+
throw 'msg';
775+
}
776+
}
777+
778+
f() {
779+
var one = 1;
780+
new C().a;
781+
var two = 2;
782+
}''');
783+
await computeAnalysisResult(source);
784+
assertErrors(source, [HintCode.DEAD_CODE]);
785+
verify([source]);
786+
}
787+
722788
test_deprecatedAnnotationUse_assignment() async {
723789
Source source = addSource(r'''
724790
class A {

pkg/analyzer/test/generated/non_hint_code_test.dart

+35
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,24 @@ main() {
2020

2121
@reflectiveTest
2222
class NonHintCodeTest extends ResolverTestCase {
23+
@override
24+
void reset() {
25+
super.resetWith(packages: [
26+
[
27+
'meta',
28+
r'''
29+
library meta;
30+
31+
const _AlwaysThrows alwaysThrows = const _AlwaysThrows();
32+
33+
class _AlwaysThrows {
34+
const _AlwaysThrows();
35+
}
36+
'''
37+
]
38+
]);
39+
}
40+
2341
test_async_future_object_without_return() async {
2442
Source source = addSource('''
2543
import 'dart:async';
@@ -474,6 +492,23 @@ abstract class A {
474492
verify([source]);
475493
}
476494

495+
test_missingReturn_alwaysThrows() async {
496+
Source source = addSource(r'''
497+
import 'package:meta/meta.dart';
498+
499+
@alwaysThrows
500+
void a() {
501+
throw 'msg';
502+
}
503+
504+
int f() {
505+
a();
506+
}''');
507+
await computeAnalysisResult(source);
508+
assertNoErrors(source);
509+
verify([source]);
510+
}
511+
477512
test_nullAwareInCondition_for_noCondition() async {
478513
Source source = addSource(r'''
479514
m(x) {

0 commit comments

Comments
 (0)