Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixes queries for lsp #349

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion dev/src/compiler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -488,7 +488,9 @@ void Compile(NativeRegistry &nfr, string_view fn, string_view stringsource, stri
parser.Parse();
if (query) PrepQuery(*query, filenames);
TypeChecker tc(parser, st, return_value, query, full_error);
if (query) tc.ProcessQuery(); // Failed to find location during type checking.
if (query) { // Failed to find location during type checking.
if(!tc.ProcessQuery()) THROW_OR_ABORT("query_unknown_ident: " + query->iden);
}
if (lex.num_errors) THROW_OR_ABORT("errors encountered, aborting");
tc.Stats(filenames);
// Optimizer is not optional, must always run, since TypeChecker and CodeGen
Expand Down
5 changes: 5 additions & 0 deletions dev/src/lobster/idents.h
Original file line number Diff line number Diff line change
Expand Up @@ -903,6 +903,11 @@ struct SymbolTable {
}
auto uit = gudts.find(name);
if (uit != gudts.end()) return uit->second;
for (auto gudt = gudttable.rbegin(); gudt != gudttable.rend(); ++gudt) {
inferrna marked this conversation as resolved.
Show resolved Hide resolved
if((*gudt)->name == name) {
return *gudt;
}
}
return nullptr;
}

Expand Down
118 changes: 86 additions & 32 deletions dev/src/lobster/typecheck.h
Original file line number Diff line number Diff line change
Expand Up @@ -2260,7 +2260,12 @@ struct TypeChecker {
// Check if we need to do any lifetime adjustments.
AdjustLifetime(n, recip, idents);
// Check for queries.
if (query && query->qloc == n->line) ProcessQuery();
if (query) {
if ((reqret==0 && n->line == query->qloc) //reqret usually in the end of line
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

reqret indicates if there is a surrounding expression that requires a return value of the current expression, what is the effect of checking this? I don't understand the comment.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So can I use reqret==0 as an indicator that it is the last pass on this line?

|| (n->line.line > query->qloc.line && n->line.fileidx==query->qloc.fileidx)) { //If above missed
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this will attempt to do the query in all lines following..

Copy link
Contributor Author

@inferrna inferrna Jan 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I meant to fix this behavior:
For example you need to know about x here

let x = foo()

if you call ProcessQuery() on the first encounter of this line you will get undefined.
I try to call it on the last by checking reqret==0 or on one of the next lines. Looking more sane check n->line.line == query->qloc.line+1 isn't working if the next line is an empty one. I thought reqret==0 should be enough, but still not sure.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I now finally understand what you're trying to do :)
reqret==0 will work under most circumstances but it is very shaky, there may be exceptions.
Just using > for the line comparison seems more robust, and should be sufficient.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But how then will be queried the last line…) I searched for anything that would be the last line indicator, but nothing found.
As for now, reqret==0 is checked on the queried line, so there are two possible fails:

  1. Early reqret==0. A little chance query to fail (found nothing).
  2. No reqret==0 at all on this line. May be a problem if line is the last one. Can we have the last line without reqret==0 in it? If the answer is "No", then reqret==0 is still a solution.

ProcessQuery();
}
}
}

// TODO: Can't do this transform ahead of time, since it often depends upon the input args.
Expand Down Expand Up @@ -2328,47 +2333,96 @@ struct TypeChecker {
}
}
}
std::optional<TypeRef> FindVarType(string_view ident, SubFunction *sf) {
inferrna marked this conversation as resolved.
Show resolved Hide resolved
for (auto &vars : {sf->args, sf->locals, sf->freevars}) {
for (auto &var : vars) {
if (var.sid->id->name == ident) {
return var.sid->type;
}
}
}
return std::nullopt;
}

bool ProcessDefinition(GUDT *parent, string full_iden, SubFunction **sf) {
size_t pos = full_iden.find('.');
bool got_pos = pos != std::string::npos;
string ident = full_iden;
if (got_pos) {
ident = full_iden.substr(0, pos);
//Possible a class or a struct name
auto ident_type = FindVarType(ident, *sf);
if (ident_type.has_value()) {
ident = TypeName(ident_type.value());
}
}
auto new_parent_struct = st.LookupStruct(ident);

void ProcessQuery() {
if (new_parent_struct && !got_pos){ //Just class instance
LocationQuery(new_parent_struct->line, Signature(*new_parent_struct));
}

// FIXME: may not work when namespaces are involved.
auto f = st.FindFunction(ident);
if (f) {
auto ov = f->overloads[0];
if(parent) { //Try to find method of parent class with same name
for (auto &candidate_ov : f->overloads) {
if (TypeName(candidate_ov->givenargs[0]) == parent->name) {
ov = candidate_ov;
break;
}
}
}
if (ov->gbody) { // FIXME: ignores function types. Now fixed (or not?)
LocationQuery(ov->gbody->line, ov->sf ? Signature(*ov->sf) : "");
}
}
auto fld = st.FieldUse(ident);
if (fld && parent) {
// To know what this belongs to, would need to find the object it belongs to.
// For now, simply see if we can find any class that has this field.
int fi = parent->Has(fld);
if (fi >= 0) {
auto struct_type = st.LookupStruct(TypeName(parent->fields[fi].giventype));
if (got_pos) { //Go further with detected struct as a parent
ProcessDefinition(struct_type, full_iden.substr(pos+1), sf);
}
LocationQuery(parent->fields[fi].defined_in, TypeName(parent->fields[fi].giventype));
}
}
auto nf = parser.natreg.FindNative(ident);
if (nf) {
// This doesn't have a source code location, so output a signature the IDE can display.
THROW_OR_ABORT("query_signature: " + Signature(*nf));
}
if (fld) { //Failed to find field in parent or no parent
for (auto gudt : st.gudttable) {
int fi = gudt->Has(fld);
if (fi >= 0) {
// FIXME: this is really basic, lets at least find the field line.
LocationQuery(gudt->line, TypeName(gudt->fields[fi].giventype));
}
}
}
if (got_pos) { //Go further
ProcessDefinition(new_parent_struct, full_iden.substr(pos+1), sf);
}
return false;
}

bool ProcessQuery() {
if (query->kind == "definition") {
// The top scope includes a list of free vars so should be able to resolve any var
// at the given location.. if no scopes, use top fun.
auto sf = scopes.empty() ? st.toplevel : scopes.back().sf;
FindVar(sf->args);
FindVar(sf->locals);
FindVar(sf->freevars);
auto gudt = st.LookupStruct(query->iden);
if (gudt) {
LocationQuery(gudt->line, Signature(*gudt));
}
// FIXME: may not work when namespaces are involved.
auto f = st.FindFunction(query->iden);
if (f) {
auto ov = f->overloads[0];
if (ov->gbody) { // FIXME: ignores function types.
LocationQuery(ov->gbody->line, ov->sf ? Signature(*ov->sf) : "");
}
}
auto nf = parser.natreg.FindNative(query->iden);
if (nf) {
// This doesn't have a source code location, so output a signature the IDE can display.
THROW_OR_ABORT("query_signature: " + Signature(*nf));
}
auto fld = st.FieldUse(query->iden);
if (fld) {
// To know what this belongs to, would need to find the object it belongs to.
// For now, simply see if we can find any class that has this field.
for (auto gudt : st.gudttable) {
int fi = gudt->Has(fld);
if (fi >= 0) {
// FIXME: this is really basic, lets at least find the field line.
LocationQuery(gudt->line, TypeName(gudt->fields[fi].giventype));
}
}
}
THROW_OR_ABORT("query_unknown_ident: " + query->iden);
return ProcessDefinition(nullptr, query->iden, &sf);
} else {
THROW_OR_ABORT("query_unknown_kind: " + query->kind);
return false;
}
}

Expand Down
Loading