Swap the verbosity mechanism for ConformancePatternRule from an argument to a global switch.
PiperOrigin-RevId: 249425522
diff --git a/internal/tsetse/rules/conformance_pattern_rule.ts b/internal/tsetse/rules/conformance_pattern_rule.ts
index ec3c8bf..c78f90c 100644
--- a/internal/tsetse/rules/conformance_pattern_rule.ts
+++ b/internal/tsetse/rules/conformance_pattern_rule.ts
@@ -20,16 +20,14 @@
private readonly engine: PatternEngine<P>;
- constructor(
- config: Config<P>, fixer?: Fixer<MatchedNodeTypes[P]>,
- verbose?: boolean) {
+ constructor(config: Config<P>, fixer?: Fixer<MatchedNodeTypes[P]>) {
// TODO(rjamet): This cheats a bit with the typing, as TS doesn't realize
// that P is Config.kind.
// tslint:disable-next-line:no-any See above.
let engine: PatternEngine<any>;
switch (config.kind) {
case PatternKind.BANNED_PROPERTY_WRITE:
- engine = new PropertyWriteEngine(config, fixer, verbose);
+ engine = new PropertyWriteEngine(config, fixer);
break;
default:
throw new Error('Config type not recognized, or not implemented yet.');
diff --git a/internal/tsetse/tests/ban_conformance_pattern/util_test.ts b/internal/tsetse/tests/ban_conformance_pattern/util_test.ts
new file mode 100644
index 0000000..0588b47
--- /dev/null
+++ b/internal/tsetse/tests/ban_conformance_pattern/util_test.ts
@@ -0,0 +1,37 @@
+/**
+ * @fileoverview Tests for the various utilities that are not tied to a single
+ * rule.
+ */
+
+import 'jasmine';
+import {ConformancePatternRule, PatternKind} from '../../rules/conformance_pattern_rule';
+import {setDebug} from '../../util/ast_tools';
+import {compileAndCheck} from '../../util/testing/test_support';
+
+describe('Debug output', () => {
+ it('turns on and off', () => {
+ const source = `const obj = {prop:'val'}; obj.prop = 'foo';`;
+ const rule = new ConformancePatternRule({
+ errorMessage: 'does not matter',
+ kind: PatternKind.BANNED_PROPERTY_WRITE,
+ values: ['HTMLQuoteElement.prototype.cite']
+ });
+
+ const logs: string[] = [];
+ const realConsoleLog = console.log;
+ spyOn(console, 'log').and.callFake((s: string) => {
+ logs.push(s);
+ realConsoleLog(s);
+ });
+ setDebug(true);
+ compileAndCheck(rule, source);
+
+ expect(logs).toEqual([`inspecting obj.prop = 'foo'`]);
+
+ setDebug(false);
+ compileAndCheck(rule, source);
+
+ // Nothing more appended: debug was false
+ expect(logs).toEqual([`inspecting obj.prop = 'foo'`]);
+ });
+});
diff --git a/internal/tsetse/util/ast_tools.ts b/internal/tsetse/util/ast_tools.ts
index 44d26ee..cb0f0cb 100644
--- a/internal/tsetse/util/ast_tools.ts
+++ b/internal/tsetse/util/ast_tools.ts
@@ -6,6 +6,25 @@
import * as ts from 'typescript';
/**
+ * Triggers increased verbosity in the rules.
+ */
+let DEBUG = false;
+
+/**
+ * Turns on or off logging for ConformancePatternRules.
+ */
+export function setDebug(state: boolean) {
+ DEBUG = state;
+}
+
+/**
+ * Debug helper.
+ */
+export function debugLog(msg: string) {
+ if (DEBUG) console.log(msg);
+}
+
+/**
* Returns `n`'s parents in order.
*/
export function parents(n: ts.Node): ts.Node[] {
@@ -146,13 +165,6 @@
}
/**
- * Debug helper.
- */
-export function debugLog(verbose: boolean|undefined, msg: string) {
- if (verbose) console.info(msg);
-}
-
-/**
* If verbose, logs the given error that happened while walking n, with a
* stacktrace.
*/
@@ -163,7 +175,6 @@
} catch {
}
debugLog(
- verbose,
`Walking node ${nodeText} failed with error ${e}.\n` +
- `Stacktrace:\n${e.stack}`);
+ `Stacktrace:\n${e.stack}`);
}
diff --git a/internal/tsetse/util/fixer.ts b/internal/tsetse/util/fixer.ts
index 19de12a..f9b784e 100644
--- a/internal/tsetse/util/fixer.ts
+++ b/internal/tsetse/util/fixer.ts
@@ -52,20 +52,19 @@
*/
export function maybeAddNamedImport(
source: ts.SourceFile, importWhat: string, fromFile: string,
- importAs?: string, tazeComment?: string, v?: boolean): IndividualChange|
- undefined {
+ importAs?: string, tazeComment?: string): IndividualChange|undefined {
const importStatements = source.statements.filter(ts.isImportDeclaration);
const importSpecifier =
importAs ? `${importWhat} as ${importAs}` : importWhat;
for (const iDecl of importStatements) {
- const parsedDecl = maybeParseImportNode(iDecl, v);
+ const parsedDecl = maybeParseImportNode(iDecl);
if (!parsedDecl || parsedDecl.fromFile !== fromFile) {
// Not an import from the right file, or couldn't understand the import.
continue; // Jump to the next import.
}
if (ts.isNamespaceImport(parsedDecl.namedBindings)) {
- debugLog(v, `... but it's a wildcard import`);
+ debugLog(`... but it's a wildcard import`);
continue; // Jump to the next import.
}
@@ -78,12 +77,12 @@
iSpec.name.getText() === importWhat); // import {foo}
if (foundRightImport) {
- debugLog(v, `"${iDecl.getFullText()}" imports ${importWhat} as we want.`);
+ debugLog(`"${iDecl.getFullText()}" imports ${importWhat} as we want.`);
return; // Our request is already imported under the right name.
}
// Else, insert our symbol in the list of imports from that file.
- debugLog(v, `No named imports from that file, generating new fix`);
+ debugLog(`No named imports from that file, generating new fix`);
return {
start: parsedDecl.namedBindings.elements[0].getStart(),
end: parsedDecl.namedBindings.elements[0].getStart(),
@@ -117,27 +116,27 @@
*/
export function maybeAddNamespaceImport(
source: ts.SourceFile, fromFile: string, importAs: string,
- tazeComment?: string, v?: boolean): IndividualChange|undefined {
+ tazeComment?: string): IndividualChange|undefined {
const importStatements = source.statements.filter(ts.isImportDeclaration);
const hasTheRightImport = importStatements.some(iDecl => {
- const parsedDecl = maybeParseImportNode(iDecl, v);
+ const parsedDecl = maybeParseImportNode(iDecl);
if (!parsedDecl || parsedDecl.fromFile !== fromFile) {
// Not an import from the right file, or couldn't understand the import.
return false;
}
- debugLog(v, `"${iDecl.getFullText()}" is an import from the right file`);
+ debugLog(`"${iDecl.getFullText()}" is an import from the right file`);
if (ts.isNamedImports(parsedDecl.namedBindings)) {
- debugLog(v, `... but it's a named import`);
+ debugLog(`... but it's a named import`);
return false; // irrelevant to our namespace imports
}
// Else, bindings is a NamespaceImport.
if (parsedDecl.namedBindings.name.getText() !== importAs) {
- debugLog(v, `... but not the right name, we need to reimport`);
+ debugLog(`... but not the right name, we need to reimport`);
return false;
}
- debugLog(v, `... and the right name, no need to reimport`);
+ debugLog(`... and the right name, no need to reimport`);
return true;
});
@@ -162,14 +161,13 @@
* parts, undefined if the import declaration is valid but not understandable by
* the checker.
*/
-function maybeParseImportNode(iDecl: ts.ImportDeclaration, v?: boolean): {
+function maybeParseImportNode(iDecl: ts.ImportDeclaration): {
namedBindings: ts.NamedImportBindings|ts.NamespaceImport,
fromFile: string
}|undefined {
if (!iDecl.importClause) {
// something like import "./file";
- debugLog(
- v, `Ignoring import without imported symbol: ${iDecl.getFullText()}`);
+ debugLog(`Ignoring import without imported symbol: ${iDecl.getFullText()}`);
return;
}
if (iDecl.importClause.name || !iDecl.importClause.namedBindings) {
@@ -177,11 +175,11 @@
// Not much we can do with that when trying to get a hold of some symbols,
// so just ignore that line (worst case, we'll suggest another import
// style).
- debugLog(v, `Ignoring import: ${iDecl.getFullText()}`);
+ debugLog(`Ignoring import: ${iDecl.getFullText()}`);
return;
}
if (!ts.isStringLiteral(iDecl.moduleSpecifier)) {
- debugLog(v, `Ignoring import whose module specifier is not literal`);
+ debugLog(`Ignoring import whose module specifier is not literal`);
return;
}
return {
diff --git a/internal/tsetse/util/match_symbol.ts b/internal/tsetse/util/match_symbol.ts
index 1a05c72..5df53b1 100644
--- a/internal/tsetse/util/match_symbol.ts
+++ b/internal/tsetse/util/match_symbol.ts
@@ -35,23 +35,23 @@
}
}
- matches(n: ts.Node, tc: ts.TypeChecker, verbose?: boolean): boolean {
+ matches(n: ts.Node, tc: ts.TypeChecker): boolean {
// Get the symbol (or the one at the other end of this alias) that we're
// looking at.
const s = dealias(tc.getSymbolAtLocation(n), tc);
if (!s) {
- debugLog(verbose, `cannot get symbol`);
+ debugLog(`cannot get symbol`);
return false;
}
// The TS-provided FQN tells us the full identifier, and the origin file
// in some circumstances.
const fqn = tc.getFullyQualifiedName(s);
- debugLog(verbose, `got FQN ${fqn}`);
+ debugLog(`got FQN ${fqn}`);
// Name-based check
if (!(fqn.endsWith('.' + this.bannedName) || fqn === this.bannedName)) {
- debugLog(verbose, `FQN ${fqn} doesn't match name ${this.bannedName}`);
+ debugLog(`FQN ${fqn} doesn't match name ${this.bannedName}`);
return false; // not a use of the symbols we want
}
@@ -61,7 +61,7 @@
// and bad fixes.
const p = n.parent;
if (p && (isDeclaration(p) || isPartOfImportStatement(p))) {
- debugLog(verbose, `We don't flag symbol declarations`);
+ debugLog(`We don't flag symbol declarations`);
return false;
}
@@ -74,18 +74,17 @@
// We need to trace things back, so get declarations of the symbol.
const declarations = s.getDeclarations();
if (!declarations) {
- debugLog(verbose, `Symbol never declared?`);
+ debugLog(`Symbol never declared?`);
return false;
}
if (!declarations.some(isAmbientDeclaration) &&
!declarations.some(isInStockLibraries)) {
- debugLog(
- verbose, `Symbol neither ambient nor from the stock libraries`);
+ debugLog(`Symbol neither ambient nor from the stock libraries`);
return false;
}
}
- debugLog(verbose, `all clear, report finding`);
+ debugLog(`all clear, report finding`);
return true;
}
}
@@ -123,8 +122,7 @@
/**
* @param n The PropertyAccessExpression we're looking at.
*/
- matches(
- n: ts.PropertyAccessExpression, tc: ts.TypeChecker, verbose?: boolean) {
+ matches(n: ts.PropertyAccessExpression, tc: ts.TypeChecker) {
return n.name.text === this.bannedProperty &&
this.typeMatches(tc.getTypeAtLocation(n.expression));
}
diff --git a/internal/tsetse/util/pattern_engines/pattern_engine.ts b/internal/tsetse/util/pattern_engines/pattern_engine.ts
index 4d315b5..5dbbddc 100644
--- a/internal/tsetse/util/pattern_engines/pattern_engine.ts
+++ b/internal/tsetse/util/pattern_engines/pattern_engine.ts
@@ -8,8 +8,7 @@
export abstract class PatternEngine<P extends PatternKind> {
constructor(
protected readonly config: Config<P>,
- protected readonly fixer?: Fixer<MatchedNodeTypes[P]>,
- protected readonly verbose?: boolean) {}
+ protected readonly fixer?: Fixer<MatchedNodeTypes[P]>) {}
abstract register(checker: Checker): void;
}
diff --git a/internal/tsetse/util/pattern_engines/property_write_engine.ts b/internal/tsetse/util/pattern_engines/property_write_engine.ts
index ea8fe35..7af7ccf 100644
--- a/internal/tsetse/util/pattern_engines/property_write_engine.ts
+++ b/internal/tsetse/util/pattern_engines/property_write_engine.ts
@@ -16,9 +16,8 @@
private readonly matcher: PropertyMatcher;
constructor(
config: Config<PatternKind.BANNED_PROPERTY_WRITE>,
- fixer?: Fixer<MatchedNodeTypes[PatternKind.BANNED_PROPERTY_WRITE]>,
- verbose?: boolean) {
- super(config, fixer, verbose);
+ fixer?: Fixer<MatchedNodeTypes[PatternKind.BANNED_PROPERTY_WRITE]>) {
+ super(config, fixer);
// TODO: Support more than one single value here, or even build a
// multi-pattern engine. This would help for performance.
if (this.config.values.length !== 1) {
@@ -39,8 +38,8 @@
!isPropertyWriteExpression(n)) {
return;
}
- debugLog(this.verbose, `inspecting ${n.getFullText().trim()}`);
- if (this.matcher.matches(n.left, c.typeChecker, this.verbose)) {
+ debugLog(`inspecting ${n.getFullText().trim()}`);
+ if (this.matcher.matches(n.left, c.typeChecker)) {
const fix: Fix|undefined =
this.fixer ? this.fixer.getFixForFlaggedNode(n) : undefined;
c.addFailureAtNode(n, this.config.errorMessage, fix);