All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.cqse.check.framework.util.ObjectiveCLanguageFeatureParser Maven / Gradle / Ivy

Go to download

The Teamscale Custom Check API allows users to extend Teamscale by writing custom analyses that create findings.

There is a newer version: 2024.7.2
Show newest version
/*
 * 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.BOOL;
import static eu.cqse.check.framework.scanner.ETokenType.CHAR;
import static eu.cqse.check.framework.scanner.ETokenType.COLON;
import static eu.cqse.check.framework.scanner.ETokenType.DOT;
import static eu.cqse.check.framework.scanner.ETokenType.DOUBLE;
import static eu.cqse.check.framework.scanner.ETokenType.FLOAT;
import static eu.cqse.check.framework.scanner.ETokenType.IDENTIFIER;
import static eu.cqse.check.framework.scanner.ETokenType.INT;
import static eu.cqse.check.framework.scanner.ETokenType.LONG;
import static eu.cqse.check.framework.scanner.ETokenType.SHORT;
import static eu.cqse.check.framework.scanner.ETokenType.SIGNED;
import static eu.cqse.check.framework.scanner.ETokenType.UNSIGNED;
import static eu.cqse.check.framework.scanner.ETokenType.VOID;
import static eu.cqse.check.framework.shallowparser.SubTypeNames.SIMPLE_STATEMENT;
import static eu.cqse.check.framework.shallowparser.TokenStreamUtils.NOT_FOUND;
import static eu.cqse.check.framework.shallowparser.framework.EShallowEntityType.STATEMENT;

import java.util.EnumSet;
import java.util.List;
import java.util.Optional;
import java.util.Set;

import org.conqat.lib.commons.collections.CollectionUtils;
import org.conqat.lib.commons.collections.Pair;

import eu.cqse.check.framework.scanner.ELanguage;
import eu.cqse.check.framework.scanner.ETokenType;
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;
import eu.cqse.check.framework.util.variable.CLikeVariableUseExtractor;

/** Language feature parser for Objective-C. */
public class ObjectiveCLanguageFeatureParser extends ObjectiveCLanguageFeatureParserBase {

	/** All token types that can be used to specify a type. */
	private static final EnumSet PRIMITIVE_TYPE_TOKENS = EnumSet.of(SIGNED, UNSIGNED, BOOL, CHAR, SHORT,
			INT, LONG, FLOAT, DOUBLE, VOID);

	/** Names of classes that count as generic exceptions. */
	private static final Set GENERIC_EXCEPTION_NAMES = CollectionUtils.asHashSet("NSException", "id");

	/** The names of unconditional exit statements for a switch. */
	private static final Set UNCONDITIONAL_SWITCH_EXIT_STATEMENT_NAMES = CollectionUtils.asHashSet("break",
			"return", "goto", "continue", "@throw", "abort", "exit", "_Exit");

	/**
	 * The names of unconditional exit statements for a nested switch. These statements will exit the
	 * "outer" switch, even if executed in a "nested" switch. In the following example, although case 1
	 * does not contain an exit statement itself, still it terminates unconditionally since all control
	 * flows of the nested switch leads to a termination:
	 * 
	 * 
	 * switch (x) {
	 * case 1:
	 * 	// do something
	 * 	switch (y) {
	 * 	case 2:
	 * 		// do something
	 * 		return -1;
	 * 	case 3:
	 * 		// something
	 * 		exit(0);
	 * 	default:
	 * 		return 0;
	 * 	}
	 * default:
	 * 	break;
	 * }
	 * 
* */ private static final Set UNCONDITIONAL_NESTED_SWITCH_EXIT_STATEMENT_NAMES = CollectionUtils .asHashSet("return", "@throw", "abort", "exit", "_Exit"); ObjectiveCLanguageFeatureParser() { super(ELanguage.OBJECTIVE_C, PRIMITIVE_TYPE_TOKENS, new CLikeVariableUseExtractor(EnumSet.of(DOT), VALID_IDENTIFIERS)); } @Override public boolean isImport(ShallowEntity entity) { return entity.getType() == EShallowEntityType.META && entity.getSubtype().equals(SubTypeNames.AT_IMPORT); } /** * Determines whether the given class name represents a generic exception. */ public static boolean isGenericExceptionClass(String className) { return GENERIC_EXCEPTION_NAMES.contains(className); } /** * Returns a list of parameter tokens for the given tokensAfterMethodName. Example for * -(void)foo1:(int) x1:(int) x2 . Returns [int, x1, :, int, x2]. */ @Override public List extractParameterTokens(List methodStartTokens) { return extractObjectiveCParameterTokens(methodStartTokens); } /** * Returns whether the given entity is an unconditional exit statement for a case statement. This * checks {@link #isCaseOrDefaultExitStatement(ShallowEntity, boolean)}, but also recurses in case * of blocks and conditionals. Even though there are several commonalities between Objective-C and * C/C++, there is still a few language differences for deciding on whether switch cases terminate * unconditionally, such as the use of instructions like "@throw" rather than "throw". Also, if * {@code isNestedSwitch} is true, it considers only "exit statements" that will exit the entire * switch hierarchy (e.g., "return", "@throw", "exit()"). */ public static boolean isUnconditionalSwitchExit(ShallowEntity lastEntity, boolean isNestedSwitch) { if (lastEntity.getSubtype().equals(SubTypeNames.ELSE)) { return isUnconditionalSwitchExitGivenElseAsLastEntity(lastEntity, isNestedSwitch); } return isCaseOrDefaultExitStatement(lastEntity, isNestedSwitch); } /** * Given a) that the last entity parameter is an else statement and b) whether we are in a nested * switch block or not, this method determines whether (or not) the last entity in a) * unconditionally exits the switch block. */ private static boolean isUnconditionalSwitchExitGivenElseAsLastEntity(ShallowEntity lastEntity, boolean isNestedSwitch) { // check the "else" if (!isUnconditionalSwitchExit(lastEntity.getChildren(), isNestedSwitch)) { return false; } List siblings = lastEntity.getParent().getChildren(); int index = siblings.indexOf(lastEntity); index -= 1; // check any else-if while (index > 0 && SubTypeNames.ELSE_IF.equals(siblings.get(index).getSubtype())) { if (!isUnconditionalSwitchExit(siblings.get(index).getChildren(), isNestedSwitch)) { return false; } index -= 1; } // check the "if" return isUnconditionalSwitchExit(siblings.get(index).getChildren(), isNestedSwitch); } private static boolean isUnconditionalSwitchExit(List entities, boolean isNestedSwitch) { return !entities.isEmpty() && isUnconditionalSwitchExit(CollectionUtils.getLast(entities), isNestedSwitch); } /** * Returns whether the given entity is a valid exit statement for a case statement */ public static boolean isCaseOrDefaultExitStatement(ShallowEntity entity, boolean isNestedSwitch) { return (entity.getType() == STATEMENT && entity.getSubtype().equals(SIMPLE_STATEMENT) && getUnconditionalSwitchExitStatementNames(isNestedSwitch).contains(entity.getName())); } /** * Returns the collection of exit statement names to check for whether in a nested switch block or * not. In cases of nested switch statements, we consider only exit statements that guarantee that * all the nested control-flow paths are terminated. In other words, in nested switches, we check if * the outermost switch breaks out by having its nested switches terminating with certain keywords * such as 'exit', 'throw, and 'return'. */ private static Set getUnconditionalSwitchExitStatementNames(boolean isNestedSwitch) { if (isNestedSwitch) { return UNCONDITIONAL_NESTED_SWITCH_EXIT_STATEMENT_NAMES; } return UNCONDITIONAL_SWITCH_EXIT_STATEMENT_NAMES; } /** Returns the parent class name of the entity. */ public static Optional getSuperClassName(ShallowEntity entity) { int inheritanceSequenceIndex = TokenStreamUtils.firstTokenOfTypeSequence(entity.ownStartTokens(), 0, COLON, IDENTIFIER); if (inheritanceSequenceIndex == NOT_FOUND || entity.ownStartTokens().size() == inheritanceSequenceIndex + 1) { return Optional.empty(); } return Optional.of(entity.ownStartTokens().get(inheritanceSequenceIndex + 1).getText()); } /** {@inheritDoc} */ @Override public Pair, String> getReturnTypeAndModifiers(ShallowEntity method) { return getObjectiveCReturnTypeAndModifiers(method); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy