diff --git a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java index e9738975c81c..41650e96b605 100644 --- a/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java +++ b/compiler/ballerina-lang/src/main/java/io/ballerina/compiler/api/impl/BallerinaSemanticModel.java @@ -133,7 +133,11 @@ public List visibleSymbols(Document srcFile, LinePosition linePosition) @Override public List visibleSymbols(Document sourceFile, LinePosition position, DiagnosticState... states) { - BLangCompilationUnit compilationUnit = getCompilationUnit(sourceFile); + Optional optionalCompUnit = getCompilationUnit(sourceFile); + if (optionalCompUnit.isEmpty()) { + return Collections.emptyList(); + } + BLangCompilationUnit compilationUnit = optionalCompUnit.get(); BPackageSymbol moduleSymbol = getModuleSymbol(compilationUnit); SymbolTable symbolTable = SymbolTable.getInstance(this.compilerContext); SymbolEnv pkgEnv = symbolTable.pkgEnvMap.get(moduleSymbol); @@ -166,8 +170,11 @@ public List visibleSymbols(Document sourceFile, LinePosition position, D */ @Override public Optional symbol(Document sourceDocument, LinePosition position) { - BLangCompilationUnit compilationUnit = getCompilationUnit(sourceDocument); - return lookupSymbol(compilationUnit, position); + Optional compilationUnit = getCompilationUnit(sourceDocument); + if (compilationUnit.isEmpty()) { + return Optional.empty(); + } + return lookupSymbol(compilationUnit.get(), position); } @Override @@ -178,8 +185,13 @@ public Optional symbol(Node node) { return Optional.empty(); } - BLangCompilationUnit compilationUnit = getCompilationUnit(nodeIdentifierLocation.get().lineRange().fileName()); - return lookupSymbol(compilationUnit, nodeIdentifierLocation.get().lineRange().startLine()); + Optional compilationUnit = + getCompilationUnit(nodeIdentifierLocation.get().lineRange().fileName()); + + if (compilationUnit.isEmpty()) { + return Optional.empty(); + } + return lookupSymbol(compilationUnit.get(), nodeIdentifierLocation.get().lineRange().startLine()); } /** @@ -256,8 +268,12 @@ public List references(Symbol symbol, Document targetDocument, boolean if (symbolLocation.isEmpty()) { return Collections.emptyList(); } + Optional compilationUnit = getCompilationUnit(targetDocument); + if (compilationUnit.isEmpty()) { + return Collections.emptyList(); + } BLangNode node = new NodeFinder(false) - .lookupEnclosingContainer(getCompilationUnit(targetDocument), symbolLocation.get().lineRange()); + .lookupEnclosingContainer(compilationUnit.get(), symbolLocation.get().lineRange()); return getReferences(symbolAtCursor, node, withDefinition); } @@ -273,16 +289,25 @@ public List references(Document sourceDocument, return Collections.emptyList(); } Location symbolLocation = symbolAtCursor.getPosition(); + + Optional compilationUnit = getCompilationUnit(targetDocument); + if (compilationUnit.isEmpty()) { + return Collections.emptyList(); + } + BLangNode node = new NodeFinder(false) - .lookupEnclosingContainer(getCompilationUnit(targetDocument), symbolLocation.lineRange()); + .lookupEnclosingContainer(compilationUnit.get(), symbolLocation.lineRange()); return getReferences(symbolAtCursor, node, withDefinition); } private BSymbol findSymbolAtCursorPosition(Document sourceDocument, LinePosition linePosition) { - BLangCompilationUnit sourceCompilationUnit = getCompilationUnit(sourceDocument); + Optional sourceCompilationUnit = getCompilationUnit(sourceDocument); + if (sourceCompilationUnit.isEmpty()) { + return null; + } SymbolFinder symbolFinder = new SymbolFinder(); - return symbolFinder.lookup(sourceCompilationUnit, linePosition); + return symbolFinder.lookup(sourceCompilationUnit.get(), linePosition); } private List getReferences(BSymbol symbol, BLangNode node, boolean withDefinition) { @@ -295,9 +320,12 @@ private List getReferences(BSymbol symbol, BLangNode node, boolean wit */ @Override public Optional type(LineRange range) { - BLangCompilationUnit compilationUnit = getCompilationUnit(range.fileName()); + Optional compilationUnit = getCompilationUnit(range.fileName()); + if (compilationUnit.isEmpty()) { + return Optional.empty(); + } NodeFinder nodeFinder = new NodeFinder(true); - BLangNode node = nodeFinder.lookup(compilationUnit, range); + BLangNode node = nodeFinder.lookup(compilationUnit.get(), range); if (node == null) { return Optional.empty(); @@ -308,9 +336,12 @@ public Optional type(LineRange range) { @Override public Optional typeOf(LineRange range) { - BLangCompilationUnit compilationUnit = getCompilationUnit(range.fileName()); + Optional compilationUnit = getCompilationUnit(range.fileName()); + if (compilationUnit.isEmpty()) { + return Optional.empty(); + } NodeFinder nodeFinder = new NodeFinder(false); - BLangNode node = nodeFinder.lookup(compilationUnit, range); + BLangNode node = nodeFinder.lookup(compilationUnit.get(), range); if (!isNonNamedArgExprNode(node) && !isObjectConstructorExpr(node) && !isAnonFunctionExpr(node)) { return Optional.empty(); @@ -395,10 +426,13 @@ public List diagnostics() { @Override public Optional expectedType(Document sourceDocument, LinePosition linePosition) { Optional typeSymbol = null; - BLangCompilationUnit compilationUnit = getCompilationUnit(sourceDocument); + Optional compilationUnit = getCompilationUnit(sourceDocument); + if (compilationUnit.isEmpty()) { + return Optional.empty(); + } SyntaxTree syntaxTree = sourceDocument.syntaxTree(); Node node = findInnerMostNode(linePosition, syntaxTree); - ExpectedTypeFinder expectedTypeFinder = new ExpectedTypeFinder(this, compilationUnit, + ExpectedTypeFinder expectedTypeFinder = new ExpectedTypeFinder(this, compilationUnit.get(), this.compilerContext, linePosition, sourceDocument); while (node != null) { try { @@ -478,21 +512,18 @@ private boolean isImportedSymbol(BSymbol symbol) { (Symbols.isFlagOn(symbol.flags, Flags.PUBLIC) || symbol.getKind() == SymbolKind.PACKAGE); } - private BLangCompilationUnit getCompilationUnit(Document srcFile) { + private Optional getCompilationUnit(Document srcFile) { return getCompilationUnit(srcFile.name()); } - private BLangCompilationUnit getCompilationUnit(String srcFile) { + private Optional getCompilationUnit(String srcFile) { List testSrcs = new ArrayList<>(); for (BLangTestablePackage pkg : bLangPackage.testablePkgs) { testSrcs.addAll(pkg.compUnits); } Stream units = Stream.concat(bLangPackage.compUnits.stream(), testSrcs.stream()); - return units - .filter(unit -> unit.name.equals(srcFile)) - .findFirst() - .get(); + return units.filter(unit -> unit.name.equals(srcFile)).findFirst(); } private boolean isCursorNotAtDefinition(BLangCompilationUnit compilationUnit, BSymbol symbolAtCursor, diff --git a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TestSourcesTest.java b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TestSourcesTest.java index 41dcc72f33e5..906d9dcf6c4d 100644 --- a/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TestSourcesTest.java +++ b/tests/ballerina-compiler-api-test/src/test/java/io/ballerina/semantic/api/test/TestSourcesTest.java @@ -21,6 +21,10 @@ import io.ballerina.compiler.api.symbols.Symbol; import io.ballerina.compiler.api.symbols.TypeDescKind; import io.ballerina.compiler.api.symbols.TypeSymbol; +import io.ballerina.compiler.syntax.tree.BasicLiteralNode; +import io.ballerina.compiler.syntax.tree.ConstantDeclarationNode; +import io.ballerina.compiler.syntax.tree.NodeVisitor; +import io.ballerina.compiler.syntax.tree.SyntaxTree; import io.ballerina.projects.Document; import io.ballerina.projects.DocumentId; import io.ballerina.projects.Module; @@ -43,6 +47,7 @@ import static io.ballerina.tools.text.LinePosition.from; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertNull; +import static org.testng.Assert.assertTrue; /** * Test cases for the positions of symbols. @@ -53,10 +58,11 @@ public class TestSourcesTest { private SemanticModel model; private Document srcFile; + private Project project; @BeforeClass public void setup() { - Project project = BCompileUtil.loadProject("test-src/test-project"); + this.project = BCompileUtil.loadProject("test-src/test-project"); Module baz = getModule(project, "baz"); model = project.currentPackage().getCompilation().getSemanticModel(baz.moduleId()); DocumentId id = baz.testDocumentIds().iterator().next(); @@ -103,7 +109,7 @@ public void testVisibleSymbols(int line, int col, List expSymbols) { public Object[][] getVisibleSymbolPos() { List moduleSymbols = List.of("PI", "ZERO", "add", "concat", "newPerson", "Person", "Employee", "BasicType", "PersonObj", "Digit", "FileNotFoundError", "EofError", - "Error", "Address"); + "Error", "Address", "IV"); return new Object[][]{ {17, 0, getSymbolNames(moduleSymbols, "testConstUse", "testAdd", "test")}, {26, 31, getSymbolNames(moduleSymbols, "testConstUse", "testAdd", "test", "sum")}, @@ -126,4 +132,71 @@ public Object[][] getExprPos() { {26, 22, 26, 25, INT}, }; } + + @Test + public void testSemanticModelForWrongModule() { + Module foo = getModule(this.project, "foo"); + SemanticModel fooSemanticModel = foo.getCompilation().getSemanticModel(); + + Module baz = getModule(this.project, "baz"); + SemanticModel bazSemanticModel = baz.getCompilation().getSemanticModel(); + + DocumentId bazBalDocId = baz.documentIds().stream() + .filter(docId -> docId.toString().contains("baz.bal")) + .findFirst().get(); + Document bazBalDoc = baz.document(bazBalDocId); + SyntaxTree bazSyntaxTree = bazBalDoc.syntaxTree(); + + // `symbol(Node node)`, `typeOf(Node node)` and `type(Node node)` + // use `fooSemanticModel` for `baz` module + bazSyntaxTree.rootNode().accept(getNodeVisitor(fooSemanticModel)); + + // `symbol(Document sourceDocument, LinePosition position)` + assertTrue(fooSemanticModel.symbol(bazBalDoc, from(16, 13)).isEmpty()); + + // `visibleSymbols(Document sourceFile, LinePosition position, DiagnosticState... states)` + assertTrue(fooSemanticModel.visibleSymbols(bazBalDoc, from(16, 23)).isEmpty()); + + // `references(Document sourceDocument, LinePosition position)` + assertTrue(fooSemanticModel.references(bazBalDoc, from(0, 0)).isEmpty()); + + // `references(Document sourceDocument, LinePosition position, boolean withDefinition)` + assertTrue(fooSemanticModel.references(bazBalDoc, from(0, 0), true).isEmpty()); + + // `references(Symbol symbol, Document targetDocument, boolean withDefinition)` + Optional constSym = bazSemanticModel.symbol(bazBalDoc, from(16, 13)); + assertTrue(constSym.isPresent()); + assertTrue(fooSemanticModel.references(constSym.get(), bazBalDoc, true).isEmpty()); + + // `references(Document sourceDocument, Document targetDocument, LinePosition position, boolean withDefinition)` + assertTrue(fooSemanticModel.references(bazBalDoc, bazBalDoc, from(16, 13), true).isEmpty()); + + DocumentId fooBalDocId = foo.documentIds().stream() + .filter(docId -> docId.toString().contains("constants.bal")) + .findFirst().get(); + assertTrue(fooSemanticModel.references(foo.document(fooBalDocId), bazBalDoc, from(16, 13), true).isEmpty()); + + // `expectedType(Document sourceDocument, LinePosition linePosition)` + assertTrue(fooSemanticModel.expectedType(bazBalDoc, from(16, 18)).isEmpty()); + + // `type(LineRange lineRange)` + assertTrue(fooSemanticModel.type(LineRange.from("baz.bal", + from(16, 18), from(16, 21))).isEmpty()); + } + + private NodeVisitor getNodeVisitor(SemanticModel semanticModel) { + return new NodeVisitor() { + @Override + public void visit(ConstantDeclarationNode constantDeclarationNode) { + assertTrue(semanticModel.symbol(constantDeclarationNode).isEmpty()); + constantDeclarationNode.initializer().accept(this); + } + + @Override + public void visit(BasicLiteralNode basicLiteralNode) { + assertTrue(semanticModel.typeOf(basicLiteralNode).isEmpty()); + assertTrue(semanticModel.type(basicLiteralNode).isEmpty()); + } + }; + } } diff --git a/tests/ballerina-compiler-api-test/src/test/resources/test-src/test-project/modules/baz/baz.bal b/tests/ballerina-compiler-api-test/src/test/resources/test-src/test-project/modules/baz/baz.bal new file mode 100644 index 000000000000..f5d864cf1e5f --- /dev/null +++ b/tests/ballerina-compiler-api-test/src/test/resources/test-src/test-project/modules/baz/baz.bal @@ -0,0 +1,17 @@ +// Copyright (c) 2024 WSO2 LLC. (http://www.wso2.org). +// +// 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. + +public const IV = 255;