Skip to content

Commit

Permalink
Merge pull request #41809 from nipunayf/fix-annotation-field
Browse files Browse the repository at this point in the history
Revamp annotation support for `NodeFinder`
  • Loading branch information
KavinduZoysa authored Dec 8, 2023
2 parents d1fbfb9 + b44d412 commit e094594
Show file tree
Hide file tree
Showing 8 changed files with 536 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
import io.ballerina.tools.text.LineRange;
import org.ballerinalang.model.clauses.OrderKeyNode;
import org.ballerinalang.model.elements.Flag;
import org.ballerinalang.model.tree.AnnotatableNode;
import org.ballerinalang.model.tree.AnnotationAttachmentNode;
import org.ballerinalang.model.tree.NodeKind;
import org.ballerinalang.model.tree.TopLevelNode;
import org.ballerinalang.model.tree.expressions.RecordLiteralNode;
Expand Down Expand Up @@ -96,6 +98,7 @@
import org.wso2.ballerinalang.compiler.tree.expressions.BLangLiteral;
import org.wso2.ballerinalang.compiler.tree.expressions.BLangMatchGuard;
import org.wso2.ballerinalang.compiler.tree.expressions.BLangNamedArgsExpression;
import org.wso2.ballerinalang.compiler.tree.expressions.BLangObjectConstructorExpression;
import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryAction;
import org.wso2.ballerinalang.compiler.tree.expressions.BLangQueryExpr;
import org.wso2.ballerinalang.compiler.tree.expressions.BLangRawTemplateLiteral;
Expand Down Expand Up @@ -249,8 +252,8 @@ private BLangNode lookupTopLevelNodes(List<TopLevelNode> nodes, LineRange range)
this.enclosingNode = null;

for (TopLevelNode node : nodes) {
if (!PositionUtil.isRangeWithinNode(this.range, node.getPosition()) || isLambdaFunction(node)
|| isClassForService(node)) {
if ((!PositionUtil.isRangeWithinNode(this.range, node.getPosition()) && !isWithinNodeMetaData(node)) ||
isLambdaFunction(node) || isClassForService(node)) {
continue;
}

Expand Down Expand Up @@ -302,9 +305,18 @@ public void visit(BLangXMLNS xmlnsNode) {
@Override
public void visit(BLangFunction funcNode) {
lookupNodes(funcNode.annAttachments);

for (BLangSimpleVariable requiredParam : funcNode.requiredParams) {
lookupNodes(requiredParam.annAttachments);
}
lookupNodes(funcNode.requiredParams);

if (funcNode.restParam != null) {
lookupNodes(funcNode.restParam.annAttachments);
}
lookupNode(funcNode.restParam);
lookupNode(funcNode.returnTypeNode);
lookupNodes(funcNode.returnTypeAnnAttachments);
lookupNode(funcNode.body);
}

Expand Down Expand Up @@ -339,12 +351,14 @@ public void visit(BLangService serviceNode) {
@Override
public void visit(BLangTypeDefinition typeDefinition) {
lookupNode(typeDefinition.typeNode);
lookupNodes(typeDefinition.annAttachments);
}

@Override
public void visit(BLangConstant constant) {
lookupNode(constant.typeNode);
lookupNode(constant.expr);
lookupNodes(constant.annAttachments);
setEnclosingNode(constant, constant.name.pos);
}

Expand All @@ -359,6 +373,7 @@ public void visit(BLangSimpleVariable varNode) {
@Override
public void visit(BLangAnnotation annotationNode) {
lookupNode(annotationNode.typeNode);
lookupNodes(annotationNode.annAttachments);
setEnclosingNode(annotationNode, annotationNode.name.pos);
}

Expand Down Expand Up @@ -673,6 +688,7 @@ public void visit(BLangInvocation.BLangActionInvocation actionInvocationExpr) {
lookupNodes(actionInvocationExpr.argExprs);
lookupNodes(actionInvocationExpr.restArgs);
lookupNode(actionInvocationExpr.expr);
lookupNodes(actionInvocationExpr.annAttachments);

if (setEnclosingNode(actionInvocationExpr, actionInvocationExpr.name.pos)) {
return;
Expand Down Expand Up @@ -945,9 +961,17 @@ public void visit(BLangIntersectionTypeNode intersectionTypeNode) {
@Override
public void visit(BLangClassDefinition classDefinition) {
lookupNodes(classDefinition.annAttachments);

for (BLangSimpleVariable field : classDefinition.fields) {
lookupNodes(field.annAttachments);
}
lookupNodes(classDefinition.fields);
lookupNodes(classDefinition.referencedFields);
lookupNode(classDefinition.initFunction);

for (BLangFunction method : classDefinition.functions) {
lookupNodes(method.annAttachments);
}
lookupNodes(classDefinition.functions);
lookupNodes(classDefinition.typeRefs);
setEnclosingNode(classDefinition, classDefinition.name.pos);
Expand All @@ -965,13 +989,28 @@ public void visit(BLangInvocation.BLangResourceAccessInvocation resourceAccessIn

@Override
public void visit(BLangObjectTypeNode objectTypeNode) {
for (BLangSimpleVariable field : objectTypeNode.fields) {
lookupNodes(field.annAttachments);
}
lookupNodes(objectTypeNode.fields);

for (BLangFunction function : objectTypeNode.functions) {
lookupNodes(function.annAttachments);
}
lookupNodes(objectTypeNode.functions);
lookupNodes(objectTypeNode.typeRefs);
}

@Override
public void visit(BLangObjectConstructorExpression objectConstructorExpression) {
lookupNode(objectConstructorExpression.classNode);
}

@Override
public void visit(BLangRecordTypeNode recordTypeNode) {
for (BLangSimpleVariable field : recordTypeNode.fields) {
lookupNodes(field.annAttachments);
}
lookupNodes(recordTypeNode.fields);
lookupNodes(recordTypeNode.typeRefs);
}
Expand Down Expand Up @@ -1522,4 +1561,18 @@ private boolean isClassForService(TopLevelNode node) {
return ((BLangClassDefinition) node).flagSet.contains(Flag.SERVICE) && ((BLangClassDefinition) node).flagSet
.contains(Flag.ANONYMOUS);
}

private boolean isWithinNodeMetaData(TopLevelNode node) {
if (node instanceof AnnotatableNode) {
List<AnnotationAttachmentNode> nodes =
(List<AnnotationAttachmentNode>) ((AnnotatableNode) node).getAnnotationAttachments();

for (AnnotationAttachmentNode annotAttachment : nodes) {
if (PositionUtil.isRangeWithinNode(this.range, annotAttachment.getPosition())) {
return true;
}
}
}
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
"label": "bar",
"kind": "Field",
"detail": "module1:AnnotationType.bar",
"sortText": "K",
"sortText": "AG",
"insertText": "bar: ${1:0}",
"insertTextFormat": "Snippet"
},
{
"label": "foo",
"kind": "Field",
"detail": "module1:AnnotationType.foo",
"sortText": "K",
"sortText": "AG",
"insertText": "foo: ${1:\"\"}",
"insertTextFormat": "Snippet"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
{
"position": {
"line": 17,
"character": 18
},
"source": "annotation_ctx/source/recordFieldAnnotation5.bal",
"description": "Test completions for the config record body of an annotation attached to a record field",
"items": [
{
"label": "readonly",
"kind": "Keyword",
"detail": "Keyword",
"sortText": "U",
"filterText": "readonly",
"insertText": "readonly ",
"insertTextFormat": "Snippet"
},
{
"label": "Fill record {|string description; int id; string name?; anydata...;|} Required Fields",
"kind": "Property",
"detail": "record {|string description; int id; string name?; anydata...;|}",
"sortText": "R",
"filterText": "fill",
"insertText": "description: ${1:\"\"},\nid: ${2:0}",
"insertTextFormat": "Snippet"
},
{
"label": "name",
"kind": "Field",
"detail": "(record {|string description; int id; string name?; anydata...;|}).name",
"sortText": "K",
"insertText": "name: ${1:\"\"}",
"insertTextFormat": "Snippet"
},
{
"label": "description",
"kind": "Field",
"detail": "(record {|string description; int id; string name?; anydata...;|}).description",
"sortText": "K",
"insertText": "description: ${1:\"\"}",
"insertTextFormat": "Snippet"
},
{
"label": "id",
"kind": "Field",
"detail": "(record {|string description; int id; string name?; anydata...;|}).id",
"sortText": "AG",
"insertText": "id: ${1:0}",
"insertTextFormat": "Snippet"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
type Info record {|
int id;
string name?;
anydata description;
|};

type AnnotationConfig record {|
Info info?;
int|record {*Info; string description;} doc
|};

const annotation AnnotationConfig CustomAnnotation on source record field;

type RecordName record {|
record {|
int id;
@CustomAnnotation {
doc: {}
}
string value;
anydata description;
|} nestedRecord;
|};
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (c) 2023, WSO2 LLC. (http://www.wso2.org) All Rights Reserved.
*
* WSO2 LLC. licenses this file to you under the Apache License,
* Version 2.0 (the "License"); you may not use this file except
* in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/

package io.ballerina.semantic.api.test.expectedtype;

import io.ballerina.compiler.api.SemanticModel;
import io.ballerina.compiler.api.symbols.TypeDescKind;
import io.ballerina.compiler.api.symbols.TypeSymbol;
import io.ballerina.projects.Document;
import io.ballerina.projects.Project;
import io.ballerina.tools.text.LinePosition;
import org.ballerinalang.test.BCompileUtil;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;

import java.util.Optional;

import static io.ballerina.semantic.api.test.util.SemanticAPITestUtils.getDefaultModulesSemanticModel;
import static io.ballerina.semantic.api.test.util.SemanticAPITestUtils.getDocumentForSingleSource;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue;

/**
* Test cases to find the expected types in annotations.
*
* @since 2201.9.0
*/
public class AnnotationNodeTest {

private SemanticModel model;
private Document srcFile;

@BeforeClass
public void setup() {
Project project = BCompileUtil.loadProject("test-src/expected-type/annotation_test.bal");
model = getDefaultModulesSemanticModel(project);
srcFile = getDocumentForSingleSource(project);
}

@Test(dataProvider = "LinePosProvider")
public void testExpectedType(int infoLine, int infoCol, int docLine, int docCol) {
assertTypeSymbol(infoLine, infoCol, TypeDescKind.TYPE_REFERENCE, "Info");
assertTypeSymbol(docLine, docCol, TypeDescKind.UNION,
"int|record {|string description; int id; string name?; anydata...;|}?");
}

@DataProvider(name = "LinePosProvider")
public Object[][] getLinePos() {
return new Object[][]{
{18, 11, 19, 10},
{24, 11, 25, 10},
{32, 11, 33, 10},
{38, 11, 39, 10},
{45, 11, 46, 10},
{50, 15, 51, 14},
{56, 15, 57, 14},
{82, 11, 83, 10},
{87, 15, 88, 14},
{93, 15, 94, 14},
{100, 11, 101, 10},
{105, 15, 106, 14},
{112, 15, 113, 14},
{120, 11, 121, 10},
{125, 15, 126, 14},
{131, 15, 132, 14},
{139, 15, 140, 14},
{145, 15, 146, 14},
{152, 15, 153, 14},
{159, 68, 159, 77},
{161, 58, 161, 67},
{162, 34, 162, 43},
{163, 34, 163, 43},
{164, 34, 164, 43},
{169, 15, 170, 14},
{176, 11, 177, 10},
{182, 11, 183, 10},
{189, 15, 190, 14},
{194, 38, 194, 47},
{200, 11, 201, 10},
{206, 34, 206, 43},
// {209, 15, 210, 14}, Blocked by #41805
{217, 11, 218, 10},
{222, 15, 223, 14},
{228, 15, 229, 14},
{235, 15, 236, 14},
{243, 11, 244, 10},
{250, 15, 251, 14},
// {256, 27, 256, 36}, Blocked by #41787
// {262, 15, 263, 14}, Blocked by #41803
{269, 75, 269, 84},
{272, 71, 272, 80},
{277, 11, 278, 10},
{282, 46, 282, 54},
};
}

private void assertTypeSymbol(int line, int col, TypeDescKind expectedKind, String expectedSignature) {
Optional<TypeSymbol> typeSymbol = model.expectedType(srcFile, LinePosition.from(line, col));
assertTrue(typeSymbol.isPresent());
assertEquals(typeSymbol.get().typeKind(), expectedKind);
assertEquals(typeSymbol.get().signature(), expectedSignature);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
import static org.testng.Assert.assertTrue;

/**
* Tests cases for testing typeOf() with query expressions.
* Tests cases for testing typeOf() in call statements.
*/
public class TypeOfInCallStatementsTest {

Expand Down
Loading

0 comments on commit e094594

Please sign in to comment.