001/* 002 * Copyright (c) 2012-2018 Institut National des Sciences Appliquées de Lyon (INSA Lyon) and others 003 * 004 * This program and the accompanying materials are made available under the 005 * terms of the Eclipse Public License 2.0 which is available at 006 * http://www.eclipse.org/legal/epl-2.0. 007 * 008 * SPDX-License-Identifier: EPL-2.0 009 */ 010 011package org.eclipse.golo.compiler; 012 013import org.eclipse.golo.compiler.parser.GoloASTNode; 014import org.eclipse.golo.compiler.parser.ParseException; 015import gololang.ir.GoloElement; 016 017import java.nio.charset.UnsupportedCharsetException; 018import java.util.LinkedList; 019import java.util.List; 020 021import static java.util.Collections.unmodifiableList; 022import static gololang.Messages.message; 023 024/** 025 * A Golo compilation exception that may also report a cause and several identified problems. 026 */ 027public class GoloCompilationException extends RuntimeException { 028 029 /** 030 * A problem reported either while compiling the source code or processing the intermediate representation. 031 */ 032 public static final class Problem { 033 034 /** 035 * The possible problem types. 036 */ 037 public enum Type { 038 PARSING, 039 AUGMENT_FUNCTION_NO_ARGS, 040 UNDECLARED_REFERENCE, 041 ASSIGN_CONSTANT, 042 BREAK_OR_CONTINUE_OUTSIDE_LOOP, 043 REFERENCE_ALREADY_DECLARED_IN_BLOCK, 044 UNINITIALIZED_REFERENCE_ACCESS, 045 INVALID_ENCODING, 046 INCOMPLETE_NAMED_ARGUMENTS_USAGE, 047 AMBIGUOUS_DECLARATION 048 } 049 050 private final Type type; 051 private final PositionInSourceCode position; 052 private final String description; 053 private final Object source; 054 private final Throwable cause; 055 056 /** 057 * Constructs a new problem to report. 058 * 059 * @param type the problem type. 060 * @param source the problem source, which may be of any meaningful type. 061 * @param description the problem description in a human-readable form. 062 */ 063 private Problem(Type type, PositionInSourceCode position, String description, Object source, Throwable cause) { 064 this.type = type; 065 this.position = position; 066 this.description = description; 067 this.source = source; 068 this.cause = cause; 069 } 070 071 /** 072 * @return the problem type. 073 */ 074 public Type getType() { 075 return type; 076 } 077 078 /** 079 * @return the problem description. 080 */ 081 public String getDescription() { 082 return description; 083 } 084 085 /** 086 * @return the position in the source code. 087 */ 088 public PositionInSourceCode getPositionInSourceCode() { 089 return position; 090 } 091 092 /** 093 * @return the source of the problem. 094 */ 095 public Object getSource() { 096 return source; 097 } 098 099 /** 100 * @return the cause exception 101 */ 102 public Throwable getCause() { 103 return cause; 104 } 105 106 @Override 107 public String toString() { 108 return String.format("Problem{type=%s, description='%s', position=%s}", type, description, position); 109 } 110 } 111 112 /** 113 * An exception builder object allows preparing an exception by progressively adding problems. 114 */ 115 public static class Builder { 116 117 private final GoloCompilationException exception; 118 119 /** 120 * Makes a builder to report problems in a source file. 121 * 122 * @param goloSourceFilename the source file name. 123 */ 124 public Builder(String goloSourceFilename) { 125 exception = new GoloCompilationException(message("in_module", goloSourceFilename)); 126 exception.setSourceCode(goloSourceFilename); 127 } 128 129 /** 130 * Report a problem to the exception being built. 131 * 132 * @param type the problem type. 133 * @param source the problem source. 134 * @param description the problem description. 135 * @return the same builder object. 136 */ 137 public Builder report(Problem.Type type, GoloASTNode source, String description) { 138 exception.report(new Problem(type, 139 source != null ? source.getPositionInSourceCode() : null, 140 description, 141 source, null)); 142 return this; 143 } 144 145 /** 146 * Report a problem to the exception being built. 147 * 148 * @param type the problem type. 149 * @param source the problem source. 150 * @param description the problem description. 151 * @return the same builder object. 152 */ 153 public Builder report(Problem.Type type, GoloElement<?> source, String description) { 154 exception.report(new Problem(type, 155 source != null ? source.positionInSourceCode() : null, 156 description, 157 source, null)); 158 return this; 159 } 160 161 /** 162 * Report a problem to the exception being built. 163 * 164 * @param type the problem type. 165 * @param source the problem source. 166 * @param description the problem description. 167 * @param cause the exception that caused the problem 168 * @return the same builder object. 169 */ 170 public Builder report(Problem.Type type, GoloElement<?> source, String description, Throwable cause) { 171 exception.report(new Problem(type, 172 source != null ? source.positionInSourceCode() : null, 173 description, 174 source, cause)); 175 return this; 176 } 177 178 179 180 /** 181 * Report a parsing error problem to the exception being built. 182 * 183 * @param pe the caught {@code ParseException}. 184 * @param source the node of the {@code ParseException}. 185 * @return the same builder object. 186 */ 187 public Builder report(ParseException pe, GoloASTNode source) { 188 exception.report(new Problem(Problem.Type.PARSING, 189 PositionInSourceCode.of(pe.currentToken.beginLine, pe.currentToken.beginColumn, pe.currentToken.endLine, pe.currentToken.endColumn), 190 pe.getMessage(), 191 source, pe)); 192 return this; 193 } 194 195 /** 196 * Report an encoding error problem to the exception being built. 197 * 198 * @param uce the caught {@code UnsupportedCharsetException}. 199 * @return the same builder object. 200 */ 201 public Builder report(UnsupportedCharsetException uce) { 202 exception.report(new Problem(Problem.Type.INVALID_ENCODING, null, uce.getMessage(), null, uce)); 203 return this; 204 } 205 206 /** 207 * Stops adding problems and throws the exception, 208 * 209 * @throws GoloCompilationException everytime. 210 */ 211 public void doThrow() throws GoloCompilationException { 212 throw exception; 213 } 214 215 public List<Problem> getProblems() { 216 return exception.getProblems(); 217 } 218 } 219 220 private final List<Problem> problems = new LinkedList<>(); 221 222 private String sourceCode; 223 224 /** 225 * @return all reported problems. 226 */ 227 public List<Problem> getProblems() { 228 return unmodifiableList(problems); 229 } 230 231 private void report(Problem problem) { 232 problems.add(problem); 233 } 234 235 private GoloCompilationException() { 236 super(); 237 } 238 239 /** 240 * Gives the problematic source code, if specified. 241 * 242 * @return the source code, or {@code null} if none has been specified. 243 */ 244 public String getSourceCode() { 245 return sourceCode; 246 } 247 248 /** 249 * Specifies the problematic source code. 250 * 251 * @param sourceCode the raw source code. 252 */ 253 public void setSourceCode(String sourceCode) { 254 this.sourceCode = sourceCode; 255 } 256 257 /** 258 * Makes a new compiler exception with a message. 259 * 260 * @param message the message. 261 */ 262 public GoloCompilationException(String message) { 263 super(message); 264 } 265 266 /** 267 * Makes a new compiler exception from a root cause. 268 * 269 * @param throwable the cause. 270 */ 271 public GoloCompilationException(Throwable throwable) { 272 super(throwable); 273 } 274 275 /** 276 * Makes a new exception from a message and a root cause. 277 * 278 * @param message the message. 279 * @param cause the cause. 280 */ 281 public GoloCompilationException(String message, Throwable cause) { 282 super(message, cause); 283 } 284}