Skip to main content


Eclipse Community Forums
Forum Search:

Search      Help    Register    Login    Home
Home » Modeling » TMF (Xtext) » Quickfix NullPointerException in PartialSerializer
Quickfix NullPointerException in PartialSerializer [message #1779172] Wed, 03 January 2018 13:51 Go to next message
Sergio Otero is currently offline Sergio OteroFriend
Messages: 39
Registered: June 2012
Member
Hi

I'm having problems in Xtext 2.13 with quickfixes that i didn't have in 2.12, i suppose related to the changes applied to support multiple quickfix resolutions.

The problem appears when a node A is added to the AST tree and then another node is added as a child of A

java.lang.NullPointerException
	at org.eclipse.xtext.ide.serializer.impl.PartialSerializer$SerializeRecursiveStrategy.serialize(PartialSerializer.java:155)
	at org.eclipse.xtext.ide.serializer.impl.PartialSerializer.serializeChanges(PartialSerializer.java:280)
	at org.eclipse.xtext.ide.serializer.impl.RecordingXtextResourceUpdater.applyChange(RecordingXtextResourceUpdater.java:80)
	at org.eclipse.xtext.ide.serializer.impl.ChangeSerializer.endRecordChanges(ChangeSerializer.java:162)
	at org.eclipse.xtext.ide.serializer.impl.ChangeSerializer.applyModifications(ChangeSerializer.java:93)
	at org.eclipse.xtext.ui.editor.model.edit.BatchModification.applyInWorkspace(BatchModification.java:142)


The problem happens with a single quickfix, but it can be avoided somehow waiting to add the node A until it's fully set.
The problem is much more difficult to solve if several quickfixes are applied together and the business rules are complex

As an example, i've made a simple test case

grammar org.xtext.example.mydsl.MyDsl with org.eclipse.xtext.common.Terminals

generate myDsl "http://www.xtext.org/example/mydsl/MyDsl"

Model:
	classes+=Class* statements+=Statement+;
	
Class:
	'class' name=ID '{' vars+=Variable+ '}';
	
Variable: name=ID;
	
Statement: var=[Variable|VariableQN] '=' INT;	

VariableQN hidden(): ID '.' ID;


	@Fix(Diagnostic.LINKING_DIAGNOSTIC)
	def void fixMissingEntity(Issue issue, IssueResolutionAcceptor acceptor) {
		if (issue.message.contains("Variable")) {
			fixMissingVariable(issue, acceptor);
		}
	}

	private def fixMissingVariable(Issue issue, IssueResolutionAcceptor acceptor) {
		acceptor.acceptMulti(
			issue,
			"Create missing variable definition",
			"Create missing variable definition",
			"Entity.gif",
			[ Statement element, ICompositeModificationContext<Statement> context |
				val fullReferenceName = getIssueMissingReferenceName(context.issue)
				val nameArray = fullReferenceName.split("\\.")
				val className = nameArray.get(0)
				val fieldName = nameArray.get(1)
				
				val root = element.eContainer as Model
				
				context.addModification(root, [ctx |
					var cl = root.classes.findFirst[it.name == className]
					
					if (cl === null) {
						// The class didn't exist
						cl =  MyDslFactory::eINSTANCE.createClass() => [
							name = className
						];
						
						// before adding the class, we can add the field
						addVariable(cl, fieldName)
						
						ctx.classes.add(cl)

						// after adding the class, adding the field will cause NullPointerException in Serialization
						// addVariable(cl, fieldName)
					} else {
						// The class existed (maybe created in a previous multiquickfix)
						// NullPointerException if 2 fixs are applied together and the parent class was added in the first fix
						addVariable(cl, fieldName)
					}
				])
				
			]
		)
	}
	
	protected def boolean addVariable(Class cl, String fieldName) {
		val atr = MyDslFactory::eINSTANCE.createVariable() => [
			name = fieldName
		];
		cl.vars.add(atr)
	}
	
	private def String getIssueMissingReferenceName(Issue issue) {
		// Not the best way ...
		
		val issueMessage = issue.message
		val j = issueMessage.lastIndexOf("'")
		val i = issueMessage.lastIndexOf("'", j - 1)
		
		issueMessage.substring(i+1, j)
	}


Example code:

// aplying the fix to this 2 problems together works because they create their own classes
Class1.test1 = 1 
Class2.test2 = 1

// aplying the fix to this 2 problems together don't work because the first adds the class plus field and the second adds another field to the new class
Class3.test3 = 1 
Class3.test4 = 1
Re: Quickfix NullPointerException in PartialSerializer [message #1779192 is a reply to message #1779172] Wed, 03 January 2018 17:19 Go to previous messageGo to next message
Christian Dietrich is currently offline Christian DietrichFriend
Messages: 14661
Registered: July 2009
Senior Member
did you opt in to used the new serializer? seems so since you use the multi quickfix.
can you please create a issue at github.com/eclipse/xtext-core


Twitter : @chrdietrich
Blog : https://www.dietrich-it.de
Re: Quickfix NullPointerException in PartialSerializer [message #1779226 is a reply to message #1779192] Thu, 04 January 2018 08:06 Go to previous message
Sergio Otero is currently offline Sergio OteroFriend
Messages: 39
Registered: June 2012
Member
https://github.com/eclipse/xtext-core/issues/597

I have a grammar used with legacy code and clients always send code with missing parts, so it's very useful to be able to apply massive quickfixes.
I kind of implemented a menu option to apply single quickfixes one after another project-wide (only aplied to a few specific custom quickfixes) and it worked pretty well in 2.12
After changing to 2.13, the output of the multiple single-quickfixes was corrupted, so i changed it to multiquickfixes and then i faced with the nullPointers

As a side question, if a quickfix has to modify a referenced resource (for example, add a field to a class in other source file), can it be done as if it were in the same source file or it needs additional steps?

Thanks
Previous Topic:TypeReference for nested annotations class
Next Topic:How to parse a normal text file for unit test
Goto Forum:
  


Current Time: Fri Mar 29 09:32:38 GMT 2024

Powered by FUDForum. Page generated in 0.04697 seconds
.:: Contact :: Home ::.

Powered by: FUDforum 3.0.2.
Copyright ©2001-2010 FUDforum Bulletin Board Software

Back to the top