1414import com .intellij .psi .PsiRecursiveElementVisitor ;
1515import com .intellij .psi .PsiWhiteSpace ;
1616import com .intellij .psi .util .ClassUtil ;
17- import com .intellij .psi .util .PsiClassUtil ;
1817import net .neoforged .accesstransformer .parser .AccessTransformerFiles ;
1918import net .neoforged .accesstransformer .parser .Target ;
2019import net .neoforged .accesstransformer .parser .Transformation ;
@@ -46,12 +45,20 @@ class ApplyATsVisitor extends PsiRecursiveElementVisitor {
4645 private final AccessTransformerFiles ats ;
4746 private final Replacements replacements ;
4847 private final Map <Target , Transformation > pendingATs ;
48+ private final boolean inheritMethodATs ;
4949 private final Logger logger ;
5050 boolean errored = false ;
5151
52- public ApplyATsVisitor (AccessTransformerFiles ats , Replacements replacements , Map <Target , Transformation > pendingATs , Logger logger ) {
52+ public ApplyATsVisitor (
53+ AccessTransformerFiles ats ,
54+ Replacements replacements ,
55+ Map <Target , Transformation > pendingATs ,
56+ boolean inheritMethodATs ,
57+ Logger logger
58+ ) {
5359 this .ats = ats ;
5460 this .replacements = replacements ;
61+ this .inheritMethodATs = inheritMethodATs ;
5562 this .logger = logger ;
5663 this .pendingATs = pendingATs ;
5764 }
@@ -61,7 +68,25 @@ public void visitElement(@NotNull PsiElement element) {
6168 if (element instanceof PsiClass psiClass ) {
6269 if (psiClass .getQualifiedName () != null ) {
6370 String className = ClassUtil .getJVMClassName (psiClass );
64- if (!ats .containsClassTarget (className )) {
71+ boolean shouldBeSkipped = !ats .containsClassTarget (className );
72+
73+ // Classes may need changing that are not in the source at set.
74+ // Check if any of this classe's parents are in the source at set first to potentially inherit their ATs.
75+ if (shouldBeSkipped && this .inheritMethodATs ) {
76+ for (
77+ PsiClass parentType = psiClass .getSuperClass ();
78+ parentType != null ;
79+ parentType = parentType .getSuperClass ()
80+ ) {
81+ if (this .ats .containsClassTarget (ClassUtil .getJVMClassName (parentType ))) {
82+ // Process class as we inherit method ATs and we found *some* AT.
83+ // Heuristic isn't perfect, but the alternative would be scanning *all* method targets for the parent owning type.
84+ shouldBeSkipped = false ;
85+ break ;
86+ }
87+ }
88+ }
89+ if (shouldBeSkipped ) {
6590 // Skip this class and its children, but not the inner classes
6691 for (PsiClass innerClass : psiClass .getInnerClasses ()) {
6792 visitElement (innerClass );
@@ -106,10 +131,20 @@ public void visitElement(@NotNull PsiElement element) {
106131 apply (pendingATs .remove (new Target .FieldTarget (className , field .getName ())), field , cls );
107132 }
108133 } else if (element instanceof PsiMethod method ) {
109- final var cls = method .getContainingClass ();
110- if (cls != null && cls .getQualifiedName () != null ) {
111- String className = ClassUtil .getJVMClassName (cls );
112- apply (pendingATs .remove (method (className , method )), method , cls );
134+ final var owningType = method .getContainingClass ();
135+ if (owningType != null ) {
136+ // Locate transformation by searching owning type and its parents.
137+ // Remove if this is AT is defined for its immediate owning type.
138+ Transformation foundTransformation = this .pendingATs .remove (method (ClassUtil .getJVMClassName (owningType ), method ));
139+ PsiClass definedForSuperType = owningType ;
140+ for (;
141+ this .inheritMethodATs && foundTransformation == null && definedForSuperType != null && definedForSuperType .getQualifiedName () != null ;
142+ definedForSuperType = definedForSuperType .getSuperClass ()
143+ ) {
144+ foundTransformation = this .ats .getAccessTransformers ().get (method (ClassUtil .getJVMClassName (definedForSuperType ), method ));
145+ }
146+
147+ apply (foundTransformation , method , owningType );
113148 }
114149 }
115150
0 commit comments