Skip to content

Commit c98cb75

Browse files
committed
Remove context from RubyWarnings
1 parent 6026ead commit c98cb75

File tree

11 files changed

+277
-59
lines changed

11 files changed

+277
-59
lines changed

src/main/java/org/truffleruby/aot/ParserCache.java

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,12 @@
1212
import java.io.IOException;
1313
import java.util.HashMap;
1414
import java.util.Map;
15+
import java.util.stream.Collectors;
1516

1617
import com.oracle.truffle.api.CompilerDirectives;
1718
import org.truffleruby.core.CoreLibrary;
1819
import org.truffleruby.language.loader.ResourceLoader;
20+
import org.truffleruby.parser.RubyDeferredWarnings;
1921
import org.truffleruby.parser.RubySource;
2022
import org.truffleruby.parser.TranslatorDriver;
2123
import org.truffleruby.parser.ast.RootParseNode;
@@ -59,8 +61,15 @@ private static RubySource loadSource(String feature) {
5961
private static RootParseNode parse(RubySource source) {
6062
final StaticScope staticScope = new StaticScope(StaticScope.Type.LOCAL, null);
6163
final ParserConfiguration parserConfiguration = new ParserConfiguration(null, false, true, false);
62-
63-
return TranslatorDriver.parseToJRubyAST(null, source, staticScope, parserConfiguration);
64+
RubyDeferredWarnings rubyWarnings = new RubyDeferredWarnings();
65+
RootParseNode rootParseNode = TranslatorDriver
66+
.parseToJRubyAST(null, source, staticScope, parserConfiguration, rubyWarnings);
67+
if (!rubyWarnings.warnings.isEmpty()) {
68+
throw new RuntimeException("Core files should not emit warnings: " + String.join(
69+
", ",
70+
rubyWarnings.warnings.stream().map(w -> w.message).collect(Collectors.toList())));
71+
}
72+
return rootParseNode;
6473
}
6574

6675
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
/*
2+
* Copyright (c) 2018, 2019 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 2.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
package org.truffleruby.core.regexp;
11+
12+
import org.joni.WarnCallback;
13+
import org.truffleruby.parser.RubyDeferredWarnings;
14+
15+
public class RegexWarnDeferredCallback implements WarnCallback {
16+
17+
private final RubyDeferredWarnings warnings;
18+
19+
public RegexWarnDeferredCallback(RubyDeferredWarnings warnings) {
20+
this.warnings = warnings;
21+
}
22+
23+
@Override
24+
public void warn(String message) {
25+
warnings.warn(message);
26+
}
27+
28+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Copyright (c) 2021, 2021 Oracle and/or its affiliates. All rights reserved. This
3+
* code is released under a tri EPL/GPL/LGPL license. You can use it,
4+
* redistribute it and/or modify it under the terms of the:
5+
*
6+
* Eclipse Public License version 2.0, or
7+
* GNU General Public License version 2, or
8+
* GNU Lesser General Public License version 2.1.
9+
*/
10+
package org.truffleruby.language;
11+
12+
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
13+
import com.oracle.truffle.api.frame.VirtualFrame;
14+
import org.jcodings.specific.UTF8Encoding;
15+
import org.truffleruby.RubyContext;
16+
import org.truffleruby.core.rope.Rope;
17+
import org.truffleruby.core.string.RubyString;
18+
import org.truffleruby.core.string.StringOperations;
19+
import org.truffleruby.language.control.RaiseException;
20+
import org.truffleruby.parser.RubyDeferredWarnings;
21+
22+
import java.io.IOException;
23+
import java.nio.charset.StandardCharsets;
24+
25+
public class EmitWarningsNode extends RubyContextSourceNode {
26+
27+
private final RubyDeferredWarnings warnings;
28+
29+
public EmitWarningsNode(RubyDeferredWarnings warnings) {
30+
this.warnings = warnings;
31+
}
32+
33+
@Override
34+
public Object execute(VirtualFrame frame) {
35+
final RubyContext context = getContext();
36+
boolean isVerbose = context.getCoreLibrary().isVerbose();
37+
boolean warningsEnabled = context.getCoreLibrary().warningsEnabled();
38+
printWarnings(context, isVerbose, warningsEnabled);
39+
return nil;
40+
}
41+
42+
@TruffleBoundary
43+
private void printWarnings(RubyContext context, boolean isVerbose, boolean warningsEnabled) {
44+
for (RubyDeferredWarnings.WarningMessage warningMessage : warnings.warnings) {
45+
if (warningMessage.verbosity == RubyDeferredWarnings.Verbosity.VERBOSE) {
46+
if (isVerbose) {
47+
printWarning(context, warningMessage.message);
48+
}
49+
} else {
50+
if (warningsEnabled) {
51+
printWarning(context, warningMessage.message);
52+
}
53+
}
54+
}
55+
}
56+
57+
public static void printWarning(RubyContext context, String message) {
58+
if (context.getCoreLibrary().isLoaded()) {
59+
final Object warning = context.getCoreLibrary().warningModule;
60+
final Rope messageRope = StringOperations.encodeRope(message, UTF8Encoding.INSTANCE);
61+
final RubyString messageString = StringOperations
62+
.createString(context, context.getLanguageSlow(), messageRope);
63+
RubyContext.send(warning, "warn", messageString);
64+
} else {
65+
try {
66+
context.getEnv().err().write(message.getBytes(StandardCharsets.UTF_8));
67+
} catch (IOException e) {
68+
throw new RaiseException(context, context.getCoreExceptions().ioError(e, null));
69+
}
70+
}
71+
}
72+
}

src/main/java/org/truffleruby/parser/BodyTranslator.java

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@
4949
import org.truffleruby.core.regexp.EncodingCache;
5050
import org.truffleruby.core.regexp.InterpolatedRegexpNode;
5151
import org.truffleruby.core.regexp.MatchDataNodes.GetIndexNode;
52-
import org.truffleruby.core.regexp.RegexWarnCallback;
52+
import org.truffleruby.core.regexp.RegexWarnDeferredCallback;
5353
import org.truffleruby.core.regexp.RegexpNodes;
5454
import org.truffleruby.core.regexp.RegexpOptions;
5555
import org.truffleruby.core.regexp.RubyRegexp;
@@ -280,6 +280,7 @@ public class BodyTranslator extends Translator {
280280

281281
protected final BodyTranslator parent;
282282
protected final TranslatorEnvironment environment;
283+
private final RubyDeferredWarnings rubyWarnings;
283284

284285
public boolean translatingForStatement = false;
285286
private boolean translatingNextExpression = false;
@@ -292,10 +293,12 @@ public BodyTranslator(
292293
TranslatorEnvironment environment,
293294
Source source,
294295
ParserContext parserContext,
295-
Node currentNode) {
296+
Node currentNode,
297+
RubyDeferredWarnings rubyWarnings) {
296298
super(context, source, parserContext, currentNode);
297299
this.parent = parent;
298300
this.environment = environment;
301+
this.rubyWarnings = rubyWarnings;
299302
}
300303

301304
private static RubyNode[] createArray(int size) {
@@ -1032,7 +1035,8 @@ private RubyNode openModule(SourceIndexLength sourceSection, RubyNode defineOrGe
10321035
newEnvironment,
10331036
source,
10341037
parserContext,
1035-
currentNode);
1038+
currentNode,
1039+
rubyWarnings);
10361040

10371041
final ModuleBodyDefinitionNode definition = moduleTranslator
10381042
.compileClassNode(sourceSection, bodyNode, type);
@@ -1484,7 +1488,8 @@ protected RubyNode translateMethodDefinition(SourceIndexLength sourceSection, Ru
14841488
parserContext,
14851489
currentNode,
14861490
argsNode,
1487-
null);
1491+
null,
1492+
rubyWarnings);
14881493

14891494
return withSourceSection(sourceSection, new LiteralMethodDefinitionNode(
14901495
moduleNode,
@@ -1998,7 +2003,8 @@ private RubyNode translateBlockLikeNode(IterParseNode node, boolean isLambda) {
19982003
parserContext,
19992004
currentNode,
20002005
argsNode,
2001-
currentCallMethodName);
2006+
currentCallMethodName,
2007+
rubyWarnings);
20022008

20032009
if (isProc) {
20042010
methodCompiler.translatingForStatement = translatingForStatement;
@@ -2116,7 +2122,7 @@ public RubyNode visitMatch2Node(Match2ParseNode node) {
21162122
regexpNode.getOptions().toOptions(),
21172123
regexpNode.getEncoding(),
21182124
Syntax.RUBY,
2119-
new RegexWarnCallback(context));
2125+
new RegexWarnDeferredCallback(rubyWarnings));
21202126
final int numberOfNames = regex.numberOfNames();
21212127

21222128
if (numberOfNames > 0) {

src/main/java/org/truffleruby/parser/MethodTranslator.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,8 +84,9 @@ public MethodTranslator(
8484
ParserContext parserContext,
8585
Node currentNode,
8686
ArgsParseNode argsNode,
87-
String methodNameForBlock) {
88-
super(context, parent, environment, source, parserContext, currentNode);
87+
String methodNameForBlock,
88+
RubyDeferredWarnings rubyWarnings) {
89+
super(context, parent, environment, source, parserContext, currentNode, rubyWarnings);
8990
this.isBlock = isBlock;
9091
this.argsNode = argsNode;
9192
this.methodNameForBlock = methodNameForBlock;
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
/*
2+
***** BEGIN LICENSE BLOCK *****
3+
* Version: EPL 2.0/GPL 2.0/LGPL 2.1
4+
*
5+
* The contents of this file are subject to the Eclipse Public
6+
* License Version 2.0 (the "License"); you may not use this file
7+
* except in compliance with the License. You may obtain a copy of
8+
* the License at http://www.eclipse.org/legal/epl-v20.html
9+
*
10+
* Software distributed under the License is distributed on an "AS
11+
* IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or
12+
* implied. See the License for the specific language governing
13+
* rights and limitations under the License.
14+
*
15+
* Copyright (C) 2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
16+
* Copyright (C) 2002-2004 Anders Bengtsson <ndrsbngtssn@yahoo.se>
17+
* Copyright (C) 2002-2004 Jan Arne Petersen <jpetersen@uni-bonn.de>
18+
* Copyright (C) 2004 Thomas E Enebo <enebo@acm.org>
19+
*
20+
* Alternatively, the contents of this file may be used under the terms of
21+
* either of the GNU General Public License Version 2 or later (the "GPL"),
22+
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
23+
* in which case the provisions of the GPL or the LGPL are applicable instead
24+
* of those above. If you wish to allow use of your version of this file only
25+
* under the terms of either the GPL or the LGPL, and not to allow others to
26+
* use your version of this file under the terms of the EPL, indicate your
27+
* decision by deleting the provisions above and replace them with the notice
28+
* and other provisions required by the GPL or the LGPL. If you do not delete
29+
* the provisions above, a recipient may use your version of this file under
30+
* the terms of any one of the EPL, the GPL or the LGPL.
31+
***** END LICENSE BLOCK *****/
32+
package org.truffleruby.parser;
33+
34+
import java.util.ArrayList;
35+
import java.util.List;
36+
37+
import org.joni.WarnCallback;
38+
39+
public class RubyDeferredWarnings implements WarnCallback {
40+
41+
public List<WarningMessage> warnings = new ArrayList<>();
42+
43+
public enum Verbosity {
44+
VERBOSE, // -W2
45+
NON_VERBOSE // -W1
46+
}
47+
48+
public class WarningMessage {
49+
public final Verbosity verbosity;
50+
public final String message;
51+
52+
public WarningMessage(Verbosity verbosity, String message) {
53+
this.verbosity = verbosity;
54+
this.message = message;
55+
}
56+
}
57+
58+
@Override
59+
public void warn(String message) {
60+
warn(null, message);
61+
}
62+
63+
/** Prints a warning, unless $VERBOSE is nil. */
64+
public void warn(String fileName, int lineNumber, String message) {
65+
warnings.add(new WarningMessage(Verbosity.NON_VERBOSE, makeWarningMessage(fileName, lineNumber, message)));
66+
}
67+
68+
/** Prints a warning, unless $VERBOSE is nil. */
69+
public void warn(String fileName, String message) {
70+
StringBuilder buffer = new StringBuilder(100);
71+
if (fileName != null) {
72+
buffer.append(fileName).append(' ');
73+
}
74+
buffer.append("warning: ").append(message).append('\n');
75+
warnings.add(new WarningMessage(Verbosity.NON_VERBOSE, buffer.toString()));
76+
}
77+
78+
private String makeWarningMessage(String fileName, int lineNumber, String message) {
79+
StringBuilder buffer = new StringBuilder(100);
80+
buffer.append(fileName).append(':').append(lineNumber).append(": ");
81+
buffer.append("warning: ").append(message).append('\n');
82+
return buffer.toString();
83+
}
84+
85+
/** Prints a warning, only in verbose mode. */
86+
public void warning(String fileName, int lineNumber, String message) {
87+
warnings.add(new WarningMessage(Verbosity.VERBOSE, makeWarningMessage(fileName, lineNumber, message)));
88+
}
89+
90+
}

src/main/java/org/truffleruby/parser/TranslatorDriver.java

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@
5252
import org.truffleruby.core.kernel.KernelPrintLastLineNode;
5353
import org.truffleruby.core.module.RubyModule;
5454
import org.truffleruby.language.DataNode;
55+
import org.truffleruby.language.EmitWarningsNode;
5556
import org.truffleruby.language.LexicalScope;
5657
import org.truffleruby.language.RubyNode;
5758
import org.truffleruby.language.RubyRootNode;
@@ -165,6 +166,7 @@ public RubyRootNode parse(RubySource rubySource, ParserContext parserContext, St
165166
// Parse to the JRuby AST
166167

167168
final RootParseNode node;
169+
final RubyDeferredWarnings rubyWarnings = new RubyDeferredWarnings();
168170

169171
// Only use the cache while loading top-level core library files, as eval() later could use
170172
// the same Source name but should not use the cache. For instance,
@@ -177,7 +179,7 @@ public RubyRootNode parse(RubySource rubySource, ParserContext parserContext, St
177179
node = context.getMetricsProfiler().callWithMetrics(
178180
"parsing",
179181
source.getName(),
180-
() -> parseToJRubyAST(context, rubySource, staticScope, parserConfiguration));
182+
() -> parseToJRubyAST(context, rubySource, staticScope, parserConfiguration, rubyWarnings));
181183
printParseTranslateExecuteMetric("after-parsing", context, source);
182184
}
183185

@@ -253,7 +255,8 @@ public RubyRootNode parse(RubySource rubySource, ParserContext parserContext, St
253255
environment,
254256
source,
255257
parserContext,
256-
currentNode);
258+
currentNode,
259+
rubyWarnings);
257260

258261
final Memo<RubyNode> beginNodeMemo = new Memo<>(null);
259262
RubyNode truffleNode;
@@ -299,6 +302,12 @@ public RubyRootNode parse(RubySource rubySource, ParserContext parserContext, St
299302
Arrays.asList(BodyTranslator.initFlipFlopStates(environment, sourceIndexLength), truffleNode));
300303
}
301304

305+
if (!rubyWarnings.warnings.isEmpty()) {
306+
truffleNode = Translator.sequence(
307+
sourceIndexLength,
308+
Arrays.asList(new EmitWarningsNode(rubyWarnings), truffleNode));
309+
}
310+
302311
if (parserContext == ParserContext.TOP_LEVEL_FIRST && context.getOptions().GETS_LOOP) {
303312
if (context.getOptions().PRINT_LOOP) {
304313
truffleNode = Translator.sequence(
@@ -392,7 +401,7 @@ private String getMethodName(ParserContext parserContext, MaterializedFrame pare
392401
}
393402

394403
public static RootParseNode parseToJRubyAST(RubyContext context, RubySource rubySource, StaticScope blockScope,
395-
ParserConfiguration configuration) {
404+
ParserConfiguration configuration, RubyDeferredWarnings rubyWarnings) {
396405
LexerSource lexerSource = new LexerSource(rubySource, configuration.getDefaultEncoding());
397406
// We only need to pass in current scope if we are evaluating as a block (which
398407
// is only done for evals). We need to pass this in so that we can appropriately scope
@@ -401,11 +410,18 @@ public static RootParseNode parseToJRubyAST(RubyContext context, RubySource ruby
401410
configuration.parseAsBlock(blockScope);
402411
}
403412

404-
RubyParser parser = new RubyParser(context, lexerSource, new RubyWarnings(configuration.getContext()));
413+
RubyParser parser = new RubyParser(context, lexerSource, rubyWarnings);
405414
RubyParserResult result;
406415
try {
407416
result = parser.parse(configuration);
408417
} catch (SyntaxException e) {
418+
if (!rubyWarnings.warnings.isEmpty()) {
419+
if (context != null) {
420+
for (RubyDeferredWarnings.WarningMessage warningMessage : rubyWarnings.warnings) {
421+
EmitWarningsNode.printWarning(context, warningMessage.message);
422+
}
423+
}
424+
}
409425
switch (e.getPid()) {
410426
case UNKNOWN_ENCODING:
411427
case NOT_ASCII_COMPATIBLE:

0 commit comments

Comments
 (0)