Skip to content
This repository has been archived by the owner on Mar 25, 2021. It is now read-only.

Added no-tautology-expression rule #4470

Merged
merged 11 commits into from Mar 11, 2019
1 change: 1 addition & 0 deletions src/configs/all.ts
Expand Up @@ -129,6 +129,7 @@ export const rules = {
"no-string-throw": true,
"no-sparse-arrays": true,
"no-submodule-imports": true,
"no-tautology-expression": true,
"no-unbound-method": true,
"no-unnecessary-class": [true, "allow-empty-class"],
"no-unsafe-any": true,
Expand Down
86 changes: 86 additions & 0 deletions src/rules/noTautologyExpressionRule.ts
@@ -0,0 +1,86 @@
/**
* @license
* Copyright 2013 Palantir Technologies, Inc.
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import * as tsutils from "tsutils";
import * as ts from "typescript";

import * as Lint from "../index";

const TAUTOLOGY_DISCOVERED_ERROR_STRING =
"Expression is either a tautology or a contradiction. Binary expression is redundant.";
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
export class Rule extends Lint.Rules.AbstractRule {
public static metadata: Lint.IRuleMetadata = {
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
description: Lint.Utils.dedent`
Enforces that relational/equality binary operators does not take two equal variables/literals as operands.
Expression like 3 === 3, someVar === someVar, "1" > "1" are either a tautology or contradiction, and will produce an error.
`,
optionExamples: [true],
options: null,
optionsDescription: "Not configurable.",
rationale: `Clean redundant code and unnecessary comparison of objects and literals.`,
ruleName: "no-tautology-expression",
type: "functionality",
typescriptOnly: false,
};

public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, walk);
}
}

function walk(context: Lint.WalkContext<void>) {
const cb = (node: ts.Node): void => {
if (
tsutils.isBinaryExpression(node) &&
isRelationalOrOrLogicalOperator(node.operatorToken)
) {
if (isLiteral(node.left) && isLiteral(node.right)) {
if (node.left.text === node.right.text) {
context.addFailureAtNode(node, TAUTOLOGY_DISCOVERED_ERROR_STRING);
}
} else if (
tsutils.isPropertyAccessExpression(node.left) &&
tsutils.isPropertyAccessExpression(node.right)
) {
if (node.left.name.text === node.right.name.text) {
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
context.addFailureAtNode(node, TAUTOLOGY_DISCOVERED_ERROR_STRING);
}
}
}
return ts.forEachChild(node, cb);
};
return ts.forEachChild(context.sourceFile, cb);
}

function isLiteral(node: ts.Node): node is ts.StringLiteral | ts.NumericLiteral {
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
return (node as ts.StringLiteral) !== undefined || (node as ts.NumericLiteral) !== undefined;
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
}

function isRelationalOrOrLogicalOperator(operator: ts.BinaryOperatorToken): boolean {
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
return new Set([
ts.SyntaxKind.LessThanToken,
ts.SyntaxKind.GreaterThanToken,
ts.SyntaxKind.LessThanEqualsToken,
ts.SyntaxKind.GreaterThanEqualsToken,
ts.SyntaxKind.EqualsEqualsToken,
ts.SyntaxKind.EqualsEqualsEqualsToken,
ts.SyntaxKind.ExclamationEqualsToken,
ts.SyntaxKind.ExclamationEqualsEqualsToken,
ts.SyntaxKind.AmpersandAmpersandToken,
ts.SyntaxKind.BarBarToken,
]).has(operator.kind);
}
108 changes: 108 additions & 0 deletions test/rules/no-tautology-expression/test.ts.lint
@@ -0,0 +1,108 @@

JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved
const expr = "someStr" === "someStr";
~~~~~~~~~~~~~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
const expr = 123 === 123;
~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
const someVar = 100;
const expr = someVar === someVar;
~~~~~~~~~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]

const someFunc = () => {
if ("SomeStr" === "SomeStr") {
~~~~~~~~~~~~~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someFunc = () => {
if (100 === 100) {
~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someFunc = () => {
let someVar = 100;
if (someVar === someVar) {
~~~~~~~~~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someFunc = () => {
if (1 !== 1) {
~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someFunc = () => {
if (1 > 1) {
~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someFunc = () => {
const someVar = 123;
if (someVar < someVar) {
~~~~~~~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someFunc = () => {
if ("str" == "str") {
~~~~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someFunc = () => {
if (123 != 123) {
~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someFunc = () => {
if ("str" <= "str") {
~~~~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someFunc = () => {
if ("str" >= "str") {
~~~~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someFunc = () => {
const someVar = true;
if (someVar || someVar) {
~~~~~~~~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someFunc = () => {
const someVar = true;
if (someVar && someVar) {
~~~~~~~~~~~~~~~~~~ [Expression is either a tautology or a contradiction. Binary expression is redundant.]
return true;
}
}

const someVar1 = 3 + 3;
const someVar2 = 3 - 3;
const someVar3 = 3 * 3;
const someVar4 = 3 % 3;
const someVar5 = 3 / 3;
JoshuaKGoldberg marked this conversation as resolved.
Show resolved Hide resolved






5 changes: 5 additions & 0 deletions test/rules/no-tautology-expression/tslint.json
@@ -0,0 +1,5 @@
{
"rules": {
"no-tautology-expression": true
}
}