AvoidDriftInsertMissingConflictTargetRule class

Warns when a Drift insert targets a table with a UNIQUE @TableIndex on a non-primary-key column and does not pass a matching onConflict: DoUpdate(target: [uniqueCol]).

Since: v12.3.4 | Rule version: v1

Drift tables declaring @TableIndex(..., unique: true) on a non-PK column need an explicit conflict target on every batch.insert(...) / into(table).insert(...). Without target:, SQLite falls back to ON CONFLICT("id") — the primary key — and misses the real UNIQUE constraint. The insert succeeds for new rows and raises SqliteException(2067): UNIQUE constraint failed the moment the unique value (UUID, email, etc.) already exists on disk or appears twice in one batch. The crash is usually discovered late: mid-way through a static-data import, after a debugger() in debugException has silently frozen the isolate. The hazard is invisible at the call site because the table's annotation lives in a different file from the IO method.

The rule scans the current compilation unit for @TableIndex(unique: true) annotations on non-PK columns, then flags any insert, insertAll, or insertOnConflictUpdate call against those tables that does not either:

  • pass onConflict: DoUpdate(target: [<UNIQUE col>]) / DoUpdate.withExcluded(target: [...]) referencing at least one of the UNIQUE columns, or
  • use mode: InsertMode.replace (explicit REPLACE is an acceptable alternative).

insertOnConflictUpdate without an explicit target: is ALWAYS flagged against a UNIQUE-indexed non-PK column — the method looks safe but defaults to ON CONFLICT("id") and misses the real UNIQUE index.

BAD:

@TableIndex(name: 'idx', columns: {#uuid}, unique: true)
class ContactPoints extends Table {
  IntColumn get id => integer().autoIncrement()();
  TextColumn get uuid => text()();
}

Future<void> putAll(AppDatabase db, List<ContactPointsCompanion> rows) async {
  await db.batch((batch) {
    for (final row in rows) {
      batch.insert(db.contactPoints, row); // UNIQUE(uuid) will raise 2067
    }
  });
}

GOOD:

Future<void> putAll(AppDatabase db, List<ContactPointsCompanion> rows) async {
  await db.batch((batch) {
    for (final row in rows) {
      batch.insert(
        db.contactPoints,
        row,
        onConflict: DoUpdate(
          (_) => row,
          target: [db.contactPoints.uuid],
        ),
      );
    }
  });
}
Inheritance

Properties

accuracyTarget → AccuracyTarget?
Optional accuracy target for this rule (for documentation and tooling). Does not enforce; used by reports and rule-audit scripts.
no setterinherited
applicableFileTypes → Set<FileType>?
The file types this rule applies to.
no setterinherited
canUseParsedResult → bool
Indicates whether this analysis rule can work with just the parsed information or if it requires a resolved unit.
no setterinherited
certIds → List<String>
CERT coding standard identifiers (e.g. STR02-C). Populate only where there is a clear mapping; leave empty for most rules initially.
no setterinherited
code → LintCode
The lint code for this rule.
no setterinherited
configAliases → List<String>
Alternate config keys that can be used to reference this rule.
no setterinherited
conflictingRules → List<String>
Curated opposite/competing rule names.
no setterinherited
cost → RuleCost
The estimated execution cost of this rule.
no setteroverride
cweIds → List<int>
CWE identifiers this rule helps prevent or detect. https://cwe.mitre.org/ — e.g. CWE-798 (Hardcoded Credentials).
no setterinherited
description → String
Short description suitable for display in console output and IDEs.
finalinherited
diagnosticCode → DiagnosticCode
The code to report for a violation.
no setterinherited
diagnosticCodes → List<DiagnosticCode>
The diagnostic codes associated with this analysis rule.
no setterinherited
documentationUrl → String
Returns the documentation URL for this rule.
no setterinherited
effectiveSeverity → DiagnosticSeverity?
Get the effective severity for this rule, considering overrides.
no setterinherited
exampleBad → String?
Short code example that VIOLATES this rule (shown in CLI walkthrough).
no setterinherited
exampleGood → String?
Short code example of COMPLIANT code (shown in CLI walkthrough).
no setterinherited
fixGenerators → List<SaropaFixGenerator>
Fix producer generators for this rule.
no setterinherited
hashCode → int
The hash code for this object.
no setterinherited
hyphenatedName → String
Returns the rule name in hyphenated format for display.
no setterinherited
impact → LintImpact
The business impact of this rule's violations.
no setteroverride
incompatibleRules → List<String>
A list of incompatible rule names.
no setterinherited
isDisabled → bool
Check if this rule is disabled via configuration.
no setterinherited
maximumLineCount → int
Maximum line count for this rule to run.
no setterinherited
minimumLineCount → int
Minimum line count for this rule to run.
no setterinherited
name → String
The rule name.
finalinherited
owasp → OwaspMapping?
OWASP categories this rule helps prevent.
no setterinherited
pubspecVisitor → PubspecVisitor?
A visitor that visits a Pubspec to perform analysis.
no setterinherited
relatedRules → List<String>
Curated "see also" rule names for discoverability in docs/IDE tooling.
no setterinherited
reporter ← DiagnosticReporter
Sets the DiagnosticReporter for the CompilationUnit currently being visited.
no getterinherited
requiredPatterns → Set<String>?
String patterns that must be present in the file for this rule to run.
no setterinherited
requiresAsync → bool
Whether this rule only applies to async code.
no setterinherited
requiresBlocImport → bool
Whether this rule only applies to files that import Bloc.
no setterinherited
requiresClassDeclaration → bool
Whether this rule only applies to files with class declarations.
no setterinherited
requiresFlutterImport → bool
Whether this rule only applies to files that import Flutter.
no setterinherited
requiresImports → bool
Whether this rule only applies to files with imports.
no setterinherited
requiresMainFunction → bool
Whether this rule only applies to files with a main() function.
no setterinherited
requiresProviderImport → bool
Whether this rule only applies to files that import Provider.
no setterinherited
requiresRiverpodImport → bool
Whether this rule only applies to files that import Riverpod.
no setterinherited
requiresWidgets → bool
Whether this rule only applies to Flutter widget code.
no setterinherited
ruleStatus → RuleStatus
Lifecycle status. Default RuleStatus.ready. Use RuleStatus.beta for new or heuristic-heavy rules; RuleStatus.deprecated for sunset.
no setterinherited
ruleType → RuleType?
Semantic type of this rule. Default null = unspecified (legacy). When set, used for quality gates, accuracy targets, and reporting.
no setteroverride
runtimeType → Type
A representation of the runtime type of the object.
no setterinherited
skipExampleFiles → bool
Whether to skip example files (example/**).
no setterinherited
skipFixtureFiles → bool
Whether to skip fixture files (fixture/, fixtures/).
no setterinherited
skipGeneratedCode → bool
Whether to skip generated files (*.g.dart, *.freezed.dart, *.gen.dart).
no setterinherited
skipTestFiles → bool
Whether to skip test files (*_test.dart, test/**).
no setterinherited
state → RuleState
The state of this analysis rule.
finalinherited
supersedesRules → List<String>
Rule names this rule supersedes/replaces.
no setterinherited
tags → Set<String>
Tags for filtering and discovery (e.g. in docs, IDE, or CI). Examples: 'performance', 'accessibility', 'suspicious', 'convention'.
no setteroverride
testRelevance → TestRelevance
How this rule relates to test files.
no setterinherited
usesTypeResolution → bool
Whether this rule triggers lazy cross-library type resolution in the analyzer (e.g. .staticType, .library, .allSupertypes, .enclosingElement).
no setterinherited

Methods

noSuchMethod(Invocation invocation) → dynamic
Invoked when a nonexistent method or property is accessed.
inherited
registerNodeProcessors(RuleVisitorRegistry registry, RuleContext context) → void
Registers node processors in the given registry.
inherited
registerNodeProcessors2(RuleVisitorRegistry2 registry, RuleContext context) → void
Registers node processors in the given registry.
inherited
reportAtNode(AstNode? node, {List<Object> arguments = const [], List<DiagnosticMessage>? contextMessages}) → Diagnostic?
Reports a diagnostic at node with message arguments and contextMessages.
inherited
reportAtOffset(int offset, int length, {List<Object> arguments = const [], List<DiagnosticMessage>? contextMessages}) → Diagnostic
Reports a diagnostic at offset, with length, with message arguments and contextMessages.
inherited
reportAtPubNode(PubspecNode node, {List<Object> arguments = const [], List<DiagnosticMessage> contextMessages = const []}) → Diagnostic
Reports a diagnostic at Pubspec node, with message arguments and contextMessages.
inherited
reportAtSourceRange(SourceRange sourceRange, {List<Object> arguments = const [], List<DiagnosticMessage>? contextMessages}) → Diagnostic
Reports a diagnostic at sourceRange, with message arguments and contextMessages.
inherited
reportAtToken(Token token, {List<Object> arguments = const [], List<DiagnosticMessage>? contextMessages}) → Diagnostic?
Reports a diagnostic at token, with message arguments and contextMessages.
inherited
runWithReporter(SaropaDiagnosticReporter reporter, SaropaContext context) → void
Override this method to implement your lint rule.
override
shouldSkipFile(String path) → bool
Check if a file path should be skipped based on context settings.
inherited
toString() → String
A string representation of this object.
inherited

Operators

operator ==(Object other) → bool
The equality operator.
inherited