stage next

This commit is contained in:
2026-06-27 20:03:48 +02:00
parent a7c3b08164
commit 4511e0e02f
3 changed files with 475 additions and 66 deletions

View File

@@ -512,5 +512,166 @@ class JdtDataFlowModelTest {
String val = model.resolveValue(initEv, context);
assertThat(val).isEqualTo("PAY");
}
@Test
void testFlowSensitiveLocalFieldMutations(@TempDir Path tempDir) throws IOException {
Path comFoo = tempDir.resolve("com/foo");
Files.createDirectories(comFoo);
Files.writeString(comFoo.resolve("Test.java"),
"package com.foo;\n" +
"public class Test {\n" +
" public static class Command {\n" +
" private String event = \"DEFAULT\";\n" +
" public void setEvent(String event) {\n" +
" this.event = event;\n" +
" }\n" +
" public String getEvent() {\n" +
" return this.event;\n" +
" }\n" +
" }\n" +
" public void run() {\n" +
" Command cmd = new Command();\n" +
" cmd.setEvent(\"PAY\");\n" +
" String res = cmd.getEvent();\n" +
" }\n" +
"}"
);
CodebaseContext context = new CodebaseContext();
context.setResolveBindings(true);
context.setSourcepath(List.of(tempDir.toAbsolutePath().toString()));
context.scan(tempDir);
TypeDeclaration td = context.getTypeDeclaration("com.foo.Test");
assertThat(td).isNotNull();
MethodDeclaration md = null;
for (MethodDeclaration m : td.getMethods()) {
if (m.getName().getIdentifier().equals("run")) {
md = m;
}
}
assertThat(md).isNotNull();
VariableDeclarationStatement resStmt = (VariableDeclarationStatement) md.getBody().statements().get(2);
VariableDeclarationFragment resFrag = (VariableDeclarationFragment) resStmt.fragments().get(0);
Expression initializer = resFrag.getInitializer();
DataFlowModel model = new JdtDataFlowModel(context);
String val = model.resolveValue(initializer, context);
assertThat(val).isEqualTo("PAY");
}
@Test
void testFluentBuilderPatternPropagation(@TempDir Path tempDir) throws IOException {
Path comFoo = tempDir.resolve("com/foo");
Files.createDirectories(comFoo);
Files.writeString(comFoo.resolve("Test.java"),
"package com.foo;\n" +
"public class Test {\n" +
" public static class EventWrapper {\n" +
" private final String event;\n" +
" private EventWrapper(String event) {\n" +
" this.event = event;\n" +
" }\n" +
" public String getEvent() { return this.event; }\n" +
" public static Builder builder() {\n" +
" return new Builder();\n" +
" }\n" +
" public static class Builder {\n" +
" private String event;\n" +
" public Builder event(String event) {\n" +
" this.event = event;\n" +
" return this;\n" +
" }\n" +
" public EventWrapper build() {\n" +
" return new EventWrapper(this.event);\n" +
" }\n" +
" }\n" +
" }\n" +
" public void run() {\n" +
" EventWrapper w = EventWrapper.builder().event(\"PAY_SUCCESS\").build();\n" +
" String res = w.getEvent();\n" +
" }\n" +
"}"
);
CodebaseContext context = new CodebaseContext();
context.setResolveBindings(true);
context.setSourcepath(List.of(tempDir.toAbsolutePath().toString()));
context.scan(tempDir);
TypeDeclaration td = context.getTypeDeclaration("com.foo.Test");
assertThat(td).isNotNull();
MethodDeclaration md = null;
for (MethodDeclaration m : td.getMethods()) {
if (m.getName().getIdentifier().equals("run")) {
md = m;
}
}
assertThat(md).isNotNull();
VariableDeclarationStatement resStmt = (VariableDeclarationStatement) md.getBody().statements().get(1);
VariableDeclarationFragment resFrag = (VariableDeclarationFragment) resStmt.fragments().get(0);
Expression initializer = resFrag.getInitializer();
DataFlowModel model = new JdtDataFlowModel(context);
String val = model.resolveValue(initializer, context);
assertThat(val).isEqualTo("PAY_SUCCESS");
}
@Test
void testPolymorphicUnionFallback(@TempDir Path tempDir) throws IOException {
Path comFoo = tempDir.resolve("com/foo");
Files.createDirectories(comFoo);
Files.writeString(comFoo.resolve("Test.java"),
"package com.foo;\n" +
"public class Test {\n" +
" public interface Service {\n" +
" String getEvent();\n" +
" }\n" +
" public static class PayService implements Service {\n" +
" public String getEvent() { return \"PAY\"; }\n" +
" }\n" +
" public static class CancelService implements Service {\n" +
" public String getEvent() { return \"CANCEL\"; }\n" +
" }\n" +
" public void run(Service s) {\n" +
" String ev = s.getEvent();\n" +
" }\n" +
"}"
);
CodebaseContext context = new CodebaseContext();
context.setResolveBindings(true);
context.setSourcepath(List.of(tempDir.toAbsolutePath().toString()));
context.scan(tempDir);
TypeDeclaration td = context.getTypeDeclaration("com.foo.Test");
assertThat(td).isNotNull();
MethodDeclaration md = null;
for (MethodDeclaration m : td.getMethods()) {
if (m.getName().getIdentifier().equals("run")) {
md = m;
}
}
assertThat(md).isNotNull();
VariableDeclarationStatement evStmt = (VariableDeclarationStatement) md.getBody().statements().get(0);
VariableDeclarationFragment evFrag = (VariableDeclarationFragment) evStmt.fragments().get(0);
Expression initEv = evFrag.getInitializer();
DataFlowModel model = new JdtDataFlowModel(context);
List<Expression> defs = model.getReachingDefinitions(initEv);
assertThat(defs).hasSize(2);
List<String> values = defs.stream()
.filter(e -> e instanceof StringLiteral)
.map(e -> ((StringLiteral) e).getLiteralValue())
.toList();
assertThat(values).containsExactlyInAnyOrder("PAY", "CANCEL");
}
}