JetBrains/intellij-community · error · ContractInheritedException
changeSignature.contract.converter.inherited.annotation
changeSignature.contract.converter.inherited.annotation
Error message
Annotation is inherited from base method
What it means
Thrown by ContractConverter.convertContract when the @Contract annotation found for a method is declared on a base method, not on the method being refactored (annotation.getOwner() != method.getModifierList() throws ContractInheritedException). Change Signature cannot rewrite a contract that is physically declared elsewhere in the hierarchy, since updating it would affect all overrides.
Source
Thrown at java/java-impl/src/com/intellij/refactoring/changeSignature/ContractConverter.java:39
import java.util.List;
public final class ContractConverter {
private ContractConverter() {}
public static @Nullable PsiAnnotation convertContract(@NotNull PsiMethod method, @NotNull JavaChangeInfo info) throws ContractConversionException {
return convertContract(method, info.getOldParameterNames(), info.getNewParameters());
}
public static @Nullable PsiAnnotation convertContract(@NotNull PsiMethod method,
String @NotNull [] oldParameterNames,
JavaParameterInfo @NotNull [] newParameters) throws ContractConversionException {
PsiAnnotation annotation = JavaMethodContractUtil.findContractAnnotation(method);
if (annotation == null || AnnotationUtil.isInferredAnnotation(annotation)) return null;
if (AnnotationUtil.isExternalAnnotation(annotation)) {
throw new ContractConversionException(JavaRefactoringBundle.message("changeSignature.contract.converter.external.annotations"));
}
if (annotation.getOwner() != method.getModifierList()) {
throw new ContractInheritedException();
}
if (annotation.findDeclaredAttributeValue(MutationSignature.ATTR_MUTATES) != null) {
throw new ContractConversionException(JavaRefactoringBundle.message("changeSignature.contract.converter.mutation.contract"));
}
String text = AnnotationUtil.getStringAttributeValue(annotation, null);
List<StandardMethodContract> contracts = Collections.emptyList();
if (text != null) {
try {
contracts = StandardMethodContract.parseContract(text);
}
catch (StandardMethodContract.ParseException exception) {
String definitionError = JavaRefactoringBundle.message("changeSignature.contract.converter.definition.error",
exception.getMessage());
throw new ContractConversionException(definitionError);
}
}
int[] newToOldIndex = StreamEx.of(newParameters).mapToInt(ParameterInfo::getOldIndex).toArray();
int[] oldToNewIndex = reverseIndex(oldParameterNames.length, newToOldIndex);View on GitHub (pinned to be881553f2)
Solutions
- Run Change Signature on the base method that actually declares the @Contract, so the annotation is updated together with the hierarchy.
- Remove the inherited contract dependency: declare (or delete) the contract on the base method explicitly before refactoring.
- Manually update the @Contract on the base method after the refactoring if the IDE refuses to convert it.
Example fix
// before
interface Base { @Contract("null -> false") boolean check(String s); }
class Impl implements Base { public boolean check(String s){...} } // refactor Impl -> throws
// after: refactor Base instead
interface Base { @Contract("null -> false") boolean check(@Nullable String s); } Defensive patterns
Strategy: type-guard
Validate before calling
PsiAnnotation a = JavaMethodContractUtil.findContractAnnotation(method);
if (a != null && a.getOwner() != method.getModifierList()) {
// contract is declared on a base method; refactor the base method instead
} Type guard
static boolean ownsItsContract(@NotNull PsiMethod m) {
PsiAnnotation a = JavaMethodContractUtil.findContractAnnotation(m);
return a == null || a.getOwner() == m.getModifierList();
} Try / catch
try { ContractConverter.convertContract(method, info); } catch (ContractInheritedException e) { /* locate declaring base method via findContractAnnotation owner and refactor that */ } Prevention
- Refactor the method that declares the @Contract, not overrides that inherit it.
- When annotating hierarchies, decide deliberately whether the contract lives on the interface/base or each impl.
- Use Find Usages/gutter navigation on the annotation to find its physical declaration before Change Signature.
When it happens
Trigger: Running Change Signature on an overriding method that inherits @Contract from a superclass or interface method (JavaMethodContractUtil.findContractAnnotation walks up the hierarchy); the contract needs conversion because parameters moved/changed.
Common situations: Interface methods annotated with @Contract and implementations being refactored; library base classes annotated upstream; changing a subclass method signature while the parent's contract still references old parameter positions.
Related errors
- changeSignature.contract.converter.external.annotations
- changeSignature.contract.converter.mutation.contract
- changeSignature.contract.converter.definition.error
- changeSignature.contract.converter.invalid.clause
- changeSignature.contract.converter.parameter.removed
AI-assisted analysis of JetBrains/intellij-community@be881553f2 (2026-08-14).
Data as JSON: /api/errors/8445d4d05f1724d0.
Report an issue: GitHub.