001/* 002 * Copyright (c) 2012-2021 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 UNKNOWN_MACRO, 049 MACRO_EXPANSION 050 } 051 052 private final Type type; 053 private final PositionInSourceCode position; 054 private final String description; 055 private final Object source; 056 private final Throwable cause; 057 058 /** 059 * Constructs a new problem to report. 060 * 061 * @param type the problem type. 062 * @param source the problem source, which may be of any meaningful type. 063 * @param description the problem description in a human-readable form. 064 */ 065 private Problem(Type type, PositionInSourceCode position, String description, Object source, Throwable cause) { 066 this.type = type; 067 this.position = position; 068 this.description = description; 069 this.source = source; 070 this.cause = cause; 071 } 072 073 /** 074 * @return the problem type. 075 */ 076 public Type getType() { 077 return type; 078 } 079 080 /** 081 * @return the problem description. 082 */ 083 public String getDescription() { 084 return description; 085 } 086 087 /** 088 * @return the position in the source code. 089 */ 090 public PositionInSourceCode getPositionInSourceCode() { 091 return position; 092 } 093 094 /** 095 * @return the source of the problem. 096 */ 097 public Object getSource() { 098 return source; 099 } 100 101 /** 102 * @return the cause exception 103 */ 104 public Throwable getCause() { 105 return cause; 106 } 107 108 @Override 109 public String toString() { 110 return String.format("Problem{type=%s, description='%s', position=%s}", type, description, position); 111 } 112 } 113 114 /** 115 * An exception builder object allows preparing an exception by progressively adding problems. 116 */ 117 public static class Builder { 118 119 private final GoloCompilationException exception; 120 121 /** 122 * Makes a builder to report problems in a source file. 123 * 124 * @param goloSourceFilename the source file name. 125 */ 126 public Builder(String goloSourceFilename) { 127 exception = new GoloCompilationException(message("in_module", goloSourceFilename)); 128 exception.setSourceCode(goloSourceFilename); 129 } 130 131 /** 132 * Report a problem to the exception being built. 133 * 134 * @param type the problem type. 135 * @param source the problem source. 136 * @param description the problem description. 137 * @return the same builder object. 138 */ 139 public Builder report(Problem.Type type, GoloASTNode source, String description) { 140 exception.report(new Problem(type, 141 source != null ? source.getPositionInSourceCode() : null, 142 description, 143 source, null)); 144 return this; 145 } 146 147 /** 148 * Report a problem to the exception being built. 149 * 150 * @param type the problem type. 151 * @param source the problem source. 152 * @param description the problem description. 153 * @return the same builder object. 154 */ 155 public Builder report(Problem.Type type, GoloElement<?> source, String description) { 156 exception.report(new Problem(type, 157 source != null ? source.positionInSourceCode() : null, 158 description, 159 source, null)); 160 return this; 161 } 162 163 /** 164 * Report a problem to the exception being built. 165 * 166 * @param type the problem type. 167 * @param source the problem source. 168 * @param description the problem description. 169 * @param cause the exception that caused the problem 170 * @return the same builder object. 171 */ 172 public Builder report(Problem.Type type, GoloElement<?> source, String description, Throwable cause) { 173 exception.report(new Problem(type, 174 source != null ? source.positionInSourceCode() : null, 175 description, 176 source, cause)); 177 return this; 178 } 179 180 181 182 /** 183 * Report a parsing error problem to the exception being built. 184 * 185 * @param pe the caught {@code ParseException}. 186 * @param source the node of the {@code ParseException}. 187 * @return the same builder object. 188 */ 189 public Builder report(ParseException pe, GoloASTNode source) { 190 exception.report(new Problem(Problem.Type.PARSING, 191 PositionInSourceCode.of(pe.currentToken.beginLine, pe.currentToken.beginColumn, pe.currentToken.endLine, pe.currentToken.endColumn), 192 pe.getMessage(), 193 source, pe)); 194 return this; 195 } 196 197 /** 198 * Report an encoding error problem to the exception being built. 199 * 200 * @param uce the caught {@code UnsupportedCharsetException}. 201 * @return the same builder object. 202 */ 203 public Builder report(UnsupportedCharsetException uce) { 204 exception.report(new Problem(Problem.Type.INVALID_ENCODING, null, uce.getMessage(), null, uce)); 205 return this; 206 } 207 208 /** 209 * Stops adding problems and throws the exception, 210 * 211 * @throws GoloCompilationException everytime. 212 */ 213 public void doThrow() throws GoloCompilationException { 214 throw exception; 215 } 216 217 public List<Problem> getProblems() { 218 return exception.getProblems(); 219 } 220 } 221 222 private final List<Problem> problems = new LinkedList<>(); 223 224 private String sourceCode; 225 226 /** 227 * @return all reported problems. 228 */ 229 public List<Problem> getProblems() { 230 return unmodifiableList(problems); 231 } 232 233 private void report(Problem problem) { 234 problems.add(problem); 235 } 236 237 private GoloCompilationException() { 238 super(); 239 } 240 241 /** 242 * Gives the problematic source code, if specified. 243 * 244 * @return the source code, or {@code null} if none has been specified. 245 */ 246 public String getSourceCode() { 247 return sourceCode; 248 } 249 250 /** 251 * Specifies the problematic source code. 252 * 253 * @param sourceCode the raw source code. 254 */ 255 public void setSourceCode(String sourceCode) { 256 this.sourceCode = sourceCode; 257 } 258 259 /** 260 * Makes a new compiler exception with a message. 261 * 262 * @param message the message. 263 */ 264 public GoloCompilationException(String message) { 265 super(message); 266 } 267 268 /** 269 * Makes a new compiler exception from a root cause. 270 * 271 * @param throwable the cause. 272 */ 273 public GoloCompilationException(Throwable throwable) { 274 super(throwable); 275 } 276 277 /** 278 * Makes a new exception from a message and a root cause. 279 * 280 * @param message the message. 281 * @param cause the cause. 282 */ 283 public GoloCompilationException(String message, Throwable cause) { 284 super(message, cause); 285 } 286}