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

eu.cqse.check.framework.util.CsLanguageFeatureParser 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.BASE;
import static eu.cqse.check.framework.scanner.ETokenType.BOOL;
import static eu.cqse.check.framework.scanner.ETokenType.BYTE;
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.CONST;
import static eu.cqse.check.framework.scanner.ETokenType.DECIMAL;
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.EQ;
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.IN;
import static eu.cqse.check.framework.scanner.ETokenType.INT;
import static eu.cqse.check.framework.scanner.ETokenType.INTERNAL;
import static eu.cqse.check.framework.scanner.ETokenType.LONG;
import static eu.cqse.check.framework.scanner.ETokenType.LPAREN;
import static eu.cqse.check.framework.scanner.ETokenType.NEW;
import static eu.cqse.check.framework.scanner.ETokenType.OBJECT;
import static eu.cqse.check.framework.scanner.ETokenType.OUT;
import static eu.cqse.check.framework.scanner.ETokenType.PARAMS;
import static eu.cqse.check.framework.scanner.ETokenType.PRIVATE;
import static eu.cqse.check.framework.scanner.ETokenType.PROTECTED;
import static eu.cqse.check.framework.scanner.ETokenType.PUBLIC;
import static eu.cqse.check.framework.scanner.ETokenType.QUESTION;
import static eu.cqse.check.framework.scanner.ETokenType.READONLY;
import static eu.cqse.check.framework.scanner.ETokenType.RPAREN;
import static eu.cqse.check.framework.scanner.ETokenType.SBYTE;
import static eu.cqse.check.framework.scanner.ETokenType.SHORT;
import static eu.cqse.check.framework.scanner.ETokenType.STATIC;
import static eu.cqse.check.framework.scanner.ETokenType.STRING;
import static eu.cqse.check.framework.scanner.ETokenType.THIS;
import static eu.cqse.check.framework.scanner.ETokenType.UINT;
import static eu.cqse.check.framework.scanner.ETokenType.ULONG;
import static eu.cqse.check.framework.scanner.ETokenType.USHORT;
import static eu.cqse.check.framework.scanner.ETokenType.VAR;
import static eu.cqse.check.framework.scanner.ETokenType.VOID;
import static eu.cqse.check.framework.scanner.ETokenType.VOLATILE;

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

import org.conqat.lib.commons.assertion.CCSMAssert;
import org.conqat.lib.commons.collections.CollectionUtils;
import org.conqat.lib.commons.collections.UnmodifiableSet;

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.TokenStreamTextUtils;
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.shallowparser.languages.cs.CsShallowParser;
import eu.cqse.check.framework.util.tokens.TokenPattern;
import eu.cqse.check.framework.util.tokens.TokenPatternMatch;
import eu.cqse.check.framework.util.variable.CSVariableUseExtractor;

/**
 * Language feature parser for Cs.
 */
public class CsLanguageFeatureParser extends CLikeLanguageFeatureParserBase {

	/** IN and OUT keyword token types. */
	private static final Set IN_OUT_KEYWORDS = CollectionUtils.asUnmodifiable(EnumSet.of(IN, OUT));

	/**
	 * All token types that can be used to call a constructor from the current or a parent class
	 */
	private static final UnmodifiableSet ACCESS_KEYWORDS = CollectionUtils
			.asUnmodifiable(EnumSet.of(BASE, THIS));

	/** All token types that can be used to specify a type. */
	private static final UnmodifiableSet ADDITIONAL_TYPE_TOKENS = CollectionUtils
			.asUnmodifiable(EnumSet.of(IDENTIFIER, VAR, STRING, OBJECT));

	/** All token types that can be used to specify a primitive type. */
	public static final UnmodifiableSet PRIMITIVE_TYPE_TOKENS = CollectionUtils.asUnmodifiable(EnumSet.of(
			BOOL, CHAR, BYTE, SBYTE, SHORT, INT, LONG, USHORT, UINT, ULONG, FLOAT, DOUBLE, DECIMAL, VOID, QUESTION));

	/** All token types that are modifiers for variables or attributes. */
	private static final UnmodifiableSet VARIABLE_DECLARATION_MODIFIERS = CollectionUtils
			.asUnmodifiable(EnumSet.of(PUBLIC, PRIVATE, INTERNAL, PROTECTED, CONST, READONLY, STATIC, VOLATILE, NEW));

	/** Suffix for CS-EventHandlers. */
	private static final String EVENT_ARGS_SUFFIX = "EventArgs";

	/**
	 * Group index used in the {@link #ACCESS_CONSTRUCTORS_IN_CONSTRUCTOR_PATTERN} to group the access
	 * keywords used in a constructor declaration
	 */
	private static final int ACCESS_KEYWORD_GROUP_INDEX = 0;

	/**
	 * Pattern to match any constructor calls by using {@link #ACCESS_KEYWORDS} in a constructor
	 * declaration. Examples:
	 * 
    *
  • SubClass (int x) : base(x)
  • *
  • BaseClass () : this(0)
  • *
* * @see this * @see base */ private static final TokenPattern ACCESS_CONSTRUCTORS_IN_CONSTRUCTOR_PATTERN = new TokenPattern() .sequence(IDENTIFIER, new TokenPattern().skipNested(LPAREN, RPAREN, false), COLON) .sequence(new TokenPattern().alternative(ACCESS_KEYWORDS)).skipNested(LPAREN, RPAREN, false) .group(ACCESS_KEYWORD_GROUP_INDEX); /** Constructor. */ public CsLanguageFeatureParser() { super(ELanguage.CS, CsShallowParser.VALID_IDENTIFIERS, PRIMITIVE_TYPE_TOKENS, ADDITIONAL_TYPE_TOKENS, DOT, ".", new CSVariableUseExtractor(DOT, CsShallowParser.VALID_IDENTIFIERS)); } /** Names of classes that count as generic exceptions. */ private static final Set GENERIC_EXCEPTION_NAMES = CollectionUtils.asHashSet("Exception", "ApplicationException", "SystemException"); /** Name of the Null Pointer Exception class */ public static final UnmodifiableSet NULL_POINTER_EXCEPTION_NAMES = CollectionUtils .asUnmodifiable(CollectionUtils.asHashSet("NullReferenceException")); /** * Names of exception classes that should not be caught, because it is considered bad practise. */ public static final UnmodifiableSet SHOULD_NOT_BE_CAUGHT_EXCEPTION_NAMES = CollectionUtils .asUnmodifiable(CollectionUtils.asHashSet("NullReferenceException", "StackOverflowException", "OutOfMemoryException", "ThreadAbortException", "ExecutionEngineException", "IndexOutOfRangeException", "AccessViolationException", "DivideByZeroException", "InvalidCastException", "ArithmeticException", "TypeInitializationException", "ArgumentException")); /** * Returns the imported namespace name from a given "using" entity. If the "using" clause is an * aliasing clause, null is returned. */ @Override public String getImportName(ShallowEntity entity) { CCSMAssert.isTrue(isImport(entity), "entity.getType() must be equal to EShallowEntityType.META and " + "entity.getSubtype() must be equal to SubTypeNames.USING"); List tokens = entity.ownStartTokens(); if (TokenStreamUtils.containsAll(tokens, EQ)) { // if this using directive is aliasing a namespace (thus it contains // a "="), we don't extract the namespace return null; } int lastIdentifierIndex = TokenStreamUtils.lastTokenOfType(tokens, CsShallowParser.VALID_IDENTIFIERS); if (lastIdentifierIndex == TokenStreamUtils.NOT_FOUND) { return null; } return TokenStreamTextUtils.concatTokenTexts(tokens.subList(1, lastIdentifierIndex + 1)); } /** Returns whether the given entity is a using directive. */ @Override public boolean isImport(ShallowEntity entity) { return entity.getType().equals(EShallowEntityType.META) && entity.getSubtype().equals(SubTypeNames.USING); } /** Checks whether the given method is a CS-EventHandler. */ public boolean isEventHandler(ShallowEntity method) { CCSMAssert.isTrue(method.getType() == EShallowEntityType.METHOD, "method.getType() must be \"METHOD\""); if (!hasVoidReturnType(method)) { return false; } List> parameterTokens = getSplitParameterTokens(method); if (parameterTokens.size() != 2) { return false; } List firstParameter = parameterTokens.get(0); List secondParameter = parameterTokens.get(1); if (!TokenStreamUtils.hasTypes(firstParameter, ETokenType.OBJECT, ETokenType.IDENTIFIER)) { return false; } String secondType = getModifiersAndTypeFromTokens(secondParameter).getSecond(); return secondType.endsWith(EVENT_ARGS_SUFFIX); } /** Returns whether the given entity is partial. */ public boolean isPartial(ShallowEntity entity) { return TokenStreamUtils.firstTokenOfType(entity.ownStartTokens(), ETokenType.PARTIAL) != TokenStreamUtils.NOT_FOUND; } /** * Returns all parameter tokens of constructor calls which come from a new constructor declaration. * If there's no constructor call, this will return an empty list. In C# this is possible by using * the standard constructor declaration followed by a colon and either *
    *
  • base (...) (to call it from the base-class) or
  • *
  • this (...) (to call it from the own class)
  • *
* * Example: TestClass () : this (null) {} */ public List getConstructorCallParameterTokens(ShallowEntity constructor) { TokenPatternMatch match = ACCESS_CONSTRUCTORS_IN_CONSTRUCTOR_PATTERN .findFirstMatch(constructor.includedTokens()); if (match == null) { return CollectionUtils.emptyList(); } return TokenStreamUtils.tokensBetweenWithNesting(match.groupTokens(ACCESS_KEYWORD_GROUP_INDEX), LPAREN, RPAREN); } @Override public boolean containsVariableLengthArgumentListIndicator(List tokens) { return TokenStreamUtils.containsAny(tokens, PARAMS, THIS, EQ); } /** * Filters IN and OUT tokens from generics. * * {@inheritDoc} */ @Override protected List filterGenericTokens(List genericTokens) { return CollectionUtils.filter(genericTokens, token -> !IN_OUT_KEYWORDS.contains(token.getType())); } /** Returns a list of all class names the given entity inherits from. */ public List getParentNames(ShallowEntity type) { CCSMAssert.isTrue(type.getType() == EShallowEntityType.TYPE, "type.getType() must be \"TYPE\""); List inheritanceTokens = TokenStreamUtils.tokensBetween(type.ownStartTokens(), ETokenType.COLON, ETokenType.LBRACE); if (inheritanceTokens.isEmpty()) { return CollectionUtils.emptyList(); } List> splitInheritanceTokens = TokenStreamUtils.splitWithNesting(inheritanceTokens, ETokenType.COMMA, ETokenType.LT, ETokenType.GT); return TokenStreamTextUtils.concatAllTokenTexts(splitInheritanceTokens); } /** * Returns all variable names from a variable declaration within a for statement. */ public List getVariableNamesFromForLikeTokens(List forLikeTokens, ETokenType endToken) { List variableDeclarationTokens = getVariableTokensFromForLikeTokens(forLikeTokens, endToken); return getVariableDeclarationNamesFromTokens(variableDeclarationTokens); } /** * Returns all variable names from the given variable declaration tokens. Names are only returned if * they are newly introduced, that means, that e. g. the variable "t" from "using(t) {" will not be * returned. This method does not support generic types at the moment. */ public List getVariableDeclarationNamesFromTokens(List variableDeclarationTokens) { List variableNames = new ArrayList<>(); int offset = 0; while (variableDeclarationTokens.size() > 0 && VARIABLE_DECLARATION_MODIFIERS.contains(variableDeclarationTokens.get(offset).getType())) { offset += 1; } if (variableDeclarationTokens.size() >= 2 + offset) { IToken token1 = variableDeclarationTokens.get(offset); IToken token2 = variableDeclarationTokens.get(1 + offset); if (isVariableDeclaration(token1, token2)) { variableNames.add(token2); addAdditionalVariableNameTokens(variableDeclarationTokens, offset, variableNames); } } return variableNames; } /** Adds additional variable name tokens. */ private static void addAdditionalVariableNameTokens(List variableDeclarationTokens, int offset, List variableNames) { int parenthesisNesting = 0; for (int i = 2 + offset; i < variableDeclarationTokens.size(); ++i) { IToken token = variableDeclarationTokens.get(i); ETokenType tokenType = token.getType(); if (tokenType == ETokenType.LPAREN) { parenthesisNesting += 1; } else if (tokenType == ETokenType.RPAREN) { parenthesisNesting -= 1; } else if (tokenType == ETokenType.IDENTIFIER && parenthesisNesting == 0 && variableDeclarationTokens.get(i - 1).getType() == ETokenType.COMMA) { variableNames.add(token); } } } /** Returns whether the two given tokens indicate a variable declaration. */ private boolean isVariableDeclaration(IToken token1, IToken token2) { return (typeTokens.contains(token1.getType()) && validIdentifiers.contains(token2.getType())); } /** * Determines whether the given class name represents a generic exception. */ public boolean isGenericExceptionClass(String className) { return GENERIC_EXCEPTION_NAMES.contains(className); } /** * Extracts the namespace from a list of top level shallow entities. If empty is returned, no * namespace is set for the given entities. */ public static Optional getNamespace(List shallowEntities) { return shallowEntities.stream().filter(entity -> entity.getType() == EShallowEntityType.MODULE && entity.getSubtype().equals(SubTypeNames.NAMESPACE)).map(ShallowEntity::getName).findFirst(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy