/******************************************************************************* * Copyright (c) 2016 Bruno Medeiros and other Contributors. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Bruno Medeiros - initial API and implementation *******************************************************************************/ package com.github.rustdt.tooling; import static melnorme.utilbox.core.Assert.AssertNamespace.assertFail; import static melnorme.utilbox.core.Assert.AssertNamespace.assertTrue; import static melnorme.utilbox.status.Severity.ERROR; import static melnorme.utilbox.status.Severity.INFO; import java.io.StringReader; import org.junit.Test; import melnorme.lang.tooling.common.SourceLineColumnRange; import melnorme.lang.tooling.common.ToolSourceMessage; import melnorme.utilbox.collections.ArrayList2; import melnorme.utilbox.collections.Indexable; import melnorme.utilbox.core.CommonException; public class RustMessageParserTest extends CommonRustMessageParserTest { // This is "<std macros>" in original JSON, // but we convert to "", due to a minor API limitation public static final String STD_MACROS_PATH = ""; public static Indexable<ToolSourceMessage> DONT_CHECK_ToolMessages = null; public static final String CANNOT_BORROW_MUT_MORE_THAN = "cannot borrow `xpto` as mutable more than once at a time"; public static final SourceLineColumnRange PARENT_MSG_RANGE = range(1, 1, 1, 1); // Maybe should be -1 ? public static final RustMainMessage MSG_Simple = new RustMainMessage( new ToolSourceMessage(path(""), PARENT_MSG_RANGE, ERROR, "unresolved name `xpto`"), "E0425", null, list( new RustSubMessage(msg("src/main.rs", 8, 19, 8, 23, ERROR, "unresolved name")) ) ); public static final RustMainMessage MSG_CannotReborrow = new RustMainMessage( new ToolSourceMessage(path(""), PARENT_MSG_RANGE, ERROR, CANNOT_BORROW_MUT_MORE_THAN), "E0499", null, list( new RustSubMessage(msg("src/main.rs", 6,19,6,23, INFO, "first mutable borrow occurs here"), false), new RustSubMessage(msg("src/main.rs", 8,19,8,23, ERROR, "second mutable borrow occurs here"), true), new RustSubMessage(msg("src/main.rs", 14,1,14,2, INFO, "first borrow ends here"), false) ) ); @Test public void test() throws Exception { test$(); } public void test$() throws Exception { // Test empty testParseMessage( "", null, list() ); // Test simple message 2 testParseMessage( getClassResource("rustc_error_simple.json"), MSG_Simple, list( msg(path("src/main.rs"), 8, 19, 8, 23, ERROR, "unresolved name `xpto`"+ " [E0425]:"+ subm("unresolved name")) ) ); // Test message with notes String MSG_WithNotes_Main = "expected &MyTrait, found integral variable"; String MSG_WithNotes_Top = "mismatched types"; String MSG_MISMATCHED_A = MSG_WithNotes_Top+ " [E0308]:"+ subm("expected type `&MyTrait`")+ subm(" found type `{integer}`")+ subm(MSG_WithNotes_Main); testParseMessage( getClassResource("rustc_error_with_notes.json"), new RustMainMessage( new ToolSourceMessage(path(""), PARENT_MSG_RANGE, ERROR, MSG_WithNotes_Top), "E0308", list( "expected type `&MyTrait`", " found type `{integer}`" ), list( new RustSubMessage(msg("src/main.rs", 14, 9, 14, 12, ERROR, MSG_WithNotes_Main)) ) ), list(msg(path("src/main.rs"), 14, 9, 14, 12, ERROR, MSG_MISMATCHED_A)) ); // Test message with notes (span.label = null) String MSG_Main = "invalid fragment specifier `id`"; String MSG_Notes = "valid fragment specifiers are..."; testParseMessage( getClassResource("rustc_error_with_notes2.json"), new RustMainMessage( new ToolSourceMessage(path(""), PARENT_MSG_RANGE, ERROR, MSG_Main), "", list(MSG_Notes), list(new RustSubMessage(msg("src/macro_tests.rs", 55,6,55,11, ERROR, ""))) ), list( msg(path("src/macro_tests.rs"), 55,6,55,11, ERROR, MSG_Main + ":\n" + MSG_Notes) ) ); // test composite testParseMessage( getClassResource("rustc_error_composite.json"), MSG_CannotReborrow, list( msg(path("src/main.rs"), 6, 19, 6, 23, INFO, "first mutable borrow occurs here"), msg(path("src/main.rs"), 8, 19, 8, 23, ERROR, CANNOT_BORROW_MUT_MORE_THAN + " [E0499]:" + subm("second mutable borrow occurs here") ), msg(path("src/main.rs"), 14, 1, 14, 2, INFO, "first borrow ends here") ) ); // Test macros String spanMessage = "expected bool, found integral variable"; String MSG_MISMATCHED_B = MSG_WithNotes_Top+ " [E0308]:"+ subm("expected type `bool`")+ subm(" found type `{integer}`"); testParseMessage( getClassResource("rustc_error_macro.json"), new RustMainMessage( new ToolSourceMessage(path(""), PARENT_MSG_RANGE, ERROR, MSG_WithNotes_Top), "E0308", list( "expected type `bool`", " found type `{integer}`" ), list(new RustSubMessage( msg("", 5,22,5,33, ERROR, spanMessage), true, new RustSubMessage(msg("src/main.rs", 15,5,15,26, ERROR, "")), new RustSubMessage(msg("", 1,1,18,71, ERROR, "")) )) ), list( msg(path(""), 5,22,5,33, ERROR, MSG_MISMATCHED_B+subm(spanMessage)), msg(path("src/main.rs"), 15,5,15,26, INFO, MSG_MISMATCHED_B) // msg(path(""), 1,1,18,71, ERROR, MSG_MISMATCHED_B) ) ); // test and def_site_span = null, and code = null // Obtained with Rust code: /* println!("{}"); */ testParseMessage( getClassResource("rustc_error_macro_NullDefSiteSpan.json"), new RustMainMessage( new ToolSourceMessage(path(""), PARENT_MSG_RANGE, ERROR, "invalid reference to argument `0` (no arguments given)"), "", null, list(new RustSubMessage( msg(STD_MACROS_PATH, 1,33,1,58, ERROR, ""), true, new RustSubMessage( msg(STD_MACROS_PATH, 1,33,1,58, ERROR, ""), true, new RustSubMessage( msg(STD_MACROS_PATH, 2,27,2,58, ERROR, ""), true, new RustSubMessage( msg(STD_MACROS_PATH, 1,23,1,60, ERROR, ""), true, new RustSubMessage(msg("src/macro_tests.rs", 41,2,41,17, ERROR, "")), new RustSubMessage(msg(STD_MACROS_PATH, 1,1,3,58, ERROR, "")) ), new RustSubMessage(msg(STD_MACROS_PATH, 1,1,2,64, ERROR, "")) ), null ), null )) ), DONT_CHECK_ToolMessages ); } public String subm(String subMessage) { return "\n"+subMessage; } public void testParseMessage( String messageJson, RustMessage expected, Indexable<ToolSourceMessage> expectedSourceMessages ) throws CommonException { RustJsonMessageParser msgParser = new RustJsonMessageParser(); ArrayList2<RustMainMessage> rustMessages = msgParser.parseStructuredMessages(new StringReader(messageJson)); if(expected == null) { assertTrue(rustMessages.isEmpty()); return; } RustMainMessage message = unwrapSingle(rustMessages); checkEquals(message, expected); if(expectedSourceMessages != DONT_CHECK_ToolMessages) { assertEqualIndexable(message.retrieveToolMessages(), expectedSourceMessages); } } public void checkEquals(RustMessage message, RustMessage expected) { if(message == expected) { return; } if(!expected.equals(message)) { // Helper for the interactive debugger: assertAreEqual(message.sourceMessage, expected.sourceMessage); assertAreEqual(message.notes, expected.notes); checkIndexable(message.spans, expected.spans, this::checkEquals); if(message instanceof RustSubMessage && expected instanceof RustSubMessage) { checkSubMessage((RustSubMessage) message, (RustSubMessage) expected); } assertFail(); } } public void checkSubMessage(RustSubMessage message, RustSubMessage expected) { checkEquals(message.defSiteMsg, expected.defSiteMsg); checkEquals(message.expansionMsg, expected.expansionMsg); } }