Fix #1368081: First suggestion in #1368081 seems to be working better
[phpeclipse.git] / net.sourceforge.phpeclipse / src / net / sourceforge / phpdt / internal / compiler / parser / Parser.java
index 459987a..c1e2cf7 100644 (file)
@@ -2,7 +2,7 @@
  * Copyright (c) 2002 www.phpeclipse.de All rights reserved. This program and the accompanying material are made available under the
  * terms of the Common Public License v1.0 which accompanies this distribution, and is available at
  * http://www.eclipse.org/legal/cpl-v10.html
- * 
+ *
  * Contributors: www.phpeclipse.de
  **********************************************************************************************************************************/
 package net.sourceforge.phpdt.internal.compiler.parser;
@@ -14,6 +14,33 @@ import java.util.HashSet;
 import net.sourceforge.phpdt.core.compiler.CharOperation;
 import net.sourceforge.phpdt.core.compiler.ITerminalSymbols;
 import net.sourceforge.phpdt.core.compiler.InvalidInputException;
+import net.sourceforge.phpdt.internal.compiler.ast.AND_AND_Expression;
+import net.sourceforge.phpdt.internal.compiler.ast.ASTNode;
+import net.sourceforge.phpdt.internal.compiler.ast.AbstractMethodDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.BinaryExpression;
+import net.sourceforge.phpdt.internal.compiler.ast.Block;
+import net.sourceforge.phpdt.internal.compiler.ast.BreakStatement;
+import net.sourceforge.phpdt.internal.compiler.ast.CompilationUnitDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.ConditionalExpression;
+import net.sourceforge.phpdt.internal.compiler.ast.ContinueStatement;
+import net.sourceforge.phpdt.internal.compiler.ast.EqualExpression;
+import net.sourceforge.phpdt.internal.compiler.ast.Expression;
+import net.sourceforge.phpdt.internal.compiler.ast.FieldDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.FieldReference;
+import net.sourceforge.phpdt.internal.compiler.ast.IfStatement;
+import net.sourceforge.phpdt.internal.compiler.ast.ImportReference;
+import net.sourceforge.phpdt.internal.compiler.ast.InstanceOfExpression;
+import net.sourceforge.phpdt.internal.compiler.ast.MethodDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.OR_OR_Expression;
+import net.sourceforge.phpdt.internal.compiler.ast.OperatorIds;
+import net.sourceforge.phpdt.internal.compiler.ast.ReturnStatement;
+import net.sourceforge.phpdt.internal.compiler.ast.SingleTypeReference;
+import net.sourceforge.phpdt.internal.compiler.ast.Statement;
+import net.sourceforge.phpdt.internal.compiler.ast.StringLiteral;
+import net.sourceforge.phpdt.internal.compiler.ast.StringLiteralDQ;
+import net.sourceforge.phpdt.internal.compiler.ast.StringLiteralSQ;
+import net.sourceforge.phpdt.internal.compiler.ast.TypeDeclaration;
+import net.sourceforge.phpdt.internal.compiler.ast.TypeReference;
 import net.sourceforge.phpdt.internal.compiler.impl.CompilerOptions;
 import net.sourceforge.phpdt.internal.compiler.impl.ReferenceContext;
 import net.sourceforge.phpdt.internal.compiler.lookup.CompilerModifiers;
@@ -23,33 +50,6 @@ import net.sourceforge.phpdt.internal.compiler.problem.ProblemSeverities;
 import net.sourceforge.phpdt.internal.compiler.util.Util;
 import net.sourceforge.phpdt.internal.ui.util.PHPFileUtil;
 import net.sourceforge.phpeclipse.builder.IdentifierIndexManager;
-import net.sourceforge.phpeclipse.internal.compiler.ast.AND_AND_Expression;
-import net.sourceforge.phpeclipse.internal.compiler.ast.ASTNode;
-import net.sourceforge.phpeclipse.internal.compiler.ast.AbstractMethodDeclaration;
-import net.sourceforge.phpeclipse.internal.compiler.ast.BinaryExpression;
-import net.sourceforge.phpeclipse.internal.compiler.ast.Block;
-import net.sourceforge.phpeclipse.internal.compiler.ast.BreakStatement;
-import net.sourceforge.phpeclipse.internal.compiler.ast.CompilationUnitDeclaration;
-import net.sourceforge.phpeclipse.internal.compiler.ast.ConditionalExpression;
-import net.sourceforge.phpeclipse.internal.compiler.ast.ContinueStatement;
-import net.sourceforge.phpeclipse.internal.compiler.ast.EqualExpression;
-import net.sourceforge.phpeclipse.internal.compiler.ast.Expression;
-import net.sourceforge.phpeclipse.internal.compiler.ast.FieldDeclaration;
-import net.sourceforge.phpeclipse.internal.compiler.ast.FieldReference;
-import net.sourceforge.phpeclipse.internal.compiler.ast.IfStatement;
-import net.sourceforge.phpeclipse.internal.compiler.ast.ImportReference;
-import net.sourceforge.phpeclipse.internal.compiler.ast.InstanceOfExpression;
-import net.sourceforge.phpeclipse.internal.compiler.ast.MethodDeclaration;
-import net.sourceforge.phpeclipse.internal.compiler.ast.OR_OR_Expression;
-import net.sourceforge.phpeclipse.internal.compiler.ast.OperatorIds;
-import net.sourceforge.phpeclipse.internal.compiler.ast.ReturnStatement;
-import net.sourceforge.phpeclipse.internal.compiler.ast.SingleTypeReference;
-import net.sourceforge.phpeclipse.internal.compiler.ast.Statement;
-import net.sourceforge.phpeclipse.internal.compiler.ast.StringLiteral;
-import net.sourceforge.phpeclipse.internal.compiler.ast.StringLiteralDQ;
-import net.sourceforge.phpeclipse.internal.compiler.ast.StringLiteralSQ;
-import net.sourceforge.phpeclipse.internal.compiler.ast.TypeDeclaration;
-import net.sourceforge.phpeclipse.internal.compiler.ast.TypeReference;
 import net.sourceforge.phpeclipse.ui.overlaypages.ProjectPrefUtil;
 
 import org.eclipse.core.resources.IFile;
@@ -99,7 +99,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
 
        /**
         * ClassDeclaration Constructor.
-        * 
+        *
         * @param s
         * @param sess
         *          Description of Parameter
@@ -142,7 +142,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
        /**
         * This method will throw the SyntaxError. It will add the good lines and
         * columns to the Error
-        * 
+        *
         * @param error
         *          the error message
         * @throws SyntaxError
@@ -163,7 +163,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
        /**
         * This method will throw the SyntaxError. It will add the good lines and
         * columns to the Error
-        * 
+        *
         * @param error
         *          the error message
         * @throws SyntaxError
@@ -422,6 +422,9 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
                        try {
                                statement = statement();
                                blockStatements.add(statement);
+                               if (token == TokenNameEOF) {
+                                       return null;
+                               }
                                if (branchStatement && statement != null) {
                                        // reportSyntaxError("Unreachable code", statement.sourceStart,
                                        // statement.sourceEnd);
@@ -749,32 +752,8 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
                        }
                        return statement;
                } else if (token == TokenNameINLINE_HTML) {
-                       if (scanner.phpExpressionTag) {
-                               // start of <?= ... ?> block
-                               getNextToken();
-                               expr();
-                               if (token == TokenNameSEMICOLON) {
-                                       getNextToken();
-                               }
-                               if (token != TokenNameINLINE_HTML) {
-                                       throwSyntaxError("Missing '?>' for open PHP expression block ('<?=').");
-                               }
-                       } else {
-                               getNextToken();
-                       }
+                       getNextToken();
                        return statement;
-                       // } else if (token == TokenNameprint) {
-                       // getNextToken();
-                       // expression();
-                       // if (token == TokenNameSEMICOLON) {
-                       // getNextToken();
-                       // } else {
-                       // if (token != TokenNameStopPHP) {
-                       // throwSyntaxError("';' expected after 'print' statement.");
-                       // }
-                       // getNextToken();
-                       // }
-                       // return;
                } else if (token == TokenNameglobal) {
                        getNextToken();
                        global_var_list();
@@ -2504,21 +2483,31 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
                        case TokenNameIdentifier:
                        case TokenNameVariable:
                        case TokenNameDOLLAR:
+                               Expression lhs = null;
                                boolean rememberedVar = false;
-                               Expression lhs = variable(true, true);
-                               if (lhs != null && lhs instanceof FieldReference && token != TokenNameEQUAL && token != TokenNamePLUS_EQUAL
-                                               && token != TokenNameMINUS_EQUAL && token != TokenNameMULTIPLY_EQUAL && token != TokenNameDIVIDE_EQUAL
-                                               && token != TokenNameDOT_EQUAL && token != TokenNameREMAINDER_EQUAL && token != TokenNameAND_EQUAL
-                                               && token != TokenNameOR_EQUAL && token != TokenNameXOR_EQUAL && token != TokenNameRIGHT_SHIFT_EQUAL
-                                               && token != TokenNameLEFT_SHIFT_EQUAL) {
-                                       FieldReference ref = (FieldReference) lhs;
-                                       if (!containsVariableSet(ref.token)) {
-                                               problemReporter.uninitializedLocalVariable(new String(ref.token), ref.sourceStart(), ref.sourceEnd(), referenceContext,
-                                                               compilationUnit.compilationResult);
-                                               addVariableSet(ref.token);
+                               if (token == TokenNameIdentifier) {
+                                       lhs = identifier(true, true);
+                                       if (lhs != null) {
+                                               expression = lhs;
+                                       }
+                               } else {
+                                       lhs = variable(true, true);
+                                       if (lhs != null) {
+                                               expression = lhs;
+                                       }
+                                       if (lhs != null && lhs instanceof FieldReference && token != TokenNameEQUAL && token != TokenNamePLUS_EQUAL
+                                                       && token != TokenNameMINUS_EQUAL && token != TokenNameMULTIPLY_EQUAL && token != TokenNameDIVIDE_EQUAL
+                                                       && token != TokenNameDOT_EQUAL && token != TokenNameREMAINDER_EQUAL && token != TokenNameAND_EQUAL
+                                                       && token != TokenNameOR_EQUAL && token != TokenNameXOR_EQUAL && token != TokenNameRIGHT_SHIFT_EQUAL
+                                                       && token != TokenNameLEFT_SHIFT_EQUAL) {
+                                               FieldReference ref = (FieldReference) lhs;
+                                               if (!containsVariableSet(ref.token)) {
+                                                       problemReporter.uninitializedLocalVariable(new String(ref.token), ref.sourceStart(), ref.sourceEnd(),
+                                                                       referenceContext, compilationUnit.compilationResult);
+                                                       addVariableSet(ref.token);
+                                               }
                                        }
                                }
-
                                switch (token) {
                                case TokenNameEQUAL:
                                        if (lhs != null && lhs instanceof FieldReference) {
@@ -2774,7 +2763,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
                if (Scanner.TRACE) {
                        System.out.println("TRACE: dynamic_class_name_reference()");
                }
-               base_variable();
+               base_variable(true);
                if (token == TokenNameMINUS_GREATER) {
                        getNextToken();
                        object_property();
@@ -3000,26 +2989,24 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
                                        }
                                }
                        }
-                       // TODO is this ok ?
-                       return ref;
-                       // throwSyntaxError("'(' expected in function call.");
-               }
-               getNextToken();
-               if (token == TokenNameRPAREN) {
+               } else {
                        getNextToken();
-                       return ref;
-               }
-               non_empty_function_call_parameter_list();
-               if (token != TokenNameRPAREN) {
-                       String functionName;
-                       if (ident == null) {
-                               functionName = new String(" ");
-                       } else {
-                               functionName = new String(ident);
+                       if (token == TokenNameRPAREN) {
+                               getNextToken();
+                               return ref;
+                       }
+                       non_empty_function_call_parameter_list();
+                       if (token != TokenNameRPAREN) {
+                               String functionName;
+                               if (ident == null) {
+                                       functionName = new String(" ");
+                               } else {
+                                       functionName = new String(ident);
+                               }
+                               throwSyntaxError("')' expected in function call (" + functionName + ").");
                        }
-                       throwSyntaxError("')' expected in function call (" + functionName + ").");
+                       getNextToken();
                }
-               getNextToken();
                return ref;
        }
 
@@ -3092,7 +3079,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
                return function_call(lefthandside, ignoreVar);
        }
 
-       private Expression base_variable() {
+       private Expression base_variable(boolean lefthandside) {
                // base_variable:
                // reference_variable
                // | simple_indirect_reference reference_variable
@@ -3107,7 +3094,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
                        while (token == TokenNameDOLLAR) {
                                getNextToken();
                        }
-                       reference_variable(false, false);
+                       reference_variable(lefthandside, false);
                }
                return ref;
        }
@@ -3137,11 +3124,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
                                }
                                getNextToken();
                        } else if (token == TokenNameLBRACKET) {
-                               if (ref != null && ref instanceof FieldReference) {
-                                       FieldReference fref = (FieldReference) ref;
-                                       addVariableSet(fref.token);
-                               }
-                               ref = null;
+                               // To remove "ref = null;" here, is probably better than the patch commented in #1368081 - axelcl
                                getNextToken();
                                if (token != TokenNameRBRACKET) {
                                        expr();
@@ -3330,6 +3313,120 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
                }
        }
 
+       private Expression identifier(boolean lefthandside, boolean ignoreVar) {
+               // variable:
+               // base_variable_with_function_calls T_OBJECT_OPERATOR
+               // object_property method_or_not variable_properties
+               // | base_variable_with_function_calls
+
+               // Expression ref = function_call(lefthandside, ignoreVar);
+
+               // function_call:
+               // T_STRING '(' function_call_parameter_list ')'
+               // | class_constant '(' function_call_parameter_list ')'
+               // | static_member '(' function_call_parameter_list ')'
+               // | variable_without_objects '(' function_call_parameter_list ')'
+               char[] defineName = null;
+               char[] ident = null;
+               int startPos = 0;
+               int endPos = 0;
+               Expression ref = null;
+               if (Scanner.TRACE) {
+                       System.out.println("TRACE: function_call()");
+               }
+               if (token == TokenNameIdentifier) {
+                       ident = scanner.getCurrentIdentifierSource();
+                       defineName = ident;
+                       startPos = scanner.getCurrentTokenStartPosition();
+                       endPos = scanner.getCurrentTokenEndPosition();
+                       getNextToken();
+
+                       if (token == TokenNameEQUAL || token == TokenNamePLUS_EQUAL || token == TokenNameMINUS_EQUAL
+                                       || token == TokenNameMULTIPLY_EQUAL || token == TokenNameDIVIDE_EQUAL || token == TokenNameDOT_EQUAL
+                                       || token == TokenNameREMAINDER_EQUAL || token == TokenNameAND_EQUAL || token == TokenNameOR_EQUAL
+                                       || token == TokenNameXOR_EQUAL || token == TokenNameRIGHT_SHIFT_EQUAL || token == TokenNameLEFT_SHIFT_EQUAL) {
+                               String error = "Assignment operator '" + scanner.toStringAction(token) + "' not allowed after identifier '"
+                                               + new String(ident) + "' (use 'define(...)' to define constants).";
+                               reportSyntaxError(error);
+                       }
+
+                       switch (token) {
+                       case TokenNamePAAMAYIM_NEKUDOTAYIM:
+                               // static member:
+                               defineName = null;
+                               getNextToken();
+                               if (token == TokenNameIdentifier) {
+                                       // class _constant
+                                       getNextToken();
+                               } else {
+                                       // static member:
+                                       variable_without_objects(true, false);
+                               }
+                               break;
+                       }
+               } else {
+                       ref = variable_without_objects(lefthandside, ignoreVar);
+               }
+               if (token != TokenNameLPAREN) {
+                       if (defineName != null) {
+                               // does this identifier contain only uppercase characters?
+                               if (defineName.length == 3) {
+                                       if (defineName[0] == 'd' && defineName[1] == 'i' && defineName[2] == 'e') {
+                                               defineName = null;
+                                       }
+                               } else if (defineName.length == 4) {
+                                       if (defineName[0] == 't' && defineName[1] == 'r' && defineName[2] == 'u' && defineName[3] == 'e') {
+                                               defineName = null;
+                                       } else if (defineName[0] == 'n' && defineName[1] == 'u' && defineName[2] == 'l' && defineName[3] == 'l') {
+                                               defineName = null;
+                                       }
+                               } else if (defineName.length == 5) {
+                                       if (defineName[0] == 'f' && defineName[1] == 'a' && defineName[2] == 'l' && defineName[3] == 's' && defineName[4] == 'e') {
+                                               defineName = null;
+                                       }
+                               }
+                               if (defineName != null) {
+                                       for (int i = 0; i < defineName.length; i++) {
+                                               if (Character.isLowerCase(defineName[i])) {
+                                                       problemReporter.phpUppercaseIdentifierWarning(startPos, endPos, referenceContext, compilationUnit.compilationResult);
+                                                       break;
+                                               }
+                                       }
+                               }
+                       }
+                       // TODO is this ok ?
+                       // return ref;
+                       // throwSyntaxError("'(' expected in function call.");
+               } else {
+                       getNextToken();
+
+                       if (token == TokenNameRPAREN) {
+                               getNextToken();
+                               ref = null;
+                       } else {
+                               non_empty_function_call_parameter_list();
+                               if (token != TokenNameRPAREN) {
+                                       String functionName;
+                                       if (ident == null) {
+                                               functionName = new String(" ");
+                                       } else {
+                                               functionName = new String(ident);
+                                       }
+                                       throwSyntaxError("')' expected in function call (" + functionName + ").");
+                               }
+                               getNextToken();
+                       }
+               }
+               if (token == TokenNameMINUS_GREATER) {
+                       ref = null;
+                       getNextToken();
+                       object_property();
+                       method_or_not();
+                       variable_properties();
+               }
+               return ref;
+       }
+
        private void method_or_not() {
                // method_or_not:
                // '(' function_call_parameter_list ')'
@@ -3676,7 +3773,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
 
        /**
         * Parse and check the include file name
-        * 
+        *
         * @param includeToken
         */
        private void checkFileName(int includeToken) {
@@ -3975,7 +4072,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
        // /* remember current scanner position */
        // int startPos = scanner.startPosition;
        // int currentPos = scanner.currentPosition;
-       //    
+       //
        // this.checkAndReportBracketAnomalies(problemReporter());
        // /* reset scanner where it was */
        // scanner.startPosition = startPos;
@@ -4561,7 +4658,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
                        "$_SESSION", "$_SERVER" };
 
        /**
-        * 
+        *
         */
        private void pushFunctionVariableSet() {
                HashSet set = new HashSet();
@@ -4600,7 +4697,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
 
        /**
         * add the current identifier source to the <i>set of assigned variables </i>
-        * 
+        *
         * @param set
         */
        private void addVariableSet(HashSet set) {
@@ -4611,7 +4708,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
 
        /**
         * add the current identifier source to the <i>set of assigned variables </i>
-        * 
+        *
         */
        private void addVariableSet() {
                HashSet set = peekVariableSet();
@@ -4622,7 +4719,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
 
        /**
         * add the current identifier source to the <i>set of assigned variables </i>
-        * 
+        *
         */
        private void addVariableSet(char[] token) {
                HashSet set = peekVariableSet();
@@ -4635,7 +4732,7 @@ public class Parser implements ITerminalSymbols, CompilerModifiers, ParserBasicI
         * check if the current identifier source is in the <i>set of assigned
         * variables </i> Returns true, if no set is defined for the current scanner
         * position
-        * 
+        *
         */
        private boolean containsVariableSet() {
                return containsVariableSet(scanner.getCurrentTokenSource());