blob: 982cc51831816b409c78114592077fd704e1f702 [file] [log] [blame]
Alexis Hetu76a343a2015-06-04 17:21:22 -04001//
2// Copyright (c) 2002-2015 The ANGLE Project Authors. All rights reserved.
3// Use of this source code is governed by a BSD-style license that can be
4// found in the LICENSE file.
5//
6
7#include "ValidateSwitch.h"
8
9#include "ParseHelper.h"
10
11bool ValidateSwitch::validate(TBasicType switchType, TParseContext *context,
12 TIntermAggregate *statementList, const TSourceLoc &loc)
13{
14 ValidateSwitch validate(switchType, context);
15 ASSERT(statementList);
16 statementList->traverse(&validate);
17 return validate.validateInternal(loc);
18}
19
20ValidateSwitch::ValidateSwitch(TBasicType switchType, TParseContext *context)
21 : TIntermTraverser(true, false, true),
22 mSwitchType(switchType),
23 mContext(context),
24 mCaseTypeMismatch(false),
25 mFirstCaseFound(false),
26 mStatementBeforeCase(false),
27 mLastStatementWasCase(false),
28 mControlFlowDepth(0),
29 mCaseInsideControlFlow(false),
30 mDefaultCount(0),
31 mDuplicateCases(false)
32{}
33
34void ValidateSwitch::visitSymbol(TIntermSymbol *)
35{
36 if (!mFirstCaseFound)
37 mStatementBeforeCase = true;
38 mLastStatementWasCase = false;
39}
40
41void ValidateSwitch::visitConstantUnion(TIntermConstantUnion *)
42{
43 // Conditions of case labels are not traversed, so this is some other constant
44 // Could be just a statement like "0;"
45 if (!mFirstCaseFound)
46 mStatementBeforeCase = true;
47 mLastStatementWasCase = false;
48}
49
50bool ValidateSwitch::visitBinary(Visit, TIntermBinary *)
51{
52 if (!mFirstCaseFound)
53 mStatementBeforeCase = true;
54 mLastStatementWasCase = false;
55 return true;
56}
57
58bool ValidateSwitch::visitUnary(Visit, TIntermUnary *)
59{
60 if (!mFirstCaseFound)
61 mStatementBeforeCase = true;
62 mLastStatementWasCase = false;
63 return true;
64}
65
66bool ValidateSwitch::visitSelection(Visit visit, TIntermSelection *)
67{
68 if (visit == PreVisit)
69 ++mControlFlowDepth;
70 if (visit == PostVisit)
71 --mControlFlowDepth;
72 if (!mFirstCaseFound)
73 mStatementBeforeCase = true;
74 mLastStatementWasCase = false;
75 return true;
76}
77
78bool ValidateSwitch::visitSwitch(Visit, TIntermSwitch *)
79{
80 if (!mFirstCaseFound)
81 mStatementBeforeCase = true;
82 mLastStatementWasCase = false;
83 // Don't go into nested switch statements
84 return false;
85}
86
87bool ValidateSwitch::visitCase(Visit, TIntermCase *node)
88{
89 const char *nodeStr = node->hasCondition() ? "case" : "default";
90 if (mControlFlowDepth > 0)
91 {
92 mContext->error(node->getLine(), "label statement nested inside control flow", nodeStr);
93 mCaseInsideControlFlow = true;
94 }
95 mFirstCaseFound = true;
96 mLastStatementWasCase = true;
97 if (!node->hasCondition())
98 {
99 ++mDefaultCount;
100 if (mDefaultCount > 1)
101 {
102 mContext->error(node->getLine(), "duplicate default label", nodeStr);
103 }
104 }
105 else
106 {
107 TIntermConstantUnion *condition = node->getCondition()->getAsConstantUnion();
108 if (condition == nullptr)
109 {
110 // This can happen in error cases.
111 return false;
112 }
113 TBasicType conditionType = condition->getBasicType();
114 if (conditionType != mSwitchType)
115 {
116 mContext->error(condition->getLine(),
117 "case label type does not match switch init-expression type", nodeStr);
118 mCaseTypeMismatch = true;
119 }
120
121 if (conditionType == EbtInt)
122 {
123 int iConst = condition->getIConst(0);
124 if (mCasesSigned.find(iConst) != mCasesSigned.end())
125 {
126 mContext->error(condition->getLine(), "duplicate case label", nodeStr);
127 mDuplicateCases = true;
128 }
129 else
130 {
131 mCasesSigned.insert(iConst);
132 }
133 }
134 else if (conditionType == EbtUInt)
135 {
136 unsigned int uConst = condition->getUConst(0);
137 if (mCasesUnsigned.find(uConst) != mCasesUnsigned.end())
138 {
139 mContext->error(condition->getLine(), "duplicate case label", nodeStr);
140 mDuplicateCases = true;
141 }
142 else
143 {
144 mCasesUnsigned.insert(uConst);
145 }
146 }
147 // Other types are possible only in error cases, where the error has already been generated
148 // when parsing the case statement.
149 }
150 // Don't traverse the condition of the case statement
151 return false;
152}
153
154bool ValidateSwitch::visitAggregate(Visit visit, TIntermAggregate *)
155{
156 if (getParentNode() != nullptr)
157 {
158 // This is not the statementList node, but some other node.
159 if (!mFirstCaseFound)
160 mStatementBeforeCase = true;
161 mLastStatementWasCase = false;
162 }
163 return true;
164}
165
166bool ValidateSwitch::visitLoop(Visit visit, TIntermLoop *)
167{
168 if (visit == PreVisit)
169 ++mControlFlowDepth;
170 if (visit == PostVisit)
171 --mControlFlowDepth;
172 if (!mFirstCaseFound)
173 mStatementBeforeCase = true;
174 mLastStatementWasCase = false;
175 return true;
176}
177
178bool ValidateSwitch::visitBranch(Visit, TIntermBranch *)
179{
180 if (!mFirstCaseFound)
181 mStatementBeforeCase = true;
182 mLastStatementWasCase = false;
183 return true;
184}
185
186bool ValidateSwitch::validateInternal(const TSourceLoc &loc)
187{
188 if (mStatementBeforeCase)
189 {
190 mContext->error(loc,
191 "statement before the first label", "switch");
192 }
193 if (mLastStatementWasCase)
194 {
195 mContext->error(loc,
196 "no statement between the last label and the end of the switch statement", "switch");
197 }
198 return !mStatementBeforeCase && !mLastStatementWasCase && !mCaseInsideControlFlow &&
199 !mCaseTypeMismatch && mDefaultCount <= 1 && !mDuplicateCases;
200}