Skip to content

feat(lint): add UncheckedFlagAccess, ExpiredFeatureFlag, and InvalidFlagReference detectors#176

Merged
kirich1409 merged 3 commits into
mainfrom
feat/lint-invalid-flag-reference
Apr 29, 2026
Merged

feat(lint): add UncheckedFlagAccess, ExpiredFeatureFlag, and InvalidFlagReference detectors#176
kirich1409 merged 3 commits into
mainfrom
feat/lint-invalid-flag-reference

Conversation

@kirich1409

@kirich1409 kirich1409 commented Apr 29, 2026

Copy link
Copy Markdown
Contributor

Adds three Android Lint detectors to featured-lint-rules.

Closes #157
Closes #158
Closes #159

Changes

UncheckedFlagAccessDetector

UAST scanner for UCallExpression. Resolves @BehindFlag via PSI (SOURCE retention workaround). Walks the UAST parent chain for guard contexts: if/when referencing the flag by name, or an enclosing @BehindFlag-annotated function. v1 scope: direct calls only — callable references, companion scope escape, and @AssumesFlag deferred.

ExpiredFeatureFlagDetector

Visits @ExpiresAt annotations on ConfigParam properties. Parses the ISO-8601 date and reports if it is in the past. Deduplicates via visitedSourceElements to avoid double-firing from @Target(PROPERTY, FIELD).

InvalidFlagReferenceDetector

File-level UAST scanner. Collects ConfigParam property names in the file, then validates @BehindFlag/@AssumesFlag flagName arguments against them. Skips files with no ConfigParam declarations to avoid false positives on generated code.

Tests

27/27 tests pass (positive + negative cases for each detector).

Detects @BehindFlag/@AssumesFlag annotations whose flagName does not
match any ConfigParam property in the same file. Skips files with no
ConfigParam declarations to avoid false positives from generated code.

Also fixes pre-existing compile errors in ExpiredFeatureFlagDetector
and UncheckedFlagAccessDetector (ambiguous overload, missing uastContents
API) and adds their test files that were present but never committed.
@qodo-code-review

Copy link
Copy Markdown
ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one.

Detects ConfigParam properties annotated with @ExpiresAt where the date
has passed. Rewrites the previous broken implementation to use
getApplicableUastTypes + visitAnnotation, which avoids the duplicate-fire
issue caused by Kotlin @target(PROPERTY, FIELD) generating two UAST
annotation nodes from the same KtAnnotationEntry. Deduplicates remaining
visits via sourcePsi identity tracking within the file handler.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@kirich1409 kirich1409 changed the title feat(lint): add InvalidFlagReference detector (#159) feat(lint): add InvalidFlagReference and ExpiredFeatureFlag detectors (#159, #158) Apr 29, 2026
@kirich1409 kirich1409 changed the title feat(lint): add InvalidFlagReference and ExpiredFeatureFlag detectors (#159, #158) feat(lint): add UncheckedFlagAccess, ExpiredFeatureFlag, and InvalidFlagReference detectors Apr 29, 2026
@kirich1409 kirich1409 merged commit 8979f24 into main Apr 29, 2026
7 of 8 checks passed
@kirich1409 kirich1409 deleted the feat/lint-invalid-flag-reference branch April 29, 2026 20:18
@kirich1409 kirich1409 mentioned this pull request May 17, 2026
7 tasks
kirich1409 added a commit that referenced this pull request May 17, 2026
Rename [Unreleased] to [1.0.0-Beta1] - 2026-05-17, add empty
[Unreleased] section, add compare-link, restructure Added
section by area, and backfill items audited against git log:

- Added: enum DSL (#162), CC support (#164), JavaPreferences
  default (#167), lint detectors (#141, #176, #181), Detekt
  rules (#142), FlagRegistry codegen (#110), AGP Variant
  ProGuard auto-wire, E2E test, R8 DCE module (#165),
  SECURITY.md (#173), GH templates (#175).
- Changed: AGP 9.1 / Gradle 9.3 migration (#135), providers/
  directory reshuffle (#128).
- Removed: BCV plugin (#150), @LocalFlag/@RemoteFlag.
- Fixed: Firebase wrap RuntimeException (#151), MIT POM (#174),
  quickstart artifact IDs (#179).

Closes #166.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

1 participant