{"record":{"id":"8445d4d05f1724d0","repo":"JetBrains/intellij-community","slug":"changesignature-contract-converter-inherited-annot","errorCode":"changeSignature.contract.converter.inherited.annotation","errorMessage":"Annotation is inherited from base method","messagePattern":"Annotation is inherited from base method","errorType":"exception","errorClass":"ContractInheritedException","httpStatus":null,"severity":"error","filePath":"java/java-impl/src/com/intellij/refactoring/changeSignature/ContractConverter.java","lineNumber":39,"sourceCode":"import java.util.List;\n\npublic final class ContractConverter {\n  private ContractConverter() {}\n\n  public static @Nullable PsiAnnotation convertContract(@NotNull PsiMethod method, @NotNull JavaChangeInfo info) throws ContractConversionException {\n    return convertContract(method, info.getOldParameterNames(), info.getNewParameters());\n  }\n\n  public static @Nullable PsiAnnotation convertContract(@NotNull PsiMethod method,\n                                                        String @NotNull [] oldParameterNames,\n                                                        JavaParameterInfo @NotNull [] newParameters) throws ContractConversionException {\n    PsiAnnotation annotation = JavaMethodContractUtil.findContractAnnotation(method);\n    if (annotation == null || AnnotationUtil.isInferredAnnotation(annotation)) return null;\n    if (AnnotationUtil.isExternalAnnotation(annotation)) {\n      throw new ContractConversionException(JavaRefactoringBundle.message(\"changeSignature.contract.converter.external.annotations\"));\n    }\n    if (annotation.getOwner() != method.getModifierList()) {\n      throw new ContractInheritedException();\n    }\n    if (annotation.findDeclaredAttributeValue(MutationSignature.ATTR_MUTATES) != null) {\n      throw new ContractConversionException(JavaRefactoringBundle.message(\"changeSignature.contract.converter.mutation.contract\"));\n    }\n    String text = AnnotationUtil.getStringAttributeValue(annotation, null);\n    List<StandardMethodContract> contracts = Collections.emptyList();\n    if (text != null) {\n      try {\n        contracts = StandardMethodContract.parseContract(text);\n      }\n      catch (StandardMethodContract.ParseException exception) {\n        String definitionError = JavaRefactoringBundle.message(\"changeSignature.contract.converter.definition.error\",\n                                                               exception.getMessage());\n        throw new ContractConversionException(definitionError);\n      }\n    }\n    int[] newToOldIndex = StreamEx.of(newParameters).mapToInt(ParameterInfo::getOldIndex).toArray();\n    int[] oldToNewIndex = reverseIndex(oldParameterNames.length, newToOldIndex);","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/JetBrains/intellij-community/blob/be881553f2a76ac8b4ea53950d93f0de78295c19/java/java-impl/src/com/intellij/refactoring/changeSignature/ContractConverter.java#L21-L57","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\ninterface Base { @Contract(\"null -> false\") boolean check(String s); }\nclass Impl implements Base { public boolean check(String s){...} } // refactor Impl -> throws\n// after: refactor Base instead\ninterface Base { @Contract(\"null -> false\") boolean check(@Nullable String s); }","handlingStrategy":"type-guard","validationCode":"PsiAnnotation a = JavaMethodContractUtil.findContractAnnotation(method);\nif (a != null && a.getOwner() != method.getModifierList()) {\n  // contract is declared on a base method; refactor the base method instead\n}","typeGuard":"static boolean ownsItsContract(@NotNull PsiMethod m) {\n  PsiAnnotation a = JavaMethodContractUtil.findContractAnnotation(m);\n  return a == null || a.getOwner() == m.getModifierList();\n}","tryCatchPattern":"try { ContractConverter.convertContract(method, info); } catch (ContractInheritedException e) { /* locate declaring base method via findContractAnnotation owner and refactor that */ }","preventionTips":["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."],"tags":["java","refactoring","change-signature","contract-annotation","inheritance"],"backgroundTag":null,"analyzedSha":"be881553f2a76ac8b4ea53950d93f0de78295c19","analyzedAt":"2026-08-14T14:13:06.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}