/* * $Id$ * * SARL is an general-purpose agent programming language. * More details on http://www.sarl.io * * Copyright (C) 2014-2017 the original authors or authors. * * 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 io.sarl.lang.validation; import static com.google.common.collect.Iterables.any; import static com.google.common.collect.Iterables.toArray; import static com.google.common.collect.Lists.newArrayList; import static io.sarl.lang.sarl.SarlPackage.Literals.SARL_AGENT__EXTENDS; import static io.sarl.lang.sarl.SarlPackage.Literals.SARL_BEHAVIOR__EXTENDS; import static io.sarl.lang.sarl.SarlPackage.Literals.SARL_CAPACITY_USES__CAPACITIES; import static io.sarl.lang.sarl.SarlPackage.Literals.SARL_CAPACITY__EXTENDS; import static io.sarl.lang.sarl.SarlPackage.Literals.SARL_EVENT__EXTENDS; import static io.sarl.lang.sarl.SarlPackage.Literals.SARL_FORMAL_PARAMETER__DEFAULT_VALUE; import static io.sarl.lang.sarl.SarlPackage.Literals.SARL_SKILL__EXTENDS; import static io.sarl.lang.sarl.SarlPackage.Literals.SARL_SKILL__IMPLEMENTS; import static io.sarl.lang.validation.IssueCodes.DISCOURAGED_BOOLEAN_EXPRESSION; import static io.sarl.lang.validation.IssueCodes.DISCOURAGED_CAPACITY_DEFINITION; import static io.sarl.lang.validation.IssueCodes.DISCOURAGED_FUNCTION_NAME; import static io.sarl.lang.validation.IssueCodes.INVALID_CAPACITY_TYPE; import static io.sarl.lang.validation.IssueCodes.INVALID_EXTENDED_TYPE; import static io.sarl.lang.validation.IssueCodes.INVALID_FIRING_EVENT_TYPE; import static io.sarl.lang.validation.IssueCodes.INVALID_IMPLEMENTED_TYPE; import static io.sarl.lang.validation.IssueCodes.INVALID_NESTED_DEFINITION; import static io.sarl.lang.validation.IssueCodes.REDUNDANT_CAPACITY_USE; import static io.sarl.lang.validation.IssueCodes.REDUNDANT_INTERFACE_IMPLEMENTATION; import static io.sarl.lang.validation.IssueCodes.RETURN_TYPE_SPECIFICATION_IS_RECOMMENDED; import static io.sarl.lang.validation.IssueCodes.UNREACHABLE_BEHAVIOR_UNIT; import static io.sarl.lang.validation.IssueCodes.UNUSED_AGENT_CAPACITY; import static org.eclipse.xtend.core.validation.IssueCodes.ABSTRACT_METHOD_WITH_BODY; import static org.eclipse.xtend.core.validation.IssueCodes.CLASS_EXPECTED; import static org.eclipse.xtend.core.validation.IssueCodes.CREATE_FUNCTIONS_MUST_NOT_BE_ABSTRACT; import static org.eclipse.xtend.core.validation.IssueCodes.CYCLIC_INHERITANCE; import static org.eclipse.xtend.core.validation.IssueCodes.DISPATCH_FUNCTIONS_MUST_NOT_BE_ABSTRACT; import static org.eclipse.xtend.core.validation.IssueCodes.INTERFACE_EXPECTED; import static org.eclipse.xtend.core.validation.IssueCodes.INVALID_MEMBER_NAME; import static org.eclipse.xtend.core.validation.IssueCodes.JDK_NOT_ON_CLASSPATH; import static org.eclipse.xtend.core.validation.IssueCodes.MISSING_ABSTRACT; import static org.eclipse.xtend.core.validation.IssueCodes.MISSING_ABSTRACT_IN_ANONYMOUS; import static org.eclipse.xtend.core.validation.IssueCodes.MISSING_OVERRIDE; import static org.eclipse.xtend.core.validation.IssueCodes.MISSING_STATIC_MODIFIER; import static org.eclipse.xtend.core.validation.IssueCodes.MUST_INVOKE_SUPER_CONSTRUCTOR; import static org.eclipse.xtend.core.validation.IssueCodes.OBSOLETE_OVERRIDE; import static org.eclipse.xtend.core.validation.IssueCodes.OVERRIDDEN_FINAL; import static org.eclipse.xtend.core.validation.IssueCodes.OVERRIDE_REDUCES_VISIBILITY; import static org.eclipse.xtend.core.validation.IssueCodes.XBASE_LIB_NOT_ON_CLASSPATH; import static org.eclipse.xtend.core.xtend.XtendPackage.Literals.XTEND_CLASS__IMPLEMENTS; import static org.eclipse.xtend.core.xtend.XtendPackage.Literals.XTEND_FIELD__NAME; import static org.eclipse.xtend.core.xtend.XtendPackage.Literals.XTEND_FUNCTION__NAME; import static org.eclipse.xtend.core.xtend.XtendPackage.Literals.XTEND_INTERFACE__EXTENDS; import static org.eclipse.xtend.core.xtend.XtendPackage.Literals.XTEND_TYPE_DECLARATION__NAME; import static org.eclipse.xtext.util.JavaVersion.JAVA8; import static org.eclipse.xtext.xbase.validation.IssueCodes.DISCOURAGED_REFERENCE; import static org.eclipse.xtext.xbase.validation.IssueCodes.FORBIDDEN_REFERENCE; import static org.eclipse.xtext.xbase.validation.IssueCodes.INCOMPATIBLE_RETURN_TYPE; import static org.eclipse.xtext.xbase.validation.IssueCodes.INCOMPATIBLE_TYPES; import static org.eclipse.xtext.xbase.validation.IssueCodes.MISSING_TYPE; import static org.eclipse.xtext.xbase.validation.IssueCodes.TYPE_BOUNDS_MISMATCH; import static org.eclipse.xtext.xbase.validation.IssueCodes.VARIABLE_NAME_DISALLOWED; import static org.eclipse.xtext.xbase.validation.IssueCodes.VARIABLE_NAME_SHADOWING; import java.lang.annotation.ElementType; import java.lang.reflect.Field; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.Comparator; import java.util.EnumSet; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import com.google.common.base.Objects; import com.google.common.base.Predicate; import com.google.common.collect.ImmutableMultimap; import com.google.common.collect.Iterators; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import com.google.inject.Inject; import org.eclipse.emf.common.notify.Notifier; import org.eclipse.emf.ecore.EClass; import org.eclipse.emf.ecore.EObject; import org.eclipse.emf.ecore.EReference; import org.eclipse.emf.ecore.EStructuralFeature; import org.eclipse.emf.ecore.resource.Resource; import org.eclipse.emf.ecore.resource.ResourceSet; import org.eclipse.xtend.core.typesystem.LocalClassAwareTypeNames; import org.eclipse.xtend.core.validation.ModifierValidator; import org.eclipse.xtend.core.validation.XtendValidator; import org.eclipse.xtend.core.xtend.XtendAnnotationTarget; import org.eclipse.xtend.core.xtend.XtendAnnotationType; import org.eclipse.xtend.core.xtend.XtendClass; import org.eclipse.xtend.core.xtend.XtendConstructor; import org.eclipse.xtend.core.xtend.XtendEnum; import org.eclipse.xtend.core.xtend.XtendField; import org.eclipse.xtend.core.xtend.XtendFile; import org.eclipse.xtend.core.xtend.XtendFunction; import org.eclipse.xtend.core.xtend.XtendInterface; import org.eclipse.xtend.core.xtend.XtendMember; import org.eclipse.xtend.core.xtend.XtendPackage; import org.eclipse.xtend.core.xtend.XtendTypeDeclaration; import org.eclipse.xtext.EcoreUtil2; import org.eclipse.xtext.common.types.JvmConstructor; import org.eclipse.xtext.common.types.JvmDeclaredType; import org.eclipse.xtext.common.types.JvmField; import org.eclipse.xtext.common.types.JvmGenericType; import org.eclipse.xtext.common.types.JvmIdentifiableElement; import org.eclipse.xtext.common.types.JvmOperation; import org.eclipse.xtext.common.types.JvmParameterizedTypeReference; import org.eclipse.xtext.common.types.JvmType; import org.eclipse.xtext.common.types.JvmTypeReference; import org.eclipse.xtext.common.types.util.TypeReferences; import org.eclipse.xtext.naming.IQualifiedNameConverter; import org.eclipse.xtext.naming.QualifiedName; import org.eclipse.xtext.util.JavaVersion; import org.eclipse.xtext.util.XtextVersion; import org.eclipse.xtext.validation.Check; import org.eclipse.xtext.validation.CheckType; import org.eclipse.xtext.validation.IssueSeverities; import org.eclipse.xtext.validation.ValidationMessageAcceptor; import org.eclipse.xtext.xbase.XAbstractFeatureCall; import org.eclipse.xtext.xbase.XAbstractWhileExpression; import org.eclipse.xtext.xbase.XAssignment; import org.eclipse.xtext.xbase.XBasicForLoopExpression; import org.eclipse.xtext.xbase.XBlockExpression; import org.eclipse.xtext.xbase.XBooleanLiteral; import org.eclipse.xtext.xbase.XConstructorCall; import org.eclipse.xtext.xbase.XExpression; import org.eclipse.xtext.xbase.XFeatureCall; import org.eclipse.xtext.xbase.XForLoopExpression; import org.eclipse.xtext.xbase.XVariableDeclaration; import org.eclipse.xtext.xbase.XbasePackage; import org.eclipse.xtext.xbase.annotations.xAnnotations.XAnnotation; import org.eclipse.xtext.xbase.compiler.GeneratorConfig; import org.eclipse.xtext.xbase.lib.CollectionLiterals; import org.eclipse.xtext.xbase.lib.Inline; import org.eclipse.xtext.xbase.lib.util.ToStringBuilder; import org.eclipse.xtext.xbase.typesystem.override.IOverrideCheckResult.OverrideCheckDetails; import org.eclipse.xtext.xbase.typesystem.override.IResolvedOperation; import org.eclipse.xtext.xbase.typesystem.references.LightweightTypeReference; import org.eclipse.xtext.xbase.validation.FeatureNameValidator; import io.sarl.lang.SARLVersion; import io.sarl.lang.actionprototype.ActionParameterTypes; import io.sarl.lang.actionprototype.IActionPrototypeProvider; import io.sarl.lang.annotation.EarlyExit; import io.sarl.lang.core.Agent; import io.sarl.lang.core.Behavior; import io.sarl.lang.core.Capacity; import io.sarl.lang.core.Event; import io.sarl.lang.core.Skill; import io.sarl.lang.jvmmodel.SarlJvmModelAssociations; import io.sarl.lang.sarl.SarlAction; import io.sarl.lang.sarl.SarlAgent; import io.sarl.lang.sarl.SarlAnnotationType; import io.sarl.lang.sarl.SarlArtifact; import io.sarl.lang.sarl.SarlBehavior; import io.sarl.lang.sarl.SarlBehaviorUnit; import io.sarl.lang.sarl.SarlBreakExpression; import io.sarl.lang.sarl.SarlCapacity; import io.sarl.lang.sarl.SarlCapacityUses; import io.sarl.lang.sarl.SarlClass; import io.sarl.lang.sarl.SarlConstructor; import io.sarl.lang.sarl.SarlEnumeration; import io.sarl.lang.sarl.SarlEvent; import io.sarl.lang.sarl.SarlField; import io.sarl.lang.sarl.SarlFormalParameter; import io.sarl.lang.sarl.SarlInterface; import io.sarl.lang.sarl.SarlRequiredCapacity; import io.sarl.lang.sarl.SarlSkill; import io.sarl.lang.sarl.SarlSpace; import io.sarl.lang.services.SARLGrammarKeywordAccess; import io.sarl.lang.typesystem.SARLExpressionHelper; import io.sarl.lang.util.OutParameter; import io.sarl.lang.util.Utils; import io.sarl.lang.util.Utils.SarlLibraryErrorCode; /** * Validator for the SARL elements. * * <p>The check type may be one of:<ul> * <li>{@link CheckType#FAST}: is executed after a delay of 500ms after ANY editing action (type, enter, delete);</li> * <li>{@link CheckType#NORMAL}: is executed after a build (manual, or automatic);</li> * <li>{@link CheckType#EXPENSIVE}: is executed by right clicking ANYWHERE in the editor window and chooseing "Validate".</li> * </ul> * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ * @see "https://www.eclipse.org/Xtext/documentation/303_runtime_concepts.html#validation" */ @SuppressWarnings({"checkstyle:classfanoutcomplexity", "checkstyle:methodcount"}) public class SARLValidator extends AbstractSARLValidator { @SuppressWarnings("synthetic-access") private final SARLModifierValidator constructorModifierValidator = new SARLModifierValidator( newArrayList(SARLValidator.this.visibilityModifers)); @SuppressWarnings("synthetic-access") private final SARLModifierValidator agentModifierValidator = new SARLModifierValidator( newArrayList("public", "package", "abstract", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ "final")); //$NON-NLS-1$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator methodInAgentModifierValidator = new SARLModifierValidator( newArrayList( "package", //$NON-NLS-1$ "protected", "private", "static", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ "abstract", "dispatch", "final", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ "def", "override", "synchronized")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator fieldInAgentModifierValidator = new SARLModifierValidator( newArrayList( "package", //$NON-NLS-1$ "protected", "private", //$NON-NLS-1$//$NON-NLS-2$ "final", "val", "var")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator behaviorModifierValidator = new SARLModifierValidator( newArrayList("public", "package", "abstract", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ "final")); //$NON-NLS-1$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator methodInBehaviorModifierValidator = new SARLModifierValidator( newArrayList( "public", "package", //$NON-NLS-1$ //$NON-NLS-2$ "protected", "private", //$NON-NLS-1$//$NON-NLS-2$ "abstract", "dispatch", "final", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ "def", "override", "synchronized")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator fieldInBehaviorModifierValidator = new SARLModifierValidator( newArrayList( "package", //$NON-NLS-1$ "protected", "private", //$NON-NLS-1$//$NON-NLS-2$ "final", "val", "var")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator capacityModifierValidator = new SARLModifierValidator( newArrayList("public", "package")); //$NON-NLS-1$//$NON-NLS-2$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator methodInCapacityModifierValidator = new SARLModifierValidator( newArrayList( "public", "def", "override")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator eventModifierValidator = new SARLModifierValidator( newArrayList("public", "package", "final")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator fieldInEventModifierValidator = new SARLModifierValidator( newArrayList( "public", //$NON-NLS-1$ "final", "val", "var")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator skillModifierValidator = new SARLModifierValidator( newArrayList("public", "package", "abstract", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ "final")); //$NON-NLS-1$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator methodInSkillModifierValidator = new SARLModifierValidator( newArrayList( "public", "package", //$NON-NLS-1$ //$NON-NLS-2$ "protected", "private", //$NON-NLS-1$//$NON-NLS-2$ "abstract", "dispatch", "final", //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ "def", "override", "synchronized")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator fieldInSkillModifierValidator = new SARLModifierValidator( newArrayList( "public", "package", //$NON-NLS-1$ //$NON-NLS-2$ "protected", "private", //$NON-NLS-1$//$NON-NLS-2$ "final", "val", "var")); //$NON-NLS-1$//$NON-NLS-2$//$NON-NLS-3$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator nestedClassInAgentModifierValidator = new SARLModifierValidator( newArrayList( "package", "protected", //$NON-NLS-1$ //$NON-NLS-2$ "private", "static", "final", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ "abstract")); //$NON-NLS-1$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator nestedInterfaceInAgentModifierValidator = new SARLModifierValidator( newArrayList( "package", "protected", "private", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ "static", "abstract")); //$NON-NLS-1$ //$NON-NLS-2$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator nestedEnumerationInAgentModifierValidator = new SARLModifierValidator( newArrayList( "package", "protected", "private", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ "static")); //$NON-NLS-1$ @SuppressWarnings("synthetic-access") private final SARLModifierValidator nestedAnnotationTypeInAgentModifierValidator = new SARLModifierValidator( newArrayList( "package", "protected", "private", //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ "static", "abstract")); //$NON-NLS-1$ //$NON-NLS-2$ @Inject private SarlJvmModelAssociations associations; @Inject private FeatureNameValidator featureNames; @Inject private IActionPrototypeProvider sarlActionSignatures; @Inject private IFeatureCallValidator featureCallValidator; @Inject private SARLGrammarKeywordAccess grammarAccess; @Inject private TypeReferences typeReferences; @Inject private LocalClassAwareTypeNames localClassAwareTypeNames; @Inject private SARLExpressionHelper expressionHelper; @Inject private IProgrammaticWarningSuppressor warningSuppressor; @Inject private IQualifiedNameConverter qualifiedNameConverter; // Update thet annotation target information { final ImmutableMultimap.Builder<Class<?>, ElementType> result = ImmutableMultimap.builder(); result.putAll(this.targetInfos); result.put(SarlAgent.class, ElementType.TYPE); result.put(SarlCapacity.class, ElementType.TYPE); result.put(SarlSkill.class, ElementType.TYPE); result.put(SarlEvent.class, ElementType.TYPE); result.put(SarlBehavior.class, ElementType.TYPE); result.put(SarlSpace.class, ElementType.TYPE); result.put(SarlArtifact.class, ElementType.TYPE); result.put(SarlClass.class, ElementType.TYPE); result.put(SarlInterface.class, ElementType.TYPE); result.put(SarlEnumeration.class, ElementType.TYPE); result.putAll(SarlAnnotationType.class, ElementType.ANNOTATION_TYPE, ElementType.TYPE); result.put(SarlField.class, ElementType.FIELD); result.put(SarlAction.class, ElementType.METHOD); result.put(SarlFormalParameter.class, ElementType.PARAMETER); // Override the target informations try { final Field field = XtendValidator.class.getDeclaredField("targetInfos"); //$NON-NLS-1$ field.setAccessible(true); field.set(this, result.build()); } catch (Exception exception) { throw new Error(exception); } } /** Copied from the Xtend validtor. * * <p>TODO: Change the visilibility in the Xtend validator. * * @param annotationTarget the target to test. * @return <code>true</code> if the annotation target is relevant for validation. */ protected boolean isRelevantAnnotationTarget(final XtendAnnotationTarget annotationTarget) { return any(this.targetInfos.keySet(), new Predicate<Class<?>>() { @Override public boolean apply(Class<?> input) { return input.isInstance(annotationTarget); } }); } @Override protected IssueSeverities getIssueSeverities(Map<Object, Object> context, EObject eObject) { final IssueSeverities severities = super.getIssueSeverities(context, eObject); return this.warningSuppressor.getIssueSeverities(context, eObject, severities); } /** Replies if the given issue is ignored for the given object. * * @param issueCode the code if the issue. * @param currentObject the current object. * @return <code>true</code> if the issue is ignored. * @see #isIgnored(String) */ protected boolean isIgnored(String issueCode, EObject currentObject) { final IssueSeverities severities = getIssueSeverities(getContext(), currentObject); return severities.isIgnored(issueCode); } /** Replies the canonical name of the given object. * * @param object - the object. * @return the canonical name or <code>null</code> if it cannot be computed. */ protected String canonicalName(EObject object) { if (object instanceof JvmIdentifiableElement) { return ((JvmIdentifiableElement) object).getQualifiedName(); } final EObject jvmElement = this.associations.getPrimaryJvmElement(object); if (jvmElement instanceof JvmIdentifiableElement) { return ((JvmIdentifiableElement) jvmElement).getQualifiedName(); } return null; } /** Space keyword is reserved. * * @param space - the space to check. */ @Check public void checkSpaceUse(SarlSpace space) { error(MessageFormat.format( Messages.SARLValidator_0, this.grammarAccess.getSpaceKeyword()), space, null); } /** Artifact keyword is reserved. * * @param artifact - the artifact to check. */ @Check public void checkArtifactUse(SarlArtifact artifact) { error(MessageFormat.format( Messages.SARLValidator_0, this.grammarAccess.getSpaceKeyword()), artifact, null); } /** Emit a warning when the "fires" keyword is used. * * @param action - the action to check. */ @Check public void checkFiresKeywordUse(SarlAction action) { if (!action.getFiredEvents().isEmpty()) { warning(MessageFormat.format( Messages.SARLValidator_1, this.grammarAccess.getFiresKeyword()), action, null); } } /** Emit a warning when the "requires" keyword is used. * * @param statement - the statement to check. */ @Check public void checkRequiredCapacityUse(SarlRequiredCapacity statement) { warning(MessageFormat.format( Messages.SARLValidator_0, this.grammarAccess.getRequiresKeyword()), statement, null); } /** Check if the correct SARL libraries are on the classpath. * * <p>This function is overriding the function given by the Xtend validator * for having finer tests, and firing a warning in place of an error. * * @param sarlScript - the SARL script. */ @Check(CheckType.NORMAL) @Override @SuppressWarnings("checkstyle:npathcomplexity") public void checkClassPath(XtendFile sarlScript) { final TypeReferences typeReferences = getServices().getTypeReferences(); if (!Utils.isCompatibleJREVersion()) { error( MessageFormat.format( Messages.SARLValidator_3, System.getProperty("java.specification.version"), //$NON-NLS-1$ SARLVersion.MINIMAL_JDK_VERSION), sarlScript, XtendPackage.Literals.XTEND_FILE__PACKAGE, JDK_NOT_ON_CLASSPATH); } else { final GeneratorConfig generatorConfiguration = getGeneratorConfig(sarlScript); final JavaVersion javaVersion = JavaVersion.fromQualifier(SARLVersion.MINIMAL_JDK_VERSION); final JavaVersion generatorVersion = generatorConfiguration.getJavaSourceVersion(); if (generatorVersion == null || javaVersion == null || !generatorVersion.isAtLeast(javaVersion)) { error( MessageFormat.format( Messages.SARLValidator_4, generatorVersion, SARLVersion.MINIMAL_JDK_VERSION), sarlScript, XtendPackage.Literals.XTEND_FILE__PACKAGE, JDK_NOT_ON_CLASSPATH); } } if (!Utils.isCompatibleXtextVersion()) { error( MessageFormat.format( Messages.SARLValidator_5, XtextVersion.getCurrent(), SARLVersion.MINIMAL_XTEXT_VERSION), sarlScript, XtendPackage.Literals.XTEND_FILE__PACKAGE, XBASE_LIB_NOT_ON_CLASSPATH); } else { final JvmType type = typeReferences.findDeclaredType(ToStringBuilder.class.getName(), sarlScript); if (type == null) { error( MessageFormat.format( Messages.SARLValidator_6, SARLVersion.MINIMAL_XTEXT_VERSION), sarlScript, XtendPackage.Literals.XTEND_FILE__PACKAGE, XBASE_LIB_NOT_ON_CLASSPATH); } } final OutParameter<String> sarlLibraryVersion = new OutParameter<>(); final SarlLibraryErrorCode errorCode = Utils.getSARLLibraryVersionOnClasspath(typeReferences, sarlScript, sarlLibraryVersion); if (errorCode != SarlLibraryErrorCode.SARL_FOUND) { final ResourceSet resourceSet = EcoreUtil2.getResourceSet(sarlScript); final StringBuilder classPath = new StringBuilder(); for (final Resource resource : resourceSet.getResources()) { classPath.append(resource.getURI().toString()); classPath.append("\n"); //$NON-NLS-1$ } final StringBuilder fields = new StringBuilder(); try { final JvmDeclaredType type = (JvmDeclaredType) typeReferences.findDeclaredType(SARLVersion.class, sarlScript); for (final JvmField field : type.getDeclaredFields()) { fields.append(field.getIdentifier()); fields.append(" / "); //$NON-NLS-1$ fields.append(field.getSimpleName()); fields.append("\n"); //$NON-NLS-1$ } } catch (Exception e) { // } if (fields.length() == 0) { for (final Field field : SARLVersion.class.getDeclaredFields()) { fields.append(field.getName()); fields.append("\n"); //$NON-NLS-1$ } } error( MessageFormat.format(Messages.SARLValidator_7, errorCode.name(), classPath.toString(), fields.toString()), sarlScript, XtendPackage.Literals.XTEND_FILE__PACKAGE, io.sarl.lang.validation.IssueCodes.SARL_LIB_NOT_ON_CLASSPATH); } else if (!Utils.isCompatibleSARLLibraryVersion(sarlLibraryVersion.get())) { error( MessageFormat.format(Messages.SARLValidator_8, sarlLibraryVersion.get(), SARLVersion.SPECIFICATION_RELEASE_VERSION), sarlScript, XtendPackage.Literals.XTEND_FILE__PACKAGE, io.sarl.lang.validation.IssueCodes.INVALID_SARL_LIB_ON_CLASSPATH); } } @Check @Override protected void checkModifiers(XtendConstructor constructor) { final XtendTypeDeclaration declaringType = constructor.getDeclaringType(); if (declaringType != null) { if (declaringType instanceof SarlEvent || declaringType instanceof SarlAgent || declaringType instanceof SarlSkill || declaringType instanceof SarlBehavior) { final String typeName = ((XtendTypeDeclaration) constructor.eContainer()).getName(); this.constructorModifierValidator.checkModifiers(constructor, MessageFormat.format(Messages.SARLValidator_9, typeName)); } else { super.checkModifiers(constructor); } } } @Check @Override protected void checkModifiers(XtendFunction function) { final XtendTypeDeclaration declaringType = function.getDeclaringType(); if (declaringType != null) { if (declaringType instanceof SarlAgent) { final String typeName = ((XtendTypeDeclaration) function.eContainer()).getName(); this.methodInAgentModifierValidator.checkModifiers(function, MessageFormat.format(Messages.SARLValidator_10, function.getName(), typeName)); } else if (declaringType instanceof SarlCapacity) { final String typeName = ((XtendTypeDeclaration) function.eContainer()).getName(); this.methodInCapacityModifierValidator.checkModifiers(function, MessageFormat.format(Messages.SARLValidator_10, function.getName(), typeName)); } else if (declaringType instanceof SarlSkill) { final String typeName = ((XtendTypeDeclaration) function.eContainer()).getName(); this.methodInSkillModifierValidator.checkModifiers(function, MessageFormat.format(Messages.SARLValidator_10, function.getName(), typeName)); } else if (declaringType instanceof SarlBehavior) { final String typeName = ((XtendTypeDeclaration) function.eContainer()).getName(); this.methodInBehaviorModifierValidator.checkModifiers(function, MessageFormat.format(Messages.SARLValidator_10, function.getName(), typeName)); } else { super.checkModifiers(function); } } } @Check @Override protected void checkModifiers(XtendField field) { final XtendTypeDeclaration declaringType = field.getDeclaringType(); if (declaringType != null) { if (declaringType instanceof SarlEvent) { final String typeName = ((XtendTypeDeclaration) field.eContainer()).getName(); this.fieldInEventModifierValidator.checkModifiers(field, MessageFormat.format(Messages.SARLValidator_10, field.getName(), typeName)); } else if (declaringType instanceof SarlAgent) { final String typeName = ((XtendTypeDeclaration) field.eContainer()).getName(); this.fieldInAgentModifierValidator.checkModifiers(field, MessageFormat.format(Messages.SARLValidator_10, field.getName(), typeName)); } else if (declaringType instanceof SarlSkill) { final String typeName = ((XtendTypeDeclaration) field.eContainer()).getName(); this.fieldInSkillModifierValidator.checkModifiers(field, MessageFormat.format(Messages.SARLValidator_10, field.getName(), typeName)); } else if (declaringType instanceof SarlBehavior) { final String typeName = ((XtendTypeDeclaration) field.eContainer()).getName(); this.fieldInBehaviorModifierValidator.checkModifiers(field, MessageFormat.format(Messages.SARLValidator_10, field.getName(), typeName)); } else { super.checkModifiers(field); } } } /** Check if the modifiers for the SARL events. * * @param event the event. */ @Check protected void checkModifiers(SarlEvent event) { this.eventModifierValidator.checkModifiers(event, MessageFormat.format(Messages.SARLValidator_9, event.getName())); } /** Check the modifiers for the SARL agents. * * @param agent the agent. */ @Check protected void checkModifiers(SarlAgent agent) { this.agentModifierValidator.checkModifiers(agent, MessageFormat.format(Messages.SARLValidator_9, agent.getName())); } /** Check the modifiers for the SARL behaviors. * * @param behavior the behavior. */ @Check protected void checkModifiers(SarlBehavior behavior) { this.behaviorModifierValidator.checkModifiers(behavior, MessageFormat.format(Messages.SARLValidator_9, behavior.getName())); } /** Check the modifiers for the SARL capacities. * * @param capacity the capacity. */ @Check protected void checkModifiers(SarlCapacity capacity) { this.capacityModifierValidator.checkModifiers(capacity, MessageFormat.format(Messages.SARLValidator_9, capacity.getName())); } /** Check the modifiers for the SARL skills. * * @param skill the skill. */ @Check protected void checkModifiers(SarlSkill skill) { this.skillModifierValidator.checkModifiers(skill, MessageFormat.format(Messages.SARLValidator_9, skill.getName())); } @Check @Override protected void checkModifiers(XtendInterface oopInterface) { final EObject econtainer = oopInterface.eContainer(); if (econtainer instanceof SarlAgent) { this.nestedInterfaceInAgentModifierValidator.checkModifiers(oopInterface, MessageFormat.format(Messages.SARLValidator_9, oopInterface.getName())); } else { super.checkModifiers(oopInterface); } } @Check @Override protected void checkModifiers(XtendClass oopClass) { final EObject econtainer = oopClass.eContainer(); if (econtainer instanceof SarlAgent) { this.nestedClassInAgentModifierValidator.checkModifiers(oopClass, MessageFormat.format(Messages.SARLValidator_9, oopClass.getName())); } else { super.checkModifiers(oopClass); } // TODO remove this constraint when it is removed from the Xtend validator. if (!oopClass.isStatic() && ((econtainer instanceof SarlAgent) || (econtainer instanceof SarlBehavior) || (econtainer instanceof SarlSkill))) { error(Messages.SARLValidator_25, XTEND_TYPE_DECLARATION__NAME, -1, MISSING_STATIC_MODIFIER); } } @Check @Override protected void checkModifiers(XtendEnum oopEnum) { final EObject econtainer = oopEnum.eContainer(); if (econtainer instanceof SarlAgent) { this.nestedEnumerationInAgentModifierValidator.checkModifiers(oopEnum, MessageFormat.format(Messages.SARLValidator_9, oopEnum.getName())); } else { super.checkModifiers(oopEnum); } } @Check @Override protected void checkModifiers(XtendAnnotationType oopAnnotationType) { final EObject econtainer = oopAnnotationType.eContainer(); if (econtainer instanceof SarlAgent) { this.nestedAnnotationTypeInAgentModifierValidator.checkModifiers(oopAnnotationType, MessageFormat.format(Messages.SARLValidator_9, oopAnnotationType.getName())); } else { super.checkModifiers(oopAnnotationType); } } /** Check the container for the SARL agents. * * @param agent the agent. */ @Check public void checkContainerType(SarlAgent agent) { final XtendTypeDeclaration declaringType = agent.getDeclaringType(); if (declaringType != null) { final String name = canonicalName(declaringType); assert name != null; error(MessageFormat.format(Messages.SARLValidator_28, name), agent, null, INVALID_NESTED_DEFINITION); } } /** Check the container for the SARL behaviors. * * @param behavior the behavior. */ @Check public void checkContainerType(SarlBehavior behavior) { final XtendTypeDeclaration declaringType = behavior.getDeclaringType(); if (declaringType != null) { final String name = canonicalName(declaringType); assert name != null; error(MessageFormat.format(Messages.SARLValidator_29, name), behavior, null, INVALID_NESTED_DEFINITION); } } /** Check the container for the SARL capacities. * * @param capacity the capacity. */ @Check public void checkContainerType(SarlCapacity capacity) { final XtendTypeDeclaration declaringType = capacity.getDeclaringType(); if (declaringType != null) { final String name = canonicalName(declaringType); assert name != null; error(MessageFormat.format(Messages.SARLValidator_30, name), capacity, null, INVALID_NESTED_DEFINITION); } } /** Check the container for the SARL skills. * * @param skill the skill. */ @Check public void checkContainerType(SarlSkill skill) { final XtendTypeDeclaration declaringType = skill.getDeclaringType(); if (declaringType != null) { final String name = canonicalName(declaringType); assert name != null; error(MessageFormat.format(Messages.SARLValidator_31, name), skill, null, INVALID_NESTED_DEFINITION); } } /** Check if the modifiers for the SARL events. * * @param event the event. */ @Check public void checkContainerType(SarlEvent event) { final XtendTypeDeclaration declaringType = event.getDeclaringType(); if (declaringType != null) { final String name = canonicalName(declaringType); assert name != null; error(MessageFormat.format(Messages.SARLValidator_32, name), event, null, INVALID_NESTED_DEFINITION); } } /** Check if all the fields are initialized in a SARL event. * * @param event the event. */ @Check public void checkFinalFieldInitialization(SarlEvent event) { final JvmGenericType inferredType = this.associations.getInferredType(event); if (inferredType != null) { super.checkFinalFieldInitialization(inferredType); } } /** Check if all the fields are initialized in a SARL behavior. * * @param behavior the behavior. */ @Check public void checkFinalFieldInitialization(SarlBehavior behavior) { final JvmGenericType inferredType = this.associations.getInferredType(behavior); if (inferredType != null) { super.checkFinalFieldInitialization(inferredType); } } /** Check if all the fields are initialized in a SARL skill. * * @param skill the skill. */ @Check public void checkFinalFieldInitialization(SarlSkill skill) { final JvmGenericType inferredType = this.associations.getInferredType(skill); if (inferredType != null) { super.checkFinalFieldInitialization(inferredType); } } /** Check if all the fields are initialized in a SARL agent. * * @param agent the agent. */ @Check public void checkFinalFieldInitialization(SarlAgent agent) { final JvmGenericType inferredType = this.associations.getInferredType(agent); if (inferredType != null) { super.checkFinalFieldInitialization(inferredType); } } /** Check the super constructors. * * @param container - the container. * @param feature - the syntactic feature related to the supertypes. * @param defaultSignatures - the signatures of the default constructors for the given container. */ @SuppressWarnings({"unchecked", "checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity", "checkstyle:nestedifdepth"}) protected void checkSuperConstructor( XtendTypeDeclaration container, EStructuralFeature feature, Collection<ActionParameterTypes> defaultSignatures) { final JvmDeclaredType jvmElement = this.associations.getInferredType(container); if (jvmElement != null) { final Map<ActionParameterTypes, JvmConstructor> superConstructors = CollectionLiterals.newTreeMap((Comparator<ActionParameterTypes>) null); final JvmTypeReference typeRef = jvmElement.getExtendedClass(); final JvmType supertype = (typeRef == null) ? null : typeRef.getType(); if (supertype instanceof JvmGenericType) { final JvmGenericType jvmSuperElement = (JvmGenericType) supertype; for (final JvmConstructor superConstructor : jvmSuperElement.getDeclaredConstructors()) { final ActionParameterTypes sig = this.sarlActionSignatures.createParameterTypesFromJvmModel( superConstructor.isVarArgs(), superConstructor.getParameters()); superConstructors.put(sig, superConstructor); } } final ActionParameterTypes voidKey = this.sarlActionSignatures.createParameterTypesForVoid(); //boolean hasDeclaredConstructor = false; for (final XtendMember member : container.getMembers()) { if (member instanceof SarlConstructor) { final SarlConstructor constructor = (SarlConstructor) member; //hasDeclaredConstructor = true; boolean invokeDefaultConstructor = true; final XExpression body = constructor.getExpression(); if (body instanceof XBlockExpression) { final XBlockExpression block = (XBlockExpression) body; if (!block.getExpressions().isEmpty()) { final XExpression firstStatement = block.getExpressions().get(0); if (firstStatement instanceof XConstructorCall || isDelegateConstructorCall(firstStatement)) { invokeDefaultConstructor = false; } } } else if (body instanceof XConstructorCall || isDelegateConstructorCall(body)) { invokeDefaultConstructor = false; } if (invokeDefaultConstructor && !superConstructors.containsKey(voidKey)) { final List<String> issueData = new ArrayList<>(); for (final ActionParameterTypes defaultSignature : defaultSignatures) { issueData.add(defaultSignature.toString()); } error(Messages.SARLValidator_33, member, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, MUST_INVOKE_SUPER_CONSTRUCTOR, toArray(issueData, String.class)); } } } // The following code is no more needed because of the constructor inheritance mechanism which // is implemented into the JVM model inferrer. /*if (!hasDeclaredConstructor) { for (final ActionParameterTypes defaultSignature : defaultSignatures) { if (!superConstructors.containsKey(defaultSignature)) { final List<String> issueData = new ArrayList<>(); for (final JvmConstructor superConstructor : superConstructors.values()) { issueData.add(EcoreUtil.getURI(superConstructor).toString()); issueData.add(doGetReadableSignature(container.getName(), superConstructor.getParameters())); } error(Messages.SARLValidator_33, container, feature, MISSING_CONSTRUCTOR, toArray(issueData, String.class)); } } }*/ } } /** Check if the super default constructor is correctly invoked. * * @param agent the SARL element. */ @Check public void checkSuperConstructor(SarlAgent agent) { checkSuperConstructor( agent, XTEND_TYPE_DECLARATION__NAME, doGetConstructorParameterTypes(Agent.class, agent)); } /** Check if the super default constructor is correctly invoked. * * @param behavior the SARL element. */ @Check public void checkSuperConstructor(SarlBehavior behavior) { checkSuperConstructor( behavior, XTEND_TYPE_DECLARATION__NAME, doGetConstructorParameterTypes(Behavior.class, behavior)); } /** Check if the super default constructor is correctly invoked. * * @param skill the SARL element. */ @Check public void checkSuperConstructor(SarlSkill skill) { checkSuperConstructor( skill, XTEND_TYPE_DECLARATION__NAME, doGetConstructorParameterTypes(Skill.class, skill)); } /** Check if the super default constructor is correctly invoked. * * @param event the SARL element. */ @Check public void checkSuperConstructor(SarlEvent event) { checkSuperConstructor( event, XTEND_TYPE_DECLARATION__NAME, doGetConstructorParameterTypes(Event.class, event)); } private Collection<ActionParameterTypes> doGetConstructorParameterTypes(Class<?> type, Notifier context) { final Collection<ActionParameterTypes> parameters = new ArrayList<>(); final JvmTypeReference typeReference = this.typeReferences.getTypeForName(type, context); final JvmType jvmType = typeReference.getType(); if (jvmType instanceof JvmDeclaredType) { final JvmDeclaredType declaredType = (JvmDeclaredType) jvmType; for (final JvmConstructor constructor : declaredType.getDeclaredConstructors()) { final ActionParameterTypes types = this.sarlActionSignatures.createParameterTypesFromJvmModel( constructor.isVarArgs(), constructor.getParameters()); if (types != null) { parameters.add(types); } } } if (parameters.isEmpty()) { parameters.add(this.sarlActionSignatures.createParameterTypesForVoid()); } return parameters; } /** Check if the super default constructor is correctly invoked. * * @param xtendClass the Xtend element. */ @Check @Override public void checkDefaultSuperConstructor(XtendClass xtendClass) { checkSuperConstructor( xtendClass, XTEND_TYPE_DECLARATION__NAME, doGetConstructorParameterTypes(Object.class, xtendClass)); } /** Check if the call is forbidden. * * <p>One example of a forbidden feature is {@link System#exit(int)}. * * @param expression - the expression. */ @Check(CheckType.FAST) public void checkForbiddenCalls(XAbstractFeatureCall expression) { /* TODO: Remove the code in this comment because it seems to be already managed by the feature call validator // specific message for System.exit final JvmIdentifiableElement feature = expression.getFeature(); if (feature != null) { final String id = feature.getQualifiedName(); if ("java.lang.System.exit".equals(id)) { //$NON-NLS-1$ error( Messages.SARLValidator_35, expression, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, FORBIDDEN_REFERENCE); return; } } */ if (this.featureCallValidator.isDisallowedCall(expression)) { error( MessageFormat.format( Messages.SARLValidator_36, expression.getFeature().getIdentifier()), expression, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, FORBIDDEN_REFERENCE); } } /** Check if the call is discouraged. * * <p>One example of a discouraged feature is {@link System#err}. * * @param expression - the expression. */ @Check(CheckType.FAST) public void checkDiscouragedCalls(XAbstractFeatureCall expression) { if (!isIgnored(DISCOURAGED_REFERENCE) && this.featureCallValidator.isDiscouragedCall(expression)) { addIssue( MessageFormat.format(Messages.SARLValidator_37, // FIXME: this.serializer.serialize(expression) expression.getConcreteSyntaxFeatureName()), expression, DISCOURAGED_REFERENCE); } } /** Check if the default values of the formal parameters have a compatible type with the formal parameter. * * @param param - the formal parameter to check. */ @Check public void checkDefaultValueTypeCompatibleWithParameterType(SarlFormalParameter param) { final XExpression defaultValue = param.getDefaultValue(); if (defaultValue != null) { final JvmTypeReference rawType = param.getParameterType(); assert rawType != null; final LightweightTypeReference toType = toLightweightTypeReference(rawType, true); final LightweightTypeReference fromType = getActualType(defaultValue); if (!Utils.canCast(fromType, toType, true, false, true)) { error(MessageFormat.format( Messages.SARLValidator_38, getNameOfTypes(fromType), canonicalName(toType)), param, SARL_FORMAL_PARAMETER__DEFAULT_VALUE, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, INCOMPATIBLE_TYPES, canonicalName(fromType), canonicalName(toType)); } } } /** Check if the default values has not a reference to the not final fields. * * @param param - the formal parameter to check. */ @Check public void checkDefaultValueFieldReference(SarlFormalParameter param) { final XExpression defaultValue = param.getDefaultValue(); if (defaultValue != null) { final Iterator<XFeatureCall> iter; if (defaultValue instanceof XFeatureCall) { iter = Iterators.singletonIterator((XFeatureCall) defaultValue); } else { iter = Iterators.filter(defaultValue.eAllContents(), XFeatureCall.class); } while (iter.hasNext()) { final XFeatureCall call = iter.next(); final JvmIdentifiableElement feature = call.getFeature(); String invalidFieldName = null; if (feature instanceof XtendField) { final XtendField field = (XtendField) feature; if (!field.isFinal()) { invalidFieldName = field.getName(); } } else if (feature instanceof JvmField) { final JvmField field = (JvmField) feature; if (!field.isFinal()) { invalidFieldName = field.getSimpleName(); } } if (invalidFieldName != null) { error(MessageFormat.format( Messages.SARLValidator_19, invalidFieldName), call, XbasePackage.Literals.XABSTRACT_FEATURE_CALL__FEATURE, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, FORBIDDEN_REFERENCE); } } } } /** Check if the given action has a valid name. * * @param action - the action to test. * @see SARLFeatureNameValidator */ @Check(CheckType.FAST) public void checkActionName(SarlAction action) { final JvmOperation inferredType = this.associations.getDirectlyInferredOperation(action); final QualifiedName name = QualifiedName.create(inferredType.getQualifiedName('.').split("\\.")); //$NON-NLS-1$ if (this.featureNames.isDisallowedName(name)) { final String validName = Utils.fixHiddenMember(action.getName()); error(MessageFormat.format( Messages.SARLValidator_39, action.getName()), action, XTEND_FUNCTION__NAME, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, INVALID_MEMBER_NAME, validName); } else if (!isIgnored(DISCOURAGED_FUNCTION_NAME) && this.featureNames.isDiscouragedName(name)) { warning(MessageFormat.format( Messages.SARLValidator_39, action.getName()), action, XTEND_FUNCTION__NAME, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, DISCOURAGED_FUNCTION_NAME); } } /** Check if the given field has a valid name. * * @param field - the field to test. * @see SARLFeatureNameValidator */ @Check(CheckType.FAST) public void checkFieldName(SarlField field) { final JvmField inferredType = this.associations.getJvmField(field); final QualifiedName name = Utils.getQualifiedName(inferredType); if (this.featureNames.isDisallowedName(name)) { final String validName = Utils.fixHiddenMember(field.getName()); error(MessageFormat.format( Messages.SARLValidator_41, field.getName()), field, XTEND_FIELD__NAME, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, VARIABLE_NAME_DISALLOWED, validName); } else if (this.grammarAccess.getOccurrenceKeyword().equals(field.getName())) { error(MessageFormat.format( Messages.SARLValidator_41, this.grammarAccess.getOccurrenceKeyword()), field, XTEND_FIELD__NAME, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, VARIABLE_NAME_DISALLOWED); } } /** Check if the given field has a name that is shadowing an inherited field. * * @param field - the field to test. */ @Check public void checkFieldNameShadowing(SarlField field) { if (!isIgnored(VARIABLE_NAME_SHADOWING) && !Utils.isHiddenMember(field.getName())) { final JvmField inferredField = this.associations.getJvmField(field); final Map<String, JvmField> inheritedFields = new TreeMap<>(); Utils.populateInheritanceContext( inferredField.getDeclaringType(), null, null, inheritedFields, null, null, this.sarlActionSignatures); final JvmField inheritedField = inheritedFields.get(field.getName()); if (inheritedField != null) { int nameIndex = 0; String newName = field.getName() + nameIndex; while (inheritedFields.containsKey(newName)) { ++nameIndex; newName = field.getName() + nameIndex; } addIssue(MessageFormat.format( Messages.SARLValidator_42, field.getName(), inferredField.getDeclaringType().getQualifiedName(), inheritedField.getQualifiedName()), field, XTEND_FIELD__NAME, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, VARIABLE_NAME_SHADOWING, newName); } } } /** Check if the given parameter has a valid name. * * @param parameter - the parameter to test. * @see SARLFeatureNameValidator */ @Check(CheckType.FAST) public void checkParameterName(SarlFormalParameter parameter) { if (this.grammarAccess.getOccurrenceKeyword().equals(parameter.getName())) { error(MessageFormat.format( Messages.SARLValidator_14, this.grammarAccess.getOccurrenceKeyword()), parameter, XtendPackage.Literals.XTEND_PARAMETER__NAME, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, VARIABLE_NAME_DISALLOWED); } } /** Check if the given local variable has a valid name. * * @param variable - the variable to test. * @see SARLFeatureNameValidator */ @Check(CheckType.FAST) public void checkParameterName(XVariableDeclaration variable) { if (this.grammarAccess.getOccurrenceKeyword().equals(variable.getName())) { error(MessageFormat.format( Messages.SARLValidator_15, this.grammarAccess.getOccurrenceKeyword()), variable, XbasePackage.Literals.XVARIABLE_DECLARATION__NAME, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, VARIABLE_NAME_DISALLOWED); } } /** Caution: This function is overridden for translating the MISSING_OVERRIDE error into a warning, * and emit a warning when a return type should be specified. * * {@inheritDoc} */ @Override @SuppressWarnings({"checkstyle:cyclomaticcomplexity", "checkstyle:npathcomplexity"}) protected void doCheckFunctionOverrides(EObject sourceElement, IResolvedOperation resolved, List<IResolvedOperation> allInherited) { boolean overrideProblems = false; List<IResolvedOperation> exceptionMismatch = null; for (final IResolvedOperation inherited: allInherited) { if (inherited.getOverrideCheckResult().hasProblems()) { overrideProblems = true; final EnumSet<OverrideCheckDetails> details = inherited.getOverrideCheckResult().getDetails(); if (details.contains(OverrideCheckDetails.IS_FINAL)) { error(MessageFormat.format(Messages.SARLValidator_43, inherited.getSimpleSignature()), sourceElement, nameFeature(sourceElement), OVERRIDDEN_FINAL); } else if (details.contains(OverrideCheckDetails.REDUCED_VISIBILITY)) { error(MessageFormat.format(Messages.SARLValidator_44, inherited.getSimpleSignature()), sourceElement, nameFeature(sourceElement), OVERRIDE_REDUCES_VISIBILITY); } else if (details.contains(OverrideCheckDetails.EXCEPTION_MISMATCH)) { if (exceptionMismatch == null) { exceptionMismatch = Lists.newArrayListWithCapacity(allInherited.size()); } exceptionMismatch.add(inherited); } else if (details.contains(OverrideCheckDetails.RETURN_MISMATCH)) { error(MessageFormat.format(Messages.SARLValidator_45, inherited.getSimpleSignature()), sourceElement, returnTypeFeature(sourceElement), INCOMPATIBLE_RETURN_TYPE, inherited.getResolvedReturnType().getIdentifier()); } } else if (!isIgnored(RETURN_TYPE_SPECIFICATION_IS_RECOMMENDED, sourceElement) && sourceElement instanceof SarlAction) { final SarlAction function = (SarlAction) sourceElement; if (function.getReturnType() == null && !inherited.getResolvedReturnType().isPrimitiveVoid()) { warning(MessageFormat.format(Messages.SARLValidator_46, resolved.getResolvedReturnType().getHumanReadableName()), sourceElement, returnTypeFeature(sourceElement), RETURN_TYPE_SPECIFICATION_IS_RECOMMENDED, inherited.getResolvedReturnType().getIdentifier()); } } } if (exceptionMismatch != null) { createExceptionMismatchError(resolved, sourceElement, exceptionMismatch); } if (sourceElement instanceof SarlAction) { final SarlAction function = (SarlAction) sourceElement; if (!overrideProblems && !function.isOverride() && !function.isStatic() && !isIgnored(MISSING_OVERRIDE, sourceElement)) { warning(MessageFormat.format(Messages.SARLValidator_47, resolved.getSimpleSignature(), getDeclaratorName(resolved)), function, XTEND_FUNCTION__NAME, MISSING_OVERRIDE); } if (!overrideProblems && function.isOverride() && function.isStatic()) { for (final IResolvedOperation inherited: allInherited) { error(MessageFormat.format(Messages.SARLValidator_48, resolved.getSimpleSignature(), getDeclaratorName(resolved), resolved.getSimpleSignature(), getDeclaratorName(inherited)), function, XTEND_FUNCTION__NAME, function.getModifiers().indexOf(Messages.SARLValidator_49), OBSOLETE_OVERRIDE); } } } } private boolean checkRedundantInterface( XtendTypeDeclaration element, EReference structuralElement, LightweightTypeReference lightweightInterfaceReference, List<LightweightTypeReference> knownInterfaces) { int index = 0; for (final LightweightTypeReference previousInterface : knownInterfaces) { if (memberOfTypeHierarchy(previousInterface, lightweightInterfaceReference)) { error(MessageFormat.format( Messages.SARLValidator_50, canonicalName(lightweightInterfaceReference)), element, structuralElement, // The index of the element to highlight in the super-types knownInterfaces.size(), REDUNDANT_INTERFACE_IMPLEMENTATION, canonicalName(lightweightInterfaceReference), "pre"); //$NON-NLS-1$ return true; } else if (memberOfTypeHierarchy(lightweightInterfaceReference, previousInterface)) { error(MessageFormat.format( Messages.SARLValidator_50, canonicalName(previousInterface)), element, structuralElement, index, REDUNDANT_INTERFACE_IMPLEMENTATION, canonicalName(previousInterface), "post"); //$NON-NLS-1$ } ++index; } return false; } private void checkRedundantInterfaces( XtendTypeDeclaration element, EReference structuralElement, Iterable<? extends JvmTypeReference> interfaces, Iterable<? extends JvmTypeReference> superTypes) { final List<LightweightTypeReference> knownInterfaces = CollectionLiterals.newArrayList(); for (final JvmTypeReference interfaceRef : interfaces) { final LightweightTypeReference lightweightInterfaceReference = toLightweightTypeReference(interfaceRef); // Check the interface against the other interfaces if (!checkRedundantInterface( element, structuralElement, lightweightInterfaceReference, knownInterfaces)) { // Check the interface against the super-types if (superTypes != null && !isIgnored(REDUNDANT_INTERFACE_IMPLEMENTATION, element)) { for (final JvmTypeReference superType : superTypes) { final LightweightTypeReference lightweightSuperType = toLightweightTypeReference(superType); if (memberOfTypeHierarchy(lightweightSuperType, lightweightInterfaceReference)) { addIssue(MessageFormat.format( Messages.SARLValidator_52, canonicalName(lightweightInterfaceReference), canonicalName(lightweightSuperType)), element, structuralElement, // The index of the element to highlight in the super-types knownInterfaces.size(), REDUNDANT_INTERFACE_IMPLEMENTATION, canonicalName(lightweightInterfaceReference), "unknow"); //$NON-NLS-1$ } } } } // Prepare next loop knownInterfaces.add(lightweightInterfaceReference); } } /** Check if implemented interfaces of a skill are redundant. * * @param skill the skill. */ @Check public void checkRedundantImplementedInterfaces(SarlSkill skill) { checkRedundantInterfaces( skill, SARL_SKILL__IMPLEMENTS, skill.getImplements(), Utils.singletonList(skill.getExtends())); } /** Check if implemented interfaces of a Xtend Class are redundant. * * @param xtendClass the class. */ @Check public void checkRedundantImplementedInterfaces(SarlClass xtendClass) { checkRedundantInterfaces( xtendClass, XTEND_CLASS__IMPLEMENTS, xtendClass.getImplements(), Utils.singletonList(xtendClass.getExtends())); } /** Check if implemented interfaces of a Xtend Interface are redundant. * * @param xtendInterface the interface. */ @Check public void checkRedundantImplementedInterfaces(SarlInterface xtendInterface) { checkRedundantInterfaces( xtendInterface, XTEND_INTERFACE__EXTENDS, xtendInterface.getExtends(), Collections.<JvmTypeReference>emptyList()); } /** Check the type of the behavior unit's guard. * * @param behaviorUnit - the behavior unit. */ @Check(CheckType.FAST) public void checkBehaviorUnitGuardType(SarlBehaviorUnit behaviorUnit) { final XExpression guard = behaviorUnit.getGuard(); if (guard != null) { if (this.expressionHelper.hasDeepSideEffects(guard)) { error(Messages.SARLValidator_53, guard, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, org.eclipse.xtext.xbase.validation.IssueCodes.INVALID_INNER_EXPRESSION); return; } if (guard instanceof XBooleanLiteral) { final XBooleanLiteral booleanLiteral = (XBooleanLiteral) guard; if (booleanLiteral.isIsTrue()) { if (!isIgnored(DISCOURAGED_BOOLEAN_EXPRESSION)) { addIssue(Messages.SARLValidator_54, booleanLiteral, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, DISCOURAGED_BOOLEAN_EXPRESSION); } } else if (!isIgnored(UNREACHABLE_BEHAVIOR_UNIT)) { addIssue(Messages.SARLValidator_55, behaviorUnit, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, UNREACHABLE_BEHAVIOR_UNIT, behaviorUnit.getName().getSimpleName()); } return; } final LightweightTypeReference fromType = getActualType(guard); if (!fromType.isAssignableFrom(Boolean.TYPE)) { error(MessageFormat.format( Messages.SARLValidator_38, getNameOfTypes(fromType), boolean.class.getName()), guard, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, INCOMPATIBLE_TYPES); } } } /** Check the type of the capacity uses. * * @param uses - the capacity uses. */ @Check(CheckType.FAST) public void checkCapacityTypeForUses(SarlCapacityUses uses) { for (final JvmParameterizedTypeReference usedType : uses.getCapacities()) { final LightweightTypeReference ref = toLightweightTypeReference(usedType); if (ref != null && !ref.isSubtypeOf(Capacity.class)) { error(MessageFormat.format( Messages.SARLValidator_57, usedType.getQualifiedName(), Messages.SARLValidator_58, this.grammarAccess.getUsesKeyword()), usedType, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, INVALID_CAPACITY_TYPE, usedType.getSimpleName()); } } } /** Check the types of the "requires" statement. * * @param requires - the "requires" statement. */ @Check(CheckType.FAST) public void checkCapacityTypeForRequires(SarlRequiredCapacity requires) { for (final JvmParameterizedTypeReference requiredType : requires.getCapacities()) { final LightweightTypeReference ref = toLightweightTypeReference(requiredType); if (ref != null && !ref.isSubtypeOf(Capacity.class)) { error(MessageFormat.format( Messages.SARLValidator_57, requiredType.getQualifiedName(), Messages.SARLValidator_58, this.grammarAccess.getRequiresKeyword()), requiredType, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, INVALID_CAPACITY_TYPE, requiredType.getSimpleName()); } } } /** Check the types of the parameters of the "fires" statement. * * @param action - the signature that contains the "fires" statement. */ @Check(CheckType.FAST) public void checkActionFires(SarlAction action) { for (final JvmTypeReference event : action.getFiredEvents()) { final LightweightTypeReference ref = toLightweightTypeReference(event); if (ref != null && !ref.isSubtypeOf(Event.class)) { error(MessageFormat.format( Messages.SARLValidator_57, event.getQualifiedName(), Messages.SARLValidator_62, this.grammarAccess.getFiresKeyword()), event, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, INVALID_FIRING_EVENT_TYPE, event.getSimpleName()); } } } /** Check the super type. * * @param element - the child type. * @param feature - the syntactic feature related to the supertypes. * @param superTypes - the current super types. * @param expectedType - the expected root type. * @param onlySubTypes - if <code>true</code> only the subtype of the <code>expectedType</code> are valid; * <code>false</code> if the <code>expectedType</code> is allowed. * @return the count of supertypes. */ @SuppressWarnings({"checkstyle:cyclomaticcomplexity"}) protected int checkSuperTypes( XtendTypeDeclaration element, EReference feature, List<? extends JvmTypeReference> superTypes, Class<?> expectedType, boolean onlySubTypes) { int nbSuperTypes = 0; final JvmDeclaredType inferredType = this.associations.getInferredType(element); if (inferredType instanceof JvmGenericType) { final LinkedList<JvmTypeReference> inferredSuperTypes = CollectionLiterals.newLinkedList(); inferredSuperTypes.addAll(inferredType.getSuperTypes()); final boolean isExpectingInterface = expectedType.isInterface(); int superTypeIndex = 0; for (final JvmTypeReference superType : superTypes) { boolean success = true; final JvmType jvmSuperType = (superType == null) ? null : superType.getType(); if (jvmSuperType != null) { final JvmTypeReference inferredSuperType = (inferredSuperTypes.isEmpty()) ? null : inferredSuperTypes.removeFirst(); final LightweightTypeReference lighweightSuperType = toLightweightTypeReference(superType); if (!(jvmSuperType instanceof JvmGenericType) || (isExpectingInterface != ((JvmGenericType) jvmSuperType).isInterface())) { if (isExpectingInterface) { error( MessageFormat.format(Messages.SARLValidator_63, Messages.SARLValidator_64), feature, superTypeIndex, INTERFACE_EXPECTED, jvmSuperType.getIdentifier()); } else { error( MessageFormat.format(Messages.SARLValidator_63, Messages.SARLValidator_66), feature, superTypeIndex, CLASS_EXPECTED, jvmSuperType.getIdentifier()); } success = false; } else if (isFinal(lighweightSuperType)) { error(Messages.SARLValidator_67, feature, superTypeIndex, OVERRIDDEN_FINAL, inferredType.getIdentifier(), jvmSuperType.getIdentifier()); success = false; } else if (!lighweightSuperType.isSubtypeOf(expectedType) || (onlySubTypes && lighweightSuperType.isType(expectedType))) { if (onlySubTypes) { error(MessageFormat.format(Messages.SARLValidator_68, expectedType.getName()), feature, superTypeIndex, INVALID_EXTENDED_TYPE, jvmSuperType.getIdentifier()); } else { error(MessageFormat.format(Messages.SARLValidator_69, expectedType.getName()), feature, superTypeIndex, INVALID_EXTENDED_TYPE, jvmSuperType.getIdentifier()); } success = false; } else if (inferredSuperType == null || !Objects.equal(inferredSuperType.getIdentifier(), jvmSuperType.getIdentifier()) || Objects.equal(inferredType.getIdentifier(), jvmSuperType.getIdentifier()) || hasCycleInHierarchy((JvmGenericType) inferredType, Sets.<JvmGenericType>newHashSet())) { error(MessageFormat.format(Messages.SARLValidator_70, inferredType.getQualifiedName()), feature, superTypeIndex, CYCLIC_INHERITANCE, jvmSuperType.getIdentifier()); success = false; } } else if (superType != null) { error(MessageFormat.format(Messages.SARLValidator_70, inferredType.getQualifiedName()), feature, superTypeIndex, CYCLIC_INHERITANCE, superType.getIdentifier()); success = false; } checkWildcardSupertype(element, superType, feature, superTypeIndex); ++superTypeIndex; if (success) { ++nbSuperTypes; } } } return nbSuperTypes; } /** Check if the supertype of the given capacity is a subtype of Capacity. * * @param capacity - the type to test. */ @Check(CheckType.FAST) public void checkSuperTypes(SarlCapacity capacity) { checkSuperTypes( capacity, SARL_CAPACITY__EXTENDS, capacity.getExtends(), Capacity.class, false); } /** Check if the supertype of the given skill is a subtype of Skill. * * @param skill - the type to test. */ @Check(CheckType.FAST) public void checkSuperType(SarlSkill skill) { final int nbSuperTypes = checkSuperTypes( skill, SARL_SKILL__EXTENDS, Utils.singletonList(skill.getExtends()), Skill.class, false); checkImplementedTypes( skill, SARL_SKILL__IMPLEMENTS, skill.getImplements(), Capacity.class, nbSuperTypes > 0 ? 0 : 1, true); } /** Check if the supertype of the given event is a subtype of Event. * * @param event - the type to test. */ @Check(CheckType.FAST) public void checkSuperType(SarlEvent event) { checkSuperTypes( event, SARL_EVENT__EXTENDS, Utils.singletonList(event.getExtends()), Event.class, false); } /** Check if the supertype of the given behavior is a subtype of Behavior. * * @param behavior - the type to test. */ @Check(CheckType.FAST) public void checkSuperType(SarlBehavior behavior) { checkSuperTypes( behavior, SARL_BEHAVIOR__EXTENDS, Utils.singletonList(behavior.getExtends()), Behavior.class, false); } /** Check if the supertype of the given agent is a subtype of Agent. * * @param agent - the type to test. */ @Check(CheckType.FAST) public void checkSuperType(SarlAgent agent) { checkSuperTypes( agent, SARL_AGENT__EXTENDS, Utils.singletonList(agent.getExtends()), Agent.class, false); } /** Check the implemeted type. * * @param element - the child type. * @param feature - the syntactic feature related to the supertypes. * @param implementedTypes - the current super types. * @param expectedType - the expected root type. * @param mandatoryNumberOfTypes - the minimal number of implemented types. * @param onlySubTypes - if <code>true</code> only the subtype of the <code>expectedType</code> are valid; * <code>false</code> if the <code>expectedType</code> is allowed. * @return the count of supertypes. */ protected boolean checkImplementedTypes( XtendTypeDeclaration element, EReference feature, List<? extends JvmTypeReference> implementedTypes, Class<?> expectedType, int mandatoryNumberOfTypes, boolean onlySubTypes) { boolean success = true; int nb = 0; int index = 0; for (final JvmTypeReference superType : implementedTypes) { final LightweightTypeReference ref = toLightweightTypeReference(superType); if (ref != null && (!ref.isInterfaceType() || !ref.isSubtypeOf(expectedType) || (onlySubTypes && ref.isType(expectedType)))) { final String msg; if (onlySubTypes) { msg = Messages.SARLValidator_72; } else { msg = Messages.SARLValidator_73; } error(MessageFormat.format( msg, superType.getQualifiedName(), expectedType.getName(), element.getName()), element, feature, index, INVALID_IMPLEMENTED_TYPE, superType.getSimpleName()); success = false; } else { ++nb; } ++index; } if (nb < mandatoryNumberOfTypes) { error(MessageFormat.format( Messages.SARLValidator_74, expectedType.getName(), element.getName()), element, feature, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, MISSING_TYPE); success = false; } return success; } /** Check if the parameter of the bahavior unit is an event. * * @param behaviorUnit - the behavior unit to test. */ @Check(CheckType.FAST) public void checkBehaviorUnitEventType(SarlBehaviorUnit behaviorUnit) { final JvmTypeReference event = behaviorUnit.getName(); final LightweightTypeReference ref = toLightweightTypeReference(event); if (ref == null || ref.isInterfaceType() || !ref.isSubtypeOf(Event.class)) { error(MessageFormat.format( Messages.SARLValidator_75, event.getQualifiedName(), Messages.SARLValidator_62, this.grammarAccess.getOnKeyword()), event, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, TYPE_BOUNDS_MISMATCH); } } /** Check if a capacity has a feature defined inside. * * @param capacity - the capacity to test. */ @Check(CheckType.FAST) public void checkCapacityFeatures(SarlCapacity capacity) { if (capacity.getMembers().isEmpty()) { if (!isIgnored(DISCOURAGED_CAPACITY_DEFINITION)) { addIssue(Messages.SARLValidator_77, capacity, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, DISCOURAGED_CAPACITY_DEFINITION, capacity.getName(), "aFunction"); //$NON-NLS-1$ } } } /** Check for unused capacities. * * @param uses - the capacity use declaration. */ @SuppressWarnings("unchecked") @Check(CheckType.NORMAL) public void checkUnusedCapacities(SarlCapacityUses uses) { if (!isIgnored(UNUSED_AGENT_CAPACITY)) { final XtendTypeDeclaration container = uses.getDeclaringType(); final JvmDeclaredType jvmContainer = (JvmDeclaredType) this.associations.getPrimaryJvmElement(container); final Map<String, JvmOperation> importedFeatures = CollectionLiterals.newHashMap(); for (final JvmOperation operation : jvmContainer.getDeclaredOperations()) { if (Utils.isNameForHiddenCapacityImplementationCallingMethod(operation.getSimpleName())) { importedFeatures.put(operation.getSimpleName(), operation); } } int index = 0; for (final JvmTypeReference capacity : uses.getCapacities()) { final String fieldName = Utils.createNameForHiddenCapacityImplementationAttribute(capacity.getIdentifier()); final String operationName = Utils.createNameForHiddenCapacityImplementationCallingMethodFromFieldName(fieldName); final JvmOperation operation = importedFeatures.get(operationName); if (operation != null && !isLocallyUsed(operation, container)) { addIssue(MessageFormat.format( Messages.SARLValidator_78, capacity.getSimpleName()), uses, SARL_CAPACITY_USES__CAPACITIES, index, UNUSED_AGENT_CAPACITY, capacity.getSimpleName()); } ++index; } } } private static Set<String> doGetPreviousCapacities(SarlCapacityUses uses, Iterator<XtendMember> iterator) { boolean continueToFill = true; final Set<String> capacityUses = CollectionLiterals.newTreeSet((Comparator<String>) null); while (continueToFill && iterator.hasNext()) { final XtendMember elt = iterator.next(); if (elt instanceof SarlCapacityUses) { final SarlCapacityUses usesElt = (SarlCapacityUses) elt; if (usesElt == uses) { continueToFill = false; } else { for (final JvmTypeReference use : usesElt.getCapacities()) { capacityUses.add(use.getIdentifier()); } } } } return capacityUses; } /** Check for multiple capacity use declaration. * * @param uses - the capacity use declaration. */ @Check(CheckType.NORMAL) public void checkMultipleCapacityUses(SarlCapacityUses uses) { if (!isIgnored(REDUNDANT_CAPACITY_USE)) { final XtendTypeDeclaration declaringType = uses.getDeclaringType(); if (declaringType != null) { final Set<String> previousCapacityUses = doGetPreviousCapacities(uses, declaringType.getMembers().iterator()); int index = 0; for (final JvmTypeReference capacity : uses.getCapacities()) { if (previousCapacityUses.contains(capacity.getIdentifier())) { addIssue(MessageFormat.format( Messages.SARLValidator_79, capacity.getSimpleName()), uses, SARL_CAPACITY_USES__CAPACITIES, index, REDUNDANT_CAPACITY_USE, capacity.getSimpleName()); } else { previousCapacityUses.add(capacity.getIdentifier()); } ++index; } } } } /** Check for abstract methods. * * <p>Override the Xtend behavior for: <ul> * <li>not generating an error when a return type is missed. Indeed, the return type if "void" by default.</li> * <li>generating a warning when "abstract" is missed.</li> * </ul> * * <p>XXX: Update this function with the code of the derived function. */ @Check @Override @SuppressWarnings("checkstyle:cyclomaticcomplexity") public void checkAbstract(XtendFunction function) { final XtendTypeDeclaration declarator = function.getDeclaringType(); if (function.getExpression() == null || function.isAbstract()) { if (declarator instanceof XtendClass || declarator.isAnonymous() || declarator instanceof SarlAgent || declarator instanceof SarlBehavior || declarator instanceof SarlSkill) { if (function.isDispatch()) { error(MessageFormat.format( Messages.SARLValidator_80, function.getName(), this.localClassAwareTypeNames.getReadableName(declarator)), XTEND_FUNCTION__NAME, -1, DISPATCH_FUNCTIONS_MUST_NOT_BE_ABSTRACT); return; } if (function.getCreateExtensionInfo() != null) { error(MessageFormat.format( Messages.SARLValidator_81, function.getName(), this.localClassAwareTypeNames.getReadableName(declarator)), XTEND_FUNCTION__NAME, -1, CREATE_FUNCTIONS_MUST_NOT_BE_ABSTRACT); return; } if (declarator.isAnonymous()) { error(MessageFormat.format( Messages.SARLValidator_82, function.getName(), this.localClassAwareTypeNames.getReadableName(declarator)), XTEND_FUNCTION__NAME, -1, MISSING_ABSTRACT_IN_ANONYMOUS); } else { final boolean isAbstract; if (declarator instanceof XtendClass) { isAbstract = ((XtendClass) declarator).isAbstract(); } else if (declarator instanceof SarlAgent) { isAbstract = ((SarlAgent) declarator).isAbstract(); } else if (declarator instanceof SarlBehavior) { isAbstract = ((SarlBehavior) declarator).isAbstract(); } else if (declarator instanceof SarlSkill) { isAbstract = ((SarlSkill) declarator).isAbstract(); } else { return; } if (!isAbstract && !function.isNative()) { error(MessageFormat.format( Messages.SARLValidator_82, function.getName(), this.localClassAwareTypeNames.getReadableName(declarator)), XTEND_FUNCTION__NAME, -1, MISSING_ABSTRACT); return; } } if (!function.getModifiers().contains("abstract")) { //$NON-NLS-1$ warning(MessageFormat.format( Messages.SARLValidator_84, function.getName(), this.localClassAwareTypeNames.getReadableName(declarator)), XTEND_FUNCTION__NAME, -1, MISSING_ABSTRACT, function.getName(), this.localClassAwareTypeNames.getReadableName(declarator)); } } else if (declarator instanceof XtendInterface || declarator instanceof SarlCapacity) { if (function.getCreateExtensionInfo() != null) { error(MessageFormat.format( Messages.SARLValidator_85, function.getName()), XTEND_FUNCTION__NAME, -1, CREATE_FUNCTIONS_MUST_NOT_BE_ABSTRACT); return; } } } else if (declarator instanceof XtendInterface || declarator instanceof SarlCapacity) { if (!getGeneratorConfig(function).getJavaSourceVersion().isAtLeast(JAVA8)) { error(Messages.SARLValidator_86, XTEND_FUNCTION__NAME, -1, ABSTRACT_METHOD_WITH_BODY); return; } } } /** Check for reserved annotations. * * @param annotationTarget thee target to test. */ @Check public void checkReservedAnnotation(XtendAnnotationTarget annotationTarget) { if (!isIgnored(IssueCodes.USED_RESERVED_SARL_ANNOTATION)) { if (annotationTarget.getAnnotations().isEmpty() || !isRelevantAnnotationTarget(annotationTarget)) { return; } final QualifiedName reservedPackage = this.qualifiedNameConverter.toQualifiedName( EarlyExit.class.getPackage().getName()); final String earlyExitAnnotation = EarlyExit.class.getName(); for (final XAnnotation annotation : annotationTarget.getAnnotations()) { final JvmType type = annotation.getAnnotationType(); if (type != null && !type.eIsProxy()) { if (Objects.equal(type.getIdentifier(), earlyExitAnnotation)) { // Special case: EarlyExit is allowed on events for declaring early-exit events if (!(annotationTarget instanceof SarlEvent)) { addIssue( MessageFormat.format(Messages.SARLValidator_87, type.getSimpleName()), annotation, IssueCodes.USED_RESERVED_SARL_ANNOTATION); } } else { final QualifiedName annotationName = this.qualifiedNameConverter.toQualifiedName( type.getIdentifier()); if (annotationName.startsWith(reservedPackage)) { addIssue( MessageFormat.format(Messages.SARLValidator_87, type.getSimpleName()), annotation, IssueCodes.USED_RESERVED_SARL_ANNOTATION); } } } } } } /** Check for {@code @Inline} annotation usage. * * @param annotationTarget thee target to test. */ @Check public void checkManualInlineDefinition(XtendAnnotationTarget annotationTarget) { if (!isIgnored(IssueCodes.MANUAL_INLINE_DEFINITION)) { if (annotationTarget.getAnnotations().isEmpty() || !isRelevantAnnotationTarget(annotationTarget)) { return; } final String inlineAnnotation = Inline.class.getName(); for (final XAnnotation annotation : annotationTarget.getAnnotations()) { final JvmType type = annotation.getAnnotationType(); if (type != null && !type.eIsProxy()) { if (Objects.equal(type.getIdentifier(), inlineAnnotation)) { addIssue( Messages.SARLValidator_16, annotation, IssueCodes.MANUAL_INLINE_DEFINITION); } } } } } @SuppressWarnings({"checkstyle:cyclomaticcomplexity", "checkstyle:nestedifdepth"}) private void checkUnmodifiableEventAccess(boolean enable1, XFeatureCall child) { EObject previous = child; EObject elt = child.eContainer(); EObject container = null; while (elt != null && container == null) { final EClass type = elt.eClass(); if (XbasePackage.Literals.XASSIGNMENT.equals(type)) { final XAssignment assign = (XAssignment) elt; if (previous == assign.getActualReceiver()) { error(Messages.SARLValidator_2, child, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, IssueCodes.INVALID_OCCURRENCE_READONLY_USE); return; } container = elt; } else if (XbasePackage.Literals.XBINARY_OPERATION.equals(type) || XbasePackage.Literals.XUNARY_OPERATION.equals(type) || XbasePackage.Literals.XPOSTFIX_OPERATION.equals(type)) { if (enable1 && getExpressionHelper().hasSideEffects((XExpression) elt)) { addIssue(Messages.SARLValidator_11, elt, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, IssueCodes.DISCOURAGED_BOOLEAN_EXPRESSION); return; } container = elt; } else if (elt instanceof XAbstractFeatureCall) { final XAbstractFeatureCall featureCall = (XAbstractFeatureCall) elt; if (featureCall.getFeature() instanceof JvmOperation && featureCall instanceof XFeatureCall) { if (enable1) { final XFeatureCall xfeatureCall = (XFeatureCall) featureCall; final JvmOperation operation = (JvmOperation) featureCall.getFeature(); boolean stopCheck = false; int paramIndex = 0; for (final XExpression arg : xfeatureCall.getActualArguments()) { if (arg == previous) { final LightweightTypeReference paramType = getActualType(operation.getParameters().get(paramIndex)); if (!paramType.isPrimitive()) { addIssue( Messages.SARLValidator_12, arg, IssueCodes.DISCOURAGED_OCCURRENCE_READONLY_USE); stopCheck = true; } } ++paramIndex; } if (stopCheck) { return; } } container = elt; } else { if (enable1 && getExpressionHelper().hasSideEffects(featureCall)) { addIssue( Messages.SARLValidator_13, elt, IssueCodes.DISCOURAGED_OCCURRENCE_READONLY_USE); return; } previous = elt; elt = elt.eContainer(); } } else { container = elt; } } if (container instanceof XVariableDeclaration && previous instanceof XExpression) { final LightweightTypeReference variableType = getActualType((XExpression) previous); if (!variableType.isPrimitive()) { addIssue( Messages.SARLValidator_12, previous, IssueCodes.DISCOURAGED_OCCURRENCE_READONLY_USE); } } } /** Check for usage of the event functions in the behavior units. * * @param unit the unit to analyze. */ @Check(CheckType.EXPENSIVE) public void checkUnmodifiableEventAccess(SarlBehaviorUnit unit) { final boolean enable1 = !isIgnored(IssueCodes.DISCOURAGED_OCCURRENCE_READONLY_USE); final XExpression root = unit.getExpression(); for (final XFeatureCall child : EcoreUtil2.getAllContentsOfType(root, XFeatureCall.class)) { if (this.grammarAccess.getOccurrenceKeyword().equals(child.getFeature().getIdentifier())) { checkUnmodifiableEventAccess(enable1, child); } } } /** Check for usage of break inside loops. * * @param expression the expression to analyze. */ @Check public void checkBreakKeywordUse(SarlBreakExpression expression) { final EObject container = Utils.getFirstContainerNotOfType(expression, (it) -> !(it instanceof XExpression) || it instanceof XAbstractWhileExpression || it instanceof XBasicForLoopExpression || it instanceof XForLoopExpression); if (container instanceof XExpression) { if (!isIgnored(IssueCodes.DISCOURAGED_BREAK_KEYWORD_USE) && container instanceof XBasicForLoopExpression) { addIssue( Messages.SARLValidator_17, expression, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, IssueCodes.DISCOURAGED_BREAK_KEYWORD_USE); } } else { error(Messages.SARLValidator_18, expression, null, ValidationMessageAcceptor.INSIGNIFICANT_INDEX, IssueCodes.INVALID_USE_OF_BREAK); } } /** The modifier validator for constructors. * * @author $Author: sgalland$ * @version $FullVersion$ * @mavengroupid $GroupId$ * @mavenartifactid $ArtifactId$ */ protected final class SARLModifierValidator extends ModifierValidator { /** * @param modifiers - the list of the supported modifiers. */ private SARLModifierValidator( List<String> modifiers) { super(modifiers, SARLValidator.this); } /** Make this function visible for the enclosing class. * * @param member - the member to check. * @param memberName - the name of the member, usually for the issue message. */ @Override protected void checkModifiers(XtendMember member, String memberName) { super.checkModifiers(member, memberName); } } }