Skip to content

Improve error messages for property declarations #5559

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 7, 2015

Conversation

MartyIX
Copy link
Contributor

@MartyIX MartyIX commented Nov 7, 2015

Proposed fix for #4045.

I'm not entirely sure if I'm on the right track. It's more complicated than I expected.

@MartyIX MartyIX changed the title Improved error messages for property declarations Improve error messages for property declarations Nov 7, 2015
@mhegazy
Copy link
Contributor

mhegazy commented Nov 7, 2015

i do not think this is the right way to accomplish this change. you probably want to allow parsing const as a modifier for property declarations, and then give an error for it later on (i.e. in checker::checkGrammarModifiers).

@CyrusNajmabadi or @vladima can help you more here.

@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 7, 2015

@mhegazy I tried the approach that Daniel mentioned (maybe I did not understand him though). He mentions there a more complex example than the original issue is about. I'll certainly appreciate any feedback.

@CyrusNajmabadi
Copy link
Contributor

I agree with @mhegazy .

We know we're in parseClassElement. In that case, just pass a flag to parseClassModifiers saying that 'const should be treated as a modifier."

Easy peasy. Then later during grammar checks, disallow 'const' on anything other than an enum.

@MartyIX MartyIX force-pushed the issue-4045 branch 3 times, most recently from 06e3cbe to 9f393fa Compare November 8, 2015 11:39
@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 8, 2015

It's definitely more easy peasy for you. 😄 However, I tried to move it a bit further.

I don't know how to fix the fourslash test. I don't understand what verify.occurrencesAtPositionCount(1); command actually checks.

@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 9, 2015

@mhegazy Can I ask you for feedback?

let flags = 0;
let modifiers: ModifiersArray;
while (true) {
const modifierStart = scanner.getStartPos();
const modifierKind = token;

if (!parseAnyContextualModifier()) {
break;
if (whitelist && whitelist.indexOf(token) >= 0) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

use ts.indexOf instead

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I have already found it (#5559 (comment))

@@ -1124,6 +1124,14 @@ namespace ts {
return token === t && tryParse(nextTokenCanFollowModifier);
}

function nextTokenIsOnSameLineAndCanFollowModifier() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So we have nextTokenIsOnSameLineAndCanFollowModifier and nextTokenCanFollowModifier...

These are supremely easy to confuse. When would i want one, versus the other?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was nextTokenCanFollowModifierWithoutGrammarRules before. It seemed more clear to me. Basically, I just need nextTokenCanFollowModifier without the leading checks. I can think of several solutions here:

  1. Inline nextTokenIsOnSameLineAndCanFollowModifier to parseModifiers (It leads to code duplication).
  2. Rename nextTokenIsOnSameLineAndCanFollowModifier and use it in nextTokenCanFollowModifier as now.
  3. Somehow pass a flag to tryParse. It seems like a big change because tryParse is a generic function that is used everywhere.

The option 2 seems reasonable overall.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

But why do some people needs checks, and others do not? That's the part I find confusing. How would I know from my code?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Well, to be frank, I don't know why the if statements with SyntaxKind in the following code are not a part of checker.ts file:

        function nextTokenCanFollowModifier() {
            if (token === SyntaxKind.ConstKeyword) {
                // 'const' is only a modifier if followed by 'enum'.
                return nextToken() === SyntaxKind.EnumKeyword;
            }
            if (token === SyntaxKind.ExportKeyword) {
                nextToken();
                if (token === SyntaxKind.DefaultKeyword) {
                    return lookAhead(nextTokenIsClassOrFunction);
                }
                return token !== SyntaxKind.AsteriskToken && token !== SyntaxKind.OpenBraceToken && canFollowModifier();
            }
            if (token === SyntaxKind.DefaultKeyword) {
                return nextTokenIsClassOrFunction();
            }
            if (token === SyntaxKind.StaticKeyword) {
                nextToken();
                return canFollowModifier();
            }

            nextToken();
            if (scanner.hasPrecedingLineBreak()) {
                return false;
            }
            return canFollowModifier();
        }

That way, one would always have all modifiers for a property or a class (possibly something else?) and they would be checked in checker.ts if they are correct. Then there would be no need for special handling of const which now seems a bit like a hack. But maybe I'm missing something because I don't know have deep knowledge of the compiler.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think what Cyrus is sort of getting at is that more than anything else, you should be prescriptive and document why this function exists and where it should be used.

@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 13, 2015

@mhegazy @DanielRosenwasser Do you have any suggestions how to make progress on this issue?

@@ -4940,7 +4951,7 @@ namespace ts {

const fullStart = getNodePos();
const decorators = parseDecorators();
const modifiers = parseModifiers();
const modifiers = parseModifiers(true /* permitConstAsModifier */);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the comment will need to be moved to the front

@DanielRosenwasser
Copy link
Member

@MartyIX is there a test with something like

class C {
    const
    x = 10;
}

@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 18, 2015

@DanielRosenwasser I added tests/cases/compiler/ClassDeclarationWithInvalidConstOnPropertyDeclaration2.ts.

@DanielRosenwasser
Copy link
Member

Looks like there is a fourslash test failure for tests/cases/fourslash/getOccurrencesConst04.ts. Can you check it out?

@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 18, 2015

Yes, I know. I mentioned it here #5559 (comment) Some documentation for fourslash tests would be helpful.

@DanielRosenwasser
Copy link
Member

Oh sorry, I missed that. We provide something called "occurrences highlighting" or "highlight spans". When the caret is placed on top of certain identifiers and keywords, we'll highlight relevant matches:

image

image

So with verify.occurrencesAtPositionCount(1); we're ensuring that only a single keyword gets highlighted. If that's the failure point, something is not working. If you have the Sublime Plugin or Visual Studio, you can update the language services (see here how to do it with VS).

@MartyIX MartyIX force-pushed the issue-4045 branch 2 times, most recently from 41ba303 to b6871c9 Compare November 20, 2015 17:28
@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 20, 2015

@DanielRosenwasser Thanks

@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 24, 2015

@mhegazy @DanielRosenwasser Can you have a look at the code?

@@ -1,7 +1,7 @@
/// <reference path='fourslash.ts' />

////export const class C {
//// private static c/*1*/onst foo;
//// private static const f/*1*/oo;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The fix shouldn't be to change the marker position, it should be to ensure that the occurrence count is 0.

@DanielRosenwasser
Copy link
Member

For some reason, we aren't observing the new error message on the following:

class C {
    private const foo;

    constructor(const) {
    }
}

We report an error on the const in parameter position, but nothing on the declaration for foo. Any ideas @CyrusNajmabadi @MartyIX?

@CyrusNajmabadi
Copy link
Contributor

Have you tried debugging through it?

@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 24, 2015

I'll have a look in a few hours (as soon as I finish some other work).

@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 24, 2015

@DanielRosenwasser I added console.logs to parseErrorAtPosition and grammarErrorOnNode and I got the following output for your snippet:

$ node built/local/tsc.js hello.ts 
parseErrorAtPosition { code: 1003,
  category: 1,
  key: 'Identifier_expected_1003',
  message: 'Identifier expected.' }
hello.ts(4,17): error TS1003: Identifier expected.
grammarErrorOnNode { code: 1246,
  category: 1,
  key: 'A_class_member_cannot_have_the_0_keyword_1246',
  message: 'A class member cannot have the \'{0}\' keyword.' }

So parseErrorAtPosition is called and grammarErrorOnNode is called too. However, tsc.ts contains:

// If we didn't have any syntactic errors, then also try getting the global and
// semantic errors.

https://github.com/Microsoft/TypeScript/blob/master/src/compiler/tsc.ts#L568

The conclusion is that semantic errors are not shown when parser errors occur.

@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 27, 2015

@DanielRosenwasser @mhegazy Is this behavior OK then? Or can I make the PR better?

@@ -15324,6 +15324,11 @@ namespace ts {
let flags = 0;
for (const modifier of node.modifiers) {
switch (modifier.kind) {
case SyntaxKind.ConstKeyword:
if (node.parent.kind === SyntaxKind.ClassDeclaration && node.kind !== SyntaxKind.EnumDeclaration) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you switch the condition around to check for node.kind first?

@DanielRosenwasser
Copy link
Member

Other than the last comment I left, looks good to me!

@MartyIX
Copy link
Contributor Author

MartyIX commented Nov 27, 2015

@DanielRosenwasser Ah, sorry. I think you have already mentioned that. I've just fixed it

@MartyIX
Copy link
Contributor Author

MartyIX commented Dec 3, 2015

@mhegazy @DanielRosenwasser Can this be merged then?

@DanielRosenwasser
Copy link
Member

Sorry about that, I must have missed the alert. Can you merge from master into your branch and resolve the conflicts?

@MartyIX
Copy link
Contributor Author

MartyIX commented Dec 4, 2015

@DanielRosenwasser I've updated it.

@MartyIX
Copy link
Contributor Author

MartyIX commented Dec 7, 2015

@mhegazy @DanielRosenwasser Ping :)

(I'm not in a hurry to get this merged, it's just I don't want to deal with potential unnecessary conflicts.)

DanielRosenwasser added a commit that referenced this pull request Dec 7, 2015
Improve error messages for property declarations
@DanielRosenwasser DanielRosenwasser merged commit 2ef436f into microsoft:master Dec 7, 2015
@DanielRosenwasser
Copy link
Member

Sorry about that, it's easy to miss in the flood of notifications, but thanks @MartyIX!

@microsoft microsoft locked and limited conversation to collaborators Jun 19, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants