Validation

Static analysis or validation is one of the most interesting aspects when developing a programming language. The users of your languages will be grateful if they get informative feedback as they type. In Xtext there are basically three different kinds of validation.

Syntactical Validation

The syntactical correctness of any textual input is validated automatically by the parser. The error messages are generated by the underlying parser technology and cannot be customized using a general hook. Any syntax errors can be retrieved from the Resource using the common EMF API:

Crosslink Validation

Any broken crosslinks can be checked generically. As crosslink resolution is done lazily (see linking), any broken links are resolved lazily as well. If you want to validate whether all links are valid, you will have to navigate through the model so that all installed EMF proxies get resolved. This is done automatically in the editor.

Any unresolvable crosslinks will be reported and can be obtained through:

Custom Validation

In addition to the afore mentioned kinds of validation, which are more or less done automatically, you can specify additional constraints specific for your Ecore model. We leverage existing EMF API (mainly EValidator) and have put some convenience stuff on top. Basically all you need to do is to make sure that an EValidator is registered for your EPackage. The registry for EValidators ( EValidator.Registry.INSTANCE) can only be filled programmatically. That means contrary to the EPackage and Resource.Factory registries there is no Equinox extension point to populate the validator registry.

For Xtext we provide a generator fragment for the convenient Java-based EValidator API. Just add the following fragment to your generator configuration and you are good to go:

<fragment class=
  "org.eclipse.xtext.generator.validation.JavaValidatorFragment"/>

The generator will provide you with two Java classes. An abstract class generated to src-gen/ which extends the library class AbstractDeclarativeValidator. This one just registers the EPackages for which this validator introduces constraints. The other class is a subclass of that abstract class and is generated to the src/ folder in order to be edited by you. That’s where you put the constraints in.

The purpose of the AbstractDeclarativeValidator is to allow you to write constraints in a declarative way – as the class name already suggests. That is instead of writing exhaustive if-else constructs or extending the generated EMF switch you just have to add the @Check annotation to any method and it will be invoked automatically when validation takes place. Moreover you can state for what type the respective constraint method is, just by declaring a typed parameter. This also lets you avoid any type casts. In addition to the reflective invocation of validation methods the AbstractDeclarativeValidator provides a couple of convenient assertions.

All in all this is very similar to how JUnit works. Here is an example:


public class DomainmodelJavaValidator 
  extends AbstractDomainmodelJavaValidator {
    
  @Check
  public void checkTypeNameStartsWithCapital(Type type) {
    if (!Character.isUpperCase(type.getName().charAt(0)))
      warning("Name should start with a capital", 
       DomainmodelPackage.TYPE__NAME);
  }
}

Validation with the Check language

In addition to the Java-based validation code you can use the language Check (from M2T/Xpand) to implement constraint checks against your model. To do so, you have to configure the generator with the CheckFragment. Please note, that you can combine both types of validation in your project.

<fragment class=
  "org.eclipse.xtext.generator.validation.CheckFragment"/>
  

After regenerating your language artifacts you will find three new files “YourLanguageChecks.chk”, “YourLanguageFastChecks.chk” and “YourLanguageExpensiveChecks.chk” in the src/ folder in the sub-package validation. The checks in these files will be executed when saving a file, while typing (FastChecks) or when triggering the validation explicitly (ExpensiveChecks). When using Check the example of the previous chapter could be written like this.

context Type#name WARNING "Name should start with a capital":
  name.toFirstUpper() == name;  

Each check works in a specific context (here: Type) and can further denote a feature to which a warning or error should be attached to (here: name). Each check could either be a WARNING or an ERROR with a given string to explain the situation. The essential part of each check is an invariant that must hold true for the given context. If it fails the check will produce an issue with the provided explanation.

Please read more about the Check language as well as the underlying expression language in Xpand’s reference documentation which is shipped as Eclipse help.