Skip to content

Commit bf982cb

Browse files
committed
LDEV-5990: Function param hint and defaultValue missing from AST
1 parent db725e2 commit bf982cb

3 files changed

Lines changed: 128 additions & 1 deletion

File tree

core/src/main/java/lucee/transformer/cfml/script/AbstrCFMLScriptTransformer.java

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1039,7 +1039,24 @@ else if (idName.indexOf('.') != -1 || idName.indexOf('[') != -1) {
10391039
if (meta == null) meta = new HashMap<String, Attribute>();
10401040
for (int i = 0; i < _attrs.length; i++) {
10411041
_attr = _attrs[i];
1042-
meta.put(_attr.getName(), _attr);
1042+
String attrName = _attr.getName();
1043+
// Extract known argument attributes
1044+
if ("hint".equalsIgnoreCase(attrName)) {
1045+
hint = data.factory.toExprString(_attr.getValue());
1046+
}
1047+
else if ("displayname".equalsIgnoreCase(attrName)) {
1048+
displayName = data.factory.toExprString(_attr.getValue());
1049+
}
1050+
else if ("default".equalsIgnoreCase(attrName) && defVal == null) {
1051+
defVal = _attr.getValue();
1052+
}
1053+
else if ("passbyreference".equalsIgnoreCase(attrName) || "passby".equalsIgnoreCase(attrName)) {
1054+
ExprBoolean eb = data.factory.toExprBoolean(_attr.getValue());
1055+
if (eb instanceof LitBoolean) passByRef = (LitBoolean) eb;
1056+
}
1057+
else {
1058+
meta.put(attrName, _attr);
1059+
}
10431060
}
10441061
}
10451062

test/tickets/LDEV5990.cfc

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
component extends="org.lucee.cfml.test.LuceeTestCase" labels="ast" {
2+
3+
variables.testDir = getDirectoryFromPath( getCurrentTemplatePath() ) & "LDEV5990/";
4+
5+
function run( testResults, testBox ) {
6+
7+
describe( "LDEV-5990: Function param hint and defaultValue missing from AST", function() {
8+
9+
it( "should include hint value on function parameters", function() {
10+
// Use astFromPath for CFC files
11+
var ast = astFromPath( variables.testDir & "hintedParams.cfc" );
12+
13+
// Find the withHint function
14+
var func = findFunction( ast, "withHint" );
15+
expect( func ).notToBeNull( "withHint function should be found in AST" );
16+
17+
// Check the first param has hint with correct value
18+
var param = func.params[ 1 ];
19+
expect( param.name.value ).toBe( "name" );
20+
expect( param.hint ).notToBeNull( "param should have hint attribute" );
21+
// Bug: hint.value is empty string instead of the actual hint text
22+
expect( param.hint.value ).toBe( "The user's full name", "hint value should contain the original hint text" );
23+
});
24+
25+
it( "should include defaultValue on function parameters", function() {
26+
var ast = astFromPath( variables.testDir & "hintedParams.cfc" );
27+
28+
// Find the withDefault function
29+
var func = findFunction( ast, "withDefault" );
30+
expect( func ).notToBeNull( "withDefault function should be found in AST" );
31+
32+
// Check the first param has defaultValue
33+
var param = func.params[ 1 ];
34+
expect( param.name.value ).toBe( "greeting" );
35+
// Bug: defaultValue key is completely missing from param
36+
expect( structKeyExists( param, "defaultValue" ) ).toBeTrue( "param should have defaultValue key" );
37+
expect( param.defaultValue.value ).toBe( "Hello", "defaultValue should contain the default value" );
38+
});
39+
40+
it( "should include both hint and defaultValue on same parameter", function() {
41+
var ast = astFromPath( variables.testDir & "hintedParams.cfc" );
42+
43+
// Find the withBoth function
44+
var func = findFunction( ast, "withBoth" );
45+
expect( func ).notToBeNull( "withBoth function should be found in AST" );
46+
47+
// Check the first param has both hint and defaultValue
48+
var param = func.params[ 1 ];
49+
expect( param.name.value ).toBe( "message" );
50+
// Bug: hint.value is empty, defaultValue key is missing
51+
expect( param.hint.value ).toBe( "The message to display", "hint value should be preserved" );
52+
expect( structKeyExists( param, "defaultValue" ) ).toBeTrue( "param should have defaultValue key" );
53+
expect( param.defaultValue.value ).toBe( "Welcome", "defaultValue should be preserved" );
54+
});
55+
56+
});
57+
58+
}
59+
60+
/**
61+
* Recursively find a FunctionDeclaration by name
62+
*/
63+
private function findFunction( required struct node, required string name ) {
64+
var nodeType = node.type ?: "";
65+
if ( isSimpleValue( nodeType ) && nodeType == "FunctionDeclaration" ) {
66+
var nodeName = node.name ?: "";
67+
if ( isStruct( nodeName ) ) nodeName = nodeName.value ?: "";
68+
if ( isSimpleValue( nodeName ) && uCase( nodeName ) == uCase( name ) ) {
69+
return node;
70+
}
71+
}
72+
for ( var key in node ) {
73+
var val = node[ key ];
74+
if ( isStruct( val ) ) {
75+
var result = findFunction( val, name );
76+
if ( !isNull( result ) ) return result;
77+
} else if ( isArray( val ) ) {
78+
for ( var item in val ) {
79+
if ( isStruct( item ) ) {
80+
var result = findFunction( item, name );
81+
if ( !isNull( result ) ) return result;
82+
}
83+
}
84+
}
85+
}
86+
return;
87+
}
88+
89+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
component {
2+
3+
function withHint(
4+
required string name hint="The user's full name"
5+
) {
6+
return name;
7+
}
8+
9+
function withDefault(
10+
string greeting default="Hello"
11+
) {
12+
return greeting;
13+
}
14+
15+
function withBoth(
16+
required string message hint="The message to display" default="Welcome"
17+
) {
18+
return message;
19+
}
20+
21+
}

0 commit comments

Comments
 (0)