Fix a couple of bugs, add some new cool stuff.
1. Fix a todo in Parser::ParseTag, to recover better. On code like
that in test/Sema/decl-invalid.c it causes us to return a single
error instead of multiple.
2. Fix an error in Sema::ParseDeclarator, where it would crash if the
declarator didn't have an identifier. Instead, diagnose the problem.
3. Start adding infrastructure to track the range of locations covered
by a declspec or declarator. This is mostly implemented for declspec,
but could be improved, it is missing for declarator.
Thanks to Neil for pointing out this crash.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@40482 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/Sema/SemaStmt.cpp b/Sema/SemaStmt.cpp
new file mode 100644
index 0000000..f9e2c0a
--- /dev/null
+++ b/Sema/SemaStmt.cpp
@@ -0,0 +1,364 @@
+//===--- SemaStmt.cpp - Semantic Analysis for Statements ------------------===//
+//
+// The LLVM Compiler Infrastructure
+//
+// This file was developed by Chris Lattner and is distributed under
+// the University of Illinois Open Source License. See LICENSE.TXT for details.
+//
+//===----------------------------------------------------------------------===//
+//
+// This file implements semantic analysis for statements.
+//
+//===----------------------------------------------------------------------===//
+
+#include "Sema.h"
+#include "clang/AST/Stmt.h"
+#include "clang/AST/Expr.h"
+#include "clang/Parse/Scope.h"
+#include "clang/Basic/Diagnostic.h"
+#include "clang/Basic/LangOptions.h"
+#include "clang/Lex/IdentifierTable.h"
+
+using namespace clang;
+
+Sema::StmtResult Sema::ParseExprStmt(ExprTy *expr) {
+ Expr *E = static_cast<Expr*>(expr);
+
+ // Exprs are statements, so there is no need to do a conversion here. However,
+ // diagnose some potentially bad code.
+ if (!E->hasLocalSideEffect())
+ Diag(E->getExprLoc(), diag::warn_unused_expr, E->getSourceRange());
+
+ return E;
+}
+
+
+Sema::StmtResult Sema::ParseNullStmt(SourceLocation SemiLoc) {
+ return new NullStmt(SemiLoc);
+}
+
+Sema::StmtResult Sema::ParseDeclStmt(DeclTy *decl) {
+ if (decl)
+ return new DeclStmt(static_cast<Decl *>(decl));
+ else
+ return true; // error
+}
+
+Action::StmtResult
+Sema::ParseCompoundStmt(SourceLocation L, SourceLocation R,
+ StmtTy **Elts, unsigned NumElts) {
+ return new CompoundStmt((Stmt**)Elts, NumElts);
+}
+
+Action::StmtResult
+Sema::ParseCaseStmt(SourceLocation CaseLoc, ExprTy *lhsval,
+ SourceLocation DotDotDotLoc, ExprTy *rhsval,
+ SourceLocation ColonLoc, StmtTy *subStmt) {
+ Stmt *SubStmt = static_cast<Stmt*>(subStmt);
+ Expr *LHSVal = ((Expr *)lhsval), *RHSVal = ((Expr *)rhsval);
+ assert((LHSVal != 0) && "missing expression in case statement");
+
+ SourceLocation ExpLoc;
+ // C99 6.8.4.2p3: The expression shall be an integer constant.
+ if (!LHSVal->isIntegerConstantExpr(Context, &ExpLoc)) {
+ Diag(ExpLoc, diag::err_case_label_not_integer_constant_expr,
+ LHSVal->getSourceRange());
+ return SubStmt;
+ }
+
+ // GCC extension: The expression shall be an integer constant.
+ if (RHSVal && !RHSVal->isIntegerConstantExpr(Context, &ExpLoc)) {
+ Diag(ExpLoc, diag::err_case_label_not_integer_constant_expr,
+ RHSVal->getSourceRange());
+ return SubStmt;
+ }
+
+ if (SwitchStack.empty()) {
+ Diag(CaseLoc, diag::err_case_not_in_switch);
+ return SubStmt;
+ }
+
+ CaseStmt *CS = new CaseStmt(LHSVal, RHSVal, SubStmt);
+ SwitchStack.back()->addSwitchCase(CS);
+ return CS;
+}
+
+Action::StmtResult
+Sema::ParseDefaultStmt(SourceLocation DefaultLoc, SourceLocation ColonLoc,
+ StmtTy *subStmt, Scope *CurScope) {
+ Stmt *SubStmt = static_cast<Stmt*>(subStmt);
+
+ if (SwitchStack.empty()) {
+ Diag(DefaultLoc, diag::err_default_not_in_switch);
+ return SubStmt;
+ }
+
+ DefaultStmt *DS = new DefaultStmt(DefaultLoc, SubStmt);
+ SwitchStack.back()->addSwitchCase(DS);
+
+ return DS;
+}
+
+Action::StmtResult
+Sema::ParseLabelStmt(SourceLocation IdentLoc, IdentifierInfo *II,
+ SourceLocation ColonLoc, StmtTy *subStmt) {
+ Stmt *SubStmt = static_cast<Stmt*>(subStmt);
+ // Look up the record for this label identifier.
+ LabelStmt *&LabelDecl = LabelMap[II];
+
+ // If not forward referenced or defined already, just create a new LabelStmt.
+ if (LabelDecl == 0)
+ return LabelDecl = new LabelStmt(IdentLoc, II, SubStmt);
+
+ assert(LabelDecl->getID() == II && "Label mismatch!");
+
+ // Otherwise, this label was either forward reference or multiply defined. If
+ // multiply defined, reject it now.
+ if (LabelDecl->getSubStmt()) {
+ Diag(IdentLoc, diag::err_redefinition_of_label, LabelDecl->getName());
+ Diag(LabelDecl->getIdentLoc(), diag::err_previous_definition);
+ return SubStmt;
+ }
+
+ // Otherwise, this label was forward declared, and we just found its real
+ // definition. Fill in the forward definition and return it.
+ LabelDecl->setIdentLoc(IdentLoc);
+ LabelDecl->setSubStmt(SubStmt);
+ return LabelDecl;
+}
+
+Action::StmtResult
+Sema::ParseIfStmt(SourceLocation IfLoc, ExprTy *CondVal,
+ StmtTy *ThenVal, SourceLocation ElseLoc,
+ StmtTy *ElseVal) {
+ Expr *condExpr = (Expr *)CondVal;
+ assert(condExpr && "ParseIfStmt(): missing expression");
+
+ DefaultFunctionArrayConversion(condExpr);
+ QualType condType = condExpr->getType();
+
+ if (!condType->isScalarType()) // C99 6.8.4.1p1
+ return Diag(IfLoc, diag::err_typecheck_statement_requires_scalar,
+ condType.getAsString(), condExpr->getSourceRange());
+
+ return new IfStmt(condExpr, (Stmt*)ThenVal, (Stmt*)ElseVal);
+}
+
+Action::StmtResult
+Sema::StartSwitchStmt(ExprTy *Cond) {
+ SwitchStmt *SS = new SwitchStmt((Expr*)Cond);
+ SwitchStack.push_back(SS);
+ return SS;
+}
+
+Action::StmtResult
+Sema::FinishSwitchStmt(SourceLocation SwitchLoc, StmtTy *Switch, ExprTy *Body) {
+ Stmt *BodyStmt = (Stmt*)Body;
+
+ SwitchStmt *SS = SwitchStack.back();
+ assert(SS == (SwitchStmt*)Switch && "switch stack missing push/pop!");
+
+ SS->setBody(BodyStmt);
+ SwitchStack.pop_back();
+
+ Expr *condExpr = SS->getCond();
+ QualType condType = condExpr->getType();
+
+ if (!condType->isIntegerType()) { // C99 6.8.4.2p1
+ Diag(SwitchLoc, diag::err_typecheck_statement_requires_integer,
+ condType.getAsString(), condExpr->getSourceRange());
+ return false;
+ }
+
+ DefaultStmt *CurDefaultStmt = 0;
+
+ // FIXME: The list of cases is backwards and needs to be reversed.
+ for (SwitchCase *SC = SS->getSwitchCaseList(); SC;
+ SC = SC->getNextSwitchCase()) {
+ if (DefaultStmt *DS = dyn_cast<DefaultStmt>(SC)) {
+ if (CurDefaultStmt) {
+ Diag(DS->getDefaultLoc(),
+ diag::err_multiple_default_labels_defined);
+ Diag(CurDefaultStmt->getDefaultLoc(),
+ diag::err_first_label);
+
+ // FIXME: Remove the default statement from the switch block
+ // so that we'll return a valid AST.
+ } else {
+ CurDefaultStmt = DS;
+ }
+
+ // FIXME: Check that case values are unique here.
+ }
+ }
+
+ return SS;
+}
+
+Action::StmtResult
+Sema::ParseWhileStmt(SourceLocation WhileLoc, ExprTy *Cond, StmtTy *Body) {
+ Expr *condExpr = (Expr *)Cond;
+ assert(condExpr && "ParseWhileStmt(): missing expression");
+
+ DefaultFunctionArrayConversion(condExpr);
+ QualType condType = condExpr->getType();
+
+ if (!condType->isScalarType()) // C99 6.8.5p2
+ return Diag(WhileLoc, diag::err_typecheck_statement_requires_scalar,
+ condType.getAsString(), condExpr->getSourceRange());
+
+ return new WhileStmt(condExpr, (Stmt*)Body);
+}
+
+Action::StmtResult
+Sema::ParseDoStmt(SourceLocation DoLoc, StmtTy *Body,
+ SourceLocation WhileLoc, ExprTy *Cond) {
+ Expr *condExpr = (Expr *)Cond;
+ assert(condExpr && "ParseDoStmt(): missing expression");
+
+ DefaultFunctionArrayConversion(condExpr);
+ QualType condType = condExpr->getType();
+
+ if (!condType->isScalarType()) // C99 6.8.5p2
+ return Diag(DoLoc, diag::err_typecheck_statement_requires_scalar,
+ condType.getAsString(), condExpr->getSourceRange());
+
+ return new DoStmt((Stmt*)Body, condExpr);
+}
+
+Action::StmtResult
+Sema::ParseForStmt(SourceLocation ForLoc, SourceLocation LParenLoc,
+ StmtTy *First, ExprTy *Second, ExprTy *Third,
+ SourceLocation RParenLoc, StmtTy *Body) {
+ if (First) {
+ // C99 6.8.5p3: FIXME. Need to hack Parser::ParseForStatement() and
+ // declaration support to create a DeclStmt node. Once this is done,
+ // we can test for DeclStmt vs. Expr (already a sub-class of Stmt).
+ }
+ if (Second) {
+ Expr *testExpr = (Expr *)Second;
+ DefaultFunctionArrayConversion(testExpr);
+ QualType testType = testExpr->getType();
+
+ if (!testType->isScalarType()) // C99 6.8.5p2
+ return Diag(ForLoc, diag::err_typecheck_statement_requires_scalar,
+ testType.getAsString(), testExpr->getSourceRange());
+ }
+ return new ForStmt((Stmt*)First, (Expr*)Second, (Expr*)Third, (Stmt*)Body);
+}
+
+
+Action::StmtResult
+Sema::ParseGotoStmt(SourceLocation GotoLoc, SourceLocation LabelLoc,
+ IdentifierInfo *LabelII) {
+ // Look up the record for this label identifier.
+ LabelStmt *&LabelDecl = LabelMap[LabelII];
+
+ // If we haven't seen this label yet, create a forward reference.
+ if (LabelDecl == 0)
+ LabelDecl = new LabelStmt(LabelLoc, LabelII, 0);
+
+ return new GotoStmt(LabelDecl);
+}
+
+Action::StmtResult
+Sema::ParseIndirectGotoStmt(SourceLocation GotoLoc,SourceLocation StarLoc,
+ ExprTy *DestExp) {
+ // FIXME: Verify that the operand is convertible to void*.
+
+ return new IndirectGotoStmt((Expr*)DestExp);
+}
+
+Action::StmtResult
+Sema::ParseContinueStmt(SourceLocation ContinueLoc, Scope *CurScope) {
+ Scope *S = CurScope->getContinueParent();
+ if (!S) {
+ // C99 6.8.6.2p1: A break shall appear only in or as a loop body.
+ Diag(ContinueLoc, diag::err_continue_not_in_loop);
+ return true;
+ }
+
+ return new ContinueStmt();
+}
+
+Action::StmtResult
+Sema::ParseBreakStmt(SourceLocation BreakLoc, Scope *CurScope) {
+ Scope *S = CurScope->getBreakParent();
+ if (!S) {
+ // C99 6.8.6.3p1: A break shall appear only in or as a switch/loop body.
+ Diag(BreakLoc, diag::err_break_not_in_loop_or_switch);
+ return true;
+ }
+
+ return new BreakStmt();
+}
+
+
+Action::StmtResult
+Sema::ParseReturnStmt(SourceLocation ReturnLoc, ExprTy *rex) {
+ Expr *RetValExp = static_cast<Expr *>(rex);
+ QualType lhsType = CurFunctionDecl->getResultType();
+
+ if (lhsType->isVoidType()) {
+ if (RetValExp) // C99 6.8.6.4p1 (ext_ since GCC warns)
+ Diag(ReturnLoc, diag::ext_return_has_expr,
+ CurFunctionDecl->getIdentifier()->getName(),
+ RetValExp->getSourceRange());
+ return new ReturnStmt(RetValExp);
+ } else {
+ if (!RetValExp) {
+ const char *funcName = CurFunctionDecl->getIdentifier()->getName();
+ if (getLangOptions().C99) // C99 6.8.6.4p1 (ext_ since GCC warns)
+ Diag(ReturnLoc, diag::ext_return_missing_expr, funcName);
+ else // C90 6.6.6.4p4
+ Diag(ReturnLoc, diag::warn_return_missing_expr, funcName);
+ return new ReturnStmt((Expr*)0);
+ }
+ }
+ // we have a non-void function with an expression, continue checking
+ QualType rhsType = RetValExp->getType();
+
+ // C99 6.8.6.4p3(136): The return statement is not an assignment. The
+ // overlap restriction of subclause 6.5.16.1 does not apply to the case of
+ // function return.
+ AssignmentCheckResult result = CheckSingleAssignmentConstraints(lhsType,
+ RetValExp);
+ bool hadError = false;
+
+ // decode the result (notice that extensions still return a type).
+ switch (result) {
+ case Compatible:
+ break;
+ case Incompatible:
+ Diag(ReturnLoc, diag::err_typecheck_return_incompatible,
+ lhsType.getAsString(), rhsType.getAsString(),
+ RetValExp->getSourceRange());
+ hadError = true;
+ break;
+ case PointerFromInt:
+ // check for null pointer constant (C99 6.3.2.3p3)
+ if (!RetValExp->isNullPointerConstant(Context)) {
+ Diag(ReturnLoc, diag::ext_typecheck_return_pointer_int,
+ lhsType.getAsString(), rhsType.getAsString(),
+ RetValExp->getSourceRange());
+ }
+ break;
+ case IntFromPointer:
+ Diag(ReturnLoc, diag::ext_typecheck_return_pointer_int,
+ lhsType.getAsString(), rhsType.getAsString(),
+ RetValExp->getSourceRange());
+ break;
+ case IncompatiblePointer:
+ Diag(ReturnLoc, diag::ext_typecheck_return_incompatible_pointer,
+ lhsType.getAsString(), rhsType.getAsString(),
+ RetValExp->getSourceRange());
+ break;
+ case CompatiblePointerDiscardsQualifiers:
+ Diag(ReturnLoc, diag::ext_typecheck_return_discards_qualifiers,
+ lhsType.getAsString(), rhsType.getAsString(),
+ RetValExp->getSourceRange());
+ break;
+ }
+ return new ReturnStmt((Expr*)RetValExp);
+}
+