@@ -88,6 +88,9 @@ Future<void> run(List<String> arguments) async {
8888 exitWithError (< String > ['The analyze.dart script must be run with --enable-asserts.' ]);
8989 }
9090
91+ print ('$clock No Double.clamp' );
92+ await verifyNoDoubleClamp (flutterRoot);
93+
9194 print ('$clock All tool test files end in _test.dart...' );
9295 await verifyToolTestsEndInTestDart (flutterRoot);
9396
@@ -203,6 +206,80 @@ Future<void> run(List<String> arguments) async {
203206
204207// TESTS
205208
209+ FeatureSet _parsingFeatureSet () => FeatureSet .fromEnableFlags2 (
210+ sdkLanguageVersion: Version .parse ('2.17.0-0' ),
211+ flags: < String > ['super-parameters' ]);
212+
213+ _Line _getLine (ParseStringResult parseResult, int offset) {
214+ final int lineNumber =
215+ parseResult.lineInfo.getLocation (offset).lineNumber;
216+ final String content = parseResult.content.substring (
217+ parseResult.lineInfo.getOffsetOfLine (lineNumber - 1 ),
218+ parseResult.lineInfo.getOffsetOfLine (lineNumber) - 1 );
219+ return _Line (lineNumber, content);
220+ }
221+
222+ class _DoubleClampVisitor extends RecursiveAstVisitor <CompilationUnit > {
223+ _DoubleClampVisitor (this .parseResult);
224+
225+ final List <_Line > clamps = < _Line > [];
226+ final ParseStringResult parseResult;
227+
228+ @override
229+ CompilationUnit ? visitMethodInvocation (MethodInvocation node) {
230+ if (node.methodName.name == 'clamp' ) {
231+ final _Line line = _getLine (parseResult, node.function.offset);
232+ if (! line.content.contains ('// ignore_clamp_double_lint' )) {
233+ clamps.add (line);
234+ }
235+ }
236+
237+ node.visitChildren (this );
238+ return null ;
239+ }
240+ }
241+
242+ /// Verify that we use clampDouble instead of Double.clamp for performance reasons.
243+ ///
244+ /// We currently can't distinguish valid uses of clamp from problematic ones so
245+ /// if the clamp is operating on a type other than a `double` the
246+ /// `// ignore_clamp_double_lint` comment must be added to the line where clamp is
247+ /// invoked.
248+ ///
249+ /// See also:
250+ /// * https://github.com/flutter/flutter/pull/103559
251+ /// * https://github.com/flutter/flutter/issues/103917
252+ Future <void > verifyNoDoubleClamp (String workingDirectory) async {
253+ final String flutterLibPath = '$workingDirectory /packages/flutter/lib' ;
254+ final Stream <File > testFiles =
255+ _allFiles (flutterLibPath, 'dart' , minimumMatches: 100 );
256+ final List <String > errors = < String > [];
257+ await for (final File file in testFiles) {
258+ try {
259+ final ParseStringResult parseResult = parseFile (
260+ featureSet: _parsingFeatureSet (),
261+ path: file.absolute.path,
262+ );
263+ final _DoubleClampVisitor visitor = _DoubleClampVisitor (parseResult);
264+ visitor.visitCompilationUnit (parseResult.unit);
265+ for (final _Line clamp in visitor.clamps) {
266+ errors.add ('${file .path }:${clamp .line }: `clamp` method used without ignore_clamp_double_lint comment.' );
267+ }
268+ } catch (ex) {
269+ // TODO(gaaclarke): There is a bug with super parameter parsing on mac so
270+ // we skip certain files until that is fixed.
271+ // https://github.com/dart-lang/sdk/issues/49032
272+ print ('skipping ${file .path }: $ex ' );
273+ }
274+ }
275+ if (errors.isNotEmpty) {
276+ exitWithError (< String > [
277+ ...errors,
278+ '\n ${bold }See: https://github.com/flutter/flutter/pull/103559' ,
279+ ]);
280+ }
281+ }
282+
206283/// Verify tool test files end in `_test.dart` .
207284///
208285/// The test runner will only recognize files ending in `_test.dart` as tests to
@@ -518,16 +595,16 @@ Future<int> _verifyNoMissingLicenseForExtension(
518595 return 0 ;
519596}
520597
521- class _TestSkip {
522- _TestSkip (this .line, this .content);
598+ class _Line {
599+ _Line (this .line, this .content);
523600
524601 final int line;
525602 final String content;
526603}
527604
528- Iterable <_TestSkip > _getTestSkips (File file) {
605+ Iterable <_Line > _getTestSkips (File file) {
529606 final ParseStringResult parseResult = parseFile (
530- featureSet: FeatureSet . fromEnableFlags2 (sdkLanguageVersion : Version . parse ( '2.17.0-0' ), flags : < String > [ 'super-parameters' ] ),
607+ featureSet: _parsingFeatureSet ( ),
531608 path: file.absolute.path,
532609 );
533610 final _TestSkipLinesVisitor <CompilationUnit > visitor = _TestSkipLinesVisitor <CompilationUnit >(parseResult);
@@ -536,10 +613,10 @@ Iterable<_TestSkip> _getTestSkips(File file) {
536613}
537614
538615class _TestSkipLinesVisitor <T > extends RecursiveAstVisitor <T > {
539- _TestSkipLinesVisitor (this .parseResult) : skips = < _TestSkip > {};
616+ _TestSkipLinesVisitor (this .parseResult) : skips = < _Line > {};
540617
541618 final ParseStringResult parseResult;
542- final Set <_TestSkip > skips;
619+ final Set <_Line > skips;
543620
544621 static bool isTestMethod (String name) {
545622 return name.startsWith ('test' ) || name == 'group' || name == 'expect' ;
@@ -550,10 +627,7 @@ class _TestSkipLinesVisitor<T> extends RecursiveAstVisitor<T> {
550627 if (isTestMethod (node.methodName.toString ())) {
551628 for (final Expression argument in node.argumentList.arguments) {
552629 if (argument is NamedExpression && argument.name.label.name == 'skip' ) {
553- final int lineNumber = parseResult.lineInfo.getLocation (argument.beginToken.charOffset).lineNumber;
554- final String content = parseResult.content.substring (parseResult.lineInfo.getOffsetOfLine (lineNumber - 1 ),
555- parseResult.lineInfo.getOffsetOfLine (lineNumber) - 1 );
556- skips.add (_TestSkip (lineNumber, content));
630+ skips.add (_getLine (parseResult, argument.beginToken.charOffset));
557631 }
558632 }
559633 }
@@ -571,7 +645,7 @@ Future<void> verifySkipTestComments(String workingDirectory) async {
571645 .where ((File f) => f.path.endsWith ('_test.dart' ));
572646
573647 await for (final File file in testFiles) {
574- for (final _TestSkip skip in _getTestSkips (file)) {
648+ for (final _Line skip in _getTestSkips (file)) {
575649 final Match ? match = _skipTestCommentPattern.firstMatch (skip.content);
576650 final String ? skipComment = match? .group (1 );
577651 if (skipComment == null ||
0 commit comments