Skip to content

Commit 2c2168f

Browse files
committed
Add type checking utils
Signed-off-by: Ben Sherman <bentshermann@gmail.com>
1 parent 30f3c3c commit 2c2168f

19 files changed

Lines changed: 338 additions & 120 deletions

modules/nf-lang/src/main/java/nextflow/config/control/VariableScopeVisitor.java

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,9 +285,11 @@ private void checkMethodCall(MethodCallExpression node) {
285285
if( !node.isImplicitThis() )
286286
return;
287287
var name = node.getMethodAsString();
288-
var defNode = vsc.findDslFunction(name, node);
289-
if( defNode != null )
290-
node.putNodeMetaData(ASTNodeMarker.METHOD_TARGET, defNode);
288+
var methods = vsc.findDslFunction(name, node);
289+
if( methods.size() == 1 )
290+
node.putNodeMetaData(ASTNodeMarker.METHOD_TARGET, methods.get(0));
291+
else if( !methods.isEmpty() )
292+
node.putNodeMetaData(ASTNodeMarker.METHOD_OVERLOADS, methods);
291293
else if( !KEYWORDS.contains(name) )
292294
vsc.addError("`" + name + "` is not defined", node.getMethod());
293295
}

modules/nf-lang/src/main/java/nextflow/script/ast/ASTNodeMarker.java

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ public enum ASTNodeMarker {
2727
// denotes that an assignment is an implicit declaration
2828
IMPLICIT_DECLARATION,
2929

30+
// the inferred return type of a closure expression
31+
INFERRED_RETURN_TYPE,
32+
3033
// the inferred type of an expression
3134
INFERRED_TYPE,
3235

@@ -39,6 +42,9 @@ public enum ASTNodeMarker {
3942
// the verbatim text of a Groovy-style type annotation (ClassNode)
4043
LEGACY_TYPE,
4144

45+
// the list of candidate MethodNode's for a MethodCallExpression
46+
METHOD_OVERLOADS,
47+
4248
// the MethodNode targeted by a MethodCallExpression
4349
METHOD_TARGET,
4450

@@ -48,6 +54,9 @@ public enum ASTNodeMarker {
4854
// denotes a nullable type annotation (ClassNode)
4955
NULLABLE,
5056

57+
// the FieldNode targeted by a PropertyExpression
58+
PROPERTY_TARGET,
59+
5160
// the starting quote sequence of a string literal or gstring expression
5261
QUOTE_CHAR,
5362

modules/nf-lang/src/main/java/nextflow/script/ast/ASTUtils.java

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,13 @@
1818

1919
import java.util.Arrays;
2020
import java.util.Collections;
21+
import java.util.LinkedHashMap;
2122
import java.util.List;
23+
import java.util.Map;
2224
import java.util.Optional;
2325
import java.util.stream.Stream;
2426

27+
import groovy.transform.NamedParams;
2528
import org.codehaus.groovy.ast.ClassHelper;
2629
import org.codehaus.groovy.ast.ClassNode;
2730
import org.codehaus.groovy.ast.AnnotatedNode;
@@ -30,9 +33,12 @@
3033
import org.codehaus.groovy.ast.Parameter;
3134
import org.codehaus.groovy.ast.PropertyNode;
3235
import org.codehaus.groovy.ast.Variable;
36+
import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;
37+
import org.codehaus.groovy.ast.expr.ClassExpression;
3338
import org.codehaus.groovy.ast.expr.ClosureExpression;
3439
import org.codehaus.groovy.ast.expr.ConstantExpression;
3540
import org.codehaus.groovy.ast.expr.Expression;
41+
import org.codehaus.groovy.ast.expr.ListExpression;
3642
import org.codehaus.groovy.ast.expr.MapExpression;
3743
import org.codehaus.groovy.ast.expr.MapEntryExpression;
3844
import org.codehaus.groovy.ast.expr.MethodCall;
@@ -145,6 +151,34 @@ public static List<MapEntryExpression> asNamedArgs(MethodCall call) {
145151
: Collections.emptyList();
146152
}
147153

154+
public static Map<String, AnnotationNode> asNamedParams(Parameter parameter) {
155+
var namedParams = new LinkedHashMap<String, AnnotationNode>();
156+
parameter.getAnnotations().stream()
157+
.filter(an -> an.getClassNode().getName().equals(NamedParams.class.getName()))
158+
.flatMap(an -> {
159+
var value = an.getMember("value");
160+
return value instanceof ListExpression le
161+
? le.getExpressions().stream()
162+
: Stream.empty();
163+
})
164+
.forEach((value) -> {
165+
if( !(value instanceof AnnotationConstantExpression) )
166+
return;
167+
var ace = (AnnotationConstantExpression) value;
168+
var namedParam = (AnnotationNode) ace.getValue();
169+
var name = namedParam.getMember("value").getText();
170+
namedParams.put(name, namedParam);
171+
});
172+
return namedParams;
173+
}
174+
175+
public static Parameter asNamedParam(AnnotationNode node) {
176+
var name = node.getMember("value").getText();
177+
var typeX = (ClassExpression) node.getMember("type");
178+
var type = typeX != null ? typeX.getType() : ClassHelper.dynamicType();
179+
return new Parameter(type, name);
180+
}
181+
148182
public static VariableExpression asVarX(Statement statement) {
149183
return statement instanceof ExpressionStatement es ? asVarX(es.getExpression()) : null;
150184
}

modules/nf-lang/src/main/java/nextflow/script/ast/OutputNode.java

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -15,23 +15,20 @@
1515
*/
1616
package nextflow.script.ast;
1717

18-
import org.codehaus.groovy.ast.ASTNode;
1918
import org.codehaus.groovy.ast.ClassNode;
19+
import org.codehaus.groovy.ast.Parameter;
2020
import org.codehaus.groovy.ast.stmt.Statement;
2121

2222
/**
2323
* An output declaration.
2424
*
2525
* @author Ben Sherman <bentshermann@gmail.com>
2626
*/
27-
public class OutputNode extends ASTNode {
28-
public final String name;
29-
public final ClassNode type;
27+
public class OutputNode extends Parameter {
3028
public final Statement body;
3129

3230
public OutputNode(String name, ClassNode type, Statement body) {
33-
this.name = name;
34-
this.type = type;
31+
super(type, name);
3532
this.body = body;
3633
}
3734
}

modules/nf-lang/src/main/java/nextflow/script/ast/ProcessNodeV2.java

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,30 +69,28 @@ private static ClassNode dummyReturnType(Statement block) {
6969
if( outputs.size() == 1 ) {
7070
var first = outputs.get(0);
7171
var output = ((ExpressionStatement) first).getExpression();
72-
if( outputName(output) == null )
72+
if( outputTarget(output) == null )
7373
return output.getType();
7474
}
7575
var cn = new ClassNode(Record.class);
7676
outputs.stream()
7777
.map(stmt -> ((ExpressionStatement) stmt).getExpression())
78-
.map(output -> outputName(output))
79-
.filter(name -> name != null)
80-
.forEach((name) -> {
81-
var type = ClassHelper.dynamicType();
82-
var fn = new FieldNode(name, Modifier.PUBLIC, type, cn, null);
78+
.map(output -> outputTarget(output))
79+
.filter(target -> target != null)
80+
.forEach((target) -> {
81+
var fn = new FieldNode(target.getName(), Modifier.PUBLIC, target.getType(), cn, null);
8382
fn.setDeclaringClass(cn);
8483
cn.addField(fn);
8584
});
8685
return cn;
8786
}
8887

89-
private static String outputName(Expression output) {
88+
private static VariableExpression outputTarget(Expression output) {
9089
if( output instanceof VariableExpression ve ) {
91-
return ve.getName();
90+
return ve;
9291
}
93-
else if( output instanceof AssignmentExpression ae ) {
94-
var target = (VariableExpression)ae.getLeftExpression();
95-
return target.getName();
92+
if( output instanceof AssignmentExpression ae ) {
93+
return (VariableExpression)ae.getLeftExpression();
9694
}
9795
return null;
9896
}

modules/nf-lang/src/main/java/nextflow/script/ast/WorkflowNode.java

Lines changed: 17 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
package nextflow.script.ast;
1717

1818
import java.lang.reflect.Modifier;
19-
import java.util.Optional;
2019

2120
import nextflow.script.types.Record;
2221
import org.codehaus.groovy.ast.ClassHelper;
@@ -65,28 +64,33 @@ public boolean isCodeSnippet() {
6564
return getLineNumber() == -1;
6665
}
6766

68-
private static ClassNode dummyReturnType(Statement emits) {
67+
private static ClassNode dummyReturnType(Statement block) {
68+
var emits = asBlockStatements(block);
69+
if( emits.size() == 1 ) {
70+
var first = emits.get(0);
71+
var emit = ((ExpressionStatement) first).getExpression();
72+
if( emitTarget(emit) == null )
73+
return emit.getType();
74+
}
6975
var cn = new ClassNode(Record.class);
70-
asBlockStatements(emits).stream()
76+
emits.stream()
7177
.map(stmt -> ((ExpressionStatement) stmt).getExpression())
72-
.map(emit -> emitName(emit))
73-
.filter(name -> name != null)
74-
.forEach((name) -> {
75-
var type = ClassHelper.dynamicType();
76-
var fn = new FieldNode(name, Modifier.PUBLIC, type, cn, null);
78+
.map(emit -> emitTarget(emit))
79+
.filter(target -> target != null)
80+
.forEach((target) -> {
81+
var fn = new FieldNode(target.getName(), Modifier.PUBLIC, target.getType(), cn, null);
7782
fn.setDeclaringClass(cn);
7883
cn.addField(fn);
7984
});
8085
return cn;
8186
}
8287

83-
private static String emitName(Expression emit) {
88+
private static VariableExpression emitTarget(Expression emit) {
8489
if( emit instanceof VariableExpression ve ) {
85-
return ve.getName();
90+
return ve;
8691
}
87-
else if( emit instanceof AssignmentExpression ae ) {
88-
var left = (VariableExpression)ae.getLeftExpression();
89-
return left.getName();
92+
if( emit instanceof AssignmentExpression ae ) {
93+
return (VariableExpression)ae.getLeftExpression();
9094
}
9195
return null;
9296
}

modules/nf-lang/src/main/java/nextflow/script/control/ResolveVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ private boolean resolveGenericsType(GenericsType genericsType) {
150150
if( genericsType.isResolved() )
151151
return true;
152152
var type = genericsType.getType();
153-
resolveOrFail(type, genericsType);
153+
resolve(type);
154154
if( resolveGenericsTypes(type.getGenericsTypes()) )
155155
genericsType.setResolved(genericsType.getType().isResolved());
156156
return genericsType.isResolved();

modules/nf-lang/src/main/java/nextflow/script/control/ScriptResolveVisitor.java

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -103,20 +103,20 @@ public void visitWorkflow(WorkflowNode node) {
103103
for( var take : node.getParameters() )
104104
resolver.resolveOrFail(take.getType(), take);
105105
resolver.visit(node.main);
106-
resolveWorkflowEmits(node.emits);
106+
resolveTypedOutputs(node.emits);
107107
resolver.visit(node.emits);
108108
resolver.visit(node.publishers);
109109
resolver.visit(node.onComplete);
110110
resolver.visit(node.onError);
111111
}
112112

113-
private void resolveWorkflowEmits(Statement emits) {
114-
for( var stmt : asBlockStatements(emits) ) {
113+
private void resolveTypedOutputs(Statement block) {
114+
for( var stmt : asBlockStatements(block) ) {
115115
var stmtX = (ExpressionStatement)stmt;
116-
var emit = stmtX.getExpression();
116+
var output = stmtX.getExpression();
117117
var target =
118-
emit instanceof AssignmentExpression ae ? ae.getLeftExpression() :
119-
emit instanceof VariableExpression ve ? ve :
118+
output instanceof AssignmentExpression ae ? ae.getLeftExpression() :
119+
output instanceof VariableExpression ve ? ve :
120120
null;
121121

122122
if( target instanceof VariableExpression ve )
@@ -130,6 +130,7 @@ public void visitProcessV2(ProcessNodeV2 node) {
130130
resolver.resolveOrFail(input.getType(), input);
131131
resolver.visit(node.directives);
132132
resolver.visit(node.stagers);
133+
resolveTypedOutputs(node.outputs);
133134
resolver.visit(node.outputs);
134135
resolver.visit(node.topics);
135136
resolver.visit(node.when);
@@ -159,6 +160,7 @@ public void visitFunction(FunctionNode node) {
159160

160161
@Override
161162
public void visitOutput(OutputNode node) {
163+
resolver.resolveOrFail(node.getType(), node.getType());
162164
resolver.visit(node.body);
163165
}
164166

modules/nf-lang/src/main/java/nextflow/script/control/ScriptToGroovyVisitor.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -239,7 +239,7 @@ public void visitOutputs(OutputBlockNode node) {
239239
var statements = node.declarations.stream()
240240
.map((output) -> {
241241
new PublishDslVisitor().visit(output.body);
242-
var name = constX(output.name);
242+
var name = constX(output.getName());
243243
var body = closureX(null, output.body);
244244
return stmt(callThisX("declare", args(name, body)));
245245
})

0 commit comments

Comments
 (0)