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

Adds Callfunc Completion and Validation #1352

Open
wants to merge 13 commits into
base: release-1.0.0
Choose a base branch
from

Conversation

markwpearce
Copy link
Collaborator

@markwpearce markwpearce commented Nov 21, 2024

Gives callFunc a whole lotta love!

  • Fixes an issue with completions on @. where ALL possible callfuncs would appear. This was a result of the parser having an exception on incomplete @. invocations
  • Adds function documentation to hovers on @. callfunc invocations
  • Stores all resolved types associated with callfuncs for a component in a symbolTable that is used a sibiling table when the callfunc is used in another scope
  • Return type of Callfunc and @. invocations are used to set types in assignments
  • Validates arg counts and type mismatches for callfunc and @.

While I was it, I also added validation and return typing for <component>.createChild()

Additional validation enhancements:

  • if a type depends on another type (eg, has a member of another type), and that member type changes, then all segments/ASTNodes that reference the wider type are also re-validated. This means that the type-safety flows down and things get appropriately re-validated when there are changes.
  • Better handling of typecast statements when setting up file segments for revalidation
  • Better handling of when component types change, and re-validation because of that.

TODO:

  • Make validation updates work better. Currently, you need to edit an XML file of the component for things to revalidate if the exported function type changes
  • Deal with how adding sibling tables messes up other validations. Like if the resolved version stay around when they actually should be unresolved
  • Do not have validation errors if the component type is the generic roSGNode

To be honest, I think that if we properly re-build the Component type when a member file changes, that would fix both problems.

Also fixes #1309

@markwpearce markwpearce marked this pull request as ready for review November 25, 2024 17:16
@markwpearce markwpearce added this to the v1.0.0 milestone Nov 25, 2024
@@ -169,6 +174,16 @@ export class AstValidationSegmenter {
const assignedSymbolsNames = new Set<string>();
this.currentClassStatement = segment.findAncestor(isClassStatement);

if (isTypecastStatement(segment)) {
Copy link
Collaborator Author

@markwpearce markwpearce Nov 25, 2024

Choose a reason for hiding this comment

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

Changes in this file are related to typecast statements... if there's a top level typecast, then if that type changes, everything referencing m should be revalidated

@@ -234,7 +234,7 @@ export class DiagnosticManager {
isMatch = !!context.tags?.includes(filter.tag);
}
if (isMatch && needToMatch.scope) {
isMatch = context.scope === filter.scope;
isMatch = context.scope?.name === filter.scope.name;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

if an xml file changes, then there is a new scope created, so a check on context.scope === filter.scope wouldn't work to remove old diagnostics

@@ -10,6 +10,7 @@ export abstract class BscType {
public readonly memberTable: SymbolTable;
protected __identifier: string;
protected hasAddedBuiltInInterfaces = false;
public isBuiltIn = false;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Added a flag at the type level to basically say "this is a type we don't need to mess with"

@@ -385,16 +402,20 @@ export class Program {
* @param xmlFile XML file with <component> tag
*/
private addDeferredComponentTypeSymbolCreation(xmlFile: XmlFile) {
this.componentSymbolsToUpdate.add({ componentKey: this.getComponentKey(xmlFile), componentName: xmlFile.componentName?.text });

const componentKey = this.getComponentKey(xmlFile);
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

changed this to a map with unique keys, because there multiple places where it can get updated

const scopesForCrossScopeValidation = [];
for (let scopeName of this.getSortedScopeNames()) {
let scope = this.scopes[scopeName];
if (this.globalScope !== scope && !scope.isValidated) {
if (this.globalScope !== scope && (someComponentTypeChanged || !scope.isValidated)) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

If a component type changes, don't try to limit the number of scopes to check.
In reality, this hardly changes the actual validation time anyway


private cachedCircularReferenceCheck: null | boolean = null;

private hasCircularReferenceWithAncestor() {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ran into some problems with circular references in symbol tables --- best way to deal with that is to check once and cache

@@ -288,18 +293,8 @@ export class ScopeValidator {
//if this is a `createObject('roSGNode'` call, only support known sg node types
if (firstParamStringValueLower === 'rosgnode' && isLiteralExpression(call?.args[1])) {
const componentName: Token = call?.args[1]?.tokens.value;
//don't validate any components with a colon in their name (probably component libraries, but regular components can have them too).
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

refactored some of this code to make checking specific KINDs of function calls easier - eg. callfunc args are offset by 1 vs. real function call args

@@ -46,6 +46,7 @@ import { DefinitionProvider } from '../bscPlugin/definition/DefinitionProvider';
export interface ProvidedSymbol {
symbol: BscSymbol;
duplicates: BscSymbol[];
requiredSymbolNames?: Set<string>;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

These are the required symbol names this symbol provided from the file needs

let openParen = this.consume(DiagnosticMessages.expectedOpenParenToFollowCallfuncIdentifier(), TokenKind.LeftParen);
let call = this.finishCall(openParen, callee, false);

let methodName = this.tryConsume(DiagnosticMessages.expectedIdentifier(), TokenKind.Identifier, ...AllowedProperties);
Copy link
Collaborator Author

@markwpearce markwpearce Nov 25, 2024

Choose a reason for hiding this comment

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

this change makes it so an exception is NOT thrown on a bad @. line, so that proper completions are provided

return true;
}

return isComponentType(targetType) && this.name.toLowerCase() === targetType.name.toLowerCase() &&
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

A better job of checking equality of Component Types, so that we can check if a change was substantial enough to re-validate

@@ -60,9 +73,11 @@ export class ComponentType extends InheritableType {
}
}

private hasStartedAddingBuiltInInterfaces = false;
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

needed to add this incase there were some circular references -- there would be stack overflows

return this.callFuncMemberTable.getSymbolType(name, options);
const callFuncType = this.callFuncMemberTable.getSymbolType(name, options);

const addAssociatedTypesTableAsSiblingToMemberTable = (type: BscType) => {
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

if you find a callFunc for this a given name, adds a sibling table of the types required for that callFunc to the type.
This makes it so the required types for the callfunc are available in a different context

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

1 participant