LogoSkills

DCM Auto-Fix Pitfalls

Side effects and countermeasures that may occur when running `dcm analyze --fix`.

Side effects and countermeasures that may occur when running dcm analyze --fix.

1. Type Inference Pitfalls (avoid-inferrable-type-arguments)#

Problem#

--fix auto-removes "inferrable" type arguments, but in reality the types may be required.

Dangerous auto-fix Cases#

// Before (working correctly)
Tween<double>(begin: 0.0, end: 1.0)
closeOverlay<void>(context)
BlocProvider.value<MyBloc>(value: bloc, child: child)
context.read<MyBloc>().add(event)

// After --fix (compile error!)
Tween(begin: 0.0, end: 1.0)           // num으로 추론 → double 불일치
closeOverlay(context)                   // 제네릭 누락 → 타입 에러
BlocProvider.value(value: bloc, child: child)  // BLoC 타입 추론 실패
context.read().add(event)              // 타입 추론 불가

Patterns Where Types Must Never Be Removed#

PatternReason
Tween<double>Incorrectly inferred as num
closeOverlay<void>Return type specification needed
closeDrawer<void>Return type specification needed
BlocProvider.value<T>BLoC type inference fails
BlocProvider<T> (typed list) List context type inferred as base type -> runtime Provider not found
context.read<T>()Provider/BLoC type required
context.watch<T>()Provider/BLoC type required
getIt<T>()DI container type required

⚠️ Actual Production Incident: BlocProvider in typed List#

This is an actual incident that occurred in commit e72306ef5.

// 리스트 타입이 BlocProvider<Bloc<Object?, Object?>>로 선언된 경우
final List<BlocProvider<Bloc<Object?, Object?>>> _providers = [
  // ❌ After auto-fix: T is inferred as Bloc<Object?, Object?>
  BlocProvider(create: (_) => getIt<AuthBloc>()),
  BlocProvider(create: (_) => getIt<ThemeBloc>()),
  // -> All Providers registered with the same type!
  // -> ProviderNotFoundException on context.read<AuthBloc>()!

  // ✅ Type specification required
  // ignore: avoid-inferrable-type-arguments
  BlocProvider<AuthBloc>(create: (_) => getIt<AuthBloc>()),
  // ignore: avoid-inferrable-type-arguments
  BlocProvider<ThemeBloc>(create: (_) => getIt<ThemeBloc>()),
];

Why it's dangerous:

  • No compile errors (type compatible)
  • Passes flutter analyze
  • Runtime errors only in release builds
  • Error message is minified making root cause hard to identify: Provider<minified:o2> not found

Countermeasure#

After running --fix, always verify compile errors with flutter analyze. Manually restore types if removed in above patterns. Especially when BlocProvider is inside a typed list, always keep the type parameter.


2. Async Removal Cascading (avoid-redundant-async)#

Problem#

Removing the async keyword from BDD step functions causes use_of_void_result errors at call sites with await.

Actual Occurrence (35 test files, 265 locations)#

// Step function: --fix removes async
// Before
Future<void> theKpiCardShouldBeDisplayed(WidgetTester tester) async {
  expect(find.byType(KpiCard), findsOneWidget);
}

// After --fix (return type changed to void)
void theKpiCardShouldBeDisplayed(WidgetTester tester) {
  expect(find.byType(KpiCard), findsOneWidget);
}
// Test file: await applied to void result causes error
testWidgets('KPI card test', (tester) async {
  await bddSetUp(tester);
  await theKpiCardShouldBeDisplayed(tester);  // use_of_void_result!
});

Countermeasure Methods#

If async is removed from step function, the await at call sites must also be removed:

// ✅ CORRECT: Call synchronous functions without await
testWidgets('KPI card test', (tester) async {
  await bddSetUp(tester);
  theKpiCardShouldBeDisplayed(tester);  // await 제거
});

Prevention#

Write step functions that only perform synchronous operations with void return from the start:

// ✅ Write as synchronous function from the start (recommended)
void theKpiCardShouldBeDisplayed(WidgetTester tester) {
  expect(find.byType(KpiCard), findsOneWidget);
}

3. @Tags Format Error#

Problem#

Test filtering fails when space-separated tags are included in a single string in @Tags annotations.

// ❌ WRONG: Space-separated tags in a single string
@Tags(['smoke @sales_analysis'])

// ✅ CORRECT: Separate each tag into individual strings
@Tags(['smoke', 'sales_analysis'])

Verification Method#

grep -r  " @Tags\[ ' "   --include= " *.dart "   | grep  "   @ "

Follow this order when applying DCM auto-fix:

1. dcm analyze --fix .           # Run auto-fix
2. flutter analyze               # Check compile errors
3. Manual fixes (restore types etc.) # Check above pitfall patterns
4. melos run test                # Verify tests pass
5. dcm analyze .                 # Final DCM check

Quick Reference#

RuleSeverityauto-fix Side EffectCountermeasure
avoid-inferrable-type-arguments Critical BlocProvider in typed list -> Runtime Provider not found (production incident) Manual restore + ignore comment
avoid-inferrable-type-arguments High Tween, context.read type removal -> Compile error Manual restore
avoid-redundant-async High Step function async removal -> call site await error (265 locations) Remove await at call sites simultaneously
@Tags format Low Not auto-fix, manually discovered Separate tag strings