update argument resolution to

This commit is contained in:
2026-06-28 03:04:17 +02:00
parent 901b43195a
commit ff2c8f8cfb
3 changed files with 158 additions and 49 deletions

View File

@@ -132,6 +132,10 @@ public class HeuristicBeanResolutionEngine implements BeanResolutionEngine {
}
}
if (matchIdx < 1) {
return false;
}
Set<String> smDivergentTerms = new HashSet<>();
if (smPrefix != null) smDivergentTerms.add(smPrefix.toLowerCase());
for (int i = matchIdx + 1; i < p1.length; i++) {

View File

@@ -2,10 +2,12 @@ package click.kamil.springstatemachineexporter.analysis.service;
import click.kamil.springstatemachineexporter.analysis.resolver.ConstantResolver;
import click.kamil.springstatemachineexporter.ast.common.CodebaseContext;
import lombok.extern.slf4j.Slf4j;
import org.eclipse.jdt.core.dom.*;
import java.util.*;
import java.util.function.Function;
@Slf4j
public class ConstantExtractor {
private final CodebaseContext context;
private final ConstantResolver constantResolver;
@@ -132,57 +134,62 @@ public class ConstantExtractor {
}
}
if (mi.getExpression() instanceof ClassInstanceCreation cic) {
if (cic.getAnonymousClassDeclaration() != null) {
for (Object declObj : cic.getAnonymousClassDeclaration().bodyDeclarations()) {
if (declObj instanceof MethodDeclaration mdecl) {
if (mdecl.getName().getIdentifier().equals(methodName) && mdecl.getBody() != null) {
mdecl.getBody().accept(new ASTVisitor() {
@Override
public boolean visit(ReturnStatement rs) {
if (rs.getExpression() != null) {
extractConstantsFromExpression(rs.getExpression(), constants);
List<Expression> receivers = new ArrayList<>();
if (mi.getExpression() != null) {
if (mi.getExpression() instanceof ClassInstanceCreation) {
receivers.add(mi.getExpression());
} else if (variableTracer != null) {
receivers.addAll(variableTracer.traceVariableAll(mi.getExpression()));
}
}
for (Expression receiverExpr : receivers) {
if (receiverExpr instanceof ClassInstanceCreation cic) {
if (cic.getAnonymousClassDeclaration() != null) {
for (Object declObj : cic.getAnonymousClassDeclaration().bodyDeclarations()) {
if (declObj instanceof MethodDeclaration mdecl) {
if (mdecl.getName().getIdentifier().equals(methodName) && mdecl.getBody() != null) {
mdecl.getBody().accept(new ASTVisitor() {
@Override
public boolean visit(ReturnStatement rs) {
if (rs.getExpression() != null) {
extractConstantsFromExpression(rs.getExpression(), constants);
}
return super.visit(rs);
}
return super.visit(rs);
}
});
});
}
}
}
}
} else if (constructorAnalyzer != null) {
boolean handledByLombok = false;
if (methodName.startsWith("get") && methodName.length() > 3) {
String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
TypeDeclaration typeDecl = context.getTypeDeclaration(typeName);
if (typeDecl != null) {
String fieldName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
int fieldIndex = constructorAnalyzer.findConstructorArgumentIndexForLombokField(typeDecl, fieldName);
if (fieldIndex >= 0 && fieldIndex < cic.arguments().size()) {
extractConstantsFromArgument((Expression) cic.arguments().get(fieldIndex), constants);
handledByLombok = true;
}
}
}
if (!handledByLombok && methodName != null && !methodName.isEmpty()) {
List<String> narrowed = constructorAnalyzer.resolveInlineInstantiationGetterResult(
cic, methodName, context, new HashSet<>());
if (narrowed.isEmpty()) {
} else if (constructorAnalyzer != null) {
boolean handledByLombok = false;
if (methodName.startsWith("get") && methodName.length() > 3) {
String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
CompilationUnit contextCu = expr.getRoot() instanceof CompilationUnit ? (CompilationUnit) expr.getRoot() : null;
TypeDeclaration typeDecl = contextCu != null ? context.getTypeDeclaration(typeName, contextCu) : context.getTypeDeclaration(typeName);
if (typeDecl == null) typeDecl = context.getTypeDeclaration(typeName);
TypeDeclaration typeDecl = context.getTypeDeclaration(typeName);
if (typeDecl != null) {
narrowed = resolveMethodReturnConstant(context.getFqn(typeDecl), methodName, 0, new HashSet<>(), contextCu);
String fieldName = Character.toLowerCase(methodName.charAt(3)) + methodName.substring(4);
int fieldIndex = constructorAnalyzer.findConstructorArgumentIndexForLombokField(typeDecl, fieldName);
if (fieldIndex >= 0 && fieldIndex < cic.arguments().size()) {
extractConstantsFromArgument((Expression) cic.arguments().get(fieldIndex), constants);
handledByLombok = true;
}
}
}
if (narrowed != null && !narrowed.isEmpty()) {
constants.addAll(narrowed);
handledByLombok = true;
}
}
if (!handledByLombok) {
for (Object argObj : cic.arguments()) {
extractConstantsFromArgument((Expression) argObj, constants);
if (!handledByLombok && methodName != null && !methodName.isEmpty()) {
List<String> narrowed = constructorAnalyzer.resolveInlineInstantiationGetterResult(
cic, methodName, context, new HashSet<>());
if (narrowed.isEmpty()) {
String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
CompilationUnit contextCu = expr.getRoot() instanceof CompilationUnit ? (CompilationUnit) expr.getRoot() : null;
TypeDeclaration typeDecl = contextCu != null ? context.getTypeDeclaration(typeName, contextCu) : context.getTypeDeclaration(typeName);
if (typeDecl == null) typeDecl = context.getTypeDeclaration(typeName);
if (typeDecl != null) {
narrowed = resolveMethodReturnConstant(context.getFqn(typeDecl), methodName, 0, new HashSet<>(), contextCu);
}
}
if (narrowed != null && !narrowed.isEmpty()) {
constants.addAll(narrowed);
}
}
}
}
@@ -258,9 +265,16 @@ public class ConstantExtractor {
}
public List<String> resolveMethodReturnConstant(String className, String methodName, int depth, Set<String> visited, CompilationUnit contextCu) {
if (depth > 20) return Collections.emptyList();
log.debug("Entering resolveMethodReturnConstant with className={}, methodName={}, depth={}", className, methodName, depth);
if (depth > 20) {
log.debug("Exiting resolveMethodReturnConstant early (depth limit) for {}.{}", className, methodName);
return Collections.emptyList();
}
String fqn = className + "." + methodName;
if (!visited.add(fqn)) return Collections.emptyList();
if (!visited.add(fqn)) {
log.debug("Exiting resolveMethodReturnConstant early (cycle detected) for fqn={}", fqn);
return Collections.emptyList();
}
List<String> constants = new ArrayList<>();
TypeDeclaration tempTd = contextCu != null ? context.getTypeDeclaration(className, contextCu) : null;
@@ -358,6 +372,7 @@ public class ConstantExtractor {
}
}
visited.remove(fqn);
log.debug("resolveMethodReturnConstant for class={}, method={} resolved constants: {}", className, methodName, constants);
return constants;
}

View File

@@ -30,6 +30,15 @@ public class ConstructorAnalyzer {
}
public List<String> traceFieldInConstructors(TypeDeclaration td, String fieldName, CodebaseContext context, Set<String> visited) {
if (td == null || fieldName == null) return Collections.emptyList();
String fqn = context.getFqn(td);
if (fqn == null) return Collections.emptyList();
String cycleKey = fqn + "#" + fieldName;
if (!visited.add(cycleKey)) {
return Collections.emptyList();
}
List<String> results = new ArrayList<>();
for (FieldDeclaration fd : td.getFields()) {
@@ -136,6 +145,49 @@ public class ConstructorAnalyzer {
}
}
if (results.isEmpty()) {
// Context-Aware Fallback (Up): Check superclass constructors
String superFqn = context.getSuperclassFqn(td);
if (superFqn != null && !superFqn.equals("java.lang.Object")) {
TypeDeclaration superTd = context.getTypeDeclaration(superFqn);
if (superTd != null) {
results.addAll(traceFieldInConstructors(superTd, fieldName, context, visited));
}
}
}
if (results.isEmpty()) {
// Context-Aware Fallback (Down): Check subclasses/implementations
List<String> impls = context.getImplementations(fqn);
if (impls != null) {
for (String implFqn : impls) {
TypeDeclaration implTd = context.getTypeDeclaration(implFqn);
if (implTd != null) {
results.addAll(traceFieldInConstructors(implTd, fieldName, context, visited));
}
}
}
}
if (results.isEmpty()) {
// Global Fallback: Search all parsed classes in the workspace
Collection<TypeDeclaration> allTypes = context.getTypeDeclarations();
if (allTypes != null) {
int matchedCount = 0;
for (TypeDeclaration typeDecl : allTypes) {
if (typeDecl == null) continue;
if (context.hasField(typeDecl, fieldName)) {
results.addAll(traceFieldInConstructors(typeDecl, fieldName, context, visited));
matchedCount++;
if (matchedCount >= 10) {
break;
}
}
}
}
}
visited.remove(cycleKey);
return results;
}
@@ -473,9 +525,47 @@ public class ConstructorAnalyzer {
String typeName = click.kamil.springstatemachineexporter.ast.common.AstUtils.extractSimpleTypeName(cic.getType());
CompilationUnit contextCu = cic.getRoot() instanceof CompilationUnit ? (CompilationUnit) cic.getRoot() : null;
TypeDeclaration td = contextCu != null ? context.getTypeDeclaration(typeName, contextCu) : context.getTypeDeclaration(typeName);
if (td == null) td = context.getTypeDeclaration(typeName);
if (td == null) return Collections.emptyList();
AbstractTypeDeclaration atd = contextCu != null ? context.getAbstractTypeDeclaration(typeName, contextCu) : context.getAbstractTypeDeclaration(typeName);
if (atd == null) atd = context.getAbstractTypeDeclaration(typeName);
if (atd == null) return Collections.emptyList();
if (atd instanceof RecordDeclaration rd) {
int compIdx = -1;
List<?> components = rd.recordComponents();
for (int i = 0; i < components.size(); i++) {
Object compObj = components.get(i);
if (compObj != null) {
try {
java.lang.reflect.Method getNameMethod = compObj.getClass().getMethod("getName");
SimpleName nameObj = (SimpleName) getNameMethod.invoke(compObj);
if (nameObj != null && nameObj.getIdentifier().equals(getterName)) {
compIdx = i;
break;
}
} catch (Exception e) {
// ignore reflection errors
}
}
}
if (compIdx >= 0 && compIdx < cic.arguments().size()) {
Expression argExpr = (Expression) cic.arguments().get(compIdx);
List<String> consts = new ArrayList<>();
if (constantExtractor != null) {
constantExtractor.extractConstantsFromArgument(argExpr, consts);
}
if (!consts.isEmpty()) {
return consts;
} else {
String resolved = constantResolver.resolve(argExpr, context);
if (resolved != null) {
return List.of(resolved);
}
}
}
return Collections.emptyList();
}
if (!(atd instanceof TypeDeclaration td)) return Collections.emptyList();
MethodDeclaration getter = context.findMethodDeclaration(td, getterName, true);
if (getter == null || getter.getBody() == null) return Collections.emptyList();