| /*************************************************************************** |
| * |
| * diagnostic.cpp |
| * |
| * $Id$ |
| * |
| *************************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed |
| * with this work for additional information regarding copyright |
| * ownership. The ASF licenses this file to you under the Apache |
| * License, Version 2.0 (the "License"); you may not use this file |
| * except in compliance with the License. You may obtain a copy of |
| * the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or |
| * implied. See the License for the specific language governing |
| * permissions and limitations under the License. |
| * |
| * Copyright 2001-2006 Rogue Wave Software. |
| * |
| **************************************************************************/ |
| |
| #include "diagnostic.h" |
| #include "scanner.h" // for Scanner::token_t |
| #include "loc_exception.h" |
| |
| #include <cstdarg> // for va_list, ... |
| #include <cstdio> // for puts(), fprintf(), ... |
| #include <set> // for set |
| |
| |
| // set of disabled warnings |
| static std::set<int> disabled; |
| |
| static bool warn = true; // warnings (on by default) |
| static bool info = false; // info messages (off by default) |
| |
| |
| // write a warning or error message to standard output. If it is a warning |
| // that is issued and that warning has not been disabled then return true. |
| bool issue_diag (int type, bool, const Scanner::token_t *token, |
| const char *fmt, ...) |
| { |
| bool enabled = false; |
| |
| if (0 == fmt) { |
| |
| // special treatment when format string is 0: a request |
| // to enable or disable this type of diagnostic, e.g., |
| // in response to a command line option |
| |
| if (W_DISABLE == type) { |
| // disable all warnings |
| enabled = warn; |
| warn = false; |
| } |
| else if (I_ENABLE == type) { |
| // enable all informational messages |
| enabled = info; |
| info = true; |
| } |
| else { |
| // disable a specific warning and return its previous |
| // setting (i.e., enabled or disabled) |
| enabled = disabled.find (type) == disabled.end (); |
| disabled.insert (type); |
| } |
| |
| return enabled; |
| } |
| |
| const bool is_info = I_FIRST <= type && type <= I_LAST; |
| const bool is_warn = !is_info && W_FIRST <= type && type <= W_LAST; |
| const bool is_error = !is_info && !is_warn; |
| |
| if (is_warn && (!warn || disabled.end () != disabled.find (type))) { |
| // warning disabled |
| return enabled; |
| } |
| |
| if (is_info && !info) { |
| // info disabled |
| return enabled; |
| } |
| |
| // all errors and those warnings that are not disabled |
| // must be issued |
| enabled = true; |
| |
| if (token && token->file) |
| std::fprintf (stderr, "%s:%d: ", token->file, token->line); |
| |
| if (is_error) |
| std::fprintf (stderr, "Error %-3d: ", type); |
| else if (is_warn) |
| std::fprintf (stderr, "Warning %-3d: ", type); |
| else |
| std::fprintf (stderr, "Note %-3d: ", type); |
| |
| // get the variable sized argument and pass it to vfprintf |
| // to be printed |
| std::va_list va; |
| va_start (va, fmt); |
| std::vfprintf (stderr, fmt, va); |
| va_end (va); |
| |
| // if the token pointer is non-zero, find the file and line |
| // the token appears on and print it out, followed by a line |
| // underscoring the token that caused the diagnostic with |
| // a string of carets ('^') |
| |
| std::FILE* const ftok = token ? std::fopen (token->file, "r") : 0; |
| |
| if (ftok) { |
| int i; |
| char line [1024]; // FIXME: handle longer lines |
| |
| // advance to the specified line in the file |
| for (i = 0; i < token->line; ++i) { |
| if (0 == std::fgets (line, 1024, ftok)) { |
| *line = '\0'; |
| break; |
| } |
| } |
| |
| if (i == token->line && '\0' != *line) { |
| std::fputs ("\t\t", stderr); |
| std::fputs (line, stderr); |
| std::fputs ("\t\t", stderr); |
| |
| // tok->col is the column number where the first character |
| // in the token begins. Go through the line saving tabs |
| // so that the '^' will line up with the token |
| |
| for (i = 0; i < token->column; ++i) |
| std::fputc (line [i] == '\t' ? '\t' : ' ', stderr); |
| |
| for (unsigned j = 0; j < token->name.size (); ++j) |
| std::fputc ('^', stderr); |
| |
| std::fputc ('\n', stderr); |
| } |
| |
| std::fclose (ftok); |
| } |
| |
| if (is_error) { |
| // throw an exception if the diagnostic is a hard error |
| throw loc_exception (); |
| } |
| |
| // return otherwise (i.e., the diagnostic is not an error) |
| return enabled; |
| } |