Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
b31f0ca
flutter lib lints
LongCatIsLooong Jun 29, 2023
9c9683e
test test
LongCatIsLooong Jun 29, 2023
95c7138
test
LongCatIsLooong Jun 30, 2023
fda6cb0
Merge branch 'flutter-internal-lints' of github.com:LongCatIsLooong/f…
LongCatIsLooong Jun 30, 2023
c31591d
wip
LongCatIsLooong Jun 30, 2023
9b81a22
tests
LongCatIsLooong Jul 5, 2023
7fb1901
more tests
LongCatIsLooong Jul 6, 2023
cc1ec80
messages
LongCatIsLooong Jul 6, 2023
ee3fd91
clean up
LongCatIsLooong Jul 6, 2023
6169cfe
use Object()
LongCatIsLooong Jul 6, 2023
1cc26e6
add ignores
LongCatIsLooong Jul 6, 2023
defe763
comments
LongCatIsLooong Jul 6, 2023
b1aa30b
fix tests
LongCatIsLooong Jul 6, 2023
46638cf
fix tests
LongCatIsLooong Jul 7, 2023
3c260ac
Merge remote-tracking branch 'upstream/master' into flutter-internal-…
LongCatIsLooong Jul 7, 2023
717b910
fix tests again
LongCatIsLooong Jul 7, 2023
a4ee0c1
Merge remote-tracking branch 'upstream/master' into flutter-internal-…
LongCatIsLooong Jul 7, 2023
981d961
review
LongCatIsLooong Jul 10, 2023
7436715
review
LongCatIsLooong Jul 10, 2023
b425f25
fix tests
LongCatIsLooong Jul 10, 2023
8dc6504
review
LongCatIsLooong Jul 11, 2023
909d194
comments
LongCatIsLooong Jul 11, 2023
ca08966
Merge remote-tracking branch 'upstream/master' into flutter-internal-…
LongCatIsLooong Jul 11, 2023
9e985f8
comments
LongCatIsLooong Jul 11, 2023
b55969e
move the annotation to foundation/debug
LongCatIsLooong Jul 11, 2023
216af33
Merge remote-tracking branch 'upstream/master' into flutter-internal-…
LongCatIsLooong Aug 17, 2023
276be09
review
LongCatIsLooong Aug 18, 2023
654b0cb
Merge remote-tracking branch 'upstream/master' into flutter-internal-…
LongCatIsLooong Aug 18, 2023
63f2c30
Remove unnecessary type matcher
LongCatIsLooong Aug 18, 2023
8aab72b
Merge remote-tracking branch upstream/main
LongCatIsLooong Feb 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion dev/bots/analyze.dart
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,7 @@ Future<void> run(List<String> arguments) async {
// Only run the private lints when the code is free of type errors. The
// lints are easier to write when they can assume, for example, there is no
// inheritance cycles.
final List<AnalyzeRule> rules = <AnalyzeRule>[noDoubleClamp, noStopwatches];
final List<AnalyzeRule> rules = <AnalyzeRule>[noDoubleClamp, noStopwatches, debugAssert];
final String ruleNames = rules.map((AnalyzeRule rule) => '\n * $rule').join();
printProgress('Analyzing code in the framework with the following rules:$ruleNames');
await analyzeWithRules(flutterRoot, rules,
Expand Down
385 changes: 385 additions & 0 deletions dev/bots/custom_rules/debug_assert.dart

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'debug_only_lib.dart';

const Null debugAssert = null;

final ProductionClassWithDebugOnlyMixin x = ProductionClassWithDebugOnlyMixin();
ProductionClassWithDebugOnlyMixin? xx;
ProductionClassWithDebugOnlyMixin y = ProductionClassWithDebugOnlyMixin();
void takeAnything(Object? input) { }

void badDebugAssertAccess() {
globalVariableFromDebugLib += 'test';
globalFunctionFromDebugLib();
void Function() f = globalFunctionFromDebugLib; // ignore: unused_local_variable
f = globalFunctionFromDebugLib.call;
MixinFromDebugLib.staticMethodFromDebugLib();
f = MixinFromDebugLib.staticMethodFromDebugLib;
x.fieldFromDebugLib; // ignore: unnecessary_statements
xx?.fieldFromDebugLib; // ignore: unnecessary_statements
x.debugGetSet;
xx?.debugGetSet;
x.debugGetSet = 2;
xx?.debugGetSet = 2;
x..fieldFromDebugLib += x.debugGetSet
..debugGetSet += x.debugGetSet;
xx?..fieldFromDebugLib += x.debugGetSet
..debugGetSet += x.debugGetSet;
takeAnything(xx?.methodFromDebugLib);
x.debugOnlyExtensionMethod();
takeAnything(xx?.debugOnlyExtensionMethod);
DebugOnlyEnum.foo; // ignore: unnecessary_statements
DebugOnlyEnum.values; // ignore: unnecessary_statements
RegularEnum.foo.debugOnlyMethod();

// Overridden Operators
x + x; // ignore: unnecessary_statements
xx! + xx!; // ignore: unnecessary_statements
y += x;
y += xx!;
~x; // ignore: unnecessary_statements
~xx!; // ignore: unnecessary_statements
x[x.debugGetSet]; // ignore: unnecessary_statements
xx?[x.debugGetSet]; // ignore: unnecessary_statements
}

/// Yours truly [globalVariableFromDebugLib] from the comment section with love.
void goodDebugAssertAccess() {
assert(() {
final _DebugOnlyClass debugObject = _DebugOnlyClass();
debugObject
.debugOnlyMemberMethod();
final void Function() f = debugObject.debugOnlyMemberMethod;
f();
return true;
}());

final ProductionClassWithDebugOnlyMixin x = ProductionClassWithDebugOnlyMixin() // ignore: unused_local_variable
..run();
RegularEnum.foo; // ignore: unnecessary_statements
}

mixin class BaseClass {
void run() { }
void stop() { }

int get value => 0;

int operator ~() => ~value;
}

@debugAssert
class _DebugOnlyClass extends BaseClass {
void debugOnlyMemberMethod() {}
}

class ProductionClassWithDebugOnlyMixin extends _DebugOnlyClass with MixinFromDebugLib {
@override
ProductionClassWithDebugOnlyMixin operator +(ProductionClassWithDebugOnlyMixin rhs) {
return ProductionClassWithDebugOnlyMixin()
..debugGetSet = debugGetSet + rhs.debugGetSet
..fieldFromDebugLib = fieldFromDebugLib + rhs.fieldFromDebugLib;
}
}

mixin MixinOnBaseClass implements BaseClass {
void runAndStop() {
run();
stop();
}

@debugAssert
@override
int get value => -1; // bad annotation.

@debugAssert
@override
int operator ~() => ~value; // bad annotation.
}

class ClassWithBadAnnotation1 extends BaseClass with MixinOnBaseClass {
@debugAssert
@override
void run() { } // bad annotation.

void run1() { }
}

class ClassWithBadAnnotation2 with MixinOnBaseClass {
@debugAssert
@override
void run() { } // bad annotation.

@override
void stop() { }

@override
int get value => -1;
}

@debugAssert
extension DebugOnly on ProductionClassWithDebugOnlyMixin {
void debugOnlyExtensionMethod() { }
}

@debugAssert
enum DebugOnlyEnum with BaseClass {
foo
}

@debugAssert
mixin DebugOnlyMixinOnRegularEnum {
void debugOnlyMethod() {}
}

enum RegularEnum with DebugOnlyMixinOnRegularEnum {
foo
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

import 'debug_only_lib.dart';

const Object debugAssert = Object();

class ProductionClass11 extends ClassFromDebugLibWithNamedConstructor {
ProductionClass11() : super.constructor(); // bad: debug-only constructor invoked
}

class ProductionClass21 extends ClassFromDebugLibWithImplicitDefaultConstructor {
ProductionClass21(); // Good: super() is synthesized thus not considered debug-only.
}

class ProductionClass22 extends ClassFromDebugLibWithImplicitDefaultConstructor { }

class ProductionClass31 extends ClassFromDebugLibWithExplicitDefaultConstructor {
ProductionClass31(); // Bad: super() is not synthesized.
ProductionClass31.named(); // Bad: super() is not synthesized.
}

class ProductionClass32 extends ClassFromDebugLibWithExplicitDefaultConstructor { }

class ProductionClass41 extends ClassFromDebugLibWithExplicitConstructorAndFormalParameters {
ProductionClass41.named(super.value); // Bad: super(int value) is not synthesized.
}

class ProductionClass5 {
ProductionClass5(this.debugOnlyField); // Bad: accessing debug-only field.
ProductionClass5.named(int value) : debugOnlyField = value; // Bad: accessing debug-only field.

@debugAssert
final int debugOnlyField;
}

class ProductionClass33 implements ClassFromDebugLibWithExplicitDefaultConstructor { }

class _DebugOnlyClass1 implements ProductionClassWithFactoryConstructors { }
@debugAssert
class _DebugOnlyClass2 implements ProductionClassWithFactoryConstructors {
_DebugOnlyClass2();
}
class _DebugOnlyClass3 implements ProductionClassWithFactoryConstructors {
@debugAssert
const _DebugOnlyClass3.named();
// ignore: unused_element
const _DebugOnlyClass3.nonDebug() : this.named(); // Bad: named is a debug-only constructor.
}

class ProductionClassWithFactoryConstructors {
// good.
factory ProductionClassWithFactoryConstructors.named1() = _DebugOnlyClass1;
// bad.
factory ProductionClassWithFactoryConstructors.named2() = _DebugOnlyClass2;
// bad.
factory ProductionClassWithFactoryConstructors.named3() = _DebugOnlyClass3.named;
}

void takeAnything(Object? input) { }

void testConstructors() {
// With named constructor.
ProductionClass11();
// With synthesized default constructor.
ProductionClass21(); // good: the constructor isn't defined by the super class.
ProductionClass22(); // good: the constructor isn't defined by the super class.
// With explicit default constructor.
ProductionClass32(); // bad: the constructor is defined by the super class thus marked as debug-only.
takeAnything(ProductionClass32.new); // Also bad for the same reason.
ProductionClass33(); // good: the debug-only constructor is not inherited.
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// Copyright 2014 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

@debugAssert
library;

const String debugAssert = '';

String globalVariableFromDebugLib = '';

void globalFunctionFromDebugLib() { }

mixin MixinFromDebugLib {
static int staticVarFromDebugLib = 0;

static void staticMethodFromDebugLib() { }

int fieldFromDebugLib = 0;

int get debugGetSet => _debugGetSet;
int _debugGetSet = 0;
set debugGetSet(int value) => _debugGetSet = value;

void methodFromDebugLib() { }

MixinFromDebugLib operator +(covariant MixinFromDebugLib rhs);

int operator ~() => ~debugGetSet;

int operator [](int index) => index;
}

class ClassFromDebugLibWithNamedConstructor {
ClassFromDebugLibWithNamedConstructor.constructor();
}

class ClassFromDebugLibWithImplicitDefaultConstructor { }

class ClassFromDebugLibWithExplicitDefaultConstructor {
ClassFromDebugLibWithExplicitDefaultConstructor();
}

class ClassFromDebugLibWithExplicitConstructorAndFormalParameters {
//ignore: avoid_unused_constructor_parameters
ClassFromDebugLibWithExplicitConstructorAndFormalParameters(int value);
//ignore: avoid_unused_constructor_parameters
ClassFromDebugLibWithExplicitConstructorAndFormalParameters.named(String value);
}
94 changes: 94 additions & 0 deletions dev/bots/test/analyze_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import 'package:path/path.dart' as path;

import '../analyze.dart';
import '../custom_rules/analyze.dart';
import '../custom_rules/debug_assert.dart';
import '../custom_rules/no_double_clamp.dart';
import '../custom_rules/no_stop_watches.dart';
import '../utils.dart';
Expand Down Expand Up @@ -267,4 +268,97 @@ void main() {
'╚═══════════════════════════════════════════════════════════════════════════════\n'
);
});

test('analyze.dart - debugAssert', () async {
final String result = await capture(() => analyzeWithRules(
testRootPath,
<AnalyzeRule>[debugAssert],
includePaths: <String>['packages/flutter/lib'],
), shouldHaveErrors: true);

final String badAnnotations = <String>[
'║ packages/flutter/lib/debug_only_access.dart: class member BaseClass.value is not annotated wtih @debugAssert, but its override MixinOnBaseClass.value is.',
'║ packages/flutter/lib/debug_only_access.dart: class member BaseClass.~ is not annotated wtih @debugAssert, but its override MixinOnBaseClass.~ is.',
'║ packages/flutter/lib/debug_only_access.dart: class member BaseClass.run is not annotated wtih @debugAssert, but its override ClassWithBadAnnotation1.run is.',
'║ packages/flutter/lib/debug_only_access.dart: class member BaseClass.run is not annotated wtih @debugAssert, but its override ClassWithBadAnnotation2.run is.',
]
.map((String line) => line.replaceAll('/', Platform.isWindows ? r'\' : '/'))
.join('\n');

expect(result,
startsWith(
'╔═╡ERROR╞═══════════════════════════════════════════════════════════════════════\n'
'║ Overriding a framework class member that was not annotated with @debugAssert and marking the override @debugAssert is not allowed.\n'
'║ A framework method/getter/setter not marked as debug-only itself cannot have a debug-only override.\n'
'║ \n'
'$badAnnotations\n'
'║ \n'
'║ Consider either removing the @debugAssert annotation, or adding the annotation to the class member that is being overridden instead.\n'
'╚═══════════════════════════════════════════════════════════════════════════════\n'
)
);

final String badConstructorAccesses = <String>[
'║ packages/flutter/lib/debug_only_constructors.dart:10: ClassFromDebugLibWithNamedConstructor.constructor accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_constructors.dart:20: ClassFromDebugLibWithExplicitDefaultConstructor.new accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_constructors.dart:21: ClassFromDebugLibWithExplicitDefaultConstructor.new accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_constructors.dart:27: ClassFromDebugLibWithExplicitConstructorAndFormalParameters.new accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_constructors.dart:31: debugOnlyField accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_constructors.dart:32: debugOnlyField accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_constructors.dart:49: _DebugOnlyClass3.named accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_constructors.dart:56: _DebugOnlyClass2.new accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_constructors.dart:58: _DebugOnlyClass3.named accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_constructors.dart:70: ProductionClass32.new accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_constructors.dart:71: ProductionClass32.new accessed outside of an assert.\n',
].map((String line) => line.replaceAll('/', Platform.isWindows ? r'\' : '/'))
.join();

final String otherBadAccesses = <String>[
'║ packages/flutter/lib/debug_only_access.dart:15: globalVariableFromDebugLib accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:15: globalVariableFromDebugLib= accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:16: globalFunctionFromDebugLib accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:17: globalFunctionFromDebugLib accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:18: globalFunctionFromDebugLib accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:19: MixinFromDebugLib.staticMethodFromDebugLib accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:20: MixinFromDebugLib.staticMethodFromDebugLib accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:21: MixinFromDebugLib.fieldFromDebugLib accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:22: MixinFromDebugLib.fieldFromDebugLib accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:23: MixinFromDebugLib.debugGetSet accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:24: MixinFromDebugLib.debugGetSet accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:25: MixinFromDebugLib.debugGetSet= accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:26: MixinFromDebugLib.debugGetSet= accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:27: MixinFromDebugLib.fieldFromDebugLib accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:27: MixinFromDebugLib.fieldFromDebugLib= accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:27: MixinFromDebugLib.debugGetSet accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:28: MixinFromDebugLib.debugGetSet accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:28: MixinFromDebugLib.debugGetSet= accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:28: MixinFromDebugLib.debugGetSet accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:29: MixinFromDebugLib.fieldFromDebugLib accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:29: MixinFromDebugLib.fieldFromDebugLib= accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:29: MixinFromDebugLib.debugGetSet accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:30: MixinFromDebugLib.debugGetSet accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:30: MixinFromDebugLib.debugGetSet= accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:30: MixinFromDebugLib.debugGetSet accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:31: MixinFromDebugLib.methodFromDebugLib accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:32: debugOnlyExtensionMethod accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:33: debugOnlyExtensionMethod accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:34: DebugOnlyEnum.foo accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:35: DebugOnlyEnum.values accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:36: DebugOnlyMixinOnRegularEnum.debugOnlyMethod accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:39: ProductionClassWithDebugOnlyMixin.+ accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:40: ProductionClassWithDebugOnlyMixin.+ accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:41: ProductionClassWithDebugOnlyMixin.+ accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:42: ProductionClassWithDebugOnlyMixin.+ accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:43: MixinFromDebugLib.~ accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:44: MixinFromDebugLib.~ accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:45: MixinFromDebugLib.[] accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:45: MixinFromDebugLib.debugGetSet accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:46: MixinFromDebugLib.[] accessed outside of an assert.\n',
'║ packages/flutter/lib/debug_only_access.dart:46: MixinFromDebugLib.debugGetSet accessed outside of an assert.',
].map((String line) => line.replaceAll('/', Platform.isWindows ? r'\' : '/'))
.join();

expect(result, contains(badConstructorAccesses));
expect(result, contains(otherBadAccesses));
});
}
Loading