001/* 002 * Copyright (c) 2012-2017 Institut National des Sciences Appliquées de Lyon (INSA-Lyon) 003 * 004 * All rights reserved. This program and the accompanying materials 005 * are made available under the terms of the Eclipse Public License v1.0 006 * which accompanies this distribution, and is available at 007 * http://www.eclipse.org/legal/epl-v10.html 008 */ 009 010package org.eclipse.golo.compiler; 011 012import org.eclipse.golo.compiler.parser.GoloASTNode; 013import org.eclipse.golo.compiler.parser.ParseException; 014import org.eclipse.golo.compiler.parser.Token; 015 016import java.nio.charset.UnsupportedCharsetException; 017import java.util.LinkedList; 018import java.util.List; 019 020import static java.util.Collections.unmodifiableList; 021import static gololang.Messages.message; 022 023/** 024 * A Golo compilation exception that may also report a cause and several identified problems. 025 */ 026public class GoloCompilationException extends RuntimeException { 027 028 /** 029 * A problem reported either while compiling the source code or processing the intermediate representation. 030 */ 031 public static class Problem { 032 033 /** 034 * The possible problem types. 035 */ 036 public static enum Type { 037 PARSING, 038 AUGMENT_FUNCTION_NO_ARGS, 039 UNDECLARED_REFERENCE, 040 ASSIGN_CONSTANT, 041 BREAK_OR_CONTINUE_OUTSIDE_LOOP, 042 REFERENCE_ALREADY_DECLARED_IN_BLOCK, 043 UNINITIALIZED_REFERENCE_ACCESS, 044 INVALID_ENCODING, 045 INCOMPLETE_NAMED_ARGUMENTS_USAGE, 046 AMBIGUOUS_DECLARATION 047 } 048 049 private final Type type; 050 private final GoloASTNode source; 051 private final Token firstToken; 052 private final Token lastToken; 053 054 private final String description; 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 public Problem(Type type, GoloASTNode source, String description) { 064 this.type = type; 065 this.description = description; 066 this.source = source; 067 if (source != null) { 068 this.firstToken = source.jjtGetFirstToken(); 069 this.lastToken = source.jjtGetLastToken(); 070 } else { 071 this.firstToken = null; 072 this.lastToken = null; 073 } 074 } 075 076 /** 077 * Constructs a new problem to report. 078 * 079 * @param type the problem type. 080 * @param source the problem source, which may be of any meaningful type. 081 * @param token the precise source token, where the problem is located. 082 * @param description the problem description in a human-readable form. 083 */ 084 public Problem(Type type, GoloASTNode source, Token token, String description) { 085 this.type = type; 086 this.source = source; 087 this.firstToken = token; 088 this.lastToken = token; 089 this.description = description; 090 } 091 092 public Problem(ParseException pe, GoloASTNode source) { 093 this.type = Type.PARSING; 094 this.source = source; 095 this.firstToken = pe.currentToken; 096 this.lastToken = pe.currentToken; 097 this.description = pe.getMessage(); 098 } 099 100 public Problem(UnsupportedCharsetException uce) { 101 this.type = Type.INVALID_ENCODING; 102 this.source = null; 103 this.firstToken = null; 104 this.lastToken = null; 105 this.description = uce.getMessage(); 106 } 107 108 /** 109 * @return the problem type. 110 */ 111 public Type getType() { 112 return type; 113 } 114 115 /** 116 * @return the problem source. 117 */ 118 public GoloASTNode getSource() { 119 return source; 120 } 121 122 /** 123 * @return the problem detailed start token in source. 124 */ 125 public Token getFirstToken() { 126 return firstToken; 127 } 128 129 /** 130 * @return the problem detailed end token in source. 131 */ 132 public Token getLastToken() { 133 return lastToken; 134 } 135 136 /** 137 * @return the problem description. 138 */ 139 public String getDescription() { 140 return description; 141 } 142 143 @Override 144 public String toString() { 145 return "Problem{" + 146 "type=" + type + 147 ", source=" + source + 148 ", description='" + description + '\'' + 149 '}'; 150 } 151 } 152 153 /** 154 * An exception builder object allows preparing an exception by progressively adding problems. 155 */ 156 public static class Builder { 157 158 private final GoloCompilationException exception; 159 160 /** 161 * Makes a builder to report problems in a source file. 162 * 163 * @param goloSourceFilename the source file name. 164 */ 165 public Builder(String goloSourceFilename) { 166 exception = new GoloCompilationException(message("in_module", goloSourceFilename)); 167 exception.setSourceCode(goloSourceFilename); 168 } 169 170 /** 171 * Report a problem to the exception being built. 172 * 173 * @param type the problem type. 174 * @param source the problem source. 175 * @param description the problem description. 176 * @return the same builder object. 177 */ 178 public Builder report(Problem.Type type, GoloASTNode source, String description) { 179 exception.report(new Problem(type, source, description)); 180 return this; 181 } 182 183 /** 184 * Report a parsing error problem to the exception being built. 185 * 186 * @param pe the caught {@code ParseException}. 187 * @param source the node of the {@code ParseException}. 188 * @return the same builder object. 189 */ 190 public Builder report(ParseException pe, GoloASTNode source) { 191 exception.report(new Problem(pe, source)); 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(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}