Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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 .agents/skills/perf-review/references/checks.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ Format: **pattern** — *expensive when (the interprocedural condition to trace)
6. **FFI / native-boundary crossing on a hot path** *(central to the shared-core effort)* — *a native crossing per-span/per-item (not batched), or transporting strings/objects rather than primitives/IDs* — flag-with-confidence (boundary cost is mechanism-determined; runtime-specific pinning → addendum) — SEV-1/2 (SEV-1 if it blocks/pins under concurrency) — fix: batch (one per flush, not per item); transport interned IDs not strings; keep crossings off the hot/concurrency path.
7. **Escape / allocation-elision defeated** *(Java/Go/.NET/V8 all have a version)* — *a refactor makes a previously-local object escape (stored, returned, captured by a closure, passed to a virtual/non-inlined call) → silent heap allocation on a hot path* — **flag-as-measure** ("may now escape and allocate; verify with an allocation profiler") — SEV-2/3 — fix: keep it local; avoid the escaping store/capture.
8. **`@NoEscape` field-storage violation** — *a field (instance or static, directly or as a generic type argument) declared with an `@NoEscape`-annotated type (`datadog.trace.api.function.NoEscape`), **or** initialized directly from a call to an `@NoEscape`-annotated method — with no comment at the declaration justifying the retention*. The type form marks a concrete type (e.g. `SubSequence`); the method form exists for a return value whose concrete type can't itself carry the annotation (an anonymous class or lambda implementing a JDK interface). The annotation's own javadoc carries a self-contained "Checker contract" section (trigger / not-a-trigger / violation example / compliant example, both forms) written so this can be checked from the diff alone, with no other context needed. The underlying rule is "should", not "must" (RFC-2119 sense): a trigger is a presumptive finding, not an automatic failure — compliant if the declaration carries either a `// Retained on purpose: <reason>`-style comment, or `@SuppressPerfContract(value = NoEscape.class, reason = "...")` (`datadog.perfcontract.SuppressPerfContract` — the generic exemption mechanism shared by every perf-contract marker, not `@NoEscape`-specific), or a named canned-exception annotation itself meta-annotated `@SuppressPerfContract` with `NoEscape.class` among its `value` (e.g. a hypothetical `@Borrowed`) — treat the meta-annotation's own `reason` as satisfying the requirement. Don't flag a `SuppressPerfContract`/canned-exception use as non-compliant just for being terse; that mirrors the annotation's own Checker contract. **flag-with-confidence** — SEV-2/3 (SEV-1 if the annotated type/method shares backing storage with something large, per its own javadoc). Current wearers: `SubSequence`, `Maybe` (type form; see J7 below for `SubSequence`'s specific retention-vs-transient discriminator). **No lint enforces this yet — the AI reviewer is the only check, so this stays here (not under deterministic-lint candidates below) until a checker lands and it can migrate down.**
9. **`@StaticLifetime` field-lifetime violation** — *a field annotated `@StaticLifetime` (`datadog.perfcontract.StaticLifetime`) that is neither `static final` nor a `final` instance field declared on a class annotated `@Singleton` (`datadog.perfcontract.Singleton`); a shared `ClassValue` holder follows the same rule*. Declaration-scan only, same shape as the `@NoEscape` field-storage check above — it does not attempt to prove a field is actually reused enough to be worth caching, and it does not catch an unannotated per-instance cache; it only guards fields that opt in. `@Singleton`, applied to a class, is a trusted, unverified declaration (v1 posture — the same stance the other perf-contract markers take toward `static final` itself) that the class has exactly one process-wide instance; declaring it satisfies `@StaticLifetime` for that class's `final` instance fields. **flag-with-confidence** — SEV-2/3, motivated by a real production defect (a per-request-constructed cache that never actually amortized anything — see the annotation's own javadoc for the full trigger/accepted-form table). Refines the "per-call cache / expensive-object creation that should be static/once" deterministic-lint candidate below by giving that pattern's opted-in subset an explicit annotation and trigger; the unannotated general case is still that line's job. **No lint enforces this yet — the AI reviewer is the only check, so this stays here (not under deterministic-lint candidates below) until a checker lands and it can migrate down.**
9. **`@StaticLifetime` field-lifetime violation** — *a field annotated `@StaticLifetime` (`datadog.perfcontract.StaticLifetime`), **or** an unannotated field whose declared type is listed in the contract's `@ImpliedFor` (`datadog.perfcontract.ImpliedFor`; read the types from the `@ImpliedFor` on `StaticLifetime` itself, subtypes included), that is neither `static final` nor a `final` instance field declared on a class annotated `@Singleton` (`datadog.perfcontract.Singleton`)*. Declaration-scan only, same shape as the `@NoEscape` field-storage check above — it does not attempt to prove a field is actually reused enough to be worth caching, and it does not catch an unannotated per-instance cache of a non-implied type; it only guards fields that opt in or whose type implies the contract. A deliberate per-instance holder of an implied type is compliant with `@SuppressPerfContract(value = StaticLifetime.class, reason = "...")`. `@Singleton`, applied to a class, is a trusted, unverified declaration (v1 posture — the same stance the other perf-contract markers take toward `static final` itself) that the class has exactly one process-wide instance; declaring it satisfies `@StaticLifetime` for that class's `final` instance fields. **flag-with-confidence** — SEV-2/3, motivated by a real production defect (a per-request-constructed cache that never actually amortized anything — see the annotation's own javadoc for the full trigger/accepted-form table). Refines the "per-call cache / expensive-object creation that should be static/once" deterministic-lint candidate below by giving that pattern's opted-in subset an explicit annotation and trigger; the unannotated general case is still that line's job. **No lint enforces this yet — the AI reviewer is the only check, so this stays here (not under deterministic-lint candidates below) until a checker lands and it can migrate down.**

## Deterministic-lint candidates (DON'T spend AI budget — make these real lints)
Fixed-signature, mechanically checkable:
Expand Down
41 changes: 41 additions & 0 deletions internal-api/src/main/java/datadog/perfcontract/ImpliedFor.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package datadog.perfcontract;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Applies a {@link PerfContract} to types that cannot carry it themselves, such as JDK or
* third-party classes. Annotate the contract annotation with the types its rule is implied for.
*
* <p>A contract is normally opt-in: it guards only declarations that carry it. Some types have the
* contract by their nature. A non-{@code static} {@link ClassValue} field, for example, discards
* its per-class cache with each instance, yet nobody annotates the broken field. Listing {@code
* ClassValue} here lets tools check every such field.
*
* <p>This marker changes no runtime behavior. {@link RetentionPolicy#CLASS} lets tools read the
* implied types from the contract's class file, like the contract itself.
*
* <p><b>Checker contract.</b> For a contract annotation carrying {@code @ImpliedFor}:
*
* <ul>
* <li><b>Implied fields:</b> a field whose declared type is a listed type, or a subtype of one,
* is checked as if it carried the contract. Only field contracts use this so far.
* <li><b>Suppression:</b> {@link SuppressPerfContract} exempts an implied finding exactly as it
* exempts an explicit one.
* <li><b>Invalid:</b> {@code @ImpliedFor} on an annotation that is not itself annotated {@link
* PerfContract}.
* </ul>
*
* <p>Add a type only when a real case shows that every declaration of it needs the contract.
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.ANNOTATION_TYPE)
public @interface ImpliedFor {

/** The types the annotated contract is implied for. */
Class<?>[] value();
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,26 +17,27 @@
* <p>This marker changes no runtime behavior. The declaration rules below guide AI review; no
* static checker currently enforces them.
*
* <p><b>Checker contract.</b> Inspect only fields annotated {@code @StaticLifetime}:
* <p><b>Checker contract.</b> Inspect fields annotated {@code @StaticLifetime}, and the fields its
* {@link ImpliedFor} implies it for:
*
* <ul>
* <li><b>Accepted:</b> a {@code static final} field, or a {@code final} instance field declared
* on a class annotated {@link Singleton}. The singleton declaration is trusted, not verified.
* <li><b>Violation:</b> any other annotated field. This includes fields without {@code final} and
* instance fields on classes without {@code @Singleton}, even if a class is constructed only
* once per session.
* <li><b>Out of scope:</b> unannotated fields, how often a value is reused, and replacement of
* state inside the referenced object. The check does not follow references through the object
* graph.
* <li><b>Out of scope:</b> unannotated fields that {@link ImpliedFor} does not cover, how often a
* value is reused, and replacement of state inside the referenced object. The check does not
* follow references through the object graph.
* </ul>
*
* <p>Requiring {@code final} prevents reassignment from discarding cached state and repeating
* setup. It does not prevent the referenced object from changing its own state.
*
* <p>A shared {@link java.lang.ClassValue} satisfies the same field rules. Its values are cached
* per class; computation may be repeated under races or after {@link
* java.lang.ClassValue#remove(Class) remove}. This contract covers the shared holder, not the
* lifetime of each cached value.
* <p>The implied types are holders whose per-instance form discards its cached values with each
* instance. A {@link ClassValue}'s values are cached per class; computation may be repeated under
* races or after {@link ClassValue#remove(Class) remove}. This contract covers the shared holder,
* not the lifetime of each cached value.
*
* <p>Violation examples: an instance cache on a class without {@code @Singleton}, or a static cache
* without {@code final}.
Expand All @@ -63,6 +64,7 @@
*/
@Documented
@PerfContract
@ImpliedFor(ClassValue.class)
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.FIELD)
public @interface StaticLifetime {}
Loading