mirror of
https://github.com/keymanapp/keyman.git
synced 2026-10-21 05:17:43 +00:00
fix(developer): add missing message parameters and cleanup kmc-kmn messages further
* kmcmplib messages need to use a standard pattern for parameters, which cannot be named. Several of the messages had the wrong pattern * Tidy up the kmn-compiler-messages module and make it more consistent to reduce risk of this happening in the future * Add a unit test to further verify the structure of the module as far as possible Fixes: #16492 Test-bot: skip
This commit is contained in:
parent
750ad89611
commit
f438a519e1
3 changed files with 382 additions and 227 deletions
|
|
@ -59,7 +59,7 @@ export enum CompilerErrorMask {
|
|||
Severity = 0x00F00000, // includes reserved bits, 16 possible severity levels
|
||||
Error = 0x000FFFFF, // error | namespace
|
||||
Namespace = 0x000FF000, // 256 possible namespaces
|
||||
BaseError = 0x00000FFF, // error code, 2,048 possible error codes per namespace
|
||||
BaseError = 0x00000FFF, // error code, 4,096 possible error codes per namespace
|
||||
Reserved = 0xFF000000, // do not use these error values at this time
|
||||
};
|
||||
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -3,9 +3,24 @@ import { assert } from 'chai';
|
|||
import { CompilerErrorMask, CompilerErrorNamespace } from '@keymanapp/developer-utils';
|
||||
import { TestCompilerCallbacks, verifyCompilerMessagesObject } from '@keymanapp/developer-test-helpers';
|
||||
import { KmnCompiler } from '../src/main.js';
|
||||
import { KmnCompilerMessageRanges, KmnCompilerMessages } from '../src/compiler/kmn-compiler-messages.js';
|
||||
import { KmcKmnCompilerEvent, KmnCompilerMessageRanges, KmnCompilerMessages } from '../src/compiler/kmn-compiler-messages.js';
|
||||
import { makePathToFixture } from './helpers/index.js';
|
||||
|
||||
|
||||
// Source - https://stackoverflow.com/a/9924463
|
||||
// Posted by Jack Allan, modified by community. See post 'Timeline' for change history
|
||||
// Retrieved 2026-09-01, License - CC BY-SA 3.0
|
||||
function getParamNames(func: any) {
|
||||
const STRIP_COMMENTS = /(\/\/.*$)|(\/\*[\s\S]*?\*\/)|(\s*=[^,\)]*(('(?:\\'|[^'\r\n])*')|("(?:\\"|[^"\r\n])*"))|(\s*=[^,\)]*))/mg;
|
||||
const ARGUMENT_NAMES = /([^\s,]+)/g;
|
||||
const fnStr = func.toString().replace(STRIP_COMMENTS, '');
|
||||
let result = fnStr.slice(fnStr.indexOf('(')+1, fnStr.indexOf(')')).match(ARGUMENT_NAMES);
|
||||
if(result === null)
|
||||
result = [];
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
describe('KmnCompilerMessages', function () {
|
||||
const callbacks = new TestCompilerCallbacks(this);
|
||||
|
||||
|
|
@ -13,6 +28,53 @@ describe('KmnCompilerMessages', function () {
|
|||
return verifyCompilerMessagesObject(KmnCompilerMessages, CompilerErrorNamespace.KmnCompiler);
|
||||
});
|
||||
|
||||
it('should have correct parameter types and numeric ranges for kmcmplib vs kmw vs kmc-kmn messages', function() {
|
||||
// See comment in kmn-compiler-messages.ts for logic.
|
||||
// This function follows the same pattern as `verifyCompilerMessagesObject` but with specific tests for
|
||||
// this more constrained object
|
||||
const toTitleCase = (s: string) => s.substring(0, 1).toUpperCase() + s.substring(1).toLowerCase();
|
||||
const keys = Object.keys(KmnCompilerMessages);
|
||||
const m = KmnCompilerMessages as Record<string,any>;
|
||||
|
||||
for(const key of keys) {
|
||||
if(typeof m[key] == 'number') {
|
||||
const o = /^(DEBUG|VERBOSE|INFO|HINT|WARN|ERROR|FATAL)_([A-Za-z0-9_]+)$/.exec(key);
|
||||
const f = toTitleCase(o[1]) + '_' + o[2];
|
||||
// note: validation done in verifyCompilerMessagesObject, don't repeat it here
|
||||
|
||||
const v: KmcKmnCompilerEvent = m[f]('','','','','','','','','','','','' /* ignore arguments*/);
|
||||
|
||||
const code = m[key] & CompilerErrorMask.BaseError;
|
||||
const hex = code.toString(16).padStart(3, '0');
|
||||
|
||||
if(code >= KmnCompilerMessageRanges.RANGE_CompilerMessage_Min &&code <= KmnCompilerMessageRanges.RANGE_CompilerMessage_Max) {
|
||||
// kmc-kmn messages, should not be using mw() function or mc() function
|
||||
//
|
||||
assert.equal(v.kmcKmnSource, 'kmc-kmn', `expected ${v.kmcKmnSource} to equal 'kmc-kmn' for ${hex}, '${key}'.`);
|
||||
} else if(code >= KmnCompilerMessageRanges.RANGE_KMN_COMPILER_MIN && code <= KmnCompilerMessageRanges.RANGE_KMN_COMPILER_MAX) {
|
||||
// kmcmplib or kmw compiler messages, should not be using m() function or mx() function
|
||||
assert.oneOf(v.kmcKmnSource, ['kmcmplib', 'kmw-compiler'], `expected ${v.kmcKmnSource} to equal 'kmcmplib' or 'kmw-compiler for ${hex}, '${key}'.`);
|
||||
if(v.kmcKmnSource == 'kmcmplib') {
|
||||
// The only allowed param is an object o with property p. We
|
||||
// cannot test parameter shape directly, so we will compare
|
||||
// outputs of calling if o.p is set, which should not match what
|
||||
// we get when o.p is not set.
|
||||
const params = getParamNames(m[f]);
|
||||
if(params.length) {
|
||||
const v2: KmcKmnCompilerEvent = m[f]({p:['1','2','3','4','5','6','7','8','9']});
|
||||
assert.notEqual(v.message, v2.message, `Message '${key}' (${hex}) must use KmcmpLibMessageParameters pattern`);
|
||||
}
|
||||
}
|
||||
} else if(code >= KmnCompilerMessageRanges.RANGE_LEXICAL_MODEL_MIN && code <= KmnCompilerMessageRanges.RANGE_LEXICAL_MODEL_MAX) {
|
||||
assert.fail(`Message identifier ${hex} for '${key}' is reserved and should not be used (LEXICAL_MODEL range)`);
|
||||
} else {
|
||||
assert.fail(`Message identifier ${hex} for '${key}' is out of range`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
it('should have a 1:1 correspondence with kmn_compiler_errors.h', function() {
|
||||
|
||||
const headerFilename = 'kmn_compiler_errors.h';
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue