On 10/12/18, David Malcolm <dmalc...@redhat.com> wrote:
> Here's a proposed "User Experience Guidelines" section for our
> internals manual
>
> It's a mixture of proposed policy, together with notes on how to
> implement the recommendations.
>
> Thoughts?

I have no comments on the actual contents of the patch, just that it's
a good idea to have such a section in general, and I hope it's
approved!

Eric

>
> gcc/ChangeLog:
>       * Makefile.in (TEXI_GCCINT_FILES): Add ux.texi.
>       * doc/gccint.texi: Include ux.texi and use it in top-level menu.
>       * doc/ux.texi: New file.
> ---
>  gcc/Makefile.in     |   2 +-
>  gcc/doc/gccint.texi |   2 +
>  gcc/doc/ux.texi     | 455
> ++++++++++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 458 insertions(+), 1 deletion(-)
>  create mode 100644 gcc/doc/ux.texi
>
> diff --git a/gcc/Makefile.in b/gcc/Makefile.in
> index 70efab7..3f05e95 100644
> --- a/gcc/Makefile.in
> +++ b/gcc/Makefile.in
> @@ -3176,7 +3176,7 @@ TEXI_GCCINT_FILES = gccint.texi gcc-common.texi
> gcc-vers.texi         \
>        gnu.texi gpl_v3.texi fdl.texi contrib.texi languages.texi      \
>        sourcebuild.texi gty.texi libgcc.texi cfg.texi tree-ssa.texi   \
>        loop.texi generic.texi gimple.texi plugins.texi optinfo.texi   \
> -      match-and-simplify.texi poly-int.texi
> +      match-and-simplify.texi ux.texi poly-int.texi
>
>  TEXI_GCCINSTALL_FILES = install.texi install-old.texi fdl.texi               
> \
>        gcc-common.texi gcc-vers.texi
> diff --git a/gcc/doc/gccint.texi b/gcc/doc/gccint.texi
> index 1a1af41..2554b31 100644
> --- a/gcc/doc/gccint.texi
> +++ b/gcc/doc/gccint.texi
> @@ -125,6 +125,7 @@ Additional tutorial information is linked to from
>  * LTO::             Using Link-Time Optimization.
>
>  * Match and Simplify:: How to write expression simplification patterns for
> GIMPLE and GENERIC
> +* User Experience Guidelines:: Guidelines for implementing diagnostics and
> options.
>  * Funding::         How to help assure funding for free software.
>  * GNU Project::     The GNU Project and GNU/Linux.
>
> @@ -162,6 +163,7 @@ Additional tutorial information is linked to from
>  @include plugins.texi
>  @include lto.texi
>  @include match-and-simplify.texi
> +@include ux.texi
>
>  @include funding.texi
>  @include gnu.texi
> diff --git a/gcc/doc/ux.texi b/gcc/doc/ux.texi
> new file mode 100644
> index 0000000..87ff599
> --- /dev/null
> +++ b/gcc/doc/ux.texi
> @@ -0,0 +1,455 @@
> +@c Copyright (C) 2018 Free Software Foundation, Inc.
> +@c Free Software Foundation, Inc.
> +@c This is part of the GCC manual.
> +@c For copying conditions, see the file gcc.texi.
> +
> +@node User Experience Guidelines
> +@chapter User Experience Guidelines
> +@cindex User Experience Guidelines
> +@cindex Guidelines, User Experience
> +
> +To borrow a slogan from
> + @uref{https://elm-lang.org/blog/compilers-as-assistants, Elm},
> +
> +@quotation
> +@strong{Compilers should be assistants, not adversaries.}  A compiler
> should
> +not just detect bugs, it should then help you understand why there is a
> bug.
> +It should not berate you in a robot voice, it should give you specific
> hints
> +that help you write better code. Ultimately, a compiler should make
> +programming faster and more fun!
> +@author Evan Czaplicki
> +@end quotation
> +
> +This chapter provides guidelines on how to implement diagnostics and
> +command-line options in ways that we hope achieve the above ideal.
> +
> +@menu
> +* Guidelines for Diagnostics::       How to implement diagnostics.
> +* Guidelines for Options::           Guidelines for command-line options.
> +@end menu
> +
> +
> +@node Guidelines for Diagnostics
> +@cindex Guidelines for Diagnostics
> +@cindex Diagnostics, Guidelines for
> +
> +@section Guidelines for Diagnostics
> +
> +@subsection Talk in terms of the user's code
> +
> +Diagnostics should be worded in terms of the user's source code, and the
> +source language, rather than GCC's own implementation details.
> +
> +@subsection Diagnostics are ``actionable''
> +
> +A good diagnostic is @emph{actionable}: it should assist the user in
> +taking action.
> +
> +Consider what an end-user will want to do when encountering a diagnostic.
> +
> +Given an error, an end-user will think: ``How do I fix this?''
> +
> +Given a warning, an end-user will want to review the warning and think:
> +
> +@itemize @bullet
> +@item
> +``Is this a ``true'' result?''
> +@item
> +``Do I care?''
> +@item
> +if they decide it's genuine: ``How do I fix this?''
> +@end itemize
> +
> +A good diagnostic provides pertinent information to allow the user to
> +easily answer the above questions.
> +
> +@subsection The user's attention is important
> +
> +A perfect compiler would issue a warning on every aspect of the user's
> +source code that ought to be fixed, and issue no other warnings.
> +Naturally, this ideal is impossible to achieve.
> +
> +Warnings should have a good ``signal:noise ratio'': we should have few
> +``false positives'' (falsely issuing a warning when no warning is
> +warranted) and few ``false negatives'' (failing to issue a warning when
> +one @emph{is} justified).
> +
> +Note that a ``false positive'' can mean, in practice, a warning that the
> +user doesn't agree with.  Ideally a diagnostic should contain enough
> +information to allow the user to make an informed choice about whether
> +they should care (and how to fix it), but a balance must be drawn against
> +overloading the user with irrelevant data.
> +
> +It's worth testing a new warning on many instances of real-world code,
> +written by different people, and seeing what it complains about, and
> +what it doesn't complain about.  This may suggest heuristics that
> +silence common false positives.
> +
> +@subsection Make mismatches clear
> +
> +Many diagnostics relate to a mismatch between two different places in the
> +user's source code.  Examples include:
> +@itemize @bullet
> +  @item
> +  a type mismatch, where the type at a usage site does not match the type
> +  at a declaration
> +
> +  @item
> +  the argument count at a call site does not match the parameter count
> +  at the declaration
> +
> +  @item
> +  something is erroneously duplicated (e.g. an error, due to breaking a
> +  uniqueness requirement, or a warning, if it's suggestive of a bug)
> +
> +  @item
> +  an ``opened'' syntactic construct (such as an open-parenthesis) is not
> +  closed
> +
> +  @c TODO: more examples?
> +@end itemize
> +
> +In each case, the diagnostic should indicate @strong{both} pertinent
> +locations (so that the user can easily see the problem and how to fix it).
> +
> +The standard way to do this is with a note (via @code{inform}).  For
> +example:
> +
> +@smallexample
> +  auto_diagnostic_group d;
> +  if (warning_at (loc, OPT_Wduplicated_cond,
> +                  "duplicated %<if%> condition"))
> +    inform (EXPR_LOCATION (t), "previously used here");
> +@end smallexample
> +
> +For cases involving punctuation where the locations might be near
> +each other, they can be conditionally consolidated via
> +@code{gcc_rich_location::add_location_if_nearby}:
> +
> +@smallexample
> +    auto_diagnostic_group d;
> +    gcc_rich_location richloc (primary_loc);
> +    bool added secondary = richloc.add_location_if_nearby (secondary_loc);
> +    error_at (&richloc, "main message");
> +    if (!added secondary)
> +      inform (secondary_loc, "message for secondary");
> +@end smallexample
> +
> +This will emit either one diagnostic with two locations:
> +@smallexample
> +  demo.c:42:10: error: main message
> +    (foo)
> +    ~   ^
> +@end smallexample
> +
> +or two diagnostics:
> +
> +@smallexample
> +  demo.c:42:4: error: main message
> +    foo)
> +       ^
> +  demo.c:40:2: note: message for secondary
> +    (
> +    ^
> +@end smallexample
> +
> +@subsection Location Information
> +
> +GCC's @code{location_t} type can support both ``ordinary'' locations,
> +and locations relating to a macro expansion.
> +
> +As of GCC 6, ordinary locations changed from supporting just a
> +point in the user's source code to supporting three points: the
> +``caret'' location, plus a start and a finish:
> +
> +@smallexample
> +      a = foo && bar;
> +          ~~~~^~~~~~
> +          |   |    |
> +          |   |    finish
> +          |   caret
> +          start
> +@end smallexample
> +
> +Tokens coming out of libcpp have locations of the form ``caret == start'',
> +such as for ``foo'' here:
> +
> +@smallexample
> +      a = foo && bar;
> +          ^~~
> +          | |
> +          | finish
> +          caret == start
> +@end smallexample
> +
> +Compound expressions should be reported using the location of the
> +expression as a whole, rather than just of one token within it.
> +
> +For example, in @code{-Wformat}, rather than underlining just the first
> +token of a bad argument:
> +
> +@smallexample
> +   printf("hello %i %s", (long)0, "world);
> +                 ~^      ~
> +                 %li
> +@end smallexample
> +
> +the whole of the expression should be underlined, so that the user can
> +easily identify what is being referred to:
> +
> +@smallexample
> +   printf("hello %i %s", (long)0, "world);
> +                 ~^      ~~~~~~~
> +                 %li
> +@end smallexample
> +
> +@c this was r251239
> +
> +Avoid using the @code{input_location} global, and the diagnostic functions
> +that implicitly use it - use @code{error_at} and @code{warning_at} rather
> +than @code{error} and @code{warning}.
> +
> +@c TODO labelling of ranges
> +
> +@subsection Coding Conventions
> +
> +See the @uref{https://gcc.gnu.org/codingconventions.html#Diagnostics,
> +diagnostics section} of the GCC coding conventions.
> +
> +In the C++ frontend, when comparing two types in a message, use @code{%H}
> +and @code{%I} rather tha @code{%T}, as this allows the diagnostics
> +subsystem to highlight differences between template-based types.
> +
> +Use @code{auto_diagnostic_group} when issuing multiple related
> +diagnostics (seen in various examples on this page).  This informs the
> +diagnostic subsystem that all diagnostics issued within the lifetime
> +of the @code{auto_diagnostic_group} are related.  (Currently it doesn't
> +do anything with this information, but we may implement that in the
> +future).
> +
> +@subsection Spelling and Terminology
> +
> +See the @uref{https://gcc.gnu.org/codingconventions.html#Spelling
> +Spelling, terminology and markup} section of the GCC coding conventions.
> +
> +@subsection Tense of messages
> +Syntax errors occur in the present tense e.g.
> +
> +@smallexample
> +error_at (loc, "cannot convert %qH to %qI");
> +@end smallexample
> +
> +and thus
> +
> +@smallexample
> +// CORRECT: usage of present tense:
> +error: cannot convert 'int' to 'void *'
> +@end smallexample
> +
> +rather than
> +
> +@smallexample
> +// BAD: usage of past tense:
> +error: could not convert 'int' to 'void *'
> +@end smallexample
> +
> +Predictions about run-time behavior should be described in the future tense
> e.g.
> +
> +@smallexample
> +warning_at (loc, OPT_some_warning,
> +            "this code will read past the end of the array");
> +@end smallexample
> +
> +@subsection Fix-it hints
> +
> +GCC's diagnostic subsystem can emit ``fix-it hints'': small suggested
> +edits to the user's source code.
> +
> +They are printed by default underneath the code in question.  They
> +can also be viewed via @option{-fdiagnostics-generate-patch} and
> +@option{-fdiagnostics-parseable-fixits}.  With the latter, an IDE
> +ought to be able to offer to automatically apply the suggested fix.
> +
> +Fix-it hints can be added to a diagnostic by using a @code{rich_location}
> +rather than a @code{location_t} - the fix-it hints are added to the
> +@code{rich_location} using one of the various @code{add_fixit} member
> +functions of @code{rich_location}.  They are documented with
> +@code{rich_location} in @file{libcpp/line-map.h}.
> +It's easiest to use the @code{gcc_rich_location} subclass of
> +@code{rich_location} found in @file{gcc-rich-location.h}.
> +
> +For example:
> +
> +@smallexample
> +   if (const char *suggestion = hint.suggestion ())
> +     @{
> +       gcc_rich_location richloc (location);
> +       richloc.add_fixit_replace (suggestion);
> +       error_at (&richloc,
> +                 "%qE does not name a type; did you mean %qs?",
> +                 id, suggestion);
> +     @}
> +@end smallexample
> +
> +which can lead to:
> +
> +@smallexample
> +spellcheck-typenames.C:73:1: error: 'singed' does not name a type; did you
> mean 'signed'?
> +73 | singed char ch;
> +   | ^~~~~~
> +   | signed
> +@end smallexample
> +
> +Non-trivial edits can be built up by adding multiple fix-it hints to one
> +@code{rich_location}.  It's best to express the edits in terms of the
> +locations of individual tokens.  Various handy functions for adding
> +fix-it hints for idiomatic C and C++ can be seen in
> +@file{gcc-rich-location.h}.
> +
> +@subsubsection Fix-it hints should be ``applyable''
> +
> +When implementing a fix-it hint, please verify that the suggested edit
> +leads to fixed, compilable code.  (Unfortunately, this currently must be
> +done by hand using @option{-fdiagnostics-generate-patch}.  It would be
> +good to have an automated way of verifying that fix-it hints actually fix
> +the code).
> +
> +For example, a ``gotcha'' here is to forget to add a space when adding a
> +missing reserved word.  Consider a C++ fix-it hint that adds
> +@code{typename} in front of a template declaration.  A naive way to
> +implement this might be:
> +
> +@smallexample
> +gcc_rich_location richloc (loc);
> +// BAD: insertion is missing a trailing space
> +richloc.add_fixit_insert_before ("typename");
> +error_at (&richloc, "need %<typename%> before %<%T::%E%> because "
> +                     "%qT is a dependent scope",
> +                     parser->scope, id, parser->scope);
> +@end smallexample
> +
> +When applied to the code, this might lead to:
> +
> +@smallexample
> +T::type x;
> +@end smallexample
> +
> +being ``corrected'' to:
> +
> +@smallexample
> +typenameT::type x;
> +@end smallexample
> +
> +In this case, the correct thing to do is to add a trailing space after
> +@code{typename}:
> +
> +@smallexample
> +gcc_rich_location richloc (loc);
> +// OK: note that here we have a trailing space
> +richloc.add_fixit_insert_before ("typename ");
> +error_at (&richloc, "need %<typename%> before %<%T::%E%> because "
> +                     "%qT is a dependent scope",
> +                     parser->scope, id, parser->scope);
> +@end smallexample
> +
> +leading to this corrected code:
> +
> +@smallexample
> +typename T::type x;
> +@end smallexample
> +
> +@subsubsection Express deletion in terms of deletion, not replacement
> +
> +It's best to express deletion suggestions in terms of deletion fix-it
> +hints, rather than replacement fix-it hints.  For example, consider this:
> +
> +@smallexample
> +    auto_diagnostic_group d;
> +    gcc_rich_location richloc (location_of (retval));
> +    tree name = DECL_NAME (arg);
> +    richloc.add_fixit_replace (IDENTIFIER_POINTER (name));
> +    warning_at (&richloc, OPT_Wredundant_move,
> +                "redundant move in return statement");
> +@end smallexample
> +
> +which is intended to e.g. replace a @code{std::move} with the underlying
> +value:
> +
> +@smallexample
> +   return std::move (retval);
> +          ~~~~~~~~~~^~~~~~~~
> +          retval
> +@end smallexample
> +
> +where the change has been expressed as replacement, replacing
> +with the name of the declaration.
> +
> +This works for simple cases, but consider this case:
> +
> +@smallexample
> +#ifdef SOME_CONFIG_FLAG
> +# define CONFIGURY_GLOBAL global_a
> +#else
> +# define CONFIGURY_GLOBAL global_b
> +#endif
> +
> +int fn ()
> +@{
> +  return std::move (CONFIGURY_GLOBAL /* some comment */);
> +@}
> +@end smallexample
> +
> +The above implementation will erroneously strip out the macro and the
> +comment in the fix-it hint:
> +
> +@smallexample
> +   return std::move (CONFIGURY_GLOBAL /* some comment */);
> +          ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +          global_a
> +@end smallexample
> +
> +and thus this resulting code:
> +
> +@smallexample
> +   return global_a;
> +@end smallexample
> +
> +It's better to do deletions in terms of deletions; deleting the
> +@code{std::move (} and the trailing close-paren, leading to
> +this:
> +
> +@smallexample
> +   return std::move (CONFIGURY_GLOBAL /* some comment */);
> +          ~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> +          CONFIGURY_GLOBAL /* some comment */
> +@end smallexample
> +
> +and thus this result:
> +
> +@smallexample
> +   return CONFIGURY_GLOBAL /* some comment */;
> +@end smallexample
> +
> +Unfortunately, the pertinent @code{location_t} values are not always
> +available.
> +
> +@c the above was https://gcc.gnu.org/ml/gcc-patches/2018-08/msg01474.html
> +
> +@subsubsection Multiple suggestions
> +
> +In the rare cases where you need to suggest more than one mutually
> +exclusive solution to a problem, this can be done by emitting
> +multiple notes and calling
> +@code{rich_location::fixits_cannot_be_auto_applied} on each note's
> +@code{rich_location}.  If this is called, then the fix-it hints in
> +the @code{rich_location} will be printed, but will not be added to
> +generated patches.
> +
> +
> +@node Guidelines for Options
> +@cindex Options, Guidelines for
> +@cindex Guidelines for Options
> +
> +@section Guidelines for Options
> +
> +@c TODO
> --
> 1.8.5.3
>
>

Reply via email to