Fix #27802 - show a warning for a regex that contains a backreference. Ripgrep interprets this as an octal escape sequence, instead of a backreference.

This commit is contained in:
Rob Lourens 2017-10-29 11:44:55 -07:00
parent d32ae8bb13
commit 50181371b2
3 changed files with 29 additions and 3 deletions

View file

@ -231,6 +231,10 @@ export function regExpLeadsToEndlessLoop(regexp: RegExp): boolean {
return (match && <any>regexp.lastIndex === 0);
}
export function regExpContainsBackreference(regexpValue: string): boolean {
return !!regexpValue.match(/([^\\]|^)(\\\\)*\\\d+/);
}
/**
* The normalize() method returns the Unicode Normalization Form of a given string. The form will be
* the Normalization Form Canonical Composition.

View file

@ -312,6 +312,22 @@ suite('Strings', () => {
assert(regExpWithFlags.multiline);
});
test('regExpContainsBackreference', () => {
assert(strings.regExpContainsBackreference('foo \\5 bar'));
assert(strings.regExpContainsBackreference('\\2'));
assert(strings.regExpContainsBackreference('(\\d)(\\n)(\\1)'));
assert(strings.regExpContainsBackreference('(A).*?\\1'));
assert(strings.regExpContainsBackreference('\\\\\\1'));
assert(strings.regExpContainsBackreference('foo \\\\\\1'));
assert(!strings.regExpContainsBackreference(''));
assert(!strings.regExpContainsBackreference('\\\\1'));
assert(!strings.regExpContainsBackreference('foo \\\\1'));
assert(!strings.regExpContainsBackreference('(A).*?\\\\1'));
assert(!strings.regExpContainsBackreference('foo \\d1 bar'));
assert(!strings.regExpContainsBackreference('123'));
});
test('getLeadingWhitespace', () => {
assert.equal(strings.getLeadingWhitespace(' foo'), ' ');
assert.equal(strings.getLeadingWhitespace(' foo', 2), '');

View file

@ -11,7 +11,7 @@ import { Widget } from 'vs/base/browser/ui/widget';
import { Action } from 'vs/base/common/actions';
import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar';
import { FindInput, IFindInputOptions } from 'vs/base/browser/ui/findinput/findInput';
import { InputBox } from 'vs/base/browser/ui/inputbox/inputBox';
import { InputBox, IMessage } from 'vs/base/browser/ui/inputbox/inputBox';
import { Button } from 'vs/base/browser/ui/button/button';
import { IKeyboardEvent } from 'vs/base/browser/keyboardEvent';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
@ -220,7 +220,7 @@ export class SearchWidget extends Widget {
private renderSearchInput(parent: HTMLElement, options: ISearchWidgetOptions): void {
let inputOptions: IFindInputOptions = {
label: nls.localize('label.Search', 'Search: Type Search Term and press Enter to search or Escape to cancel'),
validation: (value: string) => this.validatSearchInput(value),
validation: (value: string) => this.validateSearchInput(value),
placeholder: nls.localize('search.placeHolder', "Search"),
appendCaseSensitiveLabel: appendKeyBindingLabel('', this.keyBindingService2.lookupKeybinding(Constants.ToggleCaseSensitiveActionId), this.keyBindingService2),
appendWholeWordsLabel: appendKeyBindingLabel('', this.keyBindingService2.lookupKeybinding(Constants.ToggleWholeWordActionId), this.keyBindingService2),
@ -309,7 +309,7 @@ export class SearchWidget extends Widget {
}
}
private validatSearchInput(value: string): any {
private validateSearchInput(value: string): IMessage {
if (value.length === 0) {
return null;
}
@ -325,6 +325,12 @@ export class SearchWidget extends Widget {
if (strings.regExpLeadsToEndlessLoop(regExp)) {
return { content: nls.localize('regexp.validationFailure', "Expression matches everything") };
}
if (strings.regExpContainsBackreference(value)) {
return { content: nls.localize('regexp.backreferenceValidationFailure', "Backreferences are not supported") };
}
return null;
}
private onSearchInputChanged(): void {