mirror of
https://github.com/abhigyanpatwari/GitNexus.git
synced 2026-11-03 07:22:53 +00:00
fix(r): refuse a new name that registers a method of a group or common generic
BASE_GENERICS missed the S3 group generics and many dispatching base functions, so sum.items, Ops.money, Math.money, abs.diff, is.numeric.foo, as.complex.foo and unlist.foo were accepted and silently registered as methods. The list now has the Ops, Math, Summary, Complex and matrixOps groups with each member, the is.* and as.* generics, and the common base, stats, graphics and utils generics. Unit tests walk the documented group members, and two fixtures cover Ops.money and sum.items.
This commit is contained in:
parent
dd4daefa3f
commit
0bcd54d289
8 changed files with 118 additions and 14 deletions
|
|
@ -40,22 +40,48 @@ function rStringValue(text: string): string | null {
|
|||
|
||||
/**
|
||||
* Generics of base R and its default packages that an S3 method name `generic.class` can register
|
||||
* against, whether or not the indexed sources define them. Not exhaustive: it covers the generics
|
||||
* a project is most likely to meet by accident, and the repository's own functions are checked
|
||||
* separately.
|
||||
* against, whether or not the indexed sources define them: the S3 group generics (`Ops`, `Math`,
|
||||
* `Summary`, `Complex`, `matrixOps`) and the functions each group dispatches, the `is.*` and `as.*`
|
||||
* generics, and the common generics of base, stats, utils and graphics. Not exhaustive, and it
|
||||
* names no other package's generics (`tidy`, `filter`, ...); the repository's own functions are
|
||||
* checked separately.
|
||||
*/
|
||||
const BASE_GENERICS: ReadonlySet<string> = new Set(
|
||||
(
|
||||
'print format summary plot lines points text image hist barplot boxplot pairs persp ' +
|
||||
'as.character as.data.frame as.list as.vector as.numeric as.integer as.logical as.matrix ' +
|
||||
'as.Date as.POSIXct as.POSIXlt as.factor as.table as.function as.environment as.double ' +
|
||||
'length names dim dimnames levels rev sort unique duplicated anyDuplicated rep seq ' +
|
||||
'mean median quantile range max min round diff scale cut split merge subset transform ' +
|
||||
'with within head tail t c cbind rbind toString all.equal is.na labels str ' +
|
||||
'update predict residuals coef fitted anova simulate terms formula family weights ' +
|
||||
'vcov logLik AIC BIC nobs deviance confint profile model.frame row.names open close ' +
|
||||
'by aggregate droplevels relevel reorder xtfrm solve determinant julian months weekdays'
|
||||
).split(' '),
|
||||
[
|
||||
// Group generics, and the members of the Math, Summary and Complex groups
|
||||
'Ops Math Summary Complex matrixOps',
|
||||
'abs sign sqrt floor ceiling trunc round signif exp log expm1 log1p cos sin tan cospi sinpi tanpi',
|
||||
'acos asin atan cosh sinh tanh acosh asinh atanh lgamma gamma digamma trigamma',
|
||||
'cumsum cumprod cummax cummin',
|
||||
'all any sum prod max min range',
|
||||
'Arg Conj Im Mod Re',
|
||||
// is.* and as.* generics
|
||||
'is.na is.nan is.finite is.infinite is.numeric is.array is.matrix is.unsorted is.list is.vector',
|
||||
'is.character is.logical is.integer is.double is.complex is.function is.environment',
|
||||
'as.character as.numeric as.integer as.logical as.double as.complex as.raw as.vector as.list',
|
||||
'as.data.frame as.matrix as.array as.Date as.POSIXct as.POSIXlt as.difftime as.factor as.table',
|
||||
'as.function as.environment as.name as.symbol as.call as.expression as.formula as.pairlist',
|
||||
'as.numeric_version as.package_version as.hexmode as.octmode as.roman as.person as.personList',
|
||||
'as.ts as.dist as.hclust as.dendrogram as.stepfun as.single',
|
||||
// base
|
||||
'print format summary length names dim dimnames levels rev sort rep seq unique duplicated',
|
||||
'anyDuplicated anyNA unlist c t cbind rbind toString all.equal labels diff scale cut split',
|
||||
'merge subset transform with within by aggregate droplevels relevel reorder xtfrm solve',
|
||||
'determinant rowsum row.names julian months weekdays quarters units open close flush seek',
|
||||
'truncate mean median quantile sort_by unsplit stack unstack kronecker aperm',
|
||||
// stats
|
||||
'update predict residuals coef fitted anova AIC BIC logLik nobs simulate terms formula family',
|
||||
'weights vcov deviance confint profile model.frame model.matrix sigma df.residual extractAIC',
|
||||
'add1 drop1 effects alias case.names variable.names dummy.coef proj influence rstandard',
|
||||
'rstudent cooks.distance hatvalues dfbeta dfbetas covratio density lag window time start end',
|
||||
'frequency cycle deltat na.omit na.exclude na.fail na.action naprint naresid napredict',
|
||||
'biplot screeplot qqnorm termplot knots cophenetic loadings smooth qr chol',
|
||||
// graphics and utils
|
||||
'plot lines points text image hist barplot boxplot pairs persp head tail str',
|
||||
'toBibtex toLatex',
|
||||
]
|
||||
.join(' ')
|
||||
.split(' '),
|
||||
);
|
||||
|
||||
/**
|
||||
|
|
|
|||
5
gitnexus/test/fixtures/rename-integration/r/s3-new-method-group-generic/after/helper.R
vendored
Normal file
5
gitnexus/test/fixtures/rename-integration/r/s3-new-method-group-generic/after/helper.R
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
helper <- function(v) v + 1
|
||||
|
||||
use_helper <- function(v) {
|
||||
helper(v)
|
||||
}
|
||||
5
gitnexus/test/fixtures/rename-integration/r/s3-new-method-group-generic/before/helper.R
vendored
Normal file
5
gitnexus/test/fixtures/rename-integration/r/s3-new-method-group-generic/before/helper.R
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
helper <- function(v) v + 1
|
||||
|
||||
use_helper <- function(v) {
|
||||
helper(v)
|
||||
}
|
||||
9
gitnexus/test/fixtures/rename-integration/r/s3-new-method-group-generic/case.json
vendored
Normal file
9
gitnexus/test/fixtures/rename-integration/r/s3-new-method-group-generic/case.json
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"language": "r",
|
||||
"description": "Refuse a new name that registers a method of the Ops group generic (Ops.money), although the repository defines no Ops().",
|
||||
"categories": ["s3", "name-collision", "group-generic", "negative", "unchanged-on-error"],
|
||||
"target": { "name": "helper", "kind": "Function", "file": "helper.R", "startLine": 0 },
|
||||
"newName": "Ops.money",
|
||||
"expect": { "status": "error", "code": "unsupported_occurrence", "planningStatus": "unsupported" },
|
||||
"edges": []
|
||||
}
|
||||
5
gitnexus/test/fixtures/rename-integration/r/s3-new-method-summary-generic/after/helper.R
vendored
Normal file
5
gitnexus/test/fixtures/rename-integration/r/s3-new-method-summary-generic/after/helper.R
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
helper <- function(v) v + 1
|
||||
|
||||
use_helper <- function(v) {
|
||||
helper(v)
|
||||
}
|
||||
5
gitnexus/test/fixtures/rename-integration/r/s3-new-method-summary-generic/before/helper.R
vendored
Normal file
5
gitnexus/test/fixtures/rename-integration/r/s3-new-method-summary-generic/before/helper.R
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
helper <- function(v) v + 1
|
||||
|
||||
use_helper <- function(v) {
|
||||
helper(v)
|
||||
}
|
||||
9
gitnexus/test/fixtures/rename-integration/r/s3-new-method-summary-generic/case.json
vendored
Normal file
9
gitnexus/test/fixtures/rename-integration/r/s3-new-method-summary-generic/case.json
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"language": "r",
|
||||
"description": "Refuse a new name that registers a method of a Summary group member (sum.items), although the repository defines no sum().",
|
||||
"categories": ["s3", "name-collision", "group-generic", "negative", "unchanged-on-error"],
|
||||
"target": { "name": "helper", "kind": "Function", "file": "helper.R", "startLine": 0 },
|
||||
"newName": "sum.items",
|
||||
"expect": { "status": "error", "code": "unsupported_occurrence", "planningStatus": "unsupported" },
|
||||
"edges": []
|
||||
}
|
||||
|
|
@ -69,6 +69,46 @@ describe('R rename: the S3 registration guard', () => {
|
|||
}
|
||||
});
|
||||
|
||||
it('refuses a method of every S3 group generic and of each function a group dispatches', () => {
|
||||
const groups: Record<string, string> = {
|
||||
group: 'Ops Math Summary Complex matrixOps',
|
||||
Math: 'abs sign sqrt floor ceiling trunc round signif exp log expm1 log1p cos sin tan cospi sinpi tanpi acos asin atan cosh sinh tanh acosh asinh atanh lgamma gamma digamma trigamma cumsum cumprod cummax cummin',
|
||||
Summary: 'all any sum prod max min range',
|
||||
Complex: 'Arg Conj Im Mod Re',
|
||||
'is.*': 'is.numeric is.na is.nan is.finite is.infinite is.array is.matrix is.unsorted',
|
||||
'as.*':
|
||||
'as.complex as.raw as.double as.vector as.list as.matrix as.Date as.POSIXct as.factor',
|
||||
common:
|
||||
'print format summary plot toString unique rev sort rep seq length names dim levels as.character as.numeric as.data.frame c t mean median head tail merge split within with subset transform update predict residuals coef fitted anova AIC BIC logLik nobs simulate unlist',
|
||||
};
|
||||
for (const [group, names] of Object.entries(groups)) {
|
||||
for (const generic of names.split(' ')) {
|
||||
expect(verdict(`${generic}.foo`, []), `${group}: ${generic}.foo`).toBe(
|
||||
'unsupported_occurrence',
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
it('refuses a method of a group generic at any dot split', () => {
|
||||
expect(verdict('Ops.money', [])).toBe('unsupported_occurrence');
|
||||
expect(verdict('sum.items.extra', [])).toBe('unsupported_occurrence');
|
||||
expect(verdict('as.complex.foo.bar', [])).toBe('unsupported_occurrence');
|
||||
});
|
||||
|
||||
it('allows a name that only resembles a group generic or a base generic', () => {
|
||||
for (const name of [
|
||||
'sums.foo',
|
||||
'mysum.foo',
|
||||
'Opsy.foo',
|
||||
'math.foo',
|
||||
'is_numeric.foo',
|
||||
'a.sum',
|
||||
]) {
|
||||
expect(verdict(name, []), name).toBe('ok');
|
||||
}
|
||||
});
|
||||
|
||||
it('allows a dotted name when no prefix is a function or a base generic', () => {
|
||||
expect(verdict('my.helper', [])).toBe('ok');
|
||||
expect(verdict('to.json.foo', [])).toBe('ok');
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue