Skip to content

Commit

Permalink
[vscode] Go to implementations.
Browse files Browse the repository at this point in the history
  • Loading branch information
pfusik committed Apr 24, 2024
1 parent 2e48817 commit ae3e877
Show file tree
Hide file tree
Showing 9 changed files with 148 additions and 153 deletions.
1 change: 1 addition & 0 deletions AST.fu
Original file line number Diff line number Diff line change
Expand Up @@ -1246,6 +1246,7 @@ public class FuMethod : FuMethodBase
}
public override bool IsStatic() => this.CallType == FuCallType.Static;
public bool IsAbstractOrVirtual() => this.CallType == FuCallType.Abstract || this.CallType == FuCallType.Virtual;
public bool IsAbstractVirtualOrOverride() => this.CallType == FuCallType.Abstract || this.CallType == FuCallType.Virtual || this.CallType == FuCallType.Override;
public FuVar!? FirstParameter()
{
assert this.Parameters.First is FuVar! first; // FIXME: FuVar!?
Expand Down
13 changes: 4 additions & 9 deletions GenC.fu
Original file line number Diff line number Diff line change
Expand Up @@ -1813,11 +1813,8 @@ public class GenC : GenCCpp
WriteUpcast(declaringClass, klass.Parent);
}
else {
FuClass definingClass = declaringClass;
switch (method.CallType) {
case FuCallType.Abstract:
case FuCallType.Virtual:
case FuCallType.Override:
if (method.IsAbstractVirtualOrOverride()) {
FuClass definingClass = declaringClass;
if (method.CallType == FuCallType.Override) {
assert method.GetDeclaringMethod().Parent is FuClass declaringClass1;
declaringClass = declaringClass1;
Expand All @@ -1842,11 +1839,9 @@ public class GenC : GenCCpp
WriteChar(')');
Write("->");
WriteCamelCase(method.Name);
break;
default:
WriteName(method);
break;
}
else
WriteName(method);
WriteChar('(');
if (method.CallType != FuCallType.Static) {
if (obj != null)
Expand Down
19 changes: 6 additions & 13 deletions Sema.fu
Original file line number Diff line number Diff line change
Expand Up @@ -2228,20 +2228,13 @@ public class FuSema
if (method.CallType == FuCallType.Override || method.CallType == FuCallType.Sealed) {
if (klass.Parent.TryLookup(method.Name, false) is FuMethod! baseMethod) {
// TODO: check private
switch (baseMethod.CallType) {
case FuCallType.Abstract:
case FuCallType.Virtual:
case FuCallType.Override:
if (method.IsMutator() != baseMethod.IsMutator()) {
if (method.IsMutator())
ReportError(method, "Mutating method cannot override a non-mutating method");
else
ReportError(method, "Non-mutating method cannot override a mutating method");
}
break;
default:
if (!baseMethod.IsAbstractVirtualOrOverride())
ReportError(method, "Base method is not abstract or virtual");
break;
else if (method.IsMutator() != baseMethod.IsMutator()) {
if (method.IsMutator())
ReportError(method, "Mutating method cannot override a non-mutating method");
else
ReportError(method, "Non-mutating method cannot override a mutating method");
}
if (!method.Type.EqualsType(baseMethod.Type))
ReportError(method.Type, "Base method has a different return type");
Expand Down
7 changes: 4 additions & 3 deletions editors/vscode/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,10 @@ A Visual Studio Code extension for the [Fusion programming language](https://fus

* Syntax highlighting
* Error highlighting
* Outline
* Go to definition
* Go to references, Find all references
* Breadcrumbs, Outline
* Go to / Peek definition
* Go to / Peek / Find all implementations
* Go to / Peek / Find all references
* Snippets

Fusion is a language designed to be translated automatically to
Expand Down
116 changes: 73 additions & 43 deletions editors/vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
// along with Fusion Transpiler. If not, see http://www.gnu.org/licenses/

import * as vscode from "vscode";
import { FuParser, FuProgram, FuSystem, FuSema, FuSemaHost, FuSymbolReferenceVisitor, FuStatement, FuSymbol, FuContainerType, FuEnum, FuMember, FuField, FuMethod } from "./fucheck.js";
import { FuParser, FuProgram, FuSystem, FuSema, FuSemaHost, FuSymbolReferenceVisitor, FuStatement, FuSymbol, FuContainerType, FuEnum, FuClass, FuMember, FuField, FuMethod } from "./fucheck.js";

class VsCodeHost extends FuSemaHost
{
Expand Down Expand Up @@ -73,29 +73,6 @@ class VsCodeHost extends FuSemaHost
sema.setHost(this);
sema.process();
}

async findSymbol(document: vscode.TextDocument, position: vscode.Position): Promise<FuSymbol | null>
{
const parser = this.createParser();
const filename = document.uri.toString();
parser.findName(filename, position.line, position.character);
const files = await vscode.workspace.findFiles("*.fu");
if (files.some(uri => uri.toString() == filename))
await this.parseFolder(files, parser);
else
this.parseDocument(document, parser);
this.doSema();
return parser.getFoundDefinition();
}

toLocation(statement: FuStatement): vscode.Location | null
{
if (statement.loc <= 0)
return null;
const line = this.program.getLine(statement.loc);
const file = this.program.getSourceFile(line);
return new vscode.Location(vscode.Uri.parse(file.filename), new vscode.Position(line - file.line, statement.loc - this.program.lineLocs[line]));
}
}

class VsCodeDiagnostics extends VsCodeHost
Expand Down Expand Up @@ -159,39 +136,87 @@ class VsCodeDiagnostics extends VsCodeHost
}
}

class VsCodeDefinitionProvider extends VsCodeHost
{
async findDefinition(document: vscode.TextDocument, position: vscode.Position): Promise<vscode.Location | null>
{
const symbol = await this.findSymbol(document, position);
return symbol == null ? null : this.toLocation(symbol);
}
}

class VsCodeReferenceCollector extends FuSymbolReferenceVisitor
{
host: VsCodeHost;
provider: VsCodeGotoProvider;
result: vscode.Location[] = [];

constructor(host: VsCodeHost)
constructor(provider: VsCodeGotoProvider)
{
super();
this.host = host;
this.provider = provider;
}

visitFound(reference: FuStatement): void
{
const location = this.host.toLocation(reference);
if (location != null)
this.result.push(location);
this.provider.pushLocation(this.result, reference);
}
}

class VsCodeReferenceProvider extends VsCodeHost
class VsCodeGotoProvider extends VsCodeHost
{
async #findSymbol(document: vscode.TextDocument, position: vscode.Position): Promise<FuSymbol | null>
{
const parser = this.createParser();
const filename = document.uri.toString();
parser.findName(filename, position.line, position.character);
const files = await vscode.workspace.findFiles("*.fu");
if (files.some(uri => uri.toString() == filename))
await this.parseFolder(files, parser);
else
this.parseDocument(document, parser);
this.doSema();
return parser.getFoundDefinition();
}

#toLocation(statement: FuStatement): vscode.Location | null
{
if (statement.loc <= 0)
return null;
const line = this.program.getLine(statement.loc);
const file = this.program.getSourceFile(line);
return new vscode.Location(vscode.Uri.parse(file.filename), new vscode.Position(line - file.line, statement.loc - this.program.lineLocs[line]));
}

pushLocation(result: vscode.Location[], statement: FuStatement): void
{
const location = this.#toLocation(statement);
if (location != null)
result.push(location);
}

async findDefinition(document: vscode.TextDocument, position: vscode.Position): Promise<vscode.Location | null>
{
const symbol = await this.#findSymbol(document, position);
return symbol == null ? null : this.#toLocation(symbol);
}

async findImplementations(document: vscode.TextDocument, position: vscode.Position): Promise<vscode.Location[]>
{
const symbol = await this.#findSymbol(document, position);
const result: vscode.Location[] = [];
if (symbol != null) {
if (symbol instanceof FuClass) {
for (const subclass of this.program.classes) {
if (symbol.isSameOrBaseOf(subclass))
this.pushLocation(result, subclass);
}
}
else if (symbol instanceof FuMethod && symbol.isAbstractVirtualOrOverride()) {
for (const subclass of this.program.classes) {
if (symbol.parent.isSameOrBaseOf(subclass) && subclass.contains(symbol))
this.pushLocation(result, subclass.tryLookup(symbol.name, false));
}
}
else
this.pushLocation(result, symbol);
}
return result;
}

async findReferences(document: vscode.TextDocument, position: vscode.Position): Promise<vscode.Location[]>
{
const symbol = await this.findSymbol(document, position);
const symbol = await this.#findSymbol(document, position);
if (symbol == null)
return [];
const collector = new VsCodeReferenceCollector(this);
Expand Down Expand Up @@ -250,12 +275,17 @@ export function activate(context: vscode.ExtensionContext): void
context.subscriptions.push(vscode.workspace.onDidCloseTextDocument(document => diagnostics.delete(document)));
vscode.languages.registerDefinitionProvider("fusion", {
provideDefinition(document, position, token) {
return new VsCodeDefinitionProvider().findDefinition(document, position);
return new VsCodeGotoProvider().findDefinition(document, position);
}
});
vscode.languages.registerImplementationProvider("fusion", {
provideImplementation(document, position, token) {
return new VsCodeGotoProvider().findImplementations(document, position);
}
});
vscode.languages.registerReferenceProvider("fusion", {
provideReferences(document, position, context, token) {
return new VsCodeReferenceProvider().findReferences(document, position);
return new VsCodeGotoProvider().findReferences(document, position);
}
});
vscode.languages.registerDocumentSymbolProvider("fusion", {
Expand Down
73 changes: 32 additions & 41 deletions libfut.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2270,6 +2270,11 @@ bool FuMethod::isAbstractOrVirtual() const
return this->callType == FuCallType::abstract || this->callType == FuCallType::virtual_;
}

bool FuMethod::isAbstractVirtualOrOverride() const
{
return this->callType == FuCallType::abstract || this->callType == FuCallType::virtual_ || this->callType == FuCallType::override_;
}

FuVar * FuMethod::firstParameter() const
{
FuVar * first = static_cast<FuVar *>(this->parameters.first);
Expand Down Expand Up @@ -6650,20 +6655,13 @@ void FuSema::resolveCode(FuClass * klass)
if (method->body != nullptr) {
if (method->callType == FuCallType::override_ || method->callType == FuCallType::sealed) {
if (FuMethod *baseMethod = dynamic_cast<FuMethod *>(klass->parent->tryLookup(method->name, false).get())) {
switch (baseMethod->callType) {
case FuCallType::abstract:
case FuCallType::virtual_:
case FuCallType::override_:
if (method->isMutator() != baseMethod->isMutator()) {
if (method->isMutator())
reportError(method, "Mutating method cannot override a non-mutating method");
else
reportError(method, "Non-mutating method cannot override a mutating method");
}
break;
default:
if (!baseMethod->isAbstractVirtualOrOverride())
reportError(method, "Base method is not abstract or virtual");
break;
else if (method->isMutator() != baseMethod->isMutator()) {
if (method->isMutator())
reportError(method, "Mutating method cannot override a non-mutating method");
else
reportError(method, "Non-mutating method cannot override a mutating method");
}
if (!method->type->equalsType(baseMethod->type.get()))
reportError(method->type.get(), "Base method has a different return type");
Expand Down Expand Up @@ -11067,42 +11065,35 @@ void GenC::writeCCall(const FuExpr * obj, const FuMethod * method, const std::ve
writeUpcast(declaringClass, klass->parent);
}
else {
const FuClass * definingClass = declaringClass;
switch (method->callType) {
case FuCallType::abstract:
case FuCallType::virtual_:
case FuCallType::override_:
if (method->isAbstractVirtualOrOverride()) {
const FuClass * definingClass = declaringClass;
if (method->callType == FuCallType::override_) {
const FuClass * declaringClass1 = static_cast<const FuClass *>(method->getDeclaringMethod()->parent);
declaringClass = declaringClass1;
}
if (obj != nullptr)
klass = obj->type->asClassType()->class_;
{
const FuClass * ptrClass = getVtblPtrClass(klass);
const FuClass * structClass = getVtblStructClass(definingClass);
if (structClass != ptrClass) {
write("((const ");
writeName(structClass);
write("Vtbl *) ");
}
if (obj != nullptr) {
obj->accept(this, FuPriority::primary);
writeMemberAccess(obj->type.get(), ptrClass);
}
else
writeSelfForField(ptrClass);
write("vtbl");
if (structClass != ptrClass)
writeChar(')');
write("->");
writeCamelCase(method->name);
break;
const FuClass * ptrClass = getVtblPtrClass(klass);
const FuClass * structClass = getVtblStructClass(definingClass);
if (structClass != ptrClass) {
write("((const ");
writeName(structClass);
write("Vtbl *) ");
}
default:
writeName(method);
break;
if (obj != nullptr) {
obj->accept(this, FuPriority::primary);
writeMemberAccess(obj->type.get(), ptrClass);
}
else
writeSelfForField(ptrClass);
write("vtbl");
if (structClass != ptrClass)
writeChar(')');
write("->");
writeCamelCase(method->name);
}
else
writeName(method);
writeChar('(');
if (method->callType != FuCallType::static_) {
if (obj != nullptr)
Expand Down
Loading

0 comments on commit ae3e877

Please sign in to comment.