StmtCat DetectStatementCategory(out int wordAttrCount, DetectionMode mode)
{
// If the statement starts with identifier(s), we have to figure out
// whether, and how many of them, are word attributes. Also, skip over
// `new` because can be a keyword attribute in some cases.
wordAttrCount = 0;
int wordsStartAt = InputPosition;
bool haveNew = LA0 == TT.New; // "new" keyword is the most annoying wrinkle
if ((haveNew || LA0 == TT.Id || LA0 == TT.ContextualKeyword))
{
if ((!haveNew)) {
// Optimized path for common expressions that start with an Id (IdStmts)
// first lets get it working without optimization
/*var la1k = LT(1).Kind;
if la1k == TokenKind.LParen || la1k == TokenKind.Assignment ||
la1k == TokenKind.Separator || la1k->int == TT.EOF->int {
return StmtCat.IdStmt;
} else if (la1k == TokenKind.Operator) {
var la1 = LA(1);
if la1 != TT.QuestionMark && la1 != TT.LT && la1 != TT.Mul {
return StmtCat.IdStmt;
};
};*/ }
// Scan past identifiers and extra AttrKeywords at beginning of statement
bool isAttrKw = haveNew;
do {
Skip();
if ((isAttrKw)) {
wordsStartAt = InputPosition;
} else {
wordAttrCount++;
}
haveNew |= (isAttrKw = (LA0 == TT.New));
} while (((isAttrKw |= LA0 == TT.AttrKeyword || LA0 == TT.New) || LA0 == TT.Id || LA0 == TT.ContextualKeyword)
);
}
// At this point we've skipped over all simple identifiers.
// Now decide: what kind of statement do we appear to have?
int consecutive = InputPosition - wordsStartAt;
if ((LA0 == TT.TypeKeyword)) {
// We can treat this as if it were one additional identifier,
// although it cannot be treated as a word attribute.
InputPosition++;
consecutive++;
} else if ((LA0 == TT.Substitute)) {
// We can treat this as if it were one additional identifier,
// although it cannot be treated as a word attribute.
var la1 = LA(1);
if ((LA(1) == TT.LParen && LA(2) == TT.RParen)) {
InputPosition += 3;
} else {
InputPosition++;
}
consecutive++;
} else if ((LA0 == TT.This)) {
if (LA(1) == TT.LParen && LA(2) == TT.RParen) {
TT la3 = LA(3);
if (la3 == TT.Colon || la3 == TT.LBrace || la3 == TT.Semicolon && _spaceName != S.Fn) {
return StmtCat.ThisConstructor;
} else {
return StmtCat.OtherStmt;
}
} else if ((consecutive != 0)) {
// Appears to be a this[] property (there could be a type
// param, like this<T>[], so we can't check if LA(1) is "[")
InputPosition--;
return StmtCat.MethodOrPropOrVar;
}
} else if ((LT0.Kind == TokenKind.OtherKeyword)) {
if ((EasilyDetectedKeywordStatements.Contains(LA0) || LA0 == TT.Delegate && LA(1) != TT.LParen || (LA0 == TT.Checked || LA0 == TT.Unchecked) && LA(1) == TT.LBrace)
)
{
// `if` and `using` do not support word attributes:
// - `if`, because in the original plan EC# was to support a
// D-style 'if' clause at the end of property definitions,
// making "T P if ..." ambiguous if word attributes were allowed.
// - `using` because `x using T` was planned as a new cast operator.
if (!(consecutive > 0 && (LA0 == TT.If || LA0 == TT.Using))) {
return StmtCat.KeywordStmt;
}
}
} else if ((consecutive == 0)) {
if ((!haveNew)) {
return StmtCat.OtherStmt;
}
}
// At this point we know it's not a "keyword statement" or "this constructor",
// so it's either MethodOrPropOrVar, which allows word attributes, or
// something else that prohibits them (IdStmt or OtherStmt).
if (consecutive >= 2) {
// We know it's MethodOrPropOrVar, but where do the word attributes end?
int likelyStart = wordsStartAt + consecutive - 2; // most likely location
if ((ExpectedAfterTypeAndName[(int) mode].Contains(LA0))) {
InputPosition = likelyStart;
} else {
// We must distinguish among these three cases:
// 1. IEnumerator IEnumerable.GetEnumerator()
// ^likelyStart(correct) ^InputPosition
// 2. alias Map<K,V> = Dictionary <K,V>;
// ^likelyStart(correct) ^InputPosition
// 3. partial Namespace.Class Method()
// ^likelyStart(too low) ^InputPosition
InputPosition = likelyStart + 1;
if ((Scan_ComplexNameDecl() && ExpectedAfterTypeAndName[(int) mode].Contains(LA0))) {
InputPosition = likelyStart;
} else {
InputPosition = likelyStart + 1;
}
}
return StmtCat.MethodOrPropOrVar;
}
// Worst case: need arbitrary lookahead to detect var/property/method
InputPosition = wordsStartAt;
using (new SavePosition(this, 0)) {
TryMatch((int) TT.This); // skip 'this' attribute for extension methods
if ((Scan_DataType(false) && Scan_ComplexNameDecl() && ExpectedAfterTypeAndName[(int) mode].Contains(LA0))) {
return StmtCat.MethodOrPropOrVar;
}
}
if ((haveNew)) {
if ((LA(-1) == TT.New)) {
InputPosition--;
} else {
// count 'new' as a word attribute, to trigger an error if it shouldn't be there
wordAttrCount++;
}
}
return consecutive != 0 ? StmtCat.IdStmt : StmtCat.OtherStmt;
}