Skip to content

Commit 1bfa861

Browse files
pqcommit-bot@chromium.org
authored andcommitted
add @useResult
See: https://github.com/dart-lang/linter/issues/1888 Change-Id: Idd529c86236a4c2692104dc36cb0a7f0d5dd35aa Reviewed-on: https://dart-review.googlesource.com/c/sdk/+/200301 Reviewed-by: Brian Wilkerson <[email protected]> Commit-Queue: Phil Quitslund <[email protected]>
1 parent b07305d commit 1bfa861

File tree

10 files changed

+124
-1
lines changed

10 files changed

+124
-1
lines changed

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

+8
Original file line numberDiff line numberDiff line change
@@ -601,6 +601,10 @@ abstract class Element implements AnalysisTarget {
601601
/// Return `true` if this element has an annotation of the form `@sealed`.
602602
bool get hasSealed;
603603

604+
/// Return `true` if this element has an annotation of the form `@useResult`
605+
/// or `@UseResult('..')`.
606+
bool get hasUseResult;
607+
604608
/// Return `true` if this element has an annotation of the form
605609
/// `@visibleForTemplate`.
606610
bool get hasVisibleForTemplate;
@@ -807,6 +811,10 @@ abstract class ElementAnnotation implements ConstantEvaluationTarget {
807811
/// intended to be used as an annotation.
808812
bool get isTarget;
809813

814+
/// Return `true` if this annotation marks the associated returned element as
815+
/// requiring use.
816+
bool get isUseResult;
817+
810818
/// Return `true` if this annotation marks the associated member as being
811819
/// visible for template files.
812820
bool get isVisibleForTemplate;

pkg/analyzer/lib/error/error.dart

+2
Original file line numberDiff line numberDiff line change
@@ -607,6 +607,8 @@ const List<ErrorCode> errorCodeValues = [
607607
HintCode.UNUSED_IMPORT,
608608
HintCode.UNUSED_LABEL,
609609
HintCode.UNUSED_LOCAL_VARIABLE,
610+
HintCode.UNUSED_RESULT,
611+
HintCode.UNUSED_RESULT_WITH_MESSAGE,
610612
HintCode.UNUSED_SHOWN_NAME,
611613
LanguageCode.IMPLICIT_DYNAMIC_FIELD,
612614
LanguageCode.IMPLICIT_DYNAMIC_FUNCTION,

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

+28
Original file line numberDiff line numberDiff line change
@@ -2006,6 +2006,13 @@ class ElementAnnotationImpl implements ElementAnnotation {
20062006
/// specific set of target element kinds.
20072007
static const String _TARGET_CLASS_NAME = 'Target';
20082008

2009+
/// The name of the class used to mark a returned element as requiring use.
2010+
static const String _USE_RESULT_CLASS_NAME = "UseResult";
2011+
2012+
/// The name of the top-level variable used to mark a returned element as
2013+
/// requiring use.
2014+
static const String _USE_RESULT_VARIABLE_NAME = "useResult";
2015+
20092016
/// The name of the top-level variable used to mark a method as being
20102017
/// visible for templates.
20112018
static const String _VISIBLE_FOR_TEMPLATE_VARIABLE_NAME =
@@ -2118,6 +2125,12 @@ class ElementAnnotationImpl implements ElementAnnotation {
21182125
bool get isTarget => _isConstructor(
21192126
libraryName: _META_META_LIB_NAME, className: _TARGET_CLASS_NAME);
21202127

2128+
@override
2129+
bool get isUseResult =>
2130+
_isConstructor(
2131+
libraryName: _META_LIB_NAME, className: _USE_RESULT_CLASS_NAME) ||
2132+
_isPackageMetaGetter(_USE_RESULT_VARIABLE_NAME);
2133+
21212134
@override
21222135
bool get isVisibleForTemplate => _isTopGetter(
21232136
libraryName: _NG_META_LIB_NAME,
@@ -2497,6 +2510,18 @@ abstract class ElementImpl implements Element {
24972510
return false;
24982511
}
24992512

2513+
@override
2514+
bool get hasUseResult {
2515+
final metadata = this.metadata;
2516+
for (var i = 0; i < metadata.length; i++) {
2517+
var annotation = metadata[i];
2518+
if (annotation.isUseResult) {
2519+
return true;
2520+
}
2521+
}
2522+
return false;
2523+
}
2524+
25002525
@override
25012526
bool get hasVisibleForTemplate {
25022527
final metadata = this.metadata;
@@ -4870,6 +4895,9 @@ class MultiplyDefinedElementImpl implements MultiplyDefinedElement {
48704895
@override
48714896
bool get hasSealed => false;
48724897

4898+
@override
4899+
bool get hasUseResult => false;
4900+
48734901
@override
48744902
bool get hasVisibleForTemplate => false;
48754903

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

+3
Original file line numberDiff line numberDiff line change
@@ -518,6 +518,9 @@ abstract class Member implements Element {
518518
@override
519519
bool get hasSealed => _declaration.hasSealed;
520520

521+
@override
522+
bool get hasUseResult => _declaration.hasUseResult;
523+
521524
@override
522525
bool get hasVisibleForTemplate => _declaration.hasVisibleForTemplate;
523526

pkg/analyzer/lib/src/dart/error/hint_codes.dart

+31
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,37 @@ class HintCode extends AnalyzerErrorCode {
9595
"The receiver uses '?.', so its value can be null.",
9696
correction: "Replace the '.' with a '?.' in the invocation.");
9797

98+
/**
99+
* Generate a hint for method, property or function annotated with
100+
* `@useResult` whose invocation is unchecked.
101+
*
102+
* Parameters:
103+
* 0: the name of the annotated method, property or function
104+
*/
105+
static const HintCode UNUSED_RESULT = HintCode(
106+
'UNUSED_RESULT', "'{0}' should be used.",
107+
correction:
108+
"Try using the result by invoking a member, passing it to a function, or returning it from this function.",
109+
hasPublishedDocs: false);
110+
111+
/**
112+
* Generate a hint for method, property or function annotated with
113+
* `@useResult` whose invocation is unchecked.
114+
*
115+
* Parameters:
116+
* 0: the name of the annotated method, property or function
117+
* 1: message details
118+
*/
119+
static const HintCode UNUSED_RESULT_WITH_MESSAGE = HintCode(
120+
'UNUSED_RESULT',
121+
"'{0}' should be used. {1}.",
122+
// todo(pq): consider passing in correction details: https://github.com/dart-lang/sdk/issues/46066
123+
correction:
124+
"Try using the result by invoking a member, passing it to a function, or returning it from this function.",
125+
hasPublishedDocs: false,
126+
uniqueName: 'HintCode.UNUSED_RESULT_WITH_MESSAGE',
127+
);
128+
98129
/**
99130
* Dead code is code that is never reached, this can happen for instance if a
100131
* statement follows a return statement.

pkg/analyzer/lib/src/error/best_practices_verifier.dart

+3-1
Original file line numberDiff line numberDiff line change
@@ -98,7 +98,7 @@ class BestPracticesVerifier extends RecursiveAstVisitor<void> {
9898
required DeclaredVariables declaredVariables,
9999
required AnalysisOptions analysisOptions,
100100
required WorkspacePackage? workspacePackage,
101-
}) : _nullType = typeProvider.nullType,
101+
}) : _nullType = typeProvider.nullType,
102102
_typeSystem = typeSystem,
103103
_isNonNullableByDefault = typeSystem.isNonNullableByDefault,
104104
_strictInference =
@@ -2007,6 +2007,8 @@ extension on TargetKind {
20072007
return 'parameters';
20082008
case TargetKind.setter:
20092009
return 'setters';
2010+
case TargetKind.topLevelVariable:
2011+
return 'top-level variables';
20102012
case TargetKind.type:
20112013
return 'types (classes, enums, mixins, or typedefs)';
20122014
case TargetKind.typedefType:

pkg/analyzer/lib/src/test_utilities/mock_packages.dart

+13
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,19 @@ const _OptionalTypeArgs optionalTypeArgs = const _OptionalTypeArgs();
3636
const _Protected protected = const _Protected();
3737
const Required required = const Required();
3838
const _Sealed sealed = const _Sealed();
39+
const UseResult useResult = UseResult();
3940
const _VisibleForTesting visibleForTesting = const _VisibleForTesting();
4041
4142
class _AlwaysThrows {
4243
const _AlwaysThrows();
4344
}
45+
@Target({
46+
TargetKind.field,
47+
TargetKind.function,
48+
TargetKind.getter,
49+
TargetKind.method,
50+
TargetKind.topLevelVariable,
51+
})
4452
class _DoNotStore {
4553
const _DoNotStore();
4654
}
@@ -76,6 +84,10 @@ class Required {
7684
class _Sealed {
7785
const _Sealed();
7886
}
87+
class UseResult {
88+
final String reason;
89+
const UseResult([this.reason = '']);
90+
}
7991
class _VisibleForTesting {
8092
const _VisibleForTesting();
8193
}
@@ -99,6 +111,7 @@ enum TargetKind {
99111
mixinType,
100112
parameter,
101113
setter,
114+
topLevelVariable,
102115
type,
103116
typedefType,
104117
}

pkg/analyzer/test/src/diagnostics/invalid_annotation_target_test.dart

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ main() {
1313
});
1414
}
1515

16+
/// todo(pq): add tests for topLevelVariables: https://dart-review.googlesource.com/c/sdk/+/200301
1617
@reflectiveTest
1718
class InvalidAnnotationTargetTest extends PubPackageResolutionTest {
1819
void test_classType_class() async {

pkg/meta/lib/meta.dart

+31
Original file line numberDiff line numberDiff line change
@@ -253,6 +253,20 @@ const Required required = Required();
253253
/// `C` and `D` are declared in different packages.
254254
const _Sealed sealed = _Sealed();
255255

256+
/// Used to annotate a method, field, or getter within a class, mixin, or
257+
/// extension, or a or top-level getter, variable or function to indicate that
258+
/// the value obtained by invoking it should be use. A value is considered used
259+
/// if it is assigned to a variable, passed to a function, or used as the target
260+
/// of an invocation, or invoked (if the result is itself a function).
261+
///
262+
/// Tools, such as the analyzer, can provide feedback if
263+
///
264+
/// * the annotation is associated with anything other than a method, field or
265+
/// getter, top-level variable, getter or function or
266+
/// * the value obtained by a method, field, getter or top-level getter,
267+
/// variable or function annotated with `@useResult` is not used.
268+
const UseResult useResult = UseResult();
269+
256270
/// Used to annotate a field that is allowed to be overridden in Strong Mode.
257271
///
258272
/// Deprecated: Most of strong mode is now the default in 2.0, but the notion of
@@ -314,6 +328,23 @@ class Required {
314328
const Required([this.reason = '']);
315329
}
316330

331+
/// See [useResult] for more details.
332+
@Target({
333+
TargetKind.field,
334+
TargetKind.function,
335+
TargetKind.getter,
336+
TargetKind.method,
337+
TargetKind.topLevelVariable,
338+
})
339+
class UseResult {
340+
/// A human-readable explanation of the reason why the value returned by
341+
/// accessing this member should be checked.
342+
final String reason;
343+
344+
/// Initialize a newly created instance to have the given [reason].
345+
const UseResult([this.reason = '']);
346+
}
347+
317348
class _AlwaysThrows {
318349
const _AlwaysThrows();
319350
}

pkg/meta/lib/meta_meta.dart

+4
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@ enum TargetKind {
7373
/// at the top-level of a library.
7474
setter,
7575

76+
/// Indicates that an annotation is valid on any top-level variable
77+
/// declaration.
78+
topLevelVariable,
79+
7680
/// Indicates that an annotation is valid on any declaration that introduces a
7781
/// type. This includes classes, enums, mixins and typedefs, but does not
7882
/// include extensions because extensions don't introduce a type.

0 commit comments

Comments
 (0)