eu.cqse.check.framework.util.SwitchStatementUtils Maven / Gradle / Ivy
Show all versions of teamscale-check-api Show documentation
/*
* Copyright (c) CQSE GmbH
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package eu.cqse.check.framework.util;
import static eu.cqse.check.framework.scanner.ETokenType.ATTRIBUTE;
import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER;
import static eu.cqse.check.framework.scanner.ETokenType.LPAREN;
import static eu.cqse.check.framework.scanner.ETokenType.RPAREN;
import static eu.cqse.check.framework.shallowparser.SubTypeNames.ANONYMOUS_BLOCK;
import static eu.cqse.check.framework.shallowparser.SubTypeNames.CASE;
import static eu.cqse.check.framework.shallowparser.SubTypeNames.DEFAULT;
import static eu.cqse.check.framework.shallowparser.SubTypeNames.SWITCH;
import static eu.cqse.check.framework.shallowparser.SubTypeNames.SWITCH_EXPRESSION;
import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.META;
import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.STATEMENT;
import java.util.List;
import java.util.Set;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.conqat.lib.commons.collections.CollectionUtils;
import eu.cqse.check.framework.core.CheckException;
import eu.cqse.check.framework.core.ICheckContext;
import eu.cqse.check.framework.core.phase.ECodeViewOption;
import eu.cqse.check.framework.scanner.IToken;
import eu.cqse.check.framework.shallowparser.SubTypeNames;
import eu.cqse.check.framework.shallowparser.TokenStreamUtils;
import eu.cqse.check.framework.shallowparser.framework.EShallowEntityType;
import eu.cqse.check.framework.shallowparser.framework.ShallowEntity;
/**
* Provides utility methods for handling switch, case and label statements
*/
public class SwitchStatementUtils {
private static final Logger LOGGER = LogManager.getLogger();
/**
* A set of C++ attributes which may be used to indicate that a fallthrough in a switch statement is
* intended.
*/
private static final Set FALLTHROUGH_ATTRIBUTES = CollectionUtils.asHashSet("fallthrough",
"clang::fallthrough");
/** Returns whether given statement is a switch or not. */
public static boolean isSwitch(ShallowEntity entity) {
return entity.getType() == STATEMENT
&& (entity.getSubtype().equals(SWITCH) || entity.getSubtype().equals(SWITCH_EXPRESSION));
}
/** Returns whether given statement is a case or not. */
public static boolean isCase(ShallowEntity entity) {
return entity.getType() == META && entity.getSubtype().equals(CASE);
}
/** Returns whether given statement is a default or not. */
public static boolean isDefault(ShallowEntity entity) {
return entity.getType() == META && entity.getSubtype().equals(DEFAULT);
}
/** Returns whether given statement is an anonymous block. */
public static boolean isAnonymousBlock(ShallowEntity entity) {
return entity.getType() == STATEMENT && entity.getSubtype().equals(ANONYMOUS_BLOCK);
}
/** Returns true if statement entity is a META one and its a case or default. */
public static boolean isCaseOrDefault(ShallowEntity entity) {
return entity.getType() == META && (entity.getSubtype().equals(CASE) || entity.getSubtype().equals(DEFAULT));
}
/**
* Returns whether the given {@link ShallowEntity entity} represents a fallthrough attribute
*
* @param context
* the check context
* @param caseEntity
* the entity of the case in which the entity to check is located
* @param entityToCheck
* the entity for which it should be determined if it represents a fallthrough attribute
*/
public static boolean isFallThroughAttribute(ICheckContext context, ShallowEntity caseEntity,
ShallowEntity entityToCheck) {
if (caseEntity == null || entityToCheck == null) {
return false;
}
return isCppFallthroughAttribute(entityToCheck)
|| isGNUFallthroughAttribute(context, caseEntity, entityToCheck);
}
/**
* Returns whether the given entity represent a C++ fallthrough attribute.
*
* @see #FALLTHROUGH_ATTRIBUTES
*/
private static boolean isCppFallthroughAttribute(ShallowEntity entity) {
return entity.getType() == EShallowEntityType.META
&& SubTypeNames.ATTRIBUTE_ANNOTATION.equals(entity.getSubtype())
&& FALLTHROUGH_ATTRIBUTES.contains(entity.getName());
}
/**
* Returns whether the given {@link ShallowEntity entity} represents a GNU fallthrough-attribute.
*
* During pre-processing __attribute__((fallthrough));
is being reduced to an empty
* statement and only a semicolon remains. If the attribute is written without a semicolon this code
* will not work, however it will also produce a compiler warning on their side.
*/
private static boolean isGNUFallthroughAttribute(ICheckContext context, ShallowEntity caseEntity,
ShallowEntity entityToCheck) {
if (!entityToCheck.getSubtype().equals(SubTypeNames.EMPTY_STATEMENT)) {
return false;
}
List unprocessedTokens = null;
try {
unprocessedTokens = context.getTokens(ECodeViewOption.FILTERED_PREPROCESSED);
} catch (CheckException e) {
LOGGER.error("An exception occurred while determining if the entity represent a fallthrough attribute.", e);
}
// __attribute__ tokens are filtered out during the parsing, therefore the
// non-parsed tokens must be more if it is there
if (unprocessedTokens == null || unprocessedTokens.size() <= caseEntity.getAllTokensOfFile().size()) {
return false;
}
List preprocessedCaseTokens = TokenStreamUtils.getTokensBetween(unprocessedTokens,
caseEntity.getStartOffset(), entityToCheck.getEndOffset());
return hasGNUFallthroughAttribute(preprocessedCaseTokens);
}
/** Returns whether the given tokens contain a GNU fallthrough attribute. */
private static boolean hasGNUFallthroughAttribute(List tokens) {
List indices = TokenStreamUtils.firstTokenOfTypeSequences(tokens, 0, ATTRIBUTE, LPAREN, LPAREN,
IDENTIFIER, RPAREN, RPAREN);
if (indices.isEmpty()) {
return false;
}
int index = CollectionUtils.getLast(indices);
return tokens.get(index + 3).getText().equals("fallthrough");
}
}