1515 */
1616package nextflow .lsp .ast ;
1717
18- import java .lang .reflect .Modifier ;
1918import java .util .Collections ;
2019import java .util .Iterator ;
21- import java .util .List ;
22- import java .util .Optional ;
2320
24- import nextflow .script .ast .ASTNodeMarker ;
25- import nextflow .script .ast .AssignmentExpression ;
2621import nextflow .script .ast .FeatureFlagNode ;
2722import nextflow .script .ast .IncludeModuleNode ;
28- import nextflow .script .ast .ProcessNode ;
29- import nextflow .script .ast .WorkflowNode ;
30- import nextflow .script .dsl .Constant ;
31- import nextflow .script .dsl .Description ;
32- import nextflow .script .types .Channel ;
33- import nextflow .script .types .NamedTuple ;
23+ import nextflow .script .types .TypeChecker ;
3424import nextflow .script .types .Types ;
3525import org .codehaus .groovy .ast .ASTNode ;
36- import org .codehaus .groovy .ast .ClassHelper ;
3726import org .codehaus .groovy .ast .ClassNode ;
38- import org .codehaus .groovy .ast .FieldNode ;
3927import org .codehaus .groovy .ast .MethodNode ;
40- import org .codehaus .groovy .ast .Parameter ;
4128import org .codehaus .groovy .ast .Variable ;
42- import org .codehaus .groovy .ast .expr .ArgumentListExpression ;
4329import org .codehaus .groovy .ast .expr .ClassExpression ;
4430import org .codehaus .groovy .ast .expr .ConstructorCallExpression ;
45- import org .codehaus .groovy .ast .expr .DeclarationExpression ;
46- import org .codehaus .groovy .ast .expr .Expression ;
47- import org .codehaus .groovy .ast .expr .MethodCall ;
4831import org .codehaus .groovy .ast .expr .MethodCallExpression ;
4932import org .codehaus .groovy .ast .expr .PropertyExpression ;
5033import org .codehaus .groovy .ast .expr .VariableExpression ;
51- import org .codehaus .groovy .ast .stmt .ExpressionStatement ;
5234
5335import static nextflow .script .ast .ASTUtils .*;
5436
@@ -64,17 +46,16 @@ public class LanguageServerASTUtils {
6446 * class, method, or variable.
6547 *
6648 * @param node
67- * @param ast
6849 */
69- public static ASTNode getDefinition (ASTNode node , ASTNodeCache ast ) {
50+ public static ASTNode getDefinition (ASTNode node ) {
7051 if ( node instanceof VariableExpression ve )
7152 return getDefinitionFromVariable (ve .getAccessedVariable ());
7253
7354 if ( node instanceof MethodCallExpression mce )
74- return getMethodFromCallExpression (mce , ast );
55+ return TypeChecker . inferMethodTarget (mce );
7556
7657 if ( node instanceof PropertyExpression pe )
77- return getFieldFromExpression (pe , ast );
58+ return TypeChecker . inferPropertyTarget (pe );
7859
7960 if ( node instanceof ClassExpression ce )
8061 return ce .getType ().redirect ();
@@ -119,7 +100,7 @@ private static ASTNode getDefinitionFromVariable(Variable variable) {
119100 * @param includeDeclaration
120101 */
121102 public static Iterator <ASTNode > getReferences (ASTNode node , ASTNodeCache ast , boolean includeDeclaration ) {
122- var defNode = getDefinition (node , ast );
103+ var defNode = getDefinition (node );
123104 if ( defNode == null )
124105 return Collections .emptyIterator ();
125106 return ast .getNodes ().stream ()
@@ -128,257 +109,9 @@ public static Iterator<ASTNode> getReferences(ASTNode node, ASTNodeCache ast, bo
128109 return false ;
129110 if ( defNode == otherNode )
130111 return includeDeclaration ;
131- return defNode == getDefinition (otherNode , ast );
112+ return defNode == getDefinition (otherNode );
132113 })
133114 .iterator ();
134115 }
135116
136- /**
137- * Get the type (i.e. class node) of an ast node.
138- *
139- * @param node
140- * @param ast
141- */
142- public static ClassNode getType (ASTNode node , ASTNodeCache ast ) {
143- var inferredType = inferredType (node , ast );
144- return Types .SHIM_TYPES .containsKey (inferredType )
145- ? Types .SHIM_TYPES .get (inferredType )
146- : inferredType ;
147- }
148-
149- private static ClassNode inferredType (ASTNode node , ASTNodeCache ast ) {
150- if ( node instanceof ClassExpression ce ) {
151- return ce .getType ();
152- }
153-
154- if ( node instanceof ConstructorCallExpression cce ) {
155- return cce .getType ();
156- }
157-
158- if ( node instanceof MethodCallExpression mce ) {
159- var mn = getMethodFromCallExpression (mce , ast );
160- return mn != null
161- ? mn .getReturnType ()
162- : mce .getType ();
163- }
164-
165- if ( node instanceof PropertyExpression pe ) {
166- var mn = asMethodOutput (pe );
167- if ( mn instanceof ProcessNode pn )
168- return asProcessOut (pn );
169- if ( mn instanceof WorkflowNode wn )
170- return asWorkflowOut (wn );
171- var fn = getFieldFromExpression (pe , ast );
172- return fn != null
173- ? getType (fn , ast )
174- : pe .getType ();
175- }
176-
177- if ( node instanceof Variable variable ) {
178- if ( variable .isDynamicTyped () ) {
179- var defNode = getDefinition (node , ast );
180- if ( defNode instanceof Variable defVar ) {
181- if ( defVar .hasInitialExpression () ) {
182- return getType (defVar .getInitialExpression (), ast );
183- }
184- var declNode = ast .getParent (defNode );
185- if ( declNode instanceof DeclarationExpression de )
186- return getType (de .getRightExpression (), ast );
187- }
188- }
189-
190- if ( variable .getOriginType () != null )
191- return variable .getOriginType ();
192- }
193-
194- return node instanceof Expression exp
195- ? exp .getType ()
196- : null ;
197- }
198-
199- private static MethodNode asMethodOutput (PropertyExpression node ) {
200- if ( node .getObjectExpression () instanceof VariableExpression ve && "out" .equals (node .getPropertyAsString ()) )
201- return asMethodVariable (ve .getAccessedVariable ());
202- return null ;
203- }
204-
205- private static ClassNode asProcessOut (ProcessNode pn ) {
206- var cn = new ClassNode (NamedTuple .class );
207- asDirectives (pn .outputs )
208- .map (call -> getProcessEmitName (call ))
209- .filter (name -> name != null )
210- .forEach ((name ) -> {
211- var type = ClassHelper .makeCached (Channel .class );
212- var fn = new FieldNode (name , Modifier .PUBLIC , type , cn , null );
213- fn .setDeclaringClass (cn );
214- cn .addField (fn );
215- });
216- return cn ;
217- }
218-
219- private static String getProcessEmitName (MethodCallExpression output ) {
220- return Optional .of (output )
221- .flatMap (call -> Optional .ofNullable (asNamedArgs (call )))
222- .flatMap (namedArgs ->
223- namedArgs .stream ()
224- .filter (entry -> "emit" .equals (entry .getKeyExpression ().getText ()))
225- .findFirst ()
226- )
227- .flatMap (entry -> Optional .ofNullable (
228- entry .getValueExpression () instanceof VariableExpression ve ? ve .getName () : null
229- ))
230- .orElse (null );
231- }
232-
233- private static ClassNode asWorkflowOut (WorkflowNode wn ) {
234- var cn = new ClassNode (NamedTuple .class );
235- asBlockStatements (wn .emits ).stream ()
236- .map (stmt -> ((ExpressionStatement ) stmt ).getExpression ())
237- .map (emit -> getWorkflowEmitName (emit ))
238- .filter (name -> name != null )
239- .forEach ((name ) -> {
240- var type = ClassHelper .dynamicType ();
241- var fn = new FieldNode (name , Modifier .PUBLIC , type , cn , null );
242- fn .setDeclaringClass (cn );
243- cn .addField (fn );
244- });
245- return cn ;
246- }
247-
248- private static String getWorkflowEmitName (Expression emit ) {
249- if ( emit instanceof VariableExpression ve ) {
250- return ve .getName ();
251- }
252- else if ( emit instanceof AssignmentExpression ae ) {
253- var left = (VariableExpression )ae .getLeftExpression ();
254- return left .getName ();
255- }
256- return null ;
257- }
258-
259- private static FieldNode getFieldFromExpression (PropertyExpression node , ASTNodeCache ast ) {
260- var cn = getType (node .getObjectExpression (), ast );
261- if ( cn == null )
262- return null ;
263- var name = node .getPropertyAsString ();
264- var result = cn .getField (name );
265- if ( result != null )
266- return result ;
267- return cn .getMethods ().stream ()
268- .filter ((mn ) -> {
269- if ( !mn .isPublic () )
270- return false ;
271- var an = findAnnotation (mn , Constant .class );
272- if ( !an .isPresent () )
273- return false ;
274- return name .equals (an .get ().getMember ("value" ).getText ());
275- })
276- .map ((mn ) -> {
277- var fn = new FieldNode (name , 0xF , mn .getReturnType (), mn .getDeclaringClass (), null );
278- findAnnotation (mn , Description .class ).ifPresent ((an ) -> {
279- fn .addAnnotation (an );
280- });
281- return fn ;
282- })
283- .findFirst ().orElse (null );
284- }
285-
286- /**
287- * Find the method node which most closely matches a call expression.
288- *
289- * @param node
290- * @param ast
291- * @param argIndex
292- */
293- public static MethodNode getMethodFromCallExpression (MethodCall node , ASTNodeCache ast , int argIndex ) {
294- var methods = getMethodOverloadsFromCallExpression (node , ast );
295- var arguments = (ArgumentListExpression ) node .getArguments ();
296- return methods .stream ()
297- .max ((MethodNode m1 , MethodNode m2 ) -> {
298- var score1 = getArgumentsScore (m1 .getParameters (), arguments , argIndex );
299- var score2 = getArgumentsScore (m2 .getParameters (), arguments , argIndex );
300- return Integer .compare (score1 , score2 );
301- })
302- .orElse (null );
303- }
304-
305- public static MethodNode getMethodFromCallExpression (MethodCall node , ASTNodeCache ast ) {
306- return getMethodFromCallExpression (node , ast , -1 );
307- }
308-
309- /**
310- * Get the list of available method overloads for a method call expression.
311- *
312- * @param node
313- * @param ast
314- */
315- public static List <MethodNode > getMethodOverloadsFromCallExpression (MethodCall node , ASTNodeCache ast ) {
316- if ( node instanceof MethodCallExpression mce ) {
317- if ( mce .isImplicitThis () ) {
318- var target = (MethodNode ) mce .getNodeMetaData (ASTNodeMarker .METHOD_TARGET );
319- if ( target != null )
320- return List .of (target );
321- }
322- else {
323- var leftType = getType (mce .getObjectExpression (), ast );
324- if ( leftType != null )
325- return methodsForType (leftType , mce .getMethodAsString ());
326- }
327- }
328-
329- if ( node instanceof ConstructorCallExpression cce ) {
330- var constructorType = cce .getType ();
331- if ( constructorType != null ) {
332- return constructorType .getDeclaredConstructors ().stream ()
333- .map (ctor -> (MethodNode ) ctor )
334- .toList ();
335- }
336- }
337-
338- return Collections .emptyList ();
339- }
340-
341- private static List <MethodNode > methodsForType (ClassNode cn , String name ) {
342- try {
343- return cn .getAllDeclaredMethods ().stream ()
344- .filter (mn -> mn .getName ().equals (name ))
345- .filter (mn -> !ClassHelper .isObjectType (mn .getDeclaringClass ()))
346- .toList ();
347- }
348- catch ( NullPointerException e ) {
349- return Collections .emptyList ();
350- }
351- }
352-
353- private static int getArgumentsScore (Parameter [] parameters , ArgumentListExpression arguments , int argIndex ) {
354- var paramsCount = parameters .length ;
355- var expressionsCount = arguments .getExpressions ().size ();
356- var argsCount = argIndex >= expressionsCount
357- ? argIndex + 1
358- : expressionsCount ;
359- var minCount = Math .min (paramsCount , argsCount );
360-
361- int score = 0 ;
362- if ( minCount == 0 && paramsCount == argsCount )
363- score ++;
364-
365- for ( int i = 0 ; i < minCount ; i ++ ) {
366- var paramType = (i < paramsCount ) ? parameters [i ].getType () : null ;
367- var argType = (i < expressionsCount ) ? arguments .getExpression (i ).getType () : null ;
368- if ( argType != null && paramType != null ) {
369- // equal types > subtypes > different types
370- if ( argType .equals (paramType ) )
371- score += 1000 ;
372- else if ( argType .isDerivedFrom (paramType ) )
373- score += 100 ;
374- else
375- score ++;
376- }
377- else if ( paramType != null ) {
378- score ++;
379- }
380- }
381- return score ;
382- }
383-
384117}
0 commit comments